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

[WIP] PR: Add multi-cursor editing to codeeditor #22996

Draft
wants to merge 61 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?
  • 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
  • Documentation on multi-cursor usage (hints on settings toggle tooltip?)
  • Evaluate all shortcuts (add multi-cursor handling, only handle primary cursor, or disable when multi-cursor):
    • code completion
    • duplicate line up/down
    • delete line
    • move line up/down
    • go to new line/line number/definition (from cursor)/next, previous cell
    • toggle comment
    • create new cell
    • (un)blockcomment
    • transform to upper/lowercase
    • (un)indent
    • start/end of document
    • start/end, prev/next line
    • prev/next char/word
    • next/prev warning
    • killring
    • undo/redo
    • cut/copy/paste
    • delete
    • select all
    • docstring
    • autoformatting
    • scroll line up/down
    • enter array inline/table
    • inspect current object
    • last edit location
    • next/prev cursor position
    • Run Cell (and advance) (in debugger)
    • Run Selection (and advance) (from line) (in debugger) (up to line)

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
…line length.

Also revert to builtin cursor rendering for text dragging with single cursor.
@pep8speaks
Copy link

pep8speaks commented Nov 16, 2024

Hello @athompson673! Thanks for updating this PR. We checked the lines you've touched for PEP 8 issues, and found:

Line 39:1: E302 expected 2 blank lines, found 1
Line 61:1: E305 expected 2 blank lines after class or function definition, found 1

Comment last updated at 2024-12-03 00:01:06 UTC

@athompson673 athompson673 changed the title Add multi-cursor editing to codeeditor [WIP] PR: Add multi-cursor editing to codeeditor Nov 16, 2024
handle_multi_cursor_keypress no longer an event reciever, instead directly called from keyPressEvent. This way we can cleanly emit sig_key_pressed for each cursor and respond to event.isAccepted. This implements ignoring keystrokes on folded lines.
… text

similar to edits for delete function, emit a signal for each cursor prior to changing text as this prevents an out of order edit problem with code folding.
The delayed update model with the LSP makes it difficult to encapsulate the changes with a simple call to for_each_cursor. Future implementation might edit lsp_mixin?
@athompson673
Copy link
Contributor Author

I am running into difficulty figuring out how to handle folded code blocks with the move/duplicate line up/down functions. It seems difficult to prevent edge cases from breaking the code folding state. Does anyone have experience with that system?

Enter key handling gives us automatic colon insertion and proper auto whitespace. Copied from single cursor key handling and removed extraneous edit block commands as well as completion handling (don't do completion with multi-cursor).
working with a few known bugs. bugs triggered by actions that should be illogical to the user (such as trying to move a line with multiple cursors, or trying to move multiple lines against the end of the file.)
copied impl from single key handler (should key handling funcs be merged at some point?)
initial commit to get tests under way. can't figure out qtbot.mousePress at the moment. CodeEditor.mousePressEvent is not called as far as I can tell.
text color and depreciation warning fix
@athompson673
Copy link
Contributor Author

athompson673 commented Dec 2, 2024

@ccordoba12 any idea why the starter test file I wrote won't seem to send a mousePress event? I didn't think I'd hit a brick wall quite so soon in trying to start writing my own tests...

It fails on the assert bool(code_editor.extra_cursors) and adding a print statement in CodeEditor.mousePressEvent doesn't show standard output in pytest.

@ccordoba12
Copy link
Member

I am running into difficulty figuring out how to handle folded code blocks with the move/duplicate line up/down functions. It seems difficult to prevent edge cases from breaking the code folding state. Does anyone have experience with that system?

I am the one with more experience in that system. What's exactly the problem you're seeing?

@ccordoba12 any idea why the starter test file I wrote won't seem to send a mousePress event? I didn't think I'd hit a brick wall quite so soon in trying to start writing my own tests...

It seems mousePress requires to use as widget the editor's viewport, like this:

with qtbot.waitSignal(editor.sig_alt_left_mouse_pressed, raising=True):
qtbot.mousePress(editor.viewport(), Qt.LeftButton,
Qt.AltModifier, QPoint(w//2, h//2))

Have you also tried mouseClick?

@athompson673
Copy link
Contributor Author

athompson673 commented Dec 2, 2024

I am running into difficulty figuring out how to handle folded code blocks with the move/duplicate line up/down functions. It seems difficult to prevent edge cases from breaking the code folding state. Does anyone have experience with that system?

I am the one with more experience in that system. What's exactly the problem you're seeing?

I think I got to an acceptable solution with move_line_up and move_line_down, but duplicating a folded line up or down breaks the folding state.

Example: duplicate folded line

Media1.mp4

@ccordoba12 any idea why the starter test file I wrote won't seem to send a mousePress event? I didn't think I'd hit a brick wall quite so soon in trying to start writing my own tests...

It seems mousePress requires to use as widget the editor's viewport, like this:

with qtbot.waitSignal(editor.sig_alt_left_mouse_pressed, raising=True):
qtbot.mousePress(editor.viewport(), Qt.LeftButton,
Qt.AltModifier, QPoint(w//2, h//2))

Have you also tried mouseClick?

I started with mouseClick and was trying several things.. viewport() seems to have worked, Thanks! (though now it would seem I'm calculating the position of the desired click wrong nvm fixed)

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