Skip to content

Latest commit

 

History

History
166 lines (135 loc) · 9.54 KB

evil-tree-edit.org

File metadata and controls

166 lines (135 loc) · 9.54 KB

Table of content

Getting started

evil-tree-edit can be installed on MELPA.

Many, if not all languages will require custom grammars to be installed. See main README for instructions.

After installation, add hooks for any language you’d like evil-tree-edit to automatically enable in.

(add-hook 'java-mode-hook #'evil-tree-edit-mode)

It’s also recommended to use tree-edit alongside an autoformatter in it’s current state, as tree-edit does not always produce text consistent in formatting with the surrounding nodes.

Usage

The concept of the cursor, a position in the 2D plane of text, is replaced by the current node, which is a position in the syntax tree in tree-edit. All operations unless otherwise specified are performed on the current node. To help visualize the syntax tree, tree-edit provides M-x tree-edit-view-mode as seen in the demo GIF.

Tree-edit adopts a vim-style approach to editing, where certain operators also require a noun. In vim’s case, the nouns are text objects; In tree-edit’s case, the nouns are node types. For example, iv would insert a variable declaration. Due to the fact that most languages contain a large number of node types, and vary across languages, using which-key with tree-edit is highly recommended.

To activate tree-edit from normal state, press Q, and to return to normal state press ESC.

Navigation

The navigation primitives follow the tree structure of the language.

OperationKeybindDescription
NextjMove cursor to the next sibling.
PreviouskMove cursor to the previous sibling.
InwardsfMove cursor to the first child.
OutwardshMove cursor to the parent.
Jump tosAvy jump to a node of node-type for a node inside the current.
Outwards SignificantAMove outwards until a significant node (e.g. function or class declaration) is hit.
Goto PlaceholdernJump to the first placeholder node within the current.

The definition of a placeholder node is configurable, but generally it’s the TREE identifiers as seen in the GIF demo.

Editing operations

The most important feature of tree-edit: editing the syntax tree.

For any editing operation, the syntax will be added or deleted based on the needs of the operation. For example, when adding an additional argument to a function, tree-edit can infer that a comma is needed based on the grammar of the language.

tree-edit-syntax-snippets defines how node types will actually be represented upon insertion: see example here.

Any transformations will be rejected if a syntactically valid result cannot be generated.

OperationKeybindDescription
RaiserReplace the current node’s parent with the current node.
DeletedDelete the current node.
MovemCopy then delete the current node.
ChangecDelete the current node and drop into insert state. Tree state will be re-entered on ESC.
WrapwCreate a new node of node-type and insert the current one in it.
ExchangeeExchange the current node with a new node of node-type.
InsertiInsert a new node of node-type to the right of the current.
AppendaInsert a new node of node-type to the left of the current.
Insert ChildIInsert a new node of node-type as a child of the current. Useful for nodes with no named children, i.e. {}
Goto Placeholder and ChangeNJump to the first placeholder node within the current and edit it.
Append Placeholder and ChangexAdd a placeholder node and then immediately edit it.
Slurp>Grow the current node to contain the nearest right-most element.
Barf<Shrink the current node to place it’s left-most element into the parent node.
CopyyCopy the text of the current node.
UndouUndo the last operation.
Preview?Preview the possible variations of the current node.
Tree viewVEnable tree-edit-view or display if already enabled.

Pasting

Along with the standard node-types of the given language, tree-edit has a special node-type p that will attempt to parse the type of the most recently copied text. If a type can be identified and the operation is valid, the copied text will be used.

A note on raise vs. delete, wrap vs. insert

Both of the following definition for argument list produce the same result on a textual level:

argument_list = expression | seq[expression "," argument_list]
argument_list = seq[expression, repeat["," expression]]

However, at the tree level, these two constructions result in different ways to modify the node.

For the first construction, you’d need to use raise/wrap to add and remove expressions:

(foo, [bar]) ==raise==> (foo)
([foo])      ===wrap==> (foo, bar)

While for the second, you can use insert/delete.

(foo, [bar]) ==delete=> (foo)
([foo])      ==insert=> (foo, bar)

This is something you may need to be aware of if you’re running trying to perform an operation that you think should work, but doesn’t! In doubt, check the grammar.js of the language.

Customization

Currently adding customization ontop of the preset language files requires a fair bit of boilerplate, but here’s some code to get started.

(with-eval-after-load 'tree-edit-java
  (with-mode-local java-mode
    (setq-mode-local
     java-mode

     tree-edit-syntax-snippets
     (append
      ;; Put your snippets here
      '((identifier . ("FOOBAR")))
      tree-edit-syntax-snippets)

     tree-edit-nodes
     (append
      ;; Put your nodes here
      '((:type if_statement
         :key "z"
         :name "if-else statement"
         :node-override '((if_statement . ("if" parenthesized_expression block "else" block)))))
      tree-edit-nodes)))

  (evil-tree-edit-set-state-bindings 'java-mode))

See tree-edit-java.el and the docstrings of the accompanying variables for more information.