aboutsummaryrefslogtreecommitdiff
path: root/plugin
diff options
context:
space:
mode:
Diffstat (limited to 'plugin')
-rw-r--r--plugin/AGENTS/ARCH.md19
-rw-r--r--plugin/ThirdPartyNotices.md4
-rw-r--r--plugin/examples/highlighting.cps19
-rw-r--r--plugin/language-configuration.json2
-rw-r--r--plugin/syntaxes/cie-pseudocode.tmLanguage.json673
-rw-r--r--plugin/test/highlighting.test.js80
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, '-'));