Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PR: Add multi-cursor editing to codeeditor #22996

Open
wants to merge 86 commits into
base: master
Choose a base branch
from

Conversation

athompson673
Copy link
Contributor

@athompson673 athompson673 commented Nov 16, 2024

Description of Changes

Multi-line editing has been a feature I've missed having in spyder for a long time. I frequently have a notepad++ scratchpad alongside spyder simply to copy blocks of text back and forth so that I can use the column cursor. I haven't been able to contribute money to the spyder project despite using it for about a decade now, but I can hopefully at least contribute some time.

I am an engineer, not a programmer so please criticize my work so we can make it better :)

I decided to implement the functionality directly inside the codeeditor.CodeEditor class as making it an editor extension would still require a great many edits to CodeEditor anyway. The basic implementation is to hide the original cursor when there are multiples, and manually draw the primary cursor and all extras on a timer. For many editor functions a for_each_cursor wrapper does a lot of heavy lifting. Other functions need to be slightly tweaked directly. I think it is not particularly productive to try to implement code completion (snippets, call-tips, etc) during multi-cursor sessions, so I attempt to disable those functions appropriately.

These are some deep changes to one of the most complicated parts of Spyder, so I will naturally need some help getting this code to a state where it's ready to be integrated. I know it is not yet fully ready, but I wanted to get the PR in so that more people could see it and maybe get interested in testing it.

Media1.mp4
TODO list:
  • Ctrl-Alt click to add/remove cursor
  • Ctrl-Alt-Shift click to add column cursor
  • Add multi-cursor toggle to editor preferences
  • Add selection highlight color to theme preferences? (not now. hardcoded.)
  • Handle cursors moving into folded code
  • Render multiple cursors and selections
  • render cursor when dragging text selection to new position
  • Render overtype cursors (insert)
  • Arrow keys, Home/End cursor movement
  • Backspace / Delete
  • Text typing
  • Undo / Redo
  • Cut Copy Paste
  • Smart insertion of characters (colons, quotes, parenthesis, etc..)
  • Smart backspace
  • Automatic whitespace insertion
  • Cursor position history
  • Last edit position
  • How to deal with auto-completions, call tips, snippets, etc. (disable them basically)
  • Write many tests
  • Evaluate all shortcuts (add multi-cursor handling, only handle primary cursor, or disable when multi-cursor):
    • code completion (disabled)
    • duplicate line up/down (handles multi-cursor)
    • delete line (handles multi-cursor)
    • move line up/down (handles multi-cursor)
    • go to new line (handles multi-cursor)
    • line number/definition (from cursor)/next, previous cell (clears multi-cursor)
    • toggle comment (handles multi-cursor)
    • create new cell (handles multi-cursor)
    • (un)blockcomment (handles multi-cursor)
    • transform to upper/lowercase (handles multi-cursor)
    • (un)indent (handles multi-cursor)
    • start/end of document (clears multi-cursor)
    • start/end, prev/next line (handles multi-cursor)
    • prev/next char/word (handles multi-cursor)
    • next/prev warning (clears multi-cursor)
    • killring (disabled)
    • undo/redo (handles multi-cursor)
    • cut/copy/paste (handles multi-cursor)
    • delete (handles multi-cursor)
    • select all (clears multi-cursor)
    • docstring (handles multi-cursor)
    • autoformatting (clears multi-cursor)
    • scroll line up/down (not applicable)
    • enter array inline/table (clears multi-cursor)
    • inspect current object (handles main cursor)
    • last edit location (handles multi-cursor)
    • next/prev cursor position (handles multi-cursor)
    • Run Cell (and advance) (in debugger) (handles main cursor)
    • Run Selection (and advance) (from line) (in debugger) (up to line) (handles main cursor)

Issue(s) Resolved

Fixes #2112

Affirmation

By submitting this Pull Request or typing my (user)name below,
I affirm the Developer Certificate of Origin
with respect to all commits and content included in this PR,
and understand I am releasing the same under Spyder's MIT (Expat) license.

I certify the above statement is true and correct: athompson673

Required manually tracking overwriteMode and leaving it disabled except for during keyEvent. This might be a fragile solution...
…to new line, and clears extra_cursors on goto_definition
@athompson673
Copy link
Contributor Author

I think I got a workable solution for the duplicate line up/down finally. I checked over the compatibility of all the variants of "run cell" with multi cursor, and calling clear_extra_cursors in go_to_next_cell and go_to_previous_cell handles possible cursor collisions well enough. I can't however find how "run selection and advance" moves the cursor after execution. This can cause a situation where two cursors overlap and are not merged. This is not a great situation as the overlapping cursors are invisible (cursor draw inverts screen color twice to be the same as no draw). The next text to be inserted will be inserted twice at the position of the double cursor, then the cursors will merge.

@ccordoba12
Copy link
Member

I can't however find how "run selection and advance" moves the cursor after execution.

That's done here:

if extra_action_name == ExtraAction.Advance and not (eol in text):
editorstack.advance_line()

@athompson673
Copy link
Contributor Author

athompson673 commented Dec 4, 2024

Regarding the killring... This is a feature I've never used (in spyder or otherwise). I'm tempted to say it is not useful to handle multi-cursor here. It would be possible to kill/yank in existing cursor order (order of creation) or in position sorted order, but that quickly becomes quite a lot to mentally keep track of. It would be possible to simply ignore the extra_cursors and only handle the main textCursor as it is at least indicated by the line highlight. Or I could apply the restrict_single_cursor wrapper to prevent using these functions while multiple cursors are active.

Any thoughts?

As I near the end of my checklist, I also want your input on selection foreground and background color. As far as I can tell it is fixed (not related to selected theme). I had to initially just pick a color, so I just copied the pixel color from a screenshot. It seems to me this may be a different color on different platforms (linux & mac), but I don't have any systems to test on. It wouldn't look nice if the main cursor and extra cursors had different text selection colors. Currently the native Qt paint event handles drawing the selection of the primary cursor, and I manually draw the selections of extra cursors using text decorations.

I personally lean towards just hard-coding the colors, but they could also be included with the syntax highlighting theme colors. What do you think?

@ccordoba12
Copy link
Member

Regarding the killring... This is a feature I've never used (in spyder or otherwise). I'm tempted to say it is not useful to handle multi-cursor here. It would be possible to kill/yank in existing cursor order (order of creation) or in position sorted order, but that quickly becomes quite a lot to mentally keep track of. It would be possible to simply ignore the extra_cursors and only handle the main textCursor as it is at least indicated by the line highlight. Or I could apply the restrict_single_cursor wrapper to prevent using these functions while multiple cursors are active.

Any thoughts?

I agree with you, we shouldn't worry about handling multiple cursors for the killring functionality. If users ask about it later, we could consider implementing it, but not for the initial multicursor support.

I personally lean towards just hard-coding the colors, but they could also be included with the syntax highlighting theme colors. What do you think?

Please hard-code them for now. We can review them in a follow-up PR.

@athompson673 athompson673 marked this pull request as ready for review December 6, 2024 19:35
somehow block.layout may be invalid while block.isVisible
@athompson673
Copy link
Contributor Author

@ccordoba12 I'm not sure what the 3 failing tests are about, but I'd say this monstrosity is finally ready for its first review. I've enjoyed this deep dive into spyder, and I hope it can be useful to someone :)

@athompson673 athompson673 changed the title [WIP] PR: Add multi-cursor editing to codeeditor PR: Add multi-cursor editing to codeeditor Dec 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add multiline editing to the Editor
3 participants