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

Refactor CommandBuilder, arguments, etc. #979

Merged
merged 29 commits into from
Aug 30, 2024

Conversation

citizenmatt
Copy link
Member

@citizenmatt citizenmatt commented Aug 28, 2024

This PR is a fairly thorough refactoring of CommandBuilder, Argument and OperatorArguments. It might be easier to review the commits separately, as they're fairly small and self-contained, while the final diff of CommandBuilder might be too messy to see what's changed.

The idea of this PR is to tighten up the API around building a command, mostly to try to avoid confusion on concepts such as count. Previously, there were at least three ways to get the count for command being executed. Each Command had a count, and if the command was an operator+motion (such as dw) then the motion would also be a Command and have its own count. Only one of these was valid, but it was unclear which, and why. The count is also available in OperatorArguments.count, but it's not clear if this is the count for the whole command, or just for the operator or for the argument to the operator, which is a motion.

Main changes in this PR:

  • The Command class no longer represents an operator+motion (dw) as a Command with another Command as an argument. Instead, the Command has an action for the operator, and an argument that can be a pair of motion action plus optional argument (e.g. for dfx). This means only a single count.
  • Argument has been refactored to be a sealed hierarchy of classes.
  • CommandBuilder has been extensively refactored to have a simpler implementation.
    • Instead of a list of Command parts, there is now just the action, plus an argument (which might be a motion action with its own argument).
    • The API is simpler to use - mostly addAction and addArgument, with no state management.
    • More state has been encapsulated too, with fewer mutable public properties.
  • The OperatorArguments class has been substantially deprecated, although not as much as I'd like.
    • This class is confusing. It's not clear from the name what it's supposed to do. It's used while executing all kinds of actions, not just operators.
    • It contains a mode property that is different to editor.mode - it's the mode before the command is completed and is only used to capture a runtime flag for the motion action to behave differently when in Operator-pending mode. This has been deprecated, and is no longer used in the codebase - separate action implementations have been registered for Op-pending mode.
    • There is also an isOperatorPending property that also doesn't reflect the current editor.mode, and is unncessary - it could be replaced with mode is Mode.OP_PENDING. This has also been deprecated and is no longer used.
    • The count properties are unclear on what they represent. Given 2d3w, is this just the count for the operator? Furthermore, there was existing code that would multiply the count with Command.count, which is already multiplied when the command is built. The count is now guaranteed to be the same as Command.count.
    • Some usages of OperatorArguments have been refactored out.
    • The count properties are now the only useful properties in the class. It would be good to remove this class completely and pass the count directly. However, I'm not sure how disruptive this would be on third party extensions. We can look at this in the future.
  • The extension handling for AceJump/EasyMotion (where an external plugin moves the caret and IdeaVim tracks the ranges for operators and selections) now wraps the captured offsets into an editor action handler, similar to TextObjectActionHandler, which can provide a range for a given caret. This special case handling of offsets now uses the same logic as other motions, especially text objects.

There are other minor refactorings along the way. There are no significant bugs fixed as part of this PR, but it does fix some pathological edge cases.

Only Command has a count. The motion argument is now a sealed class hierarchy, and consists only of the motion action and optional argument. This is to reduce confusion over which count to use, and potential incorrect calculation of the count
Fixes selecting last register if multiple registers are used in a command
It's easier to just look at mode. We don't need the additional check on command builder, because we can't be in OP_PENDING without pushing an operator action to the command builder
Register specific handlers for Operator-pending mode instead of relying on a runtime flag for behaviour. Also refactors and renames some arrow motion handlers.
`OperatorArguments.mode` is the mode *before* the command is completed, so might be Visual, Operator-pending, Insert, etc. It's not immediately obvious this is the case, so we're going to deprecate `OperatorArguments.mode` to avoid confusion with `editor.mode`.

It's not required for this method because it's only called for Visual-block mode.
`OperatorArguments.mode` is the mode *before* the command was completed, rather than the current mode, which is non-obvious. E.g. for a command in Insert mode, it will still be Insert, and for a (simple) command in Normal, it will still be Normal. The only difference is that a command such as `dw` would be in Operator-pending before the command is completed. That logic is not required for this method, so it's safe to use the current mode.

This allows us to start to deprecate `OperatorArguments.mode`.
Also rename `pushCommandPart` and `completeCommandPart`
Try to keep related functions together: awaiting arguments, count, registers, adding action/argument, processing keystrokes, build, reset.
Also refactors command nodes a bit for better debug/trace output
This also gets rid of BAD_COMMAND, which was set but never checked - the function that set the flag would immediately reset the command builder
@@ -18,23 +18,19 @@ import com.maddyhome.idea.vim.api.moveToMotion
import com.maddyhome.idea.vim.command.Command
import com.maddyhome.idea.vim.handler.ShiftedArrowKeyHandler

/**
* @author Alex Plate
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😭

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops. Sorry. Not sure why I deleted that 😳

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No worries, I actually think these @author annotations are useless and not in trend anymore. I remember I used them just I saw the similar ones by Andrei Vlasovskikh :)

@AlexPl292 AlexPl292 merged commit fb7a2de into JetBrains:master Aug 30, 2024
3 of 4 checks passed
@AlexPl292
Copy link
Member

Thank you for your refactoring!

@citizenmatt citizenmatt deleted the refactor/argument-motion branch August 30, 2024 13:58
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.

2 participants