From c8fde8b6f99b07faca7e6f93162d887ee132e0f6 Mon Sep 17 00:00:00 2001 From: JJ Date: Sun, 26 Mar 2023 15:06:48 -0700 Subject: [PATCH] Initial Nim language support (#6123) --- book/src/generated/lang-support.md | 1 + languages.toml | 23 ++ runtime/queries/nim/highlights.scm | 315 ++++++++++++++++++++++++++++ runtime/queries/nim/indents.scm | 54 +++++ runtime/queries/nim/textobjects.scm | 19 ++ 5 files changed, 412 insertions(+) create mode 100644 runtime/queries/nim/highlights.scm create mode 100644 runtime/queries/nim/indents.scm create mode 100644 runtime/queries/nim/textobjects.scm diff --git a/book/src/generated/lang-support.md b/book/src/generated/lang-support.md index 996bdb435..5cd0c8c1f 100644 --- a/book/src/generated/lang-support.md +++ b/book/src/generated/lang-support.md @@ -88,6 +88,7 @@ | msbuild | ✓ | | ✓ | | | nasm | ✓ | ✓ | | | | nickel | ✓ | | ✓ | `nls` | +| nim | ✓ | ✓ | ✓ | `nimlangserver` | | nix | ✓ | | | `nil` | | nu | ✓ | | | | | ocaml | ✓ | | ✓ | `ocamllsp` | diff --git a/languages.toml b/languages.toml index fe94d6999..ae4135f00 100644 --- a/languages.toml +++ b/languages.toml @@ -2398,3 +2398,26 @@ grammar = "rego" [[grammar]] name = "rego" source = { git = "https://github.com/FallenAngel97/tree-sitter-rego", rev = "b2667c975f07b33be3ceb83bea5cfbad88095866" } + +[[language]] +name = "nim" +scope = "source.nim" +injection-regex = "nim" +file-types = ["nim", "nims", "nimble"] +shebangs = [] +roots = [] +comment-token = "#" +indent = { tab-width = 2, unit = " " } +language-server = { command = "nimlangserver" } + +[language.auto-pairs] +'(' = ')' +'[' = ']' +'"' = '"' +"'" = "'" +'{' = '}' + +# Nim's tree-sitter grammar is in heavy development. +[[grammar]] +name = "nim" +source = { git = "https://github.com/aMOPel/tree-sitter-nim", rev = "240239b232550e431d67de250d1b5856209e7f06" } diff --git a/runtime/queries/nim/highlights.scm b/runtime/queries/nim/highlights.scm new file mode 100644 index 000000000..1d3256853 --- /dev/null +++ b/runtime/queries/nim/highlights.scm @@ -0,0 +1,315 @@ +;; Constants, Comments, and Literals + +(comment) @comment.line +(multilineComment) @comment.block +(docComment) @comment.block.documentation +(multilineDocComment) @comment.block.documentation +; comments + +[(literal) (generalizedLit)] @constant +[(nil_lit)] @constant.builtin +[(bool_lit)] @constant.builtin.boolean +[(char_lit)] @constant.character +[(char_esc_seq) (str_esc_seq)] @constant.character.escape +[(custom_numeric_lit)] @constant.numeric +[(int_lit) (int_suffix)] @constant.numeric.integer +[(float_lit) (float_suffix)] @constant.numeric.float +; literals +; note: somewhat irritatingly for testing, lits have the same syntax highlighting as types + +[ + (str_lit) + (triplestr_lit) + (rstr_lit) + (generalized_str_lit) + (generalized_triplestr_lit) + (interpolated_str_lit) + (interpolated_triplestr_lit) +] @string +; [] @string.regexp +; string literals + +[ + "." + "," + ";" + ":" +] @punctuation.delimiter +[ + "(" + ")" + "[" + "]" + "{" + "}" + "{." + ".}" + "#[" + "]#" +] @punctuation.bracket +(interpolated_str_lit "&" @punctuation.special) +(interpolated_str_lit "{" @punctuation.special) +(interpolated_str_lit "}" @punctuation.special) +; punctuation + +[ + "and" + "or" + "xor" + "not" + "in" + "notin" + "is" + "isnot" + "div" + "mod" + "shl" + "shr" +] @keyword.operator +; operators: we list them explicitly to deliminate them from symbolic operators + +[(operator) (opr) "="] @operator +; all operators (must come after @keyword.operator) + +(pragma) @attribute +; pragmas + + +;; Imports and Exports + +(importStmt + (keyw) @keyword.control.import + (expr (primary (symbol) @namespace))? + (expr (primary (arrayConstr (exprColonExprList (exprColonExpr (expr (primary (symbol) @namespace)))))))?) +(exportStmt + (keyw) @keyword.control.import + (expr (primary (symbol) @namespace))? + (expr (primary (arrayConstr (exprColonExprList (exprColonExpr (expr (primary (symbol) @namespace)))))))?) +(fromStmt + (keyw) @keyword.control.import + (expr (primary (symbol) @namespace))? + (expr (primary (arrayConstr (exprColonExprList (exprColonExpr (expr (primary (symbol) @namespace)))))))?) +(includeStmt + (keyw) @keyword.control.import + (expr (primary (symbol) @namespace))? + (expr (primary (arrayConstr (exprColonExprList (exprColonExpr (expr (primary (symbol) @namespace)))))))?) +(importExceptStmt + (keyw) @keyword.control.import + (expr (primary (symbol) @namespace))? + (expr (primary (arrayConstr (exprColonExprList (exprColonExpr (expr (primary (symbol) @namespace)))))))?) +; import statements +; yeah, this is a bit gross. + + +;; Control Flow + +(ifStmt (keyw) @keyword.control.conditional) +(whenStmt (keyw) @keyword.control.conditional) +(elifStmt (keyw) @keyword.control.conditional) +(elseStmt (keyw) @keyword.control.conditional) +(caseStmt (keyw) @keyword.control.conditional) +(ofBranch (keyw) @keyword.control.conditional) +(inlineIfStmt (keyw) @keyword.control.conditional) +(inlineWhenStmt (keyw) @keyword.control.conditional) +; conditional statements +; todo: do block + +(forStmt + . (keyw) @keyword.control.repeat + . (symbol) @variable + . (keyw) @keyword.control.repeat) +(whileStmt (keyw) @keyword.control.repeat) +; loop statements + +(returnStmt (keyw) @keyword.control.repeat) +(yieldStmt (keyw) @keyword.control.repeat) +(discardStmt (keyw) @keyword.control.repeat) +(breakStmt (keyw) @keyword.control.repeat) +(continueStmt (keyw) @keyword.control.repeat) +; control flow statements + +(raiseStmt (keyw) @keyword.control.exception) +(tryStmt (keyw) @keyword.control.exception) +(tryExceptStmt (keyw) @keyword.control.exception) +(tryFinallyStmt (keyw) @keyword.control.exception) +(inlineTryStmt (keyw) @keyword.control.exception) +; (inlineTryExceptStmt (keyw) @keyword.control.exception) +; (inlineTryFinallyStmt (keyw) @keyword.control.exception) +; exception handling statements + +(staticStmt (keyw) @keyword) +(deferStmt (keyw) @keyword) +(asmStmt (keyw) @keyword) +(bindStmt (keyw) @keyword) +(mixinStmt (keyw) @keyword) +; miscellaneous blocks + +(blockStmt + (keyw) @keyword.control + (symbol) @label) +; block statements + + +;; Types and Type Declarations + +(typeDef + (keyw) @keyword.storage.type + (symbol) @type) +; names of new types type declarations + +(exprColonEqExpr + . (expr (primary (symbol) @variable)) + . (expr (primary (symbol) @type))) +; variables in inline tuple declarations + +(primarySuffix + (indexSuffix + (exprColonEqExprList + (exprColonEqExpr + (expr + (primary + (symbol) @type)))))) +; nested types in brackets, i.e. seq[string] + +(primaryTypeDef (symbol) @type) +; primary types of type declarations (NOT nested types) + +(primaryTypeDef (primaryPrefix (keyw) @type)) +; for consistency + +(primaryTypeDesc (symbol) @type) +; type annotations, on declarations or in objects + +(primaryTypeDesc (primaryPrefix (keyw) @type)) +; var types etc + +(genericParamList (genericParam (symbol) @type)) +; types in generic blocks + +(enumDecl (keyw) @keyword.storage.type) +(enumElement (symbol) @type.enum.variant) +; enum declarations and elements + +(tupleDecl (keyw) @keyword.storage.type) +; tuple declarations + +(objectDecl (keyw) @keyword.storage.type) +(objectPart (symbol) @variable.other.member) +; object declarations and fields + +(objectCase + (keyw) @keyword.control.conditional + (symbol) @variable.other.member) +(objectBranch (keyw) @keyword.control.conditional) +(objectElif (keyw) @keyword.control.conditional) +(objectElse (keyw) @keyword.control.conditional) +(objectWhen (keyw) @keyword.control.conditional) +; variant objects + +(conceptDecl (keyw) @keyword.storage.type) +(conceptParam (keyw) @type) +(conceptParam (symbol) @variable) +; concept declarations, parameters, and qualifiers on those parameters + +((expr + (primary (symbol)) + (operator) @operator + (primary (symbol) @type)) + (#match? @operator "is")) +((exprStmt + (primary (symbol)) + (operator) @operator + (primary (symbol) @type)) + (#match? @operator "is")) +; symbols likely to be types: "x is t" means t is either a type or a type variable + +; distinct? + + +;; Functions + +(routine + . (keyw) @keyword.function + . (symbol) @function) +; function declarations + +(routineExpr (keyw) @keyword.function) +; discarded function + +(routineExprTypeDesc (keyw) @keyword.function) +; function declarations as types + +(primary + . (symbol) @function.call + . (primarySuffix (functionCall))) +; regular function calls + +(primary + . (symbol) @function.call + . (primarySuffix (cmdCall))) +; function calls without parenthesis + +(primary + (primarySuffix (qualifiedSuffix (symbol) @function.call)) + . (primarySuffix (functionCall))) +; uniform function call syntax calls + +(primary + (primarySuffix (qualifiedSuffix (symbol) @function.call)) + . (primarySuffix (cmdCall))) +; just in case + +(primary + (symbol) @constructor + (primarySuffix (objectConstr))) +; object constructor + +; does not appear to be a way to distinguish these without verbatium matching +; [] @function.builtin +; [] @function.method +; [] @function.macro +; [] @function.special + + +;; Variables + +(paramList (paramColonEquals (symbol) @variable.parameter)) +; parameter identifiers + +(identColon (ident) @variable.other.member) +; named parts of tuples + +(symbolColonExpr (symbol) @variable) +; object constructor parameters + +(symbolEqExpr (symbol) @variable) +; named parameters + +(variable + (keyw) @keyword.storage.type + (declColonEquals (symbol) @variable)) +; let, var, const expressions + +((primary (symbol) @variable.builtin) + (#match? @variable.builtin "result")) +; `result` is an implicit builtin variable inside function scopes + +((primary (symbol) @type) + (#match? @type "^[A-Z]")) +; assume PascalCase identifiers to be types + +((primary + (primarySuffix + (qualifiedSuffix + (symbol) @type))) + (#match? @type "^[A-Z]")) +; assume PascalCase member variables to be enum entries + +(primary (symbol) @variable) +; overzealous, matches variables + +(primary (primarySuffix (qualifiedSuffix (symbol) @variable.other.member))) +; overzealous, matches member variables: i.e. x in foo.x + +(keyw) @keyword +; more specific matches are done above whenever possible diff --git a/runtime/queries/nim/indents.scm b/runtime/queries/nim/indents.scm new file mode 100644 index 000000000..677435407 --- /dev/null +++ b/runtime/queries/nim/indents.scm @@ -0,0 +1,54 @@ +[ + (typeDef) + (ifStmt) + (whenStmt) + (elifStmt) + (elseStmt) + (ofBranch) ; note: not caseStmt + (whileStmt) + (tryStmt) + (tryExceptStmt) + (tryFinallyStmt) + (forStmt) + (blockStmt) + (staticStmt) + (deferStmt) + (asmStmt) + ; exprStmt? +] @indent +;; increase the indentation level + +[ + (ifStmt) + (whenStmt) + (elifStmt) + (elseStmt) + (ofBranch) ; note: not caseStmt + (whileStmt) + (tryStmt) + (tryExceptStmt) + (tryFinallyStmt) + (forStmt) + (blockStmt) + (staticStmt) + (deferStmt) + (asmStmt) + ; exprStmt? +] @extend +;; ??? + +[ + (returnStmt) + (raiseStmt) + (yieldStmt) + (breakStmt) + (continueStmt) +] @extend.prevent-once +;; end a level of indentation while staying indented + +[ + ")" ; tuples + "]" ; arrays, seqs + "}" ; sets +] @outdent +;; end a level of indentation and unindent the line diff --git a/runtime/queries/nim/textobjects.scm b/runtime/queries/nim/textobjects.scm new file mode 100644 index 000000000..943aa7f08 --- /dev/null +++ b/runtime/queries/nim/textobjects.scm @@ -0,0 +1,19 @@ +(routine + (block) @function.inside) @function.around + +; @class.inside (types?) +; @class.around + +; paramListSuffix is strange and i do not understand it +(paramList + (paramColonEquals) @parameter.inside) @parameter.around + +(comment) @comment.inside +(multilineComment) @comment.inside +(docComment) @comment.inside +(multilineDocComment) @comment.inside + +(comment)+ @comment.around +(multilineComment) @comment.around +(docComment)+ @comment.around +(multilineDocComment) @comment.around