CodeMirror Reflector demo

Refactoring, smart navigation and code assistance for CodeMirror.

This is a promising proof-of-concept but not a stable API you should rely on and the claimed features aren't ready to use.

There is some extra information and the source code in the Reflector GitHub repository.

Demo editor

This embeds a tiny language called Miniscript created for demonstration and exploration purposes. Some Reflector extensions are active.

Key bindings

The editor uses CodeMirror's default keymap and adds some custom keybindings.

Identifier matching and highlighting

There is also highlighting of matching identifiers. When the cursor is in an identifier, matching definitions will be highlighted in blue (similar to how CodeMirror highlights matching brackets). This is a visual aid to help you understand the scope of the identifier you are working with. Similarly for uses of an identifier: when the cursor is in a definition, matching uses will be highlighted in green. It is possible (but not implemented) to show all uses and definitions of an identifier. Finally, an indentifier which doesn't have a matching definition will be shown in red.

NodeProps describe scope and identifiers

This is driven by some custom props in the Miniscript grammar. These mark grammar nodes that establish scope, uses and definitions of identifiers. The Miniscript grammar is explained below. With these props, Reflector can cross-reference identifiers and show you where they are defined and used. This is probably quite an inefficient process right now but it works for small files in a simple grammar like this one.

Linting features

This structural understanding is also used to drive a linter. This shows an error Diagnostic for duplicate definitions (e.g. a repeated parameter) and understands through the grammar annotations whether a duplicate is allowed to override an earlier definition on the same identifier in the scope, which is allowed for Miniscript local variables. If you use the highlighting or renaming feature, you will hopefully see that Reflector understands the scope of the identifier correctly even when it is redefined.

Linting actions

The linter also shows information markers when a definition is unused. Unused functions and local variables both have an Action that allows the unused definition to be removed. Here is an example of how to implement this for Miniscript. The Action walks up the syntax tree until it finds the FunctionDeclaration node, then removes its text from the document. Because this is not aware of the skipped space nodes around the removed text, the formatting can be a bit off. This is a limitation of the current implementation.

Another linter rule provides an Action that can fix an undefined identifier by declaring a local or global variable. It does this by inserting a text template and substituting in the identifier name.

I've also, for no good reason other than experimenting with recognising nearby document structure, included a lint rule to suggest you shouldn't put a comment after a statement.

Syntax errors

There is an experiment with providing context-specific syntax error messages. Lezer is very good at recovering from syntax errors but it is not easy to provide a helpful error message. The experiment tries to provide some context-sensitive guidance about what might be causing the syntax error. I'm not sure it is going to be a successful idea.

Explanation of Miniscript

The grammar is here but, in summary: