diff --git a/docs/language-configuration.md b/docs/language-configuration.md new file mode 100644 index 000000000..a0f4f9df8 --- /dev/null +++ b/docs/language-configuration.md @@ -0,0 +1,112 @@ +This document serves as documentation for [`language-configuration.json`](/vscode-lean4/language-configuration.json). + +See [the official VS code documentation](https://code.visualstudio.com/api/language-extensions/language-configuration-guide) for an overview of how `language-configuration.json` files work in general. + +Section titles here are top-level fields in the JSON file, and link to their corresponding section in the official documentation. + +## [`onEnterRules`](https://code.visualstudio.com/api/language-extensions/language-configuration-guide#on-enter-rules) + +This field specifies a list of rules which determine how lines should be indented when the user presses Enter. When the text in a line before the cursor is matched by the regex specified in a rule's `beforeText`, that rule's `action` is applied on Enter. + +### Postindented EOL tokens + +Certain end-of-line (EOL) tokens require increased indentation on subsequent lines. Hence, we call them "postindented" here for brevity; this is not standard terminology. + +The list of English-language postindented EOL tokens is: + +* `by`, `do`, `try`, `finally`, `then`, `else`, `where`, `from`, `extends`, `deriving`, `termination_by`(`'`), `decreasing_by`, `calc` + +Note that `calc` does not actually need to occur at the end of a line; we allow an arbitrary argument. We include it here nonetheless. + +The list of symbolic postindented EOL tokens is: + +* `:=`, `←`, `=>`, `::`, `<|` + +We want to avoid misidentification of the English-language tokens as part of a name (e.g. `foo.try` or `entry`); we do, however, still want to recognize these tokens in cases like `... (by`. So, we make sure the English-language tokens are preceded by a word boundary and not by `.`. We also exclude `#`, since at least `#where` appears as a temporary command in std4. (`\b(?|::|<\\|)))\\s*$", + "action" : { "indent" : "indent", "appendText": "\t" } +} +``` + +We can then account for postindented EOL tokens in the ordinary case. + +```json +{ + "beforeText" : "^((.*\\b(?|::|<\\|)))\\s*$", + "action" : { "indent" : "indent" } +} +``` + +Caveat: this produces incorrect indentation in the following case: + +``` +theorem foo (h : LongHypothesisList) : + FooType :=| <-- hit enter here + | | <-- cursor winds up here + ^-- cursor should be here +``` + +However, without access to the parser or multi-line matching (VS code limits the match to the line the cursor is on), there's no way to fix this. + +### Type signatures + +Multi-line type signatures in Lean are supposed to have non-initial lines indented twice. We can't account for all of these with `beforeText` regexes (which would at least require matching over multiple lines and balancing parentheses), but we can at least account for the following case: +``` +theorem foo (h : LongHypothesisList) ... :| <-- hit enter here + | <-- cursor ends up here +``` +We want to make sure we don't apply this rule when already in a multi-line type signature: +``` +theorem foo (h1 : LongHypothesisList) ... + (h2 : LongHypothesisList2) ... :| <-- hit enter here + | <-- cursor ends up here +``` +To accomplish this we only match on lines which do not start with a binder bracket. (This doesn't account for cases where the type of a hypothesis itself spans multiple lines, but those cases typically require careful manual formatting anyway.) + +First, however, we indent *three* times when starting a focus block; see the [focus blocks](#focus-blocks) section for more info. We use `appendText` to accomplish this; note that tabs are appropriately converted into spaces by VS code before insertion. + +```json +{ + "beforeText" : "^\\s*(·|\\.)\\s.*\\s:\\s*$", + "action" : { "indent" : "indent", "appendText": "\t\t" } +} +``` + +We then apply our standard rule: + +```json +{ + "beforeText" : "^(?!\\s*(\\(|{|\\[|⦃)).*\\s:\\s*$", + "action" : { "indent" : "indent", "appendText": "\t" } +} +``` + +### Focus blocks + +```json +{ + "beforeText" : "^\\s*(·|\\.)\\s.*$", + "action" : { "indent" : "indent" } +} +``` + +This rule ensures that hitting enter after starting a focus block during a tactic sequence produces a line within that focus block. I.e., + +``` + constructor + · intro x| <-- hit enter here + | <-- cursor ends up here +``` + +We also make sure we indent twice after encountering a postindented EOL token, and three times after starting a type signature; see [postindented EOL tokens](#postindented-eol-tokens) and [type signatures](#type-signatures). diff --git a/vscode-lean4/language-configuration.json b/vscode-lean4/language-configuration.json index ed98e16bc..5424b1303 100644 --- a/vscode-lean4/language-configuration.json +++ b/vscode-lean4/language-configuration.json @@ -130,5 +130,27 @@ ["\"", "\""], ["'", "'"] ], - "wordPattern" : "([^`~@$%^&*()\\-=+\\[{\\]}⟨⟩⦃⦄⟦⟧⟮⟯‹›<>\\\\|;:\",./\\s]+)" + "wordPattern" : "([^`~@$%^&*()\\-=+\\[{\\]}⟨⟩⦃⦄⟦⟧⟮⟯‹›<>\\\\|;:\",./\\s]+)", + "onEnterRules" : [ + { + "beforeText" : "^\\s*(·|\\.)\\s((((?!--).)*\\b(?|::|<\\|)))\\s*(--.*)?$", + "action" : { "indent" : "indent", "appendText": "\t" } + }, + { + "beforeText" : "^((((?!--).)*\\b(?|::|<\\|)))\\s*(--.*)?$", + "action" : { "indent" : "indent" } + }, + { + "beforeText" : "^\\s*(·|\\.)\\s.*\\s:\\s*$", + "action" : { "indent" : "indent", "appendText": "\t\t" } + }, + { + "beforeText" : "^(?!\\s*(\\(|{|\\[|⦃)).*\\s:\\s*$", + "action" : { "indent" : "indent", "appendText": "\t" } + }, + { + "beforeText" : "^\\s*(·|\\.)\\s.*$", + "action" : { "indent" : "indent" } + } + ] }