diff options
| author | _ov4 <ov4@ov4.org> | 2026-10-21 10:37:17 +0000 |
|---|---|---|
| committer | _ov4 <ov4@ov4.org> | 2026-10-21 10:37:17 +0000 |
| commit | 75c0e28e3d56878dbd492cb0c6d6bf341f9e6de7 (patch) | |
| tree | 2b5fc43cbdcada966ae1ac85569b7f0eff5f51de /plugin | |
| parent | af6bb061ff447f860c514b60202c3192ceec952b (diff) | |
feat(plugin): highlight type names and template calls
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'plugin')
| -rw-r--r-- | plugin/AGENTS/ARCH.md | 19 | ||||
| -rw-r--r-- | plugin/ThirdPartyNotices.md | 4 | ||||
| -rw-r--r-- | plugin/examples/highlighting.cps | 19 | ||||
| -rw-r--r-- | plugin/language-configuration.json | 2 | ||||
| -rw-r--r-- | plugin/syntaxes/cie-pseudocode.tmLanguage.json | 673 | ||||
| -rw-r--r-- | plugin/test/highlighting.test.js | 80 |
6 files changed, 649 insertions, 148 deletions
diff --git a/plugin/AGENTS/ARCH.md b/plugin/AGENTS/ARCH.md index 90e9614..5242d3e 100644 --- a/plugin/AGENTS/ARCH.md +++ b/plugin/AGENTS/ARCH.md @@ -13,7 +13,7 @@ there is no compiler library or cpptools integration. comments, brackets, quote pairs and identifiers. - `syntaxes/cie-pseudocode.tmLanguage.json` adapts small C++ comment, quote and comma rules and uses the same general token scope categories. CIE keyword, - builtin, number and operator patterns are locally defined. Exception keywords + function, type, number and operator patterns are locally defined. Exception keywords reuse C++ try/catch/throw scopes, with ENDTRY sharing the TRY scope. - cpptools was consulted for provider registration. Its formatter and language server are not called, imported or bundled. This extension is not a cpptools @@ -95,9 +95,20 @@ only a subset of its syntax: `TYPE Name = ...` declarations do not open a record block. The compiler accepts standard WHILE headers without DO, but the indentation engine still needs DO to activate their body. -- Highlighted builtins: LENGTH, MID, RIGHT, LEFT, LCASE, UCASE, ASC, CHR, IS_NUM, - NUM_TO_STR, STR_TO_NUM and EOF. The compiler's complete table is in - `lib/CodeGen/Builtins.h` and also includes MALLOC, FREE and REALLOC. +- Function highlighting accepts leading underscores and explicit, nested template + arguments. The generic call rule covers the compiler builtins EOF, MALLOC, FREE + and REALLOC as well as ordinary standard-library functions. Builtin definitions + live in `lib/CodeGen/Builtins.h`; string functions live in pseudocode-lib. +- Custom type names use C++ type scopes in TYPE/CLASS declarations, variable and + field declarations, parameter and return types, ARRAY OF, pointer aliases, NEW, + templates and EXTERN parameter lists. Array-bound contexts shield their colons + from type annotation rules; CASE OF does not introduce a type. Balanced template + call lookahead follows Parser::isTemplateCall, requiring a following parenthesis. + No symbol tracking is performed: custom cast names and ordinary value names + stay unclassified. Builtin casts retain their existing type scopes. +- A CLASS method context enables the C++ this scope case-insensitively via a + TextMate injection, including nested declaration expressions. Comments, strings, + fields outside methods and top-level code do not receive this scope. - Highlighting covers CASE/ENDCASE/OTHERWISE, CLASS/ENDCLASS/PUBLIC/PRIVATE, CONSTANT, TEMPLATE, OVERLOAD/OPERATOR, EXTERN declarations, INCLUDE_OBJ, #LIB and ellipses using C++ scopes. OVERLOAD borrows a declaration modifier, #LIB diff --git a/plugin/ThirdPartyNotices.md b/plugin/ThirdPartyNotices.md index 6bb976c..e24f2c8 100644 --- a/plugin/ThirdPartyNotices.md +++ b/plugin/ThirdPartyNotices.md @@ -8,7 +8,9 @@ access, constant, template, operator and external-declaration highlighting also reuse C++ scopes. OVERLOAD borrows a declaration modifier, #LIB borrows #pragma and INCLUDE_OBJ borrows #include; ellipses use the C++ variadic scope. Literal escapes reuse `constant.character.escape`; the adjacent C string suffix borrows -`keyword.other.suffix.literal.user-defined.string.cpp`. +`keyword.other.suffix.literal.user-defined.string.cpp`. Custom types, template +parameters and angle delimiters use the corresponding C++ type and template +scopes; class-method receivers reuse `variable.language.this.cpp`. https://github.com/microsoft/vscode/tree/main/extensions/cpp diff --git a/plugin/examples/highlighting.cps b/plugin/examples/highlighting.cps index f80fd8f..10d93d8 100644 --- a/plugin/examples/highlighting.cps +++ b/plugin/examples/highlighting.cps @@ -56,3 +56,22 @@ CASE OF Index 2 TO Limit : OUTPUT "In range" OTHERWISE : OUTPUT "Other" ENDCASE + +TEMPLATE <T> +FUNCTION _echo(Value : T) RETURNS T + RETURN Value +ENDFUNCTION +OUTPUT _echo<INTEGER>(Limit) + +CLASS Counter + PRIVATE Value : INTEGER + PUBLIC PROCEDURE Add(Amount : INTEGER) + this.Value <- this.Value + Amount + ENDPROCEDURE + PUBLIC FUNCTION Get() RETURNS INTEGER + RETURN this.Value + ENDFUNCTION +ENDCLASS +DECLARE Count : Counter +CALL Count.Add(Limit) +OUTPUT Count.Get() diff --git a/plugin/language-configuration.json b/plugin/language-configuration.json index ed3e464..36a3b08 100644 --- a/plugin/language-configuration.json +++ b/plugin/language-configuration.json @@ -9,5 +9,5 @@ { "open": "\"", "close": "\"", "notIn": ["string", "comment"] } ], "surroundingPairs": [["[", "]"], ["(", ")"], ["\"", "\""], ["'", "'"]], - "wordPattern": "[A-Za-z][A-Za-z0-9_]*" + "wordPattern": "[A-Za-z_][A-Za-z0-9_]*" } diff --git a/plugin/syntaxes/cie-pseudocode.tmLanguage.json b/plugin/syntaxes/cie-pseudocode.tmLanguage.json index cdf175a..16a4ac4 100644 --- a/plugin/syntaxes/cie-pseudocode.tmLanguage.json +++ b/plugin/syntaxes/cie-pseudocode.tmLanguage.json @@ -3,145 +3,7 @@ "scopeName": "source.cie-pseudocode", "patterns": [ { - "include": "#line_comment" - }, - { - "include": "#strings" - }, - { - "match": "^\\s*(#)(LIB)\\s*$", - "captures": { - "1": { - "name": "punctuation.definition.directive.cpp" - }, - "2": { - "name": "keyword.control.directive.pragma.cpp" - } - } - }, - { - "match": "(?<!\\w)(?:CASE|ENDCASE)(?!\\w)", - "name": "keyword.control.switch.cpp" - }, - { - "match": "(?<!\\w)(?:OTHERWISE)(?!\\w)", - "name": "keyword.control.default.cpp" - }, - { - "match": "(?<!\\w)(?:CONSTANT)(?!\\w)", - "name": "storage.modifier.const.cpp" - }, - { - "match": "(?<!\\w)(?:CLASS|ENDCLASS)(?!\\w)", - "name": "storage.type.class.cpp" - }, - { - "match": "(?<!\\w)(?:PUBLIC)(?!\\w)", - "name": "storage.type.modifier.access.control.public.cpp" - }, - { - "match": "(?<!\\w)(?:PRIVATE)(?!\\w)", - "name": "storage.type.modifier.access.control.private.cpp" - }, - { - "match": "(?<!\\w)(?:TEMPLATE)(?!\\w)", - "name": "storage.type.template.cpp" - }, - { - "match": "(?<!\\w)(?:OPERATOR)(?!\\w)", - "name": "keyword.other.operator.overload.cpp" - }, - { - "match": "(?<!\\w)(?:OVERLOAD)(?!\\w)", - "name": "storage.modifier.cpp" - }, - { - "match": "(?<!\\w)(?:EXTERN)(?!\\w)", - "name": "storage.modifier.specifier.extern.cpp" - }, - { - "match": "(?<!\\w)(?:EXTERN_FUNCTION|EXTERN_PROCEDURE)(?!\\w)", - "name": "storage.modifier.cpp" - }, - { - "match": "(?<!\\w)(?:INCLUDE_OBJ)(?!\\w)", - "name": "keyword.control.directive.include.cpp" - }, - { - "match": "\\.\\.\\.", - "name": "punctuation.vararg-ellipses.cpp" - }, - { - "match": "(?<!\\w)(?:TRY|ENDTRY)(?!\\w)", - "name": "keyword.control.try.cpp" - }, - { - "match": "(?<!\\w)CATCH(?!\\w)", - "name": "keyword.control.catch.cpp" - }, - { - "match": "(?<!\\w)THROW(?!\\w)", - "name": "keyword.control.throw.cpp" - }, - { - "match": "(?<!\\w)(?:IF|THEN|ELSE|ENDIF|WHILE|DO|ENDWHILE|REPEAT|UNTIL|FOR|TO|STEP|NEXT|RETURN|CALL|NEW|DELETE)(?!\\w)", - "name": "keyword.control.cie-pseudocode" - }, - { - "match": "(?<!\\w)(?:INCLUDE|GLOBAL|ENDGLOBAL|DECLARE|TYPE|ENDTYPE|FUNCTION|ENDFUNCTION|PROCEDURE|ENDPROCEDURE|RETURNS|BYREF|BYVAL|OF)(?!\\w)", - "name": "storage.modifier.cie-pseudocode" - }, - { - "match": "(?<!\\w)(?:INTEGER|REAL|BOOLEAN|STRING|CHAR|ARRAY|C_INT|C_CHAR|C_STRING|C_DOUBLE|C_TYPE)(?!\\w)", - "name": "storage.type.cie-pseudocode" - }, - { - "match": "(?<!\\w)(?:TRUE|FALSE)(?!\\w)", - "name": "constant.language.cie-pseudocode" - }, - { - "match": "(?<!\\w)(?:INPUT|OUTPUT|OPENFILE|READFILE|WRITEFILE|CLOSEFILE|READ|WRITE|APPEND)(?!\\w)", - "name": "keyword.other.cie-pseudocode" - }, - { - "match": "(?<!\\w)(?:LENGTH|MID|RIGHT|LEFT|LCASE|UCASE|ASC|CHR|IS_NUM|NUM_TO_STR|STR_TO_NUM|EOF)(?!\\w)(?=\\s*\\()", - "name": "entity.name.function.cie-pseudocode" - }, - { - "match": "(?<!\\w)(?:DIV|MOD|AND|OR|NOT)(?!\\w)", - "name": "keyword.operator.word.cie-pseudocode" - }, - { - "match": "<-", - "name": "keyword.operator.assignment.cpp" - }, - { - "match": "<>|<=|>=|[+*/=&^<>-]", - "name": "keyword.operator.cie-pseudocode" - }, - { - "match": "(?<!\\w)\\d+(?:\\.\\d*)?(?!\\w)", - "name": "constant.numeric.cie-pseudocode" - }, - { - "match": "\\b[A-Za-z][A-Za-z0-9_]*(?=\\s*\\()", - "name": "entity.name.function.cie-pseudocode" - }, - { - "match": "[()]", - "name": "punctuation.section.parens.cie-pseudocode" - }, - { - "match": "[\\[\\]]", - "name": "punctuation.section.brackets.cie-pseudocode" - }, - { - "match": ",", - "name": "punctuation.separator.delimiter.comma.cie-pseudocode" - }, - { - "match": "[:.]", - "name": "punctuation.separator.cie-pseudocode" + "include": "#code" } ], "repository": { @@ -214,6 +76,539 @@ "match": "\\\\[^\\r\\n]" } ] + }, + "type_tokens": { + "patterns": [ + { + "include": "#line_comment" + }, + { + "match": "(?<!\\w)(?:INTEGER|REAL|BOOLEAN|STRING|CHAR|ARRAY|C_INT|C_CHAR|C_STRING|C_DOUBLE|C_TYPE|(?i:VOID))(?!\\w)", + "name": "storage.type.cie-pseudocode" + }, + { + "match": "[A-Za-z_][A-Za-z0-9_]*", + "name": "entity.name.type.cpp" + }, + { + "match": "<", + "name": "punctuation.section.angle-brackets.begin.template.call.cpp" + }, + { + "match": ">", + "name": "punctuation.section.angle-brackets.end.template.call.cpp" + }, + { + "match": "\\.\\.\\.", + "name": "punctuation.vararg-ellipses.cpp" + }, + { + "match": ",", + "name": "punctuation.separator.delimiter.comma.cie-pseudocode" + } + ] + }, + "type_annotation": { + "match": "(:)\\s*((?<type>[A-Za-z_][A-Za-z0-9_]*(?:\\s*<\\s*\\g<type>(?:\\s*,\\s*\\g<type>)*\\s*>)?(?:\\s*\\.\\.\\.)?))", + "captures": { + "1": { + "name": "punctuation.separator.cie-pseudocode" + }, + "2": { + "patterns": [ + { + "include": "#type_tokens" + } + ] + } + } + }, + "element_type": { + "match": "(?<!\\w)(OF)\\s+((?<type>[A-Za-z_][A-Za-z0-9_]*(?:\\s*<\\s*\\g<type>(?:\\s*,\\s*\\g<type>)*\\s*>)?(?:\\s*\\.\\.\\.)?))", + "captures": { + "1": { + "name": "storage.modifier.cie-pseudocode" + }, + "2": { + "patterns": [ + { + "include": "#type_tokens" + } + ] + } + } + }, + "return_type": { + "match": "(?<!\\w)(RETURNS)\\s+((?<type>[A-Za-z_][A-Za-z0-9_]*(?:\\s*<\\s*\\g<type>(?:\\s*,\\s*\\g<type>)*\\s*>)?(?:\\s*\\.\\.\\.)?))", + "captures": { + "1": { + "name": "storage.modifier.cie-pseudocode" + }, + "2": { + "patterns": [ + { + "include": "#type_tokens" + } + ] + } + } + }, + "array_bounds": { + "begin": "\\[", + "end": "\\]", + "beginCaptures": { + "0": { + "name": "punctuation.section.brackets.cie-pseudocode" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.section.brackets.cie-pseudocode" + } + }, + "patterns": [ + { + "include": "#array_bounds" + }, + { + "include": "#code" + } + ], + "comment": "Keep array bound expressions outside declaration type annotations, including nested indexing." + }, + "declaration": { + "begin": "(?<!\\w)DECLARE(?!\\w)", + "end": "$", + "beginCaptures": { + "0": { + "name": "storage.modifier.cie-pseudocode" + } + }, + "patterns": [ + { + "include": "#array_bounds" + }, + { + "include": "#type_annotation" + }, + { + "include": "#element_type" + }, + { + "include": "#code" + } + ] + }, + "type_declaration": { + "begin": "(?<!\\w)(TYPE)\\s+([A-Za-z_][A-Za-z0-9_]*)(?!\\w)", + "end": "$", + "beginCaptures": { + "1": { + "name": "storage.modifier.cie-pseudocode" + }, + "2": { + "name": "entity.name.type.cpp" + } + }, + "patterns": [ + { + "match": "(=)\\s*(\\^)\\s*((?<type>[A-Za-z_][A-Za-z0-9_]*(?:\\s*<\\s*\\g<type>(?:\\s*,\\s*\\g<type>)*\\s*>)?(?:\\s*\\.\\.\\.)?))", + "captures": { + "1": { + "name": "keyword.operator.cie-pseudocode" + }, + "2": { + "name": "keyword.operator.cie-pseudocode" + }, + "3": { + "patterns": [ + { + "include": "#type_tokens" + } + ] + } + } + }, + { + "include": "#code" + } + ] + }, + "template_declaration": { + "match": "(?<!\\w)(TEMPLATE)\\s*(<\\s*(?<type>[A-Za-z_][A-Za-z0-9_]*(?:\\s*<\\s*\\g<type>(?:\\s*,\\s*\\g<type>)*\\s*>)?(?:\\s*\\.\\.\\.)?)(?:\\s*,\\s*\\g<type>)*\\s*>)", + "captures": { + "1": { + "name": "storage.type.template.cpp" + }, + "2": { + "patterns": [ + { + "include": "#template_parameters" + } + ] + } + } + }, + "template_call": { + "match": "(?<!\\w)([A-Za-z_][A-Za-z0-9_]*)(\\s*<\\s*(?<type>[A-Za-z_][A-Za-z0-9_]*(?:\\s*<\\s*\\g<type>(?:\\s*,\\s*\\g<type>)*\\s*>)?(?:\\s*\\.\\.\\.)?)(?:\\s*,\\s*\\g<type>)*\\s*>)(?=\\s*\\()", + "captures": { + "1": { + "name": "entity.name.function.cie-pseudocode" + }, + "2": { + "patterns": [ + { + "include": "#type_tokens" + } + ] + } + }, + "comment": "Balanced type lookahead follows Parser::isTemplateCall; a following parenthesis distinguishes calls from comparisons." + }, + "new_type": { + "match": "(?<![\\w.])(NEW)\\s+((?<type>[A-Za-z_][A-Za-z0-9_]*(?:\\s*<\\s*\\g<type>(?:\\s*,\\s*\\g<type>)*\\s*>)?(?:\\s*\\.\\.\\.)?))", + "captures": { + "1": { + "name": "keyword.operator.new.cpp" + }, + "2": { + "patterns": [ + { + "include": "#type_tokens" + } + ] + } + } + }, + "parameters": { + "begin": "\\(", + "end": "\\)", + "beginCaptures": { + "0": { + "name": "punctuation.section.parens.cie-pseudocode" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.section.parens.cie-pseudocode" + } + }, + "patterns": [ + { + "include": "#array_bounds" + }, + { + "include": "#type_annotation" + }, + { + "include": "#element_type" + }, + { + "include": "#code" + } + ] + }, + "function_header": { + "begin": "(?<!\\w)(FUNCTION|PROCEDURE)(?!\\w)(?:\\s+((?!OPERATOR\\b)[A-Za-z_][A-Za-z0-9_]*))?", + "end": "(?=(?<!\\w)(?:ENDFUNCTION|ENDPROCEDURE)(?!\\w))|$", + "beginCaptures": { + "1": { + "name": "storage.modifier.cie-pseudocode" + }, + "2": { + "name": "entity.name.function.cie-pseudocode" + } + }, + "patterns": [ + { + "include": "#parameters" + }, + { + "include": "#code" + } + ] + }, + "extern_parameters": { + "begin": "(?<!\\w)(EXTERN_FUNCTION|EXTERN_PROCEDURE)\\s+([A-Za-z_][A-Za-z0-9_]*)\\s*(\\()", + "end": "\\)", + "beginCaptures": { + "1": { + "name": "storage.modifier.cpp" + }, + "2": { + "name": "entity.name.function.cie-pseudocode" + }, + "3": { + "name": "punctuation.section.parens.cie-pseudocode" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.section.parens.cie-pseudocode" + } + }, + "patterns": [ + { + "include": "#type_tokens" + } + ] + }, + "method": { + "begin": "(?<!\\w)(?=(?:FUNCTION|PROCEDURE)(?!\\w))", + "end": "(?<!\\w)(?:ENDFUNCTION|ENDPROCEDURE)(?!\\w)", + "endCaptures": { + "0": { + "name": "storage.modifier.cie-pseudocode" + } + }, + "name": "meta.method.cie-pseudocode", + "patterns": [ + { + "include": "#code" + } + ], + "comment": "Only CLASS includes this method context; the injection also reaches nested declaration and signature contexts." + }, + "class": { + "begin": "(?<!\\w)(CLASS)\\s+([A-Za-z_][A-Za-z0-9_]*)(?!\\w)", + "end": "(?<!\\w)ENDCLASS(?!\\w)", + "beginCaptures": { + "1": { + "name": "storage.type.class.cpp" + }, + "2": { + "name": "entity.name.type.class.cpp" + } + }, + "endCaptures": { + "0": { + "name": "storage.type.class.cpp" + } + }, + "patterns": [ + { + "include": "#method" + }, + { + "match": "(?<!\\w)([A-Za-z_][A-Za-z0-9_]*)\\s*(:)\\s*((?<type>[A-Za-z_][A-Za-z0-9_]*(?:\\s*<\\s*\\g<type>(?:\\s*,\\s*\\g<type>)*\\s*>)?(?:\\s*\\.\\.\\.)?))", + "captures": { + "2": { + "name": "punctuation.separator.cie-pseudocode" + }, + "3": { + "patterns": [ + { + "include": "#type_tokens" + } + ] + } + } + }, + { + "include": "#code" + } + ] + }, + "code": { + "patterns": [ + { + "include": "#line_comment" + }, + { + "include": "#strings" + }, + { + "include": "#class" + }, + { + "include": "#type_declaration" + }, + { + "include": "#declaration" + }, + { + "include": "#template_declaration" + }, + { + "include": "#function_header" + }, + { + "include": "#extern_parameters" + }, + { + "include": "#new_type" + }, + { + "include": "#return_type" + }, + { + "match": "^\\s*(#)(LIB)\\s*$", + "captures": { + "1": { + "name": "punctuation.definition.directive.cpp" + }, + "2": { + "name": "keyword.control.directive.pragma.cpp" + } + } + }, + { + "match": "(?<!\\w)(?:CASE|ENDCASE)(?!\\w)", + "name": "keyword.control.switch.cpp" + }, + { + "match": "(?<!\\w)(?:OTHERWISE)(?!\\w)", + "name": "keyword.control.default.cpp" + }, + { + "match": "(?<!\\w)(?:CONSTANT)(?!\\w)", + "name": "storage.modifier.const.cpp" + }, + { + "match": "(?<!\\w)(?:CLASS|ENDCLASS)(?!\\w)", + "name": "storage.type.class.cpp" + }, + { + "match": "(?<!\\w)(?:PUBLIC)(?!\\w)", + "name": "storage.type.modifier.access.control.public.cpp" + }, + { + "match": "(?<!\\w)(?:PRIVATE)(?!\\w)", + "name": "storage.type.modifier.access.control.private.cpp" + }, + { + "match": "(?<!\\w)(?:TEMPLATE)(?!\\w)", + "name": "storage.type.template.cpp" + }, + { + "match": "(?<!\\w)(?:OPERATOR)(?!\\w)", + "name": "keyword.other.operator.overload.cpp" + }, + { + "match": "(?<!\\w)(?:OVERLOAD)(?!\\w)", + "name": "storage.modifier.cpp" + }, + { + "match": "(?<!\\w)(?:EXTERN)(?!\\w)", + "name": "storage.modifier.specifier.extern.cpp" + }, + { + "match": "(?<!\\w)(?:EXTERN_FUNCTION|EXTERN_PROCEDURE)(?!\\w)", + "name": "storage.modifier.cpp" + }, + { + "match": "(?<!\\w)(?:INCLUDE_OBJ)(?!\\w)", + "name": "keyword.control.directive.include.cpp" + }, + { + "match": "\\.\\.\\.", + "name": "punctuation.vararg-ellipses.cpp" + }, + { + "match": "(?<!\\w)(?:TRY|ENDTRY)(?!\\w)", + "name": "keyword.control.try.cpp" + }, + { + "match": "(?<!\\w)CATCH(?!\\w)", + "name": "keyword.control.catch.cpp" + }, + { + "match": "(?<!\\w)THROW(?!\\w)", + "name": "keyword.control.throw.cpp" + }, + { + "match": "(?<!\\w)(?:IF|THEN|ELSE|ENDIF|WHILE|DO|ENDWHILE|REPEAT|UNTIL|FOR|TO|STEP|NEXT|RETURN|CALL|NEW|DELETE)(?!\\w)", + "name": "keyword.control.cie-pseudocode" + }, + { + "match": "(?<!\\w)(?:INCLUDE|GLOBAL|ENDGLOBAL|DECLARE|TYPE|ENDTYPE|FUNCTION|ENDFUNCTION|PROCEDURE|ENDPROCEDURE|RETURNS|BYREF|BYVAL|OF)(?!\\w)", + "name": "storage.modifier.cie-pseudocode" + }, + { + "match": "(?<!\\w)(?:INTEGER|REAL|BOOLEAN|STRING|CHAR|ARRAY|C_INT|C_CHAR|C_STRING|C_DOUBLE|C_TYPE)(?!\\w)", + "name": "storage.type.cie-pseudocode" + }, + { + "match": "(?<!\\w)(?:TRUE|FALSE)(?!\\w)", + "name": "constant.language.cie-pseudocode" + }, + { + "match": "(?<!\\w)(?:INPUT|OUTPUT|OPENFILE|READFILE|WRITEFILE|CLOSEFILE|READ|WRITE|APPEND)(?!\\w)", + "name": "keyword.other.cie-pseudocode" + }, + { + "match": "(?<!\\w)(?:LENGTH|MID|RIGHT|LEFT|LCASE|UCASE|ASC|CHR|IS_NUM|NUM_TO_STR|STR_TO_NUM|EOF)(?!\\w)(?=\\s*\\()", + "name": "entity.name.function.cie-pseudocode" + }, + { + "match": "(?<!\\w)(?:DIV|MOD|AND|OR|NOT)(?!\\w)", + "name": "keyword.operator.word.cie-pseudocode" + }, + { + "match": "<-", + "name": "keyword.operator.assignment.cpp" + }, + { + "match": "<>|<=|>=|[+*/=&^<>-]", + "name": "keyword.operator.cie-pseudocode" + }, + { + "match": "(?<!\\w)\\d+(?:\\.\\d*)?(?!\\w)", + "name": "constant.numeric.cie-pseudocode" + }, + { + "include": "#template_call" + }, + { + "match": "(?<!\\w)[A-Za-z_][A-Za-z0-9_]*(?=\\s*\\()", + "name": "entity.name.function.cie-pseudocode" + }, + { + "match": "[()]", + "name": "punctuation.section.parens.cie-pseudocode" + }, + { + "match": "[\\[\\]]", + "name": "punctuation.section.brackets.cie-pseudocode" + }, + { + "match": ",", + "name": "punctuation.separator.delimiter.comma.cie-pseudocode" + }, + { + "match": "[:.]", + "name": "punctuation.separator.cie-pseudocode" + } + ] + }, + "template_parameters": { + "patterns": [ + { + "match": "[A-Za-z_][A-Za-z0-9_]*", + "name": "entity.name.type.template.cpp" + }, + { + "match": "<", + "name": "punctuation.section.angle-brackets.begin.template.definition.cpp" + }, + { + "match": ">", + "name": "punctuation.section.angle-brackets.end.template.definition.cpp" + }, + { + "include": "#type_tokens" + } + ] + } + }, + "injections": { + "L:meta.method.cie-pseudocode -comment -string": { + "patterns": [ + { + "match": "(?<!\\w)(?i:this)(?!\\w)", + "name": "variable.language.this.cpp" + } + ] } } } diff --git a/plugin/test/highlighting.test.js b/plugin/test/highlighting.test.js index 8be3d9c..c8ee3f7 100644 --- a/plugin/test/highlighting.test.js +++ b/plugin/test/highlighting.test.js @@ -22,9 +22,9 @@ before(async () => { grammar = await registry.loadGrammar('source.cie-pseudocode'); }); -function scopesAt(text, word) { +function scopesAt(text, word, state) { const offset = text.indexOf(word); - return grammar.tokenizeLine(text).tokens.find( + return grammar.tokenizeLine(text, state).tokens.find( token => token.startIndex <= offset && token.endIndex > offset).scopes; } @@ -46,11 +46,85 @@ test('C++ style scopes distinguish keywords, types, literals, operators and buil ["X <- 'A'", 'A', 'string.quoted.single'] ]) assert.ok(scopesAt(text, word).some(s => s.startsWith(scope)), text); for (const name of ['LENGTH', 'MID', 'RIGHT', 'LEFT', 'LCASE', 'UCASE', - 'ASC', 'CHR', 'IS_NUM', 'NUM_TO_STR', 'STR_TO_NUM', 'EOF']) { + 'ASC', 'CHR', 'IS_NUM', 'NUM_TO_STR', 'STR_TO_NUM', 'EOF', 'MALLOC', 'FREE', 'REALLOC']) { assert.ok(scopesAt(`${name}(X)`, name).includes('entity.name.function.cie-pseudocode')); } }); +test('function names include underscores and explicit nested template arguments', () => { + for (const text of ['CALL _print(1)', 'FUNCTION _value() RETURNS INTEGER', 'FUNCTION _value RETURNS INTEGER', + 'OUTPUT _value<Box<Pair<INTEGER, REAL>>>(X)', 'OUTPUT _value <Box<Types...>> (X)']) { + const name = text.includes('_print') ? '_print' : '_value'; + assert.ok(scopesAt(text, name).includes('entity.name.function.cie-pseudocode'), text); + } + const text = 'OUTPUT _value<Box<Pair<INTEGER, REAL>>>(X)'; + for (const name of ['Box', 'Pair']) assert.ok(scopesAt(text, name).includes('entity.name.type.cpp')); + for (const name of ['INTEGER', 'REAL']) assert.ok(scopesAt(text, name).includes('storage.type.cie-pseudocode')); + assert.ok(scopesAt(text, '<').includes('punctuation.section.angle-brackets.begin.template.call.cpp')); + assert.ok(scopesAt(text, '>').includes('punctuation.section.angle-brackets.end.template.call.cpp')); + for (const expression of ['A < B AND C > D', 'A < B + C > (D)', 'A <= B', 'A <> B']) { + assert.ok(scopesAt(expression, '<').includes('keyword.operator.cie-pseudocode'), expression); + assert.ok(!scopesAt(expression, 'B').includes('entity.name.type.cpp'), expression); + } + const configuration = JSON.parse(fs.readFileSync(path.join(__dirname, '../language-configuration.json'))); + assert.equal('_value'.match(new RegExp(configuration.wordPattern))[0], '_value'); +}); + +test('types are recognized in declarations, signatures, arrays, aliases, constructors and templates', () => { + for (const [text, names] of [ + ['TYPE Student', ['Student']], + ['CLASS Student', ['Student']], + ['TYPE Native = C_TYPE("unsigned int")', ['Native']], + ['TYPE NodePointer = ^Node', ['NodePointer', 'Node']], + ['DECLARE Value : Widget', ['Widget']], + ['DECLARE Values : ARRAY[Low:High, 1:Limit] OF Box<Widget>', ['Box', 'Widget']], + ['PROCEDURE Work(BYREF Value : Widget, Rest : ARRAY OF Node)', ['Widget', 'Node']], + ['FUNCTION Work(Value : Widget) RETURNS Result', ['Widget', 'Result']], + ['FUNCTION Work() RETURNS void', ['void']], + ['EXTERN "C" EXTERN_FUNCTION print(Native, RawPointer, ...) RETURNS NativeResult', ['Native', 'RawPointer', 'NativeResult']], + ['Value <- NEW Box<Widget>()', ['Box', 'Widget']], + ['TEMPLATE <T, Rest...>', ['T', 'Rest']] + ]) for (const name of names) { + assert.ok(scopesAt(text, name).some(s => /^(entity\.name\.type|storage\.type)\./.test(s)), `${text}: ${name}`); + } + for (const [text, word] of [ + ['CASE OF Selector', 'Selector'], ['Label : OUTPUT Value', 'OUTPUT'], + ['DECLARE Values : ARRAY[Low:High, Indices[Start]:Limit] OF Widget', 'High'], + ['DECLARE Values : ARRAY[Low:High, Indices[Start]:Limit] OF Widget', 'Limit'], + ['OUTPUT (Widget)Value', 'Widget'], ['OUTPUT Value.Field', 'Field'], + ['// DECLARE Value : Widget', 'Widget'], ['OUTPUT "DECLARE Value : Widget"', 'Widget'] + ]) assert.ok(!scopesAt(text, word).includes('entity.name.type.cpp'), `${text}: ${word}`); + let state = grammar.tokenizeLine('PROCEDURE Work(').ruleStack; + assert.ok(scopesAt('Value : Widget,', 'Widget', state).includes('entity.name.type.cpp')); + state = grammar.tokenizeLine('Value : Widget,', state).ruleStack; + state = grammar.tokenizeLine('Other : ARRAY[Low:High] OF Node)', state).ruleStack; + assert.ok(!scopesAt('Label : OUTPUT Value', 'OUTPUT', state).includes('entity.name.type.cpp')); +}); + +test('class fields have type scopes and this is confined to method code', () => { + let state = grammar.tokenizeLine('CLASS Counter').ruleStack; + for (const text of ['PRIVATE Value : Widget', 'PUBLIC Other : Widget', 'Field : Widget']) { + assert.ok(scopesAt(text, 'Widget', state).includes('entity.name.type.cpp')); + state = grammar.tokenizeLine(text, state).ruleStack; + } + assert.ok(!scopesAt('Field : this', 'this', state).includes('variable.language.this.cpp')); + state = grammar.tokenizeLine('PUBLIC PROCEDURE Update(Other : Widget)', state).ruleStack; + for (const word of ['this', 'This', 'THIS']) { + assert.ok(scopesAt(`${word}.Value <- Other`, word, state).includes('variable.language.this.cpp')); + } + for (const text of ['thisValue', '_this', 'this_1', '// this', 'OUTPUT "this"']) { + assert.ok(!scopesAt(text, 'this', state).includes('variable.language.this.cpp'), text); + } + assert.ok(!scopesAt('Label : OUTPUT Value', 'OUTPUT', state).includes('entity.name.type.cpp')); + assert.ok(scopesAt('DECLARE Copy : Widget(this.Value)', 'this', state).includes('variable.language.this.cpp')); + state = grammar.tokenizeLine('ENDPROCEDURE', state).ruleStack; + assert.ok(!scopesAt('Field : this', 'this', state).includes('variable.language.this.cpp')); + state = grammar.tokenizeLine('ENDCLASS', state).ruleStack; + assert.ok(!scopesAt('this <- 1', 'this', state).includes('variable.language.this.cpp')); + state = grammar.tokenizeLine('FUNCTION Outside() RETURNS INTEGER', state).ruleStack; + assert.ok(!scopesAt('this <- 1', 'this', state).includes('variable.language.this.cpp')); +}); + test('both assignment characters share the C++ assignment scope', () => { const text = 'X <- 1'; assert.deepEqual(scopesAt(text, '<-'), scopesAt(text, '-')); |
