Primary
Keymap
Syntax
Syntax Node (Coarse).
This selection mode is powered by Tree-sitter.
This is one of my favourite selection mode, as it enable structural editing.
There are two Syntax Node selection modes:
- Coarse: faster movement, lower accuracy
- Fine: higher accuracy, slower movement
Movement | Meaning |
---|---|
Left/Right | Next/Previous named sibling node |
Up | Parent node |
Down | First named child |
Current | Select the largest node |
Jump | Jump to largest node |
Largest Node
Using the following Javascript expression as example:
fox.bar();
There are several syntax nodes that start with f
1:
fox
(identifier)fox.bar
(member expression)fox.bar()
(call expression)
Suppose the cursor is below f
, pressing s
selects fox.bar()
, because fox.bar()
is the largest node that starts with f
.
Named node
When creating a Tree sitter grammar file for a language, the author can choose to not give names to a certain kind of nodes.
For example, "," are usually unnamed ( anonymous) in most language grammars, thus it will be skipped when using the Previous/Next movement in Syntax Node.
See more at https://tree-sitter.github.io/tree-sitter/using-parsers#named-vs-anonymous-nodes.
Examples
Syntax*
Fine Syntax Node.
Movement | Meaning |
---|---|
Left/Right | Next/Previous sibling node (including anonymous) |
Up | Parent node |
Shrink | First child (including anonymous) |
Current | Smallest node that matches the current selection |
Jump | Jump to smallest node |
Fine Syntax Node is useful when you start to expand the selection starting from the current smallest node.
Suppose we have the following Javascript expression, and the current selection is hello
, and we want to select hello.world()
.
hello.world().foo().bar().spam().wise();
If we press d
, the whole expression will be selected1, and we will need to press i
several times to shrink the selection down to hello.world()
.
However, if we use D
instead, the selection will remain as hello
, and pressing i
multiple times will get us to hello.world()
.
Line
In this selection mode, the selection is trimmed, which means that the leading and trailing spaces of each line are not selected.
Movement | Meaning |
---|---|
Up/Down | Move to line above/below |
Alpha/Beta | Move to the first/last line of the current file |
Left | Move to the parent line |
Parent lines are highlighted lines that represent the parent nodes of the current selection.
This is useful for example when you are within the body of a function and you want to jump to the function name.
This is also practical in the File Explorer because the file explorer is rendered using YAML, so going to Parent Line means going to the parent folder!
Line*
Full Line.
Same as Line, however, leading whitespaces are selected, and trailing whitespaces, including newline characters are also selected.
Token
Token.
Each unit is a sequence of alphanumeric characters including -
and _
.
Movement | Symbols | Alphanumeric |
---|---|---|
Left/Right | ✓ | |
Alpha/Beta | ✓ | |
Jump | ✓ | ✓ |
Up/Down | ✓ | ✓ |
Current | ✓ | ✓ |
Left/Right movement skips symbols, while Alpha/Beta movement moves to symbols only.
This means Left/Right movements are optimized for navigating alphanumeric tokens, while Alpha/Beta movements are optimized for symbolic tokens, which is useful for navigating sentences.
Why don't Left/Right movements also navigate symbols? Because they would be too slow; it would take a lot of unnecessary keypresses to reach the target.
Suppose the following example:
use crate::{components::editor::OpenFile, char_index::CharIndex};
If the current selection is selecting use
, the following table demonstrates how many steps it takes to navigate to OpenFile
.
Navigation include/exclude symbols | Steps | Count |
---|---|---|
Include | crate : : { components : : editor : : OpenFile | 11 |
Exclude | crate components editor OpenFile | 4 |
Why do we need the Alpha/Beta movements to navigate symbols? Because sometimes, we just need to move to a symbol.
For example, in the following code, suppose we wanted to add an &
symbol before [Color]
.
fn apply_style(&self, colors: [Color]);
If there are no movements for Token which navigate to symbols, then we are forced to change the selection mode to either Word or Char, before inserting.
Word
This selects word within a token.
For example, myOatPepperBanana
consists of 4 short words, namely: my
, Oat
, Pepper
and Banana
.
This is useful for renaming identifiers, especially if we only want to change a single word of the name. 1
Word*
Fine Word.
This is similar to Word, except that it does not skip symbols.
Char
Character.
In this selection mode, the movements behave like the usual editor, where Left/Right means left/right, and so on.
Alpha/Beta means the first/last character of the current word.
Footnotes
-
You can try it out at https://astexplorer.net/, using the
@typescript-eslint/parser
. ↩ ↩2 ↩3