From 22a23ed06ba2a839eee6d324b1036fc4b99dda89 Mon Sep 17 00:00:00 2001 From: Andrew Dupont Date: Thu, 19 Sep 2024 13:44:28 -0700 Subject: [PATCH 1/9] [language-php] Highlight null-safe property access correctly --- .../grammars/tree-sitter/queries/highlights.scm | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/language-php/grammars/tree-sitter/queries/highlights.scm b/packages/language-php/grammars/tree-sitter/queries/highlights.scm index b04b4db097..32cd40ed1a 100644 --- a/packages/language-php/grammars/tree-sitter/queries/highlights.scm +++ b/packages/language-php/grammars/tree-sitter/queries/highlights.scm @@ -302,21 +302,33 @@ ; The "bar" in `$foo->bar()`. (member_call_expression name: (name) @support.other.function.method.php) +(nullsafe_member_call_expression + name: (name) @support.other.function.method.php) ; The "bar" in `$foo->bar`. (member_access_expression name: (name) @support.other.property.php (#set! capture.final true)) +(nullsafe_member_access_expression + name: (name) @support.other.property.php + (#set! capture.final true)) ; The "$bar" in `$foo->$bar()`. (member_call_expression name: (variable_name) @variable.other.method.php (#set! capture.final true)) +(nullsafe_member_call_expression + name: (variable_name) @variable.other.method.php + (#set! capture.final true)) ; The "$bar" in `$foo->$bar`. (member_access_expression name: (variable_name) @variable.other.property.php (#set! capture.final true)) +; The "$bar" in `$foo->$bar`. +(nullsafe_member_access_expression + name: (variable_name) @variable.other.property.php + (#set! capture.final true)) ; The "Foo" in `new Foo();`. (object_creation_expression @@ -638,6 +650,7 @@ ] @keyword.operator.assignment.compound.php "->" @keyword.operator.class.php +"?->" @keyword.operator.class.null-safe.php "=>" @punctuation.separator.key-value.php "\\" @keyword.operator.namespace.php From 4d350454c2a22ba80dc8c590b0a27e161ed9cefa Mon Sep 17 00:00:00 2001 From: Andrew Dupont Date: Thu, 19 Sep 2024 13:44:59 -0700 Subject: [PATCH 2/9] [language-c] Scope template delimiters properly in C++ --- .../grammars/tree-sitter-cpp/highlights.scm | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/language-c/grammars/tree-sitter-cpp/highlights.scm b/packages/language-c/grammars/tree-sitter-cpp/highlights.scm index 8f4da9f5d8..b3c5a76ec5 100644 --- a/packages/language-c/grammars/tree-sitter-cpp/highlights.scm +++ b/packages/language-c/grammars/tree-sitter-cpp/highlights.scm @@ -489,9 +489,6 @@ ["&" "|" "^" "~" "<<" ">>"] @keyword.operator.bitwise.cpp) -(binary_expression - ["<" ">"] @keyword.operator.comparison.cpp) - (binary_expression ["+" "-" "*" "/" "%"] @keyword.operator.arithmetic.cpp) @@ -534,6 +531,14 @@ ")" @punctuation.definition.expression.end.bracket.round.cpp (#set! capture.final true))) +(template_argument_list + "<" @punctuation.definition.parameters.begin.bracket.angle.cpp + ">" @punctuation.definition.parameters.end.bracket.angle.cpp) + +(template_parameter_list + "<" @punctuation.definition.parameters.begin.bracket.angle.cpp + ">" @punctuation.definition.parameters.end.bracket.angle.cpp) + "{" @punctuation.definition.block.begin.bracket.curly.cpp "}" @punctuation.definition.block.end.bracket.curly.cpp "(" @punctuation.definition.begin.bracket.round.cpp From 3e3e0d6f0ec88c768569bdeb76c0124ce49e300d Mon Sep 17 00:00:00 2001 From: Andrew Dupont Date: Fri, 20 Sep 2024 11:52:08 -0700 Subject: [PATCH 3/9] [language-javascript] Add scope specs The first of what will hopefully be a series of similar additions, ordered (roughly) by grammar complexity. --- .../grammars/tree-sitter/highlights.scm | 36 ++- .../language-javascript/spec/.eslintrc.js | 14 ++ .../spec/fixtures/sample.js | 236 ++++++++++++++++++ .../spec/tree-sitter-grammar-spec.js | 13 + 4 files changed, 294 insertions(+), 5 deletions(-) create mode 100644 packages/language-javascript/spec/.eslintrc.js create mode 100644 packages/language-javascript/spec/fixtures/sample.js create mode 100644 packages/language-javascript/spec/tree-sitter-grammar-spec.js diff --git a/packages/language-javascript/grammars/tree-sitter/highlights.scm b/packages/language-javascript/grammars/tree-sitter/highlights.scm index f0527bb1c6..e434835f3b 100644 --- a/packages/language-javascript/grammars/tree-sitter/highlights.scm +++ b/packages/language-javascript/grammars/tree-sitter/highlights.scm @@ -162,8 +162,9 @@ ; A variable object destructuring with default value: ; The "foo" in `let { foo = true } = something` -(object_assignment_pattern +((object_assignment_pattern (shorthand_property_identifier_pattern) @variable.other.assignment.destructuring.js) + (#is-not? test.descendantOfType "formal_parameters")) ; A variable object alias destructuring: ; The "bar" and "foo" in `let { bar: foo } = something` @@ -190,24 +191,34 @@ ; TODO: This arguably isn't an object key. key: (_) @entity.other.attribute-name.js value: (assignment_pattern - left: (identifier) @variable.other.assignment.destructuring.js))) + left: (identifier) @_IGNORE_))) + +(object_pattern + (pair_pattern + key: (_) @_IGNORE_ + value: (assignment_pattern + left: (identifier) @variable.other.assignment.destructuring.js)) + (#is-not? test.descendantOfType "formal_parameters")) ; A "rest" parameter destructuring: ; The "bar" in `let { foo, ...bar } = something` (object_pattern (rest_pattern - (identifier) @variable.other.assignment.destructuring.rest.js)) + (identifier) @variable.other.assignment.destructuring.rest.js) + (#is-not? test.descendantOfType "formal_parameters")) ; An array-destructured assignment or reassignment, regardless of depth: ; The "foo" in `[foo] = bar;` and `[[foo]] = bar;`. -(array_pattern +((array_pattern (identifier) @variable.other.assignment.destructuring.js) + (#is-not? test.descendantOfType "formal_parameters")) ; An array-destructured assignment or reassignment with a default, regardless of depth: ; The "baz" in `let [foo, bar, baz = false] = something;` and `let [[baz = 5]] = something`; (array_pattern (assignment_pattern - (identifier) @variable.other.assignment.destructuring.js)) + (identifier) @variable.other.assignment.destructuring.js) + (#is-not? test.descendantOfType "formal_parameters")) ; A variable declaration in a for…(in|of) loop: @@ -279,6 +290,21 @@ (assignment_pattern left: (identifier) @variable.parameter.js)) +; The "foo" in `function ({ foo = 3 }) {`. +(object_assignment_pattern + (shorthand_property_identifier_pattern) @variable.parameter.destructuring.shorthand.js + (#is? test.descendantOfType "formal_parameters") + (#set! capture.final)) + +; The "foo" in `function ({ key: foo = 3 }) {`. +(pair_pattern + key: (property_identifier) + value: (assignment_pattern + left: (identifier) @variable.parameter.destructuring.value.js + ) + (#is? test.descendantOfType "formal_parameters") + (#set! capture.final)) + ; FUNCTIONS ; ========= diff --git a/packages/language-javascript/spec/.eslintrc.js b/packages/language-javascript/spec/.eslintrc.js new file mode 100644 index 0000000000..8cc26374a1 --- /dev/null +++ b/packages/language-javascript/spec/.eslintrc.js @@ -0,0 +1,14 @@ +module.exports = { + env: { jasmine: true }, + globals: { + waitsForPromise: true, + runGrammarTests: true, + runFoldsTests: true + }, + rules: { + "node/no-unpublished-require": "off", + "node/no-extraneous-require": "off", + "no-unused-vars": "off", + "no-empty": "off" + } +}; diff --git a/packages/language-javascript/spec/fixtures/sample.js b/packages/language-javascript/spec/fixtures/sample.js new file mode 100644 index 0000000000..9e26487803 --- /dev/null +++ b/packages/language-javascript/spec/fixtures/sample.js @@ -0,0 +1,236 @@ +/* eslint-disable */ +function diff(obj1, obj2, pathConverter) { +// ^ storage.type.function + // ^ entity.name.function.definition + if (!obj1 || typeof obj1 != 'object' || !obj2 || typeof obj2 != 'object') { +// ^ keyword.control.conditional.if + // ^ keyword.operator.unary.typeof + throw new Error('both arguments must be objects or arrays'); + // ^^^^^ keyword.control.trycatch.throw + // ^ keyword.operator.new.js + // ^ punctuation.definition.string.begin + // ^ string.quoted.single + } + + pathConverter || + // ^^ keyword.operator.logical + (pathConverter = function (arr) { + // ^ variable.parameter + return arr; + }); + + function getDiff({obj1, obj2, basePath, basePathForRemoves, diffs}) { + var obj1Keys = Object.keys(obj1); + // ^ support.object.builtin + // ^ support.function.builtin + var obj1KeysLength = obj1Keys.length; + // ^ keyword.operator.assignment + var obj2Keys = Object.keys(obj2); + var obj2KeysLength = obj2Keys.length; + var path; + + var lengthDelta = obj1.length - obj2.length; + // ^ variable.other.assignment + + if (trimFromRight(obj1, obj2)) { + // ^ punctuation.definition.begin.bracket.round + for (var i = 0; i < obj1KeysLength; i++) { + var key = Array.isArray(obj1) ? Number(obj1Keys[i]) : obj1Keys[i]; + if (!(key in obj2)) { + path = basePathForRemoves.concat(key); + diffs.remove.push({ + op: 'remove', + path: pathConverter(path), + }); + } + } + + for (var i = 0; i < obj2KeysLength; i++) { + // ^ punctuation.definition.end.bracket.round + var key = Array.isArray(obj2) ? Number(obj2Keys[i]) : obj2Keys[i]; + // ^ keyword.operator.ternary + // ^ keyword.operator.ternary + pushReplaces({ + key, + // ^ entity.other.attribute-name.shorthand + obj1, + obj2, + path: basePath.concat(key), + // ^ entity.other.attribute-name.js + pathForRemoves: basePath.concat(key), + diffs, + }); + } + } else { + // trim from left, objects are both arrays + for (var i = 0; i < lengthDelta; i++) { + path = basePathForRemoves.concat(i); + diffs.remove.push({ + op: 'remove', + path: pathConverter(path), + }); + } + + // now make a copy of obj1 with excess elements left trimmed and see if there any replaces + // ^ comment.line.double-slash.js + var obj1Trimmed = obj1.slice(lengthDelta); + for (var i = 0; i < obj2KeysLength; i++) { + pushReplaces({ + key: i, + obj1: obj1Trimmed, + obj2, + path: basePath.concat(i), + // since list of removes are reversed before presenting result, + // we need to ignore existing parent removes when doing nested removes + pathForRemoves: basePath.concat(i + lengthDelta), + diffs, + }); + } + } + } + var diffs = {remove: [], replace: [], add: []}; + getDiff({ + obj1, + obj2, + basePath: [], + basePathForRemoves: [], + diffs, + }); + + // reverse removes since we want to maintain indexes + return diffs.remove + .reverse() + .concat(diffs.replace) + .concat(diffs.add); + + function pushReplaces({key, obj1, obj2, path, pathForRemoves, diffs}) { + var obj1AtKey = obj1[key]; + var obj2AtKey = obj2[key]; + + let { bar, baz = 3 } = foo; + // ^ variable.other.assignment.destructuring + // ^ variable.other.assignment.destructuring + + if (!(key in obj1) && (key in obj2)) { + // ^ keyword.operator.logical + var obj2Value = obj2AtKey; + diffs.add.push({ + op: 'add', + path: pathConverter(path), + value: obj2Value, + }); + } else if (obj1AtKey !== obj2AtKey) { + if (Object(obj1AtKey) !== obj1AtKey || + Object(obj2AtKey) !== obj2AtKey || differentTypes(obj1AtKey, obj2AtKey) + // ^ support.other.function + ) { + pushReplace(path, diffs, obj2AtKey); + } else { + if (!Object.keys(obj1AtKey).length && + !Object.keys(obj2AtKey).length && + String(obj1AtKey) != String(obj2AtKey)) { + pushReplace(path, diffs, obj2AtKey); + } else { + getDiff({ + // ^ punctuation.definition.object.begin.bracket.curly + obj1: obj1[key], + obj2: obj2[key], + basePath: path, + basePathForRemoves: pathForRemoves, + diffs}); + // ^ punctuation.definition.object.end.bracket.curly + } + } + } + } + + function pushReplace(path, diffs, newValue) { + diffs.replace.push({ + op: 'replace', + path: pathConverter(path), + value: newValue, + }); + } +} + +let { three: alias = 3 } = foo; + // ^ variable.other.assignment.destructuring + // ^ !variable.parameter + +function foo(bar, { three: alias = "foo" } = {}) { + // ^ punctuation.definition.parameters.begin.bracket.round + // ^ entity.other.attribute-name + // ^ variable.parameter.destructuring.value + // ^ !variable.other.assignment + // ^ string.quoted.double +} + +/* This is a block comment/ */ +// <- punctuation.definition.comment.begin +// <- comment.block + // ^ punctuation.definition.comment.end + + /** + * Does something very important. + * @extends AnotherClass + */ +// ^ comment.block.documentation +class SomeClass extends AnotherClass { + // ^ entity.name.type.class + // ^ entity.other.inherited-class + instances = 0 +//^ variable.other.assignment.property.public + + #wrappers = new Set() +//^ variable.other.assignment.property.private + // ^ support.class.builtin.instance + + constructor() { + // ^ entity.name.function.method.definition + this.instances++; + // ^ variable.language.this + // ^ keyword.operator.accessor + // ^ variable.other.assignment.property + // ^ keyword.operator.increment + } + + #foo() { +// ^ entity.name.function.method.private.definition + let foo = 3; + return `this is a template string with an interpolation ${foo} inside it` + // ^ punctuation.definition.string.begin + // ^ string.quoted.template.js + // ^ punctuation.section.embedded.begin.js + // ^ punctuation.section.embedded.end.js + } + + *each () { +//^ storage.modifier.generator +// ^ entity.name.function.method.definition + try { + + } catch (err) { + // ^^^^^ keyword.control.trycatch.catch + // ^^^ variable.other.assignment.catch + debugger; + // ^ keyword.other.debugger + } finally { + // ^ keyword.control.trycatch.finally + } + } +} + +const PATH = path.dirname(__dirname); +// <- storage.type.const + // ^ support.other.object + // ^ support.object.builtin.dirname + +let cache = null; + // ^ constant.language.null +const UNDEF = undefined; + // ^ constant.language.undefined +module.exports = diff; +//^ support.object.builtin.module + // ^ punctuation.terminator.statement + +/* eslint-enable */ diff --git a/packages/language-javascript/spec/tree-sitter-grammar-spec.js b/packages/language-javascript/spec/tree-sitter-grammar-spec.js new file mode 100644 index 0000000000..e623de10dc --- /dev/null +++ b/packages/language-javascript/spec/tree-sitter-grammar-spec.js @@ -0,0 +1,13 @@ +const path = require('path'); + +describe('WASM Tree-sitter JavaScript grammar', () => { + + beforeEach(async () => { + await atom.packages.activatePackage('language-javascript'); + }); + + it('passes grammar tests', async () => { + await runGrammarTests(path.join(__dirname, 'fixtures', 'sample.js'), /\/\//) + }); + +}); From 7cfee9c77ababaac21830ca902eb88f13084be8a Mon Sep 17 00:00:00 2001 From: Andrew Dupont Date: Sun, 22 Sep 2024 13:12:06 -0700 Subject: [PATCH 4/9] =?UTF-8?q?[language-c]=20Extract=20common=20parts=20o?= =?UTF-8?q?f=20C=20and=20C++=20`highlights.scm`=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …into a new `common/highlights.scm`. Add highlighting specs for C and C++ Tree-sitter grammars. --- .../language-c/grammars/common/highlights.scm | 430 +++++++++++++++++ .../grammars/modern-tree-sitter-c.cson | 6 +- .../grammars/modern-tree-sitter-cpp.cson | 6 +- .../grammars/tree-sitter-c/highlights.scm | 419 +--------------- .../grammars/tree-sitter-cpp/highlights.scm | 448 ++---------------- packages/language-c/spec/.eslintrc.js | 14 + packages/language-c/spec/c-spec.js | 164 +++---- packages/language-c/spec/fixtures/sample.c | 92 ++++ packages/language-c/spec/fixtures/sample.cpp | 109 +++++ .../spec/tree-sitter-grammar-spec.js | 14 + 10 files changed, 785 insertions(+), 917 deletions(-) create mode 100644 packages/language-c/grammars/common/highlights.scm create mode 100644 packages/language-c/spec/.eslintrc.js create mode 100644 packages/language-c/spec/fixtures/sample.c create mode 100644 packages/language-c/spec/fixtures/sample.cpp create mode 100644 packages/language-c/spec/tree-sitter-grammar-spec.js diff --git a/packages/language-c/grammars/common/highlights.scm b/packages/language-c/grammars/common/highlights.scm new file mode 100644 index 0000000000..7c17086a9c --- /dev/null +++ b/packages/language-c/grammars/common/highlights.scm @@ -0,0 +1,430 @@ +; PREPROCESSOR +; ============ + +[ + "#if" + "#ifdef" + "#ifndef" + "#endif" + "#elif" + "#else" +] @keyword.control.directive.conditional._LANG_ + +"#define" @keyword.control.directive.define._LANG_ +"#include" @keyword.control.directive.include._LANG_ + +(["#if" "#ifdef" "#ifndef" "#endif" "#elif" "#else" "#define" "#include"] @punctuation.definition.directive._LANG_ + (#set! adjust.endAfterFirstMatchOf "^#")) + +; `preproc_directive` will be used when the parser doesn't recognize the +; directive as one of the above. It's permissive; `#afdfafsdfdfad` would be +; parsed as a `preproc_directive`. +; +; Hence this rule will match if the more specific rules above haven't matched. +; The anonymous nodes will match under ideal conditions, but might not be +; present even when they ought to be _if_ the parser is flummoxed; so this'll +; sometimes catch `#ifdef` and others. +((preproc_directive) @keyword.control.directive._LANG_ + (#set! capture.shy true)) + +((preproc_directive) @punctuation.definition.directive._LANG_ + (#set! capture.shy true) + (#set! adjust.endAfterFirstMatchOf "^#")) + +; Macro functions are definitely entities. +(preproc_function_def + (identifier) @entity.name.function.preprocessor._LANG_ + (#set! capture.final true)) + +; Identifiers in macro definitions are definitely constants. +((preproc_def + name: (identifier) @constant.preprocessor._LANG_)) + +; We can also safely treat identifiers as constants in `#ifdef`… +((preproc_ifdef + (identifier) @constant.preprocessor._LANG_)) + +; …and `#if` and `#elif`… +(preproc_if + (binary_expression + (identifier) @constant.preprocessor._LANG_)) +(preproc_elif + (binary_expression + (identifier) @constant.preprocessor._LANG_)) + +; …and `#undef`. +((preproc_call + directive: (preproc_directive) @_IGNORE_ + argument: (preproc_arg) @constant.preprocessor._LANG_) + (#eq? @_IGNORE_ "#undef")) + +(system_lib_string) @string.quoted.other.lt-gt.include._LANG_ +((system_lib_string) @punctuation.definition.string.begin._LANG_ + (#set! adjust.endAfterFirstMatchOf "^<")) +((system_lib_string) @punctuation.definition.string.end._LANG_ + (#set! adjust.startBeforeFirstMatchOf ">$")) + + +; TYPES +; ===== + +; WORKAROUND: If we're in an error state, don't trust the parser's designation +; of `type_identifier`. Someone's probably just typing on a new line. +(ERROR + (type_identifier) @_IGNORE_ + (#set! capture.final true)) + +; When the user has typed `#define FOO`, the macro injection thinks that `FOO` +; is a type declaration (for some reason). This node structure seems to exist +; only in that unusual and incorrect scenario, so we'll stop it from happening +; so that it doesn't override the underlying `constant.other.c` scope. +(translation_unit + (type_identifier) @_IGNORE_ + (#set! capture.final)) + +; These types are all reserved words; if we see an identifier with this name, +; it must be a type. +((identifier) @support.storage.type.builtin._LANG_ + (#match? @support.storage.type.builtin._LANG_ "^(char|int|float|double|long)$")) + +; Assume any identifier that ends in `_t` is a type. This convention is not +; always followed, but it's a very strong indicator when it's present. +((identifier) @support.other.storage.type._LANG_ + (#match? @support.other.storage.type._LANG_ "_t$")) + + +(enum_specifier + name: (type_identifier) @entity.name.type.enum._LANG_ + (#set! capture.final)) + +(enumerator + name: (identifier) @variable.declaration.enum._LANG_) + +((primitive_type) @support.storage.type.builtin.stdint._LANG_ + (#match? @support.storage.type.builtin.stdint._LANG_ "^(int8_t|int16_t|int32_t|int64_t|uint8_t|uint16_t|uint32_t|uint64_t|int_least8_t|int_least16_t|int_least32_t|int_least64_t|uint_least8_t|uint_least16_t|uint_least32_t|uint_least64_t|int_fast8_t|int_fast16_t|int_fast32_t|int_fast64_t|uint_fast8_t|uint_fast16_t|uint_fast32_t|uint_fast64_t|intptr_t|uintptr_t|intmax_t|intmax_t|uintmax_t|uintmax_t)$") + (#set! capture.final)) + +(primitive_type) @support.storage.type.builtin._LANG_ + +(struct_specifier + (type_identifier) @entity.name.type.struct._LANG_ + (#set! capture.final true)) + + +; STRINGS +; ======= + +; CAVEAT: tree-sitter-c doesn't identify placeholders like `%c` in strings. +; Candidate for an injection grammar. +(string_literal "\"") @string.quoted.double._LANG_ + +(string_literal + "\"" @punctuation.definition.string.begin._LANG_ + (#is? test.first true)) + +(string_literal + "\"" @punctuation.definition.string.end._LANG_ + (#is? test.last true)) + +(char_literal "'") @string.quoted.single._LANG_ + +(char_literal + "'" @punctuation.definition.string.begin._LANG_ + (#is? test.first true)) + +(char_literal + "'" @punctuation.definition.string.end._LANG_ + (#is? test.last true)) + +(string_literal (escape_sequence) @constant.character.escape._LANG_) +(char_literal (escape_sequence) @constant.character.escape._LANG_) + + +; FUNCTIONS +; ========= + +(function_declarator + (identifier) @entity.name.function._LANG_) + +(call_expression + (identifier) @support.function.c99._LANG_ + ; Regex copied from the TM grammar. + (#match? @support.function.c99._LANG_ "^(_Exit|(?:nearbyint|nextafter|nexttoward|netoward|nan)[fl]?|a(?:cos|sin)h?[fl]?|abort|abs|asctime|assert|atan(?:[h2]?[fl]?)?|atexit|ato[ifl]|atoll|bsearch|btowc|cabs[fl]?|cacos|cacos[fl]|cacosh[fl]?|calloc|carg[fl]?|casinh?[fl]?|catanh?[fl]?|cbrt[fl]?|ccosh?[fl]?|ceil[fl]?|cexp[fl]?|cimag[fl]?|clearerr|clock|clog[fl]?|conj[fl]?|copysign[fl]?|cosh?[fl]?|cpow[fl]?|cproj[fl]?|creal[fl]?|csinh?[fl]?|csqrt[fl]?|ctanh?[fl]?|ctime|difftime|div|erfc?[fl]?|exit|fabs[fl]?|exp(?:2[fl]?|[fl]|m1[fl]?)?|fclose|fdim[fl]?|fe[gs]et(?:env|exceptflag|round)|feclearexcept|feholdexcept|feof|feraiseexcept|ferror|fetestexcept|feupdateenv|fflush|fgetpos|fgetw?[sc]|floor[fl]?|fmax?[fl]?|fmin[fl]?|fmod[fl]?|fopen|fpclassify|fprintf|fputw?[sc]|fread|free|freopen|frexp[fl]?|fscanf|fseek|fsetpos|ftell|fwide|fwprintf|fwrite|fwscanf|genv|get[sc]|getchar|gmtime|gwc|gwchar|hypot[fl]?|ilogb[fl]?|imaxabs|imaxdiv|isalnum|isalpha|isblank|iscntrl|isdigit|isfinite|isgraph|isgreater|isgreaterequal|isinf|isless(?:equal|greater)?|isw?lower|isnan|isnormal|isw?print|isw?punct|isw?space|isunordered|isw?upper|iswalnum|iswalpha|iswblank|iswcntrl|iswctype|iswdigit|iswgraph|isw?xdigit|labs|ldexp[fl]?|ldiv|lgamma[fl]?|llabs|lldiv|llrint[fl]?|llround[fl]?|localeconv|localtime|log[2b]?[fl]?|log1[p0][fl]?|longjmp|lrint[fl]?|lround[fl]?|malloc|mbr?len|mbr?towc|mbsinit|mbsrtowcs|mbstowcs|memchr|memcmp|memcpy|memmove|memset|mktime|modf[fl]?|perror|pow[fl]?|printf|puts|putw?c(?:har)?|qsort|raise|rand|remainder[fl]?|realloc|remove|remquo[fl]?|rename|rewind|rint[fl]?|round[fl]?|scalbl?n[fl]?|scanf|setbuf|setjmp|setlocale|setvbuf|signal|signbit|sinh?[fl]?|snprintf|sprintf|sqrt[fl]?|srand|sscanf|strcat|strchr|strcmp|strcoll|strcpy|strcspn|strerror|strftime|strlen|strncat|strncmp|strncpy|strpbrk|strrchr|strspn|strstr|strto[kdf]|strtoimax|strtol[dl]?|strtoull?|strtoumax|strxfrm|swprintf|swscanf|system|tan|tan[fl]|tanh[fl]?|tgamma[fl]?|time|tmpfile|tmpnam|tolower|toupper|trunc[fl]?|ungetw?c|va_arg|va_copy|va_end|va_start|vfw?printf|vfw?scanf|vprintf|vscanf|vsnprintf|vsprintf|vsscanf|vswprintf|vswscanf|vwprintf|vwscanf|wcrtomb|wcscat|wcschr|wcscmp|wcscoll|wcscpy|wcscspn|wcsftime|wcslen|wcsncat|wcsncmp|wcsncpy|wcspbrk|wcsrchr|wcsrtombs|wcsspn|wcsstr|wcsto[dkf]|wcstoimax|wcstol[dl]?|wcstombs|wcstoull?|wcstoumax|wcsxfrm|wctom?b|wmem(?:set|chr|cpy|cmp|move)|wprintf|wscanf)$") + (#set! capture.final true)) + +; The "foo" in `thing->troz->foo(...)`. +(call_expression + (field_expression + field: (field_identifier) @support.other.function._LANG_) + (#set! capture.final true)) + +(call_expression + (identifier) @support.other.function._LANG_ + (#set! capture.final true)) + +; VARIABLES +; ========= + +; Declarations and assignments +; ---------------------------- + +; The "x" in `int x;` +(declaration + declarator: (identifier) @variable.other.declaration._LANG_) + +; The "x" in `int x = y;` +(init_declarator + declarator: (identifier) @variable.other.declaration._LANG_) + +; The "x" in `SomeType *x;` +; (Should work no matter how many pointers deep we are.) +(pointer_declarator + declarator: [(identifier) (field_identifier)] @variable.other.declaration.pointer._LANG_ + (#is? test.descendantOfType "declaration field_declaration")) + +; An array declarator: the "table" in `int table[4];` +(array_declarator + declarator: (identifier) @variable.other.declaration._LANG_) + +; A member of a struct. +(field_declaration + (field_identifier) @variable.other.declaration.member._LANG_) + +; An attribute in a C99 struct designated initializer: +; the "foo" in `MY_TYPE a = { .foo = true }; +(initializer_pair + (field_designator + (field_identifier) @variable.other.declaration.member._LANG_)) + +; (and the associated ".") +(initializer_pair + (field_designator + "." @keyword.operator.accessor._LANG_)) + +(field_declaration + (pointer_declarator + (field_identifier) @variable.other.declaration.member._LANG_)) + +(field_declaration + (array_declarator + (field_identifier) @variable.other.declaration.member._LANG_)) + +(init_declarator + (pointer_declarator + (identifier) @variable.other.declaration.member._LANG_)) + +; The "x" in `x = y;` +(assignment_expression + left: (identifier) @variable.other.assignment._LANG_) + +; The "foo" in `something->foo = "bar";` +(assignment_expression + left: (field_expression + field: (field_identifier) @variable.other.member.assignment._LANG_) + (#set! capture.final)) + +; Goto label definitions: the "foo" in `foo:` before a statement. +(labeled_statement + label: (statement_identifier) @entity.name.label._LANG_) + +; Goto statements — the "foo" in `goto foo;` +(goto_statement + label: (statement_identifier) @support.other.label._LANG_) + +; Function parameters +; ------------------- + +(preproc_params + (identifier) @variable.parameter.preprocessor._LANG_) + +; The "foo" in `const char foo` within a parameter list. +(parameter_declaration + declarator: (identifier) @variable.parameter._LANG_) + +; The "foo" in `const char *foo` within a parameter list. +; (Should work no matter how many pointers deep we are.) +(pointer_declarator + declarator: [(identifier) (field_identifier)] @variable.parameter.pointer._LANG_ + (#is? test.descendantOfType "parameter_declaration")) + +; The "foo" in `const char foo[]` within a parameter list. +(parameter_declaration + declarator: (array_declarator + declarator: (identifier) @variable.parameter._LANG_)) + +; The "argv" in `char* argv[]` within a parameter list. +(parameter_declaration + declarator: (pointer_declarator + declarator: (array_declarator + declarator: (identifier) @variable.parameter._LANG_))) + +; The "size" in `finfo->size`. +(field_expression + "->" + field: (field_identifier) @variable.other.member._LANG_) + +; The "bar" in `foo.bar`. +(field_expression + operator: "." + field: (field_identifier) @variable.other.member._LANG_) + + +; NUMBERS +; ======= + +(number_literal) @constant.numeric._LANG_ + + +; CONSTANTS +; ========= + +(null) @constant.language.null._LANG_ + +[ + (true) + (false) +] @constant.language.boolean._TYPE_._LANG_ + +; Don't try to scope (e.g.) `int FOO = 1` as a constant when the user types `=` +; but has not typed the value yet. +(ERROR + (identifier) @_IGNORE_ + (#set! capture.final)) + +; In most languages we wouldn't be making the assumption that an all-caps +; identifier should be treated as a constant. But those languages don't have +; macro preprocessors. The convention is decently strong in C/C++ that all-caps +; identifiers will refer to `#define`d things. +((identifier) @constant.other._LANG_ + (#match? @constant.other._LANG_ "^[_A-Z][_A-Z0-9]*$") + (#set! capture.shy)) + + +; COMMENTS +; ======== + +; Match // comments. +((comment) @comment.line.double-slash._LANG_ + (#match? @comment.line.double-slash._LANG_ "^\\s*//")) + +((comment) @punctuation.definition.comment._LANG_ + (#match? @punctuation.definition.comment._LANG_ "^\\s*//") + (#set! adjust.startAndEndAroundFirstMatchOf "//")) + +; Match /* */ comments. +((comment) @comment.block._LANG_ + (#match? @comment.block._LANG_ "^/\\*")) + +((comment) @punctuation.definition.comment.begin._LANG_ + (#match? @punctuation.definition.comment.begin._LANG_ "^/\\*") + (#set! adjust.startAndEndAroundFirstMatchOf "^/\\*")) + +((comment) @punctuation.definition.comment.end._LANG_ + (#match? @punctuation.definition.comment.end._LANG_ "\\*/$") + (#set! adjust.startAndEndAroundFirstMatchOf "\\*/$")) + +; KEYWORDS +; ======== + +[ + "break" + "case" + "continue" + "default" + "do" + "else" + "for" + "goto" + "if" + "return" + "switch" + "while" +] @keyword.control._TYPE_._LANG_ + + +; OPERATORS +; ========= + +(pointer_declarator "*" @keyword.operator.pointer._LANG_) +(abstract_pointer_declarator "*" @keyword.operator.pointer._LANG_) +(pointer_expression "*" @keyword.operator.pointer._LANG_) + +"sizeof" @keyword.operator.sizeof._LANG_ +(pointer_expression "&" @keyword.operator.pointer._LANG_) + +"=" @keyword.operator.assignment._LANG_ + +[ + "%=" + "+=" + "-=" + "*=" + "/=" + "&=" + "^=" + "<<=" + ">>=" + "|=" +] @keyword.operator.assignment.compound._LANG_ + +(binary_expression + ["==" "!=" ">" "<" ">=" "<="] @keyword.operator.comparison._LANG_) + +(binary_expression + ["&" "|" "^" "~" "<<" ">>"] + @keyword.operator.bitwise._LANG_) + +"++" @keyword.operator.increment._LANG_ +"--" @keyword.operator.decrement._LANG_ + +(binary_expression ["+" "-" "*" "/" "%"] @keyword.operator.arithmetic._LANG_) +(unary_expression ["+" "-" "!"] @keyword.operator.unary._LANG_) + +(conditional_expression + ["?" ":"] @keyword.operator.ternary._LANG_) + +(field_expression "." @keyword.operator.accessor.dot._LANG_) +(field_expression "->" @keyword.operator.accessor.arrow._LANG_) +(preproc_params "..." @keyword.operator.ellipsis._LANG_) + +["&&" "||"] @keyword.operator.logical._LANG_ + +; PUNCTUATION +; =========== + +";" @punctuation.terminator.statement._LANG_ + +("," @punctuation.separator.comma._LANG_ + (#set! capture.shy)) + +(parameter_list + "(" @punctuation.definition.parameters.begin.bracket.round._LANG_ + ")" @punctuation.definition.parameters.end.bracket.round._LANG_ + (#set! capture.final true)) + +(parenthesized_expression + "(" @punctuation.definition.expression.begin.bracket.round._LANG_ + ")" @punctuation.definition.expression.end.bracket.round._LANG_ + (#set! capture.final true)) + +"{" @punctuation.definition.block.begin.bracket.curly._LANG_ +"}" @punctuation.definition.block.end.bracket.curly._LANG_ +"(" @punctuation.definition.begin.bracket.round._LANG_ +")" @punctuation.definition.end.bracket.round._LANG_ +"[" @punctuation.definition.array.begin.bracket.square._LANG_ +"]" @punctuation.definition.array.end.bracket.square._LANG_ + +; META +; ==== + +((compound_statement) @meta.block._LANG_ + (#set! adjust.startAt firstChild.endPosition) + (#set! adjust.endAt lastChild.startPosition)) + +((enumerator_list) @meta.block.enum._LANG_ + (#set! adjust.startAt firstChild.endPosition) + (#set! adjust.endAt lastChild.startPosition)) + +((field_declaration_list) @meta.block.field._LANG_ + (#set! adjust.startAt firstChild.endPosition) + (#set! adjust.endAt lastChild.startPosition)) diff --git a/packages/language-c/grammars/modern-tree-sitter-c.cson b/packages/language-c/grammars/modern-tree-sitter-c.cson index c4be00a850..a1ed53efd1 100644 --- a/packages/language-c/grammars/modern-tree-sitter-c.cson +++ b/packages/language-c/grammars/modern-tree-sitter-c.cson @@ -9,7 +9,11 @@ injectionRegex: '^(c|C)$' treeSitter: parserSource: 'github:tree-sitter/tree-sitter-c#212a80f86452bb1316324fa0db730cf52f29e05a' grammar: 'tree-sitter-c/tree-sitter-c.wasm' - highlightsQuery: 'tree-sitter-c/highlights.scm' + languageSegment: 'c' + highlightsQuery: [ + 'common/highlights.scm' + 'tree-sitter-c/highlights.scm' + ] tagsQuery: 'tree-sitter-c/tags.scm' foldsQuery: 'tree-sitter-c/folds.scm' indentsQuery: 'tree-sitter-c/indents.scm' diff --git a/packages/language-c/grammars/modern-tree-sitter-cpp.cson b/packages/language-c/grammars/modern-tree-sitter-cpp.cson index 93e1c2978c..cbe36dc95a 100644 --- a/packages/language-c/grammars/modern-tree-sitter-cpp.cson +++ b/packages/language-c/grammars/modern-tree-sitter-cpp.cson @@ -8,7 +8,11 @@ injectionRegex: '^(c|C)(\\+\\+|pp|PP)$' treeSitter: parserSource: 'github:tree-sitter/tree-sitter-cpp#a71474021410973b29bfe99440d57bcd750246b1' grammar: 'tree-sitter-cpp/tree-sitter-cpp.wasm' - highlightsQuery: 'tree-sitter-cpp/highlights.scm' + languageSegment: 'cpp' + highlightsQuery: [ + 'common/highlights.scm' + 'tree-sitter-cpp/highlights.scm' + ] tagsQuery: 'tree-sitter-cpp/tags.scm' foldsQuery: 'tree-sitter-cpp/folds.scm' indentsQuery: 'tree-sitter-cpp/indents.scm' diff --git a/packages/language-c/grammars/tree-sitter-c/highlights.scm b/packages/language-c/grammars/tree-sitter-c/highlights.scm index 1a5a73ebd8..961aae5af2 100644 --- a/packages/language-c/grammars/tree-sitter-c/highlights.scm +++ b/packages/language-c/grammars/tree-sitter-c/highlights.scm @@ -1,108 +1,16 @@ -; PREPROCESSOR -; ============ - -[ - "#if" - "#ifdef" - "#ifndef" - "#endif" - "#elif" - "#else" -] @keyword.control.directive.conditional.c - -"#define" @keyword.control.directive.define.c -"#include" @keyword.control.directive.include.c - -(["#if" "#ifdef" "#ifndef" "#endif" "#elif" "#else" "#define" "#include"] @punctuation.definition.directive.c - (#set! adjust.endAfterFirstMatchOf "^#")) - -; `preproc_directive` will be used when the parser doesn't recognize the -; directive as one of the above. It's permissive; `#afdfafsdfdfad` would be -; parsed as a `preproc_directive`. -; -; Hence this rule will match if the more specific rules above haven't matched. -; The anonymous nodes will match under ideal conditions, but might not be -; present even when they ought to be _if_ the parser is flummoxed; so this'll -; sometimes catch `#ifdef` and others. -((preproc_directive) @keyword.control.directive.c - (#set! capture.shy true)) - -((preproc_directive) @punctuation.definition.directive.c - (#set! capture.shy true) - (#set! adjust.endAfterFirstMatchOf "^#")) - -; Macro functions are definitely entities. -(preproc_function_def - (identifier) @entity.name.function.preprocessor.c - (#set! capture.final true)) - -; Identifiers in macro definitions are definitely constants. -((preproc_def - name: (identifier) @constant.preprocessor.c)) - -; We can also safely treat identifiers as constants in `#ifdef`… -((preproc_ifdef - (identifier) @constant.preprocessor.c)) - -; …and `#if` and `#elif`… -(preproc_if - (binary_expression - (identifier) @constant.preprocessor.c)) -(preproc_elif - (binary_expression - (identifier) @constant.preprocessor.c)) - -; …and `#undef`. -((preproc_call - directive: (preproc_directive) @_IGNORE_ - argument: (preproc_arg) @constant.preprocessor.c) - (#eq? @_IGNORE_ "#undef")) - -(system_lib_string) @string.quoted.other.lt-gt.include.c -((system_lib_string) @punctuation.definition.string.begin.c - (#set! adjust.endAfterFirstMatchOf "^<")) -((system_lib_string) @punctuation.definition.string.end.c - (#set! adjust.startBeforeFirstMatchOf ">$")) - ; TYPES ; ===== -; WORKAROUND: If we're in an error state, don't trust the parser's designation -; of `type_identifier`. Someone's probably just typing on a new line. -(ERROR - (type_identifier) @_IGNORE_ - (#set! capture.final true)) - -(primitive_type) @support.storage.type.builtin.c - -; When the user has typed `#define FOO`, the macro injection thinks that `FOO` -; is a type declaration (for some reason). This node structure seems to exist -; only in that unusual and incorrect scenario, so we'll stop it from happening -; so that it doesn't override the underlying `constant.other.c` scope. -(translation_unit - (type_identifier) @_IGNORE_ - (#set! capture.final)) - (type_identifier) @support.other.storage.type.c -; These types are all reserved words; if we see an identifier with this name, -; it must be a type. -((identifier) @support.storage.type.builtin.c - (#match? @support.storage.type.builtin.c "^(char|int|float|double|long)$")) - -; Assume any identifier that ends in `_t` is a type. This convention is not -; always followed, but it's a very strong indicator when it's present. -((identifier) @support.other.storage.type.c - (#match? @support.other.storage.type.c "_t$")) - ; These refer to language constructs and remain in the `storage` namespace. [ "enum" "struct" "typedef" "union" -] @storage.type.c +] @storage.type._TYPE_.c ; These refer to value types and go under `support`. [ @@ -128,336 +36,11 @@ "volatile" ] @storage.modifier._TYPE_.c -((primitive_type) @support.storage.type.stdint.c - (#match? @support.storage.type.stdint.c "^(int8_t|int16_t|int32_t|int64_t|uint8_t|uint16_t|uint32_t|uint64_t|int_least8_t|int_least16_t|int_least32_t|int_least64_t|uint_least8_t|uint_least16_t|uint_least32_t|uint_least64_t|int_fast8_t|int_fast16_t|int_fast32_t|int_fast64_t|uint_fast8_t|uint_fast16_t|uint_fast32_t|uint_fast64_t|intptr_t|uintptr_t|intmax_t|intmax_t|uintmax_t|uintmax_t)$")) - (enum_specifier name: (type_identifier) @variable.other.declaration.type.c) (type_definition declarator: (_) @variable.other.declaration.type.c) -; CAVEAT: tree-sitter-c doesn't identify placeholders like `%c` in strings. -; Candidate for an injection grammar. -(string_literal "\"") @string.quoted.double.c - -(string_literal - "\"" @punctuation.definition.string.begin.c - (#is? test.first true)) - -(string_literal - "\"" @punctuation.definition.string.end.c - (#is? test.last true)) - -(char_literal "'") @string.quoted.single.c - -(char_literal - "'" @punctuation.definition.string.begin.c - (#is? test.first true)) - -(char_literal - "'" @punctuation.definition.string.end.c - (#is? test.last true)) - -(string_literal (escape_sequence) @constant.character.escape.c) -(char_literal (escape_sequence) @constant.character.escape.c) - -; VARIABLES -; ========= - -; Declarations and assignments -; ---------------------------- - -; The "x" in `int x;` -(declaration - declarator: (identifier) @variable.other.declaration.c) - -; The "x" in `int x = y;` -(init_declarator - declarator: (identifier) @variable.other.declaration.c) - -; The "x" in `SomeType *x;` -; (Should work no matter how many pointers deep we are.) -(pointer_declarator - declarator: [(identifier) (field_identifier)] @variable.other.declaration.pointer.c - (#is? test.descendantOfType "declaration field_declaration")) - -; An array declarator: the "table" in `int table[4];` -(array_declarator - declarator: (identifier) @variable.other.declaration.c) - -; A member of a struct. -(field_declaration - (field_identifier) @variable.other.declaration.member.c) - -; An attribute in a C99 struct designated initializer: -; the "foo" in `MY_TYPE a = { .foo = true }; -(initializer_pair - (field_designator - (field_identifier) @variable.other.declaration.member.c)) - -; (and the associated ".") -(initializer_pair - (field_designator - "." @keyword.operator.accessor.c)) - -(field_declaration - (pointer_declarator - (field_identifier) @variable.other.declaration.member.c)) - -(field_declaration - (array_declarator - (field_identifier) @variable.other.declaration.member.c)) - -(init_declarator - (pointer_declarator - (identifier) @variable.other.declaration.member.c)) - -; The "x" in `x = y;` -(assignment_expression - left: (identifier) @variable.other.assignment.c) - -; The "foo" in `something->foo = "bar";` -(assignment_expression - left: (field_expression - field: (field_identifier) @variable.other.member.assignment.c) - (#set! capture.final)) - -; Goto label definitions: the "foo" in `foo:` before a statement. -(labeled_statement - label: (statement_identifier) @entity.name.label.c) - -; Goto statements — the "foo" in `goto foo;` -(goto_statement - label: (statement_identifier) @support.other.label.c) - - -; Function parameters -; ------------------- - -(preproc_params - (identifier) @variable.parameter.preprocessor.c) - -; The "foo" in `const char foo` within a parameter list. -(parameter_declaration - declarator: (identifier) @variable.parameter.c) - -; The "foo" in `const char *foo` within a parameter list. -; (Should work no matter how many pointers deep we are.) -(pointer_declarator - declarator: [(identifier) (field_identifier)] @variable.parameter.pointer.c - (#is? test.descendantOfType "parameter_declaration")) - -; The "foo" in `const char foo[]` within a parameter list. -(parameter_declaration - declarator: (array_declarator - declarator: (identifier) @variable.parameter.c)) - -; The "argv" in `char* argv[]` within a parameter list. -(parameter_declaration - declarator: (pointer_declarator - declarator: (array_declarator - declarator: (identifier) @variable.parameter.c))) - -; The "size" in `finfo->size`. -(field_expression - "->" - field: (field_identifier) @variable.other.member.c) - -; The "bar" in `foo.bar`. -(field_expression - operator: "." - field: (field_identifier) @variable.other.member.c) - - - -; FUNCTIONS -; ========= - -(function_declarator - (identifier) @entity.name.function.c) - -(call_expression - (identifier) @support.function.c99.c - ; Regex copied from the TM grammar. - (#match? @support.function.c99.c "^(_Exit|(?:nearbyint|nextafter|nexttoward|netoward|nan)[fl]?|a(?:cos|sin)h?[fl]?|abort|abs|asctime|assert|atan(?:[h2]?[fl]?)?|atexit|ato[ifl]|atoll|bsearch|btowc|cabs[fl]?|cacos|cacos[fl]|cacosh[fl]?|calloc|carg[fl]?|casinh?[fl]?|catanh?[fl]?|cbrt[fl]?|ccosh?[fl]?|ceil[fl]?|cexp[fl]?|cimag[fl]?|clearerr|clock|clog[fl]?|conj[fl]?|copysign[fl]?|cosh?[fl]?|cpow[fl]?|cproj[fl]?|creal[fl]?|csinh?[fl]?|csqrt[fl]?|ctanh?[fl]?|ctime|difftime|div|erfc?[fl]?|exit|fabs[fl]?|exp(?:2[fl]?|[fl]|m1[fl]?)?|fclose|fdim[fl]?|fe[gs]et(?:env|exceptflag|round)|feclearexcept|feholdexcept|feof|feraiseexcept|ferror|fetestexcept|feupdateenv|fflush|fgetpos|fgetw?[sc]|floor[fl]?|fmax?[fl]?|fmin[fl]?|fmod[fl]?|fopen|fpclassify|fprintf|fputw?[sc]|fread|free|freopen|frexp[fl]?|fscanf|fseek|fsetpos|ftell|fwide|fwprintf|fwrite|fwscanf|genv|get[sc]|getchar|gmtime|gwc|gwchar|hypot[fl]?|ilogb[fl]?|imaxabs|imaxdiv|isalnum|isalpha|isblank|iscntrl|isdigit|isfinite|isgraph|isgreater|isgreaterequal|isinf|isless(?:equal|greater)?|isw?lower|isnan|isnormal|isw?print|isw?punct|isw?space|isunordered|isw?upper|iswalnum|iswalpha|iswblank|iswcntrl|iswctype|iswdigit|iswgraph|isw?xdigit|labs|ldexp[fl]?|ldiv|lgamma[fl]?|llabs|lldiv|llrint[fl]?|llround[fl]?|localeconv|localtime|log[2b]?[fl]?|log1[p0][fl]?|longjmp|lrint[fl]?|lround[fl]?|malloc|mbr?len|mbr?towc|mbsinit|mbsrtowcs|mbstowcs|memchr|memcmp|memcpy|memmove|memset|mktime|modf[fl]?|perror|pow[fl]?|printf|puts|putw?c(?:har)?|qsort|raise|rand|remainder[fl]?|realloc|remove|remquo[fl]?|rename|rewind|rint[fl]?|round[fl]?|scalbl?n[fl]?|scanf|setbuf|setjmp|setlocale|setvbuf|signal|signbit|sinh?[fl]?|snprintf|sprintf|sqrt[fl]?|srand|sscanf|strcat|strchr|strcmp|strcoll|strcpy|strcspn|strerror|strftime|strlen|strncat|strncmp|strncpy|strpbrk|strrchr|strspn|strstr|strto[kdf]|strtoimax|strtol[dl]?|strtoull?|strtoumax|strxfrm|swprintf|swscanf|system|tan|tan[fl]|tanh[fl]?|tgamma[fl]?|time|tmpfile|tmpnam|tolower|toupper|trunc[fl]?|ungetw?c|va_arg|va_copy|va_end|va_start|vfw?printf|vfw?scanf|vprintf|vscanf|vsnprintf|vsprintf|vsscanf|vswprintf|vswscanf|vwprintf|vwscanf|wcrtomb|wcscat|wcschr|wcscmp|wcscoll|wcscpy|wcscspn|wcsftime|wcslen|wcsncat|wcsncmp|wcsncpy|wcspbrk|wcsrchr|wcsrtombs|wcsspn|wcsstr|wcsto[dkf]|wcstoimax|wcstol[dl]?|wcstombs|wcstoull?|wcstoumax|wcsxfrm|wctom?b|wmem(?:set|chr|cpy|cmp|move)|wprintf|wscanf)$") - (#set! capture.final true)) - -; The "foo" in `thing->troz->foo(...)`. -(call_expression - (field_expression - field: (field_identifier) @support.other.function.c) - (#set! capture.final true)) - -(call_expression - (identifier) @support.other.function.c - (#set! capture.final true)) - -; NUMBERS -; ======= - -(number_literal) @constant.numeric.c - - -; CONSTANTS -; ========= - -[ - (null) - (true) - (false) -] @constant.language._TYPE_.c - -; Don't try to scope (e.g.) `int FOO = 1` as a constant when the user types `=` -; but has not typed the value yet. -(ERROR - (identifier) @_IGNORE_ - (#set! capture.final)) - -; In most languages we wouldn't be making the assumption that an all-caps -; identifier should be treated as a constant. But those languages don't have -; macro preprocessors. The convention is decently strong in C/C++ that all-caps -; identifiers will refer to `#define`d things. -((identifier) @constant.other.c - (#match? @constant.other.c "^[_A-Z][_A-Z0-9]*$") - (#set! capture.shy)) - - -; COMMENTS -; ======== - -; Match // comments. -((comment) @comment.line.double-slash.c - (#match? @comment.line.double-slash.c "^\\s*//")) - -((comment) @punctuation.definition.comment.c - (#match? @comment.line.double-slash.c "^\\s*//") - (#set! adjust.startAndEndAroundFirstMatchOf "//")) - -; Match /* */ comments. -((comment) @comment.block.c - (#match? @comment.block.c "^/\\*")) - -((comment) @punctuation.definition.comment.begin.c - (#match? @punctuation.definition.comment.begin.c "^/\\*") - (#set! adjust.startAndEndAroundFirstMatchOf "^/\\*")) - -((comment) @punctuation.definition.comment.end.c - (#match? @punctuation.definition.comment.end.c "\\*/$") - (#set! adjust.startAndEndAroundFirstMatchOf "\\*/$")) - - -[ - "break" - "case" - "continue" - "default" - "do" - "else" - "for" - "goto" - "if" - "return" - "switch" - "while" -] @keyword.control._TYPE_.c - -; OPERATORS -; ========= - -(pointer_declarator "*" @keyword.operator.pointer.c) -(abstract_pointer_declarator "*" @keyword.operator.pointer.c) -(pointer_expression "*" @keyword.operator.pointer.c) - -"sizeof" @keyword.operator.sizeof.c -(pointer_expression "&" @keyword.operator.pointer.c) - -"=" @keyword.operator.assignment.c - -[ - "%=" - "+=" - "-=" - "*=" - "/=" - "&=" - "^=" - "<<=" - ">>=" - "|=" -] @keyword.operator.assignment.compound.c - - -(binary_expression - ["==" "!=" ">" "<" ">=" "<="] @keyword.operator.comparison.c) - -(binary_expression - ["&" "|" "^" "~" "<<" ">>"] - @keyword.operator.bitwise.c) - -"++" @keyword.operator.increment.c -"--" @keyword.operator.decrement.c - -(binary_expression ["+" "-" "*" "/" "%"] @keyword.operator.arithmetic.c) -(unary_expression ["+" "-" "!"] @keyword.operator.unary.c) - -(conditional_expression - ["?" ":"] @keyword.operator.ternary.c) - - -["||" "&&"] @keyword.operator.logical.c - -(field_expression "." @keyword.operator.accessor.dot.c) -(field_expression "->" @keyword.operator.accessor.c) -(preproc_params "..." @keyword.operator.ellipsis.c) - -; PUNCTUATION -; =========== - -";" @punctuation.terminator.statement.c - -("," @punctuation.separator.comma.c - (#set! capture.shy)) -("->" @keyword.operator.accessor.pointer-access.c - (#set! capture.shy)) - -(parameter_list - "(" @punctuation.definition.parameters.begin.bracket.round.c - ")" @punctuation.definition.parameters.end.bracket.round.c - (#set! capture.final true)) - -(parenthesized_expression - "(" @punctuation.definition.expression.begin.bracket.round.c - ")" @punctuation.definition.expression.end.bracket.round.c - (#set! capture.final true)) - -(if_statement - condition: (parenthesized_expression - "(" @punctuation.definition.expression.begin.bracket.round.c - ")" @punctuation.definition.expression.end.bracket.round.c - (#set! capture.final true))) - -"{" @punctuation.definition.block.begin.bracket.curly.c -"}" @punctuation.definition.block.end.bracket.curly.c -"(" @punctuation.definition.begin.bracket.round.c -")" @punctuation.definition.end.bracket.round.c -"[" @punctuation.definition.array.begin.bracket.square.c -"]" @punctuation.definition.array.end.bracket.square.c - - -; META -; ==== - -((compound_statement) @meta.block.c - (#set! adjust.startAt firstChild.endPosition) - (#set! adjust.endAt lastChild.startPosition)) - -((enumerator_list) @meta.block.enum.c - (#set! adjust.startAt firstChild.endPosition) - (#set! adjust.endAt lastChild.startPosition)) - -((field_declaration_list) @meta.block.field.c - (#set! adjust.startAt firstChild.endPosition) - (#set! adjust.endAt lastChild.startPosition)) ; TODO: ; diff --git a/packages/language-c/grammars/tree-sitter-cpp/highlights.scm b/packages/language-c/grammars/tree-sitter-cpp/highlights.scm index b3c5a76ec5..e0d2343d8e 100644 --- a/packages/language-c/grammars/tree-sitter-cpp/highlights.scm +++ b/packages/language-c/grammars/tree-sitter-cpp/highlights.scm @@ -1,89 +1,7 @@ -; PREPROCESSOR -; ============ - -[ - "#if" - "#ifdef" - "#ifndef" - "#endif" - "#elif" - "#else" -] @keyword.control.directive.conditional.cpp - -"#define" @keyword.control.directive.define.cpp -"#include" @keyword.control.directive.include.cpp - -(["#if" "#ifdef" "#ifndef" "#endif" "#elif" "#else" "#define" "#include"] @punctuation.definition.directive.cpp - (#set! adjust.endAfterFirstMatchOf "^#")) - -; `preproc_directive` will be used when the parser doesn't recognize the -; directive as one of the above. It's permissive; `#afdfafsdfdfad` would be -; parsed as a `preproc_directive`. -; -; Hence this rule will match if the more specific rules above haven't matched. -; The anonymous nodes will match under ideal conditions, but might not be -; present even when they ought to be _if_ the parser is flummoxed; so this'll -; sometimes catch `#ifdef` and others. -((preproc_directive) @keyword.control.directive.cpp - (#set! capture.shy true)) - -((preproc_directive) @punctuation.definition.directive.cpp - (#set! capture.shy true) - (#set! adjust.endAfterFirstMatchOf "^#")) - -; Macro functions are definitely entities. -(preproc_function_def - (identifier) @entity.name.function.preprocessor.cpp - (#set! capture.final true)) - -; Identifiers in macro definitions are definitely constants. -((preproc_def - name: (identifier) @constant.preprocessor.cpp)) - -; We can also safely treat identifiers as constants in `#ifdef`… -((preproc_ifdef - (identifier) @constant.preprocessor.cpp)) - -; …and `#if` and `#elif`… -(preproc_if - (binary_expression - (identifier) @constant.preprocessor.cpp)) -(preproc_elif - (binary_expression - (identifier) @constant.preprocessor.cpp)) - -; …and `#undef`. -((preproc_call - directive: (preproc_directive) @_IGNORE_ - argument: (preproc_arg) @constant.preprocessor.cpp) - (#eq? @_IGNORE_ "#undef")) - -(system_lib_string) @string.quoted.other.lt-gt.include.cpp -((system_lib_string) @punctuation.definition.string.begin.cpp - (#set! adjust.endAfterFirstMatchOf "^<")) -((system_lib_string) @punctuation.definition.string.end.cpp - (#set! adjust.startBeforeFirstMatchOf ">$")) - ; TYPES ; ===== -; WORKAROUND: If we're in an error state, don't trust the parser's designation -; of `type_identifier`. Someone's probably just typing on a new line. -(ERROR - (type_identifier) @_IGNORE_ - (#set! capture.final true)) - -; When the user has typed `#define FOO`, the macro injection thinks that `FOO` -; is a type declaration (for some reason). This node structure seems to exist -; only in that unusual and incorrect scenario, so we'll stop it from happening -; so that it doesn't override the underlying `constant.other.c` scope. -(translation_unit - (type_identifier) @_IGNORE_ - (#set! capture.final)) - -(primitive_type) @support.type.builtin.cpp - (placeholder_type_specifier (auto) @support.type.builtin.auto.cpp) ; Mark all function definition types with data… @@ -99,40 +17,37 @@ (type_identifier) @entity.name.class.cpp (#set! capture.final true)) -(type_identifier) @support.type.other.cpp -; (struct_specifier) @storage.type.cpp - -; These types are all reserved words; if we see an identifier with this name, -; it must be a type. -((identifier) @support.storage.type.builtin.cpp - (#match? @support.storage.type.builtin.cpp "^(char|int|float|double|long)$")) - -; Assume any identifier that ends in `_t` is a type. This convention is not -; always followed, but it's a very strong indicator when it's present. -((identifier) @support.other.storage.type.cpp - (#match? @support.other.storage.type.cpp "_t$")) - - ; These refer to language constructs and remain in the `storage` namespace. -[ +([ "enum" "struct" "typedef" "union" "template" -] @storage.type.cpp +] @storage.type._TYPE_.cpp + (#set! capture.final)) + +; This one gets a slightly different scope name so it doesn't inadvertently +; match syntax themes that target `.syntax--class`. +("class" @storage.type.class-type.cpp + (#set! capture.final)) ; These refer to value types and go under `support`. -[ +([ "long" "short" ] @support.storage.type.builtin.cpp + (#set! capture.final)) ; These act as modifiers to value types and also go under `support`. -[ +([ "signed" "unsigned" ] @support.storage.modifier.builtin.cpp + (#set! capture.final)) + +((type_identifier) @support.storage.type.builtin.cpp + (#eq? @support.storage.type.builtin.cpp "string")) ; These act as general language modifiers and remain in the `storage` ; namespace. @@ -159,48 +74,24 @@ "typename" ] @storage.modifier._TYPE_.cpp -( - (primitive_type) @support.storage.type.stdint.cpp - (#match? @support.storage.type.stdint.cpp "^(int8_t|int16_t|int32_t|int64_t|uint8_t|uint16_t|uint32_t|uint64_t|int_least8_t|int_least16_t|int_least32_t|int_least64_t|uint_least8_t|uint_least16_t|uint_least32_t|uint_least64_t|int_fast8_t|int_fast16_t|int_fast32_t|int_fast64_t|uint_fast8_t|uint_fast16_t|uint_fast32_t|uint_fast64_t|intptr_t|uintptr_t|intmax_t|intmax_t|uintmax_t|uintmax_t)$") -) +(type_identifier) @support.storage.other.type.cpp ; FUNCTIONS ; ========= -(function_declarator - (identifier) @entity.name.function.cpp) - -(function_declarator - (destructor_name - (identifier) @entity.name.function.cpp)) - -; The "foo" in `void Bar::foo () {`. -(function_declarator - declarator: (qualified_identifier - name: (identifier) @entity.name.function.cpp)) - -(function_declarator - (field_identifier) @entity.name.function.method.cpp) - (field_initializer (field_identifier) @entity.name.function.cpp) +; The "foo" in `troz::foo(...)`. (call_expression - (identifier) @support.function.c99.cpp - ; Regex copied from the TM grammar. - (#match? @support.function.c99.cpp "^(_Exit|(?:nearbyint|nextafter|nexttoward|netoward|nan)[fl]?|a(?:cos|sin)h?[fl]?|abort|abs|asctime|assert|atan(?:[h2]?[fl]?)?|atexit|ato[ifl]|atoll|bsearch|btowc|cabs[fl]?|cacos|cacos[fl]|cacosh[fl]?|calloc|carg[fl]?|casinh?[fl]?|catanh?[fl]?|cbrt[fl]?|ccosh?[fl]?|ceil[fl]?|cexp[fl]?|cimag[fl]?|clearerr|clock|clog[fl]?|conj[fl]?|copysign[fl]?|cosh?[fl]?|cpow[fl]?|cproj[fl]?|creal[fl]?|csinh?[fl]?|csqrt[fl]?|ctanh?[fl]?|ctime|difftime|div|erfc?[fl]?|exit|fabs[fl]?|exp(?:2[fl]?|[fl]|m1[fl]?)?|fclose|fdim[fl]?|fe[gs]et(?:env|exceptflag|round)|feclearexcept|feholdexcept|feof|feraiseexcept|ferror|fetestexcept|feupdateenv|fflush|fgetpos|fgetw?[sc]|floor[fl]?|fmax?[fl]?|fmin[fl]?|fmod[fl]?|fopen|fpclassify|fprintf|fputw?[sc]|fread|free|freopen|frexp[fl]?|fscanf|fseek|fsetpos|ftell|fwide|fwprintf|fwrite|fwscanf|genv|get[sc]|getchar|gmtime|gwc|gwchar|hypot[fl]?|ilogb[fl]?|imaxabs|imaxdiv|isalnum|isalpha|isblank|iscntrl|isdigit|isfinite|isgraph|isgreater|isgreaterequal|isinf|isless(?:equal|greater)?|isw?lower|isnan|isnormal|isw?print|isw?punct|isw?space|isunordered|isw?upper|iswalnum|iswalpha|iswblank|iswcntrl|iswctype|iswdigit|iswgraph|isw?xdigit|labs|ldexp[fl]?|ldiv|lgamma[fl]?|llabs|lldiv|llrint[fl]?|llround[fl]?|localeconv|localtime|log[2b]?[fl]?|log1[p0][fl]?|longjmp|lrint[fl]?|lround[fl]?|malloc|mbr?len|mbr?towc|mbsinit|mbsrtowcs|mbstowcs|memchr|memcmp|memcpy|memmove|memset|mktime|modf[fl]?|perror|pow[fl]?|printf|puts|putw?c(?:har)?|qsort|raise|rand|remainder[fl]?|realloc|remove|remquo[fl]?|rename|rewind|rint[fl]?|round[fl]?|scalbl?n[fl]?|scanf|setbuf|setjmp|setlocale|setvbuf|signal|signbit|sinh?[fl]?|snprintf|sprintf|sqrt[fl]?|srand|sscanf|strcat|strchr|strcmp|strcoll|strcpy|strcspn|strerror|strftime|strlen|strncat|strncmp|strncpy|strpbrk|strrchr|strspn|strstr|strto[kdf]|strtoimax|strtol[dl]?|strtoull?|strtoumax|strxfrm|swprintf|swscanf|system|tan|tan[fl]|tanh[fl]?|tgamma[fl]?|time|tmpfile|tmpnam|tolower|toupper|trunc[fl]?|ungetw?c|va_arg|va_copy|va_end|va_start|vfw?printf|vfw?scanf|vprintf|vscanf|vsnprintf|vsprintf|vsscanf|vswprintf|vswscanf|vwprintf|vwscanf|wcrtomb|wcscat|wcschr|wcscmp|wcscoll|wcscpy|wcscspn|wcsftime|wcslen|wcsncat|wcsncmp|wcsncpy|wcspbrk|wcsrchr|wcsrtombs|wcsspn|wcsstr|wcsto[dkf]|wcstoimax|wcstol[dl]?|wcstombs|wcstoull?|wcstoumax|wcsxfrm|wctom?b|wmem(?:set|chr|cpy|cmp|move)|wprintf|wscanf)$") - (#set! capture.final true)) - -; The "foo" in `thing->troz->foo(...)`. -(call_expression - (field_expression - field: (field_identifier) @support.other.function.cpp) + function: (qualified_identifier + name: (identifier) @support.other.function.cpp) (#set! capture.final true)) -; The "foo" in `troz::foo(...)`. +; The "foo" in `foo(...)`. (call_expression - function: (qualified_identifier + function: (template_function name: (identifier) @support.other.function.cpp) (#set! capture.final true)) @@ -211,226 +102,32 @@ name: (identifier) @support.other.function.cpp)) (#set! capture.final true)) -(call_expression - (identifier) @support.other.function.cpp - (#set! capture.final true)) - - -; STRINGS -; ======= - -; CAVEAT: tree-sitter-c doesn't identify placeholders like `%c` in strings. -; Candidate for an injection grammar. -(string_literal "\"") @string.quoted.double.cpp - -(string_literal - "\"" @punctuation.definition.string.begin.cpp - (#is? test.first true)) - -(string_literal - "\"" @punctuation.definition.string.end.cpp - (#is? test.last true)) - -(char_literal "'") @string.quoted.single.cpp - -(char_literal - "'" @punctuation.definition.string.begin.cpp - (#is? test.first true)) - -(char_literal - "'" @punctuation.definition.string.end.cpp - (#is? test.last true)) (string_literal (escape_sequence) @constant.character.escape.cpp) (char_literal (escape_sequence) @constant.character.escape.cpp) -; VARIABLES -; ========= - (this) @variable.language.this.cpp -; Declarations and assignments -; ---------------------------- - -; The "x" in `int x;` -(declaration - declarator: (identifier) @variable.declaration.cpp) - -; The "x" in `int x = y;` -(init_declarator - declarator: (identifier) @variable.declaration.cpp) - -; The "x" in `SomeType *x;` -; (Should work no matter how many pointers deep we are.) -(pointer_declarator - declarator: [(identifier) (field_identifier)] @variable.declaration.pointer.cpp - (#is? test.descendantOfType "declaration field_declaration")) - -; A member of a struct. -(field_declaration - (field_identifier) @variable.declaration.member.cpp) - -; An attribute in a C99 struct designated initializer: -; the "foo" in `MY_TYPE a = { .foo = true }; -(initializer_pair - (field_designator - (field_identifier) @variable.declaration.member.cpp)) - -; (and the associated ".") -(initializer_pair - (field_designator - "." @keyword.operator.accessor.cpp)) - -(field_declaration - (pointer_declarator - (field_identifier) @variable.declaration.member.cpp)) - -(field_declaration - (array_declarator - (field_identifier) @variable.declaration.member.cpp)) - -(init_declarator - (pointer_declarator - (identifier) @variable.declaration.member.cpp)) - -; The "x" in `x = y;` -(assignment_expression - left: (identifier) @variable.other.assignment.cpp) - -; The "foo" in `something->foo = "bar";` -(assignment_expression - left: (field_expression - field: (field_identifier) @variable.other.member.assignment.cpp) - (#set! capture.final)) - -((reference_declarator - (identifier) @variable.declaration.cpp) - (#is-not? test.descendantOfType parameter_declaration)) - -; Goto label definitions like `foo:` before a statement. -(labeled_statement - label: (statement_identifier) @entity.name.label.cpp) - -(goto_statement - label: (statement_identifier) @support.other.label.cpp) +; VARIABLES +; ========= ; Function parameters ; ------------------- -(preproc_params - (identifier) @variable.parameter.preprocessor.cpp) - -; The "foo" in `const char foo` within a parameter list. -(parameter_declaration - declarator: (identifier) @variable.parameter.cpp) - -; The "foo" in `const char *foo` within a parameter list. -; (Should work no matter how many pointers deep we are.) -(pointer_declarator - declarator: [(identifier) (field_identifier)] @variable.parameter.pointer.cpp - (#is? test.descendantOfType "parameter_declaration")) - -(parameter_declaration - declarator: (reference_declarator - (identifier) @variable.parameter.cpp)) - -; The "foo" in `const char foo[]` within a parameter list. -(parameter_declaration - declarator: (array_declarator - declarator: (identifier) @variable.parameter.cpp)) - -; The "argv" in `char* argv[]` within a parameter list. -(parameter_declaration - declarator: (pointer_declarator - declarator: (array_declarator - declarator: (identifier) @variable.parameter.cpp))) - -; The "size" in `finfo->size`. -(field_expression - "->" - field: (field_identifier) @variable.other.member.cpp) - -; The "bar" in `foo.bar`. -(field_expression - operator: "." - field: (field_identifier) @variable.other.member.cpp) - ; Common naming idiom for C++ instanced vars: "fMemberName" ; ((identifier) @variable.other.readwrite.member.cpp ; (#match? @variable.other.readwrite.member.cpp "^(f|m)[A-Z]\\w*$")) - - -; NUMBERS -; ======= - -(number_literal) @constant.numeric.cpp - -; CONSTANTS -; ========= - -[ - (null) - (true) - (false) -] @constant.language._TYPE_.cpp - -; Don't try to scope (e.g.) `int FOO = 1` as a constant when the user types `=` -; but has not typed the value yet. -(ERROR - (identifier) @_IGNORE_ - (#set! capture.final)) - -; In most languages we wouldn't be making the assumption that an all-caps -; identifier should be treated as a constant. But those languages don't have -; macro preprocessors. The convention is decently strong in C/C++ that all-caps -; identifiers will refer to `#define`d things. -((identifier) @constant.other.cpp - (#match? @constant.other.cpp "^[_A-Z][_A-Z0-9]*$") - (#set! capture.shy)) - - -; COMMENTS -; ======== - -; Match // comments. -((comment) @comment.line.double-slash.cpp - (#match? @comment.line.double-slash.cpp "^\\s*//")) - -((comment) @punctuation.definition.comment.cpp - (#match? @comment.line.double-slash.cpp "^\\s*//") - (#set! adjust.startAndEndAroundFirstMatchOf "//")) - -; Match /* */ comments. -((comment) @comment.block.cpp - (#match? @comment.block.cpp "^/\\*")) - -((comment) @punctuation.definition.comment.begin.cpp - (#match? @punctuation.definition.comment.begin.cpp "^/\\*") - (#set! adjust.startAndEndAroundFirstMatchOf "^/\\*")) - -((comment) @punctuation.definition.comment.end.cpp - (#match? @punctuation.definition.comment.end.cpp "\\*/$") - (#set! adjust.startAndEndAroundFirstMatchOf "\\*/$")) +; The "foo" in `int &foo` with in a parameter list. +(parameter_declaration + declarator: (reference_declarator + (identifier) @variable.parameter.cpp)) ; KEYWORDS ; ======== [ - "break" - "case" - "continue" - "default" - "do" - "else" - "for" - "goto" - "if" - "return" - "switch" - "while" - "new" "delete" ] @keyword.control._TYPE_.cpp @@ -442,68 +139,21 @@ "throw" "using" "namespace" - "class" ] @keyword.control._TYPE_.cpp ; OPERATORS ; ========= -(pointer_declarator "*" @keyword.operator.pointer.cpp) -(abstract_pointer_declarator "*" @keyword.operator.pointer.cpp) -(pointer_expression "*" @keyword.operator.pointer.cpp) - -(destructor_name "~" @keyword.operator.destructor.cpp) +(qualified_identifier + "::" @keyword.operator.accessor.namespace.cpp) -"sizeof" @keyword.operator.sizeof.cpp -(pointer_expression "&" @keyword.operator.pointer.cpp) (reference_declarator "&" @keyword.operator.pointer.cpp) - - -"=" @keyword.operator.assignment.cpp - -[ - "%=" - "+=" - "-=" - "*=" - "/=" - "&=" - "^=" - "<<=" - ">>=" - "|=" -] @keyword.operator.assignment.compound.cpp - -(binary_expression - ["==" "!=" ">" "<" ">=" "<="] @keyword.operator.comparison.cpp) - - -["&&" "||"] @keyword.operator.logical.cpp (binary_expression ["and" "or"] @keyword.operator.logical.cpp) +(destructor_name "~" @keyword.operator.destructor.cpp) +("->" @keyword.operator.accessor.pointer-access.cpp + (#set! capture.shy)) -"++" @keyword.operator.increment.cpp -"--" @keyword.operator.decrement.cpp - -(binary_expression - ["&" "|" "^" "~" "<<" ">>"] - @keyword.operator.bitwise.cpp) - -(binary_expression - ["+" "-" "*" "/" "%"] @keyword.operator.arithmetic.cpp) - -(unary_expression - ["+" "-" "!"] @keyword.operator.unary.cpp) -(unary_expression "not" @keyword.operator.unary.cpp) - -(conditional_expression - ["?" ":"] @keyword.operator.ternary.cpp) - -(qualified_identifier - "::" @keyword.operator.accessor.namespace.cpp) - -(field_expression "." @keyword.operator.accessor.dot.cpp) -(preproc_params "..." @keyword.operator.ellipsis.cpp) ; PUNCTUATION ; =========== @@ -512,18 +162,6 @@ ("," @punctuation.separator.comma.cpp (#set! capture.shy)) -("->" @keyword.operator.accessor.pointer-access.cpp - (#set! capture.shy)) - -(parameter_list - "(" @punctuation.definition.parameters.begin.bracket.round.cpp - ")" @punctuation.definition.parameters.end.bracket.round.cpp - (#set! capture.final true)) - -(parenthesized_expression - "(" @punctuation.definition.expression.begin.bracket.round.cpp - ")" @punctuation.definition.expression.end.bracket.round.cpp - (#set! capture.final true)) (if_statement condition: (condition_clause @@ -539,28 +177,6 @@ "<" @punctuation.definition.parameters.begin.bracket.angle.cpp ">" @punctuation.definition.parameters.end.bracket.angle.cpp) -"{" @punctuation.definition.block.begin.bracket.curly.cpp -"}" @punctuation.definition.block.end.bracket.curly.cpp -"(" @punctuation.definition.begin.bracket.round.cpp -")" @punctuation.definition.end.bracket.round.cpp -"[" @punctuation.definition.array.begin.bracket.square.cpp -"]" @punctuation.definition.array.end.bracket.square.cpp - -; META -; ==== - -((compound_statement) @meta.block.cpp - (#set! adjust.startAt firstChild.endPosition) - (#set! adjust.endAt lastChild.startPosition)) - -((enumerator_list) @meta.block.enum.cpp - (#set! adjust.startAt firstChild.endPosition) - (#set! adjust.endAt lastChild.startPosition)) - -((field_declaration_list) @meta.block.field.cpp - (#set! adjust.startAt firstChild.endPosition) - (#set! adjust.endAt lastChild.startPosition)) - ; TODO: ; diff --git a/packages/language-c/spec/.eslintrc.js b/packages/language-c/spec/.eslintrc.js new file mode 100644 index 0000000000..8cc26374a1 --- /dev/null +++ b/packages/language-c/spec/.eslintrc.js @@ -0,0 +1,14 @@ +module.exports = { + env: { jasmine: true }, + globals: { + waitsForPromise: true, + runGrammarTests: true, + runFoldsTests: true + }, + rules: { + "node/no-unpublished-require": "off", + "node/no-extraneous-require": "off", + "no-unused-vars": "off", + "no-empty": "off" + } +}; diff --git a/packages/language-c/spec/c-spec.js b/packages/language-c/spec/c-spec.js index da1220094a..3e3bf64b3d 100644 --- a/packages/language-c/spec/c-spec.js +++ b/packages/language-c/spec/c-spec.js @@ -1,6 +1,6 @@ let TextEditor = null; -const buildTextEditor = function(params) { +const buildTextEditor = function (params) { if (atom.workspace.buildTextEditor != null) { return atom.workspace.buildTextEditor(params); } else { @@ -11,24 +11,24 @@ const buildTextEditor = function(params) { } }; -describe("Language-C", function() { +describe("Language-C", function () { let grammar = null; - beforeEach(function() { + beforeEach(function () { atom.config.set('core.useTreeSitterParsers', false); return waitsForPromise(() => atom.packages.activatePackage('language-c')); }); - describe("C", function() { + describe("C", function () { beforeEach(() => grammar = atom.grammars.grammarForScopeName('source.c')); - it("parses the grammar", function() { + it("parses the grammar", function () { expect(grammar).toBeTruthy(); expect(grammar.scopeName).toBe('source.c'); }); - it("tokenizes punctuation", function() { + it("tokenizes punctuation", function () { let {tokens} = grammar.tokenizeLine('hi;'); expect(tokens[1]).toEqual({value: ';', scopes: ['source.c', 'punctuation.terminator.statement.c']}); @@ -40,7 +40,7 @@ describe("Language-C", function() { expect(tokens[1]).toEqual({value: ',', scopes: ['source.c', 'punctuation.separator.delimiter.c']}); }); - it("tokenizes functions", function() { + it("tokenizes functions", function () { const lines = grammar.tokenizeLines(`\ int something(int param) { return 0; @@ -58,7 +58,7 @@ int something(int param) { expect(lines[2][0]).toEqual({value: '}', scopes: ['source.c', 'meta.block.c', 'punctuation.section.block.end.bracket.curly.c']}); }); - it("tokenizes varargs ellipses", function() { + it("tokenizes varargs ellipses", function () { const {tokens} = grammar.tokenizeLine('void function(...);'); expect(tokens[0]).toEqual({value: 'void', scopes: ['source.c', 'storage.type.c']}); expect(tokens[2]).toEqual({value: 'function', scopes: ['source.c', 'meta.function.c', 'entity.name.function.c']}); @@ -67,7 +67,7 @@ int something(int param) { expect(tokens[5]).toEqual({value: ')', scopes: ['source.c', 'meta.function.c', 'punctuation.section.parameters.end.bracket.round.c']}); }); - it("tokenizes various _t types", function() { + it("tokenizes various _t types", function () { let {tokens} = grammar.tokenizeLine('size_t var;'); expect(tokens[0]).toEqual({value: 'size_t', scopes: ['source.c', 'support.type.sys-types.c']}); @@ -81,14 +81,14 @@ int something(int param) { expect(tokens[0]).toEqual({value: 'myType_t', scopes: ['source.c', 'support.type.posix-reserved.c']}); }); - it("tokenizes 'line continuation' character", function() { + it("tokenizes 'line continuation' character", function () { const {tokens} = grammar.tokenizeLine("ma\\\nin(){};"); expect(tokens[0]).toEqual({value: 'ma', scopes: ['source.c']}); expect(tokens[1]).toEqual({value: '\\', scopes: ['source.c', 'constant.character.escape.line-continuation.c']}); expect(tokens[3]).toEqual({value: 'in', scopes: ['source.c', 'meta.function.c', 'entity.name.function.c']}); }); - describe("strings", () => it("tokenizes them", function() { + describe("strings", () => it("tokenizes them", function () { let tokens; const delimsByScope = { 'string.quoted.double.c': '"', @@ -131,7 +131,7 @@ int something(int param) { expect(tokens[2]).toEqual({value: '"', scopes: ['source.c', 'string.quoted.double.c', 'punctuation.definition.string.end.c']}); })); - describe("comments", () => it("tokenizes them", function() { + describe("comments", () => it("tokenizes them", function () { let {tokens} = grammar.tokenizeLine('/**/'); expect(tokens[0]).toEqual({value: '/*', scopes: ['source.c', 'comment.block.c', 'punctuation.definition.comment.begin.c']}); expect(tokens[1]).toEqual({value: '*/', scopes: ['source.c', 'comment.block.c', 'punctuation.definition.comment.end.c']}); @@ -145,8 +145,8 @@ int something(int param) { expect(tokens[0]).toEqual({value: '*/*', scopes: ['source.c', 'invalid.illegal.stray-comment-end.c']}); })); - describe("preprocessor directives", function() { - it("tokenizes '#line'", function() { + describe("preprocessor directives", function () { + it("tokenizes '#line'", function () { const {tokens} = grammar.tokenizeLine('#line 151 "copy.c"'); expect(tokens[0]).toEqual({value: '#', scopes: ['source.c', 'meta.preprocessor.c', 'keyword.control.directive.line.c', 'punctuation.definition.directive.c']}); expect(tokens[1]).toEqual({value: 'line', scopes: ['source.c', 'meta.preprocessor.c', 'keyword.control.directive.line.c']}); @@ -156,7 +156,7 @@ int something(int param) { expect(tokens[7]).toEqual({value: '"', scopes: ['source.c', 'meta.preprocessor.c', 'string.quoted.double.c', 'punctuation.definition.string.end.c']}); }); - it("tokenizes '#undef'", function() { + it("tokenizes '#undef'", function () { const {tokens} = grammar.tokenizeLine('#undef FOO'); expect(tokens[0]).toEqual({value: '#', scopes: ['source.c', 'meta.preprocessor.c', 'keyword.control.directive.undef.c', 'punctuation.definition.directive.c']}); expect(tokens[1]).toEqual({value: 'undef', scopes: ['source.c', 'meta.preprocessor.c', 'keyword.control.directive.undef.c']}); @@ -164,7 +164,7 @@ int something(int param) { expect(tokens[3]).toEqual({value: 'FOO', scopes: ['source.c', 'meta.preprocessor.c', 'entity.name.function.preprocessor.c']}); }); - it("tokenizes '#pragma'", function() { + it("tokenizes '#pragma'", function () { let {tokens} = grammar.tokenizeLine('#pragma once'); expect(tokens[0]).toEqual({value: '#', scopes: ['source.c', 'meta.preprocessor.pragma.c', 'keyword.control.directive.pragma.c', 'punctuation.definition.directive.c']}); expect(tokens[1]).toEqual({value: 'pragma', scopes: ['source.c', 'meta.preprocessor.pragma.c', 'keyword.control.directive.pragma.c']}); @@ -186,15 +186,15 @@ int something(int param) { expect(tokens[3]).toEqual({value: '– Initialization', scopes: ['source.c', 'meta.section', 'meta.preprocessor.pragma.c', 'entity.name.tag.pragma-mark.c']}); }); - describe("define", function() { - it("tokenizes '#define [identifier name]'", function() { + describe("define", function () { + it("tokenizes '#define [identifier name]'", function () { const {tokens} = grammar.tokenizeLine('#define _FILE_NAME_H_'); expect(tokens[0]).toEqual({value: '#', scopes: ['source.c', 'meta.preprocessor.macro.c', 'keyword.control.directive.define.c', 'punctuation.definition.directive.c']}); expect(tokens[1]).toEqual({value: 'define', scopes: ['source.c', 'meta.preprocessor.macro.c', 'keyword.control.directive.define.c']}); expect(tokens[3]).toEqual({value: '_FILE_NAME_H_', scopes: ['source.c', 'meta.preprocessor.macro.c', 'entity.name.function.preprocessor.c']}); }); - it("tokenizes '#define [identifier name] [value]'", function() { + it("tokenizes '#define [identifier name] [value]'", function () { let {tokens} = grammar.tokenizeLine('#define WIDTH 80'); expect(tokens[0]).toEqual({value: '#', scopes: ['source.c', 'meta.preprocessor.macro.c', 'keyword.control.directive.define.c', 'punctuation.definition.directive.c']}); expect(tokens[1]).toEqual({value: 'define', scopes: ['source.c', 'meta.preprocessor.macro.c', 'keyword.control.directive.define.c']}); @@ -223,8 +223,8 @@ int something(int param) { expect(tokens[11]).toEqual({value: ')', scopes: ['source.c', 'meta.preprocessor.macro.c', 'punctuation.section.parens.end.bracket.round.c']}); }); - describe("macros", function() { - it("tokenizes them", function() { + describe("macros", function () { + it("tokenizes them", function () { let {tokens} = grammar.tokenizeLine('#define INCREMENT(x) x++'); expect(tokens[0]).toEqual({value: '#', scopes: ['source.c', 'meta.preprocessor.macro.c', 'keyword.control.directive.define.c', 'punctuation.definition.directive.c']}); expect(tokens[1]).toEqual({value: 'define', scopes: ['source.c', 'meta.preprocessor.macro.c', 'keyword.control.directive.define.c']}); @@ -287,7 +287,7 @@ int something(int param) { expect(tokens[34]).toEqual({value: ')', scopes: ['source.c', 'meta.preprocessor.macro.c', 'punctuation.section.parens.end.bracket.round.c']}); }); - it("tokenizes multiline macros", function() { + it("tokenizes multiline macros", function () { let lines = grammar.tokenizeLines(`\ #define max(a,b) (a>b)? \\ a:b\ @@ -323,7 +323,7 @@ int something(int param) { expect(lines[4][0]).toEqual({value: '}', scopes: ['source.c', 'meta.preprocessor.macro.c', 'meta.block.c', 'punctuation.section.block.end.bracket.curly.c']}); }); - it("tokenizes complex definitions", function() { + it("tokenizes complex definitions", function () { const lines = grammar.tokenizeLines(`\ #define MakeHook(name) struct HOOK name = {{false, 0L}, \\ ((HOOKF)(*HookEnt)), ID("hook")}\ @@ -364,8 +364,8 @@ int something(int param) { }); }); - describe("includes", function() { - it("tokenizes '#include'", function() { + describe("includes", function () { + it("tokenizes '#include'", function () { let {tokens} = grammar.tokenizeLine('#include '); expect(tokens[0]).toEqual({value: '#', scopes: ['source.c', 'meta.preprocessor.include.c', 'keyword.control.directive.include.c', 'punctuation.definition.directive.c']}); expect(tokens[1]).toEqual({value: 'include', scopes: ['source.c', 'meta.preprocessor.include.c', 'keyword.control.directive.include.c']}); @@ -391,7 +391,7 @@ int something(int param) { expect(tokens[5]).toEqual({value: '"', scopes: ['source.c', 'meta.preprocessor.include.c', 'string.quoted.double.include.c', 'punctuation.definition.string.end.c']}); }); - it("tokenizes '#import'", function() { + it("tokenizes '#import'", function () { const {tokens} = grammar.tokenizeLine('#import "file"'); expect(tokens[0]).toEqual({value: '#', scopes: ['source.c', 'meta.preprocessor.include.c', 'keyword.control.directive.import.c', 'punctuation.definition.directive.c']}); expect(tokens[1]).toEqual({value: 'import', scopes: ['source.c', 'meta.preprocessor.include.c', 'keyword.control.directive.import.c']}); @@ -400,7 +400,7 @@ int something(int param) { expect(tokens[5]).toEqual({value: '"', scopes: ['source.c', 'meta.preprocessor.include.c', 'string.quoted.double.include.c', 'punctuation.definition.string.end.c']}); }); - it("tokenizes '#include_next'", function() { + it("tokenizes '#include_next'", function () { const {tokens} = grammar.tokenizeLine('#include_next "next.h"'); expect(tokens[0]).toEqual({value: '#', scopes: ['source.c', 'meta.preprocessor.include.c', 'keyword.control.directive.include_next.c', 'punctuation.definition.directive.c']}); expect(tokens[1]).toEqual({value: 'include_next', scopes: ['source.c', 'meta.preprocessor.include.c', 'keyword.control.directive.include_next.c']}); @@ -410,15 +410,15 @@ int something(int param) { }); }); - describe("diagnostics", function() { - it("tokenizes '#error'", function() { + describe("diagnostics", function () { + it("tokenizes '#error'", function () { const {tokens} = grammar.tokenizeLine('#error "C++ compiler required."'); expect(tokens[0]).toEqual({value: '#', scopes: ['source.c', 'meta.preprocessor.diagnostic.c', 'keyword.control.directive.diagnostic.error.c', 'punctuation.definition.directive.c']}); expect(tokens[1]).toEqual({value: 'error', scopes: ['source.c', 'meta.preprocessor.diagnostic.c', 'keyword.control.directive.diagnostic.error.c']}); expect(tokens[4]).toEqual({value: 'C++ compiler required.', scopes: ['source.c', 'meta.preprocessor.diagnostic.c', 'string.quoted.double.c']}); }); - it("tokenizes '#warning'", function() { + it("tokenizes '#warning'", function () { const {tokens} = grammar.tokenizeLine('#warning "This is a warning."'); expect(tokens[0]).toEqual({value: '#', scopes: ['source.c', 'meta.preprocessor.diagnostic.c', 'keyword.control.directive.diagnostic.warning.c', 'punctuation.definition.directive.c']}); expect(tokens[1]).toEqual({value: 'warning', scopes: ['source.c', 'meta.preprocessor.diagnostic.c', 'keyword.control.directive.diagnostic.warning.c']}); @@ -426,8 +426,8 @@ int something(int param) { }); }); - describe("conditionals", function() { - it("tokenizes if-elif-else preprocessor blocks", function() { + describe("conditionals", function () { + it("tokenizes if-elif-else preprocessor blocks", function () { const lines = grammar.tokenizeLines(`\ #if defined(CREDIT) credit(); @@ -461,7 +461,7 @@ int something(int param) { expect(lines[6][1]).toEqual({value: 'endif', scopes: ['source.c', 'meta.preprocessor.c', 'keyword.control.directive.conditional.c']}); }); - it("tokenizes if-true-else blocks", function() { + it("tokenizes if-true-else blocks", function () { const lines = grammar.tokenizeLines(`\ #if 1 int something() { @@ -500,7 +500,7 @@ int something() { expect(lines[12][1]).toEqual({value: 'endif', scopes: ['source.c', 'meta.preprocessor.c', 'keyword.control.directive.conditional.c']}); }); - it("tokenizes if-false-else blocks", function() { + it("tokenizes if-false-else blocks", function () { let lines = grammar.tokenizeLines(`\ int something() { #if 0 @@ -538,7 +538,7 @@ int something() { expect(lines[2][1]).toEqual({value: 'endif', scopes: ['source.c', 'meta.preprocessor.c', 'keyword.control.directive.conditional.c']}); }); - it("tokenizes ifdef-elif blocks", function() { + it("tokenizes ifdef-elif blocks", function () { const lines = grammar.tokenizeLines(`\ #ifdef __unix__ /* is defined by compilers targeting Unix systems */ # include @@ -574,7 +574,7 @@ int something() { expect(lines[4][1]).toEqual({value: 'endif', scopes: ['source.c', 'meta.preprocessor.c', 'keyword.control.directive.conditional.c']}); }); - it("tokenizes ifndef blocks", function() { + it("tokenizes ifndef blocks", function () { const lines = grammar.tokenizeLines(`\ #ifndef _INCL_GUARD #define _INCL_GUARD @@ -591,7 +591,7 @@ int something() { expect(lines[2][1]).toEqual({value: 'endif', scopes: ['source.c', 'meta.preprocessor.c', 'keyword.control.directive.conditional.c']}); }); - it("highlights stray elif, else and endif usages as invalid", function() { + it("highlights stray elif, else and endif usages as invalid", function () { const lines = grammar.tokenizeLines(`\ #if defined SOMEMACRO #else @@ -606,12 +606,12 @@ int something() { expect(lines[5][0]).toEqual({value: '#endif', scopes: ['source.c', 'invalid.illegal.stray-endif.c']}); }); - it("highlights errorneous defined usage as invalid", function() { + it("highlights errorneous defined usage as invalid", function () { const {tokens} = grammar.tokenizeLine('#if defined == VALUE'); expect(tokens[3]).toEqual({value: 'defined', scopes: ['source.c', 'meta.preprocessor.c', 'invalid.illegal.macro-name.c']}); }); - it("tokenizes multi line conditional queries", function() { + it("tokenizes multi line conditional queries", function () { const lines = grammar.tokenizeLines(`\ #if !defined (MACRO_A) \\ || !defined MACRO_C @@ -648,7 +648,7 @@ int something() { expect(lines[4][12]).toEqual({value: ' single line comment', scopes: ['source.c', 'comment.line.double-slash.cpp']}); }); - it("tokenizes ternary operator usage in preprocessor conditionals", function() { + it("tokenizes ternary operator usage in preprocessor conditionals", function () { const {tokens} = grammar.tokenizeLine('#if defined (__GNU_LIBRARY__) ? defined (__USE_GNU) : !defined (__STRICT_ANSI__)'); expect(tokens[9]).toEqual({value: '?', scopes: ['source.c', 'meta.preprocessor.c', 'keyword.operator.ternary.c']}); expect(tokens[11]).toEqual({value: 'defined', scopes: ['source.c', 'meta.preprocessor.c', 'keyword.control.directive.conditional.c']}); @@ -657,15 +657,15 @@ int something() { }); }); - describe("indentation", function() { + describe("indentation", function () { let editor = null; - beforeEach(function() { + beforeEach(function () { editor = buildTextEditor(); editor.setGrammar(grammar); }); - const expectPreservedIndentation = function(text) { + const expectPreservedIndentation = function (text) { editor.setText(text); editor.autoIndentBufferRows(0, editor.getLineCount() - 1); @@ -730,14 +730,14 @@ some_t a[3] = { ` )); - it("tokenizes binary literal", function() { + it("tokenizes binary literal", function () { const {tokens} = grammar.tokenizeLine('0b101010'); expect(tokens[0]).toEqual({value: '0b101010', scopes: ['source.c', 'constant.numeric.c']}); }); }); - describe("access", function() { - it("tokenizes the dot access operator", function() { + describe("access", function () { + it("tokenizes the dot access operator", function () { let lines = grammar.tokenizeLines(`\ { a. @@ -795,7 +795,7 @@ some_t a[3] = { expect(lines[1][3]).toEqual({value: 'b', scopes: ['source.c', 'meta.block.c', 'variable.other.member.c']}); }); - it("tokenizes the pointer access operator", function() { + it("tokenizes the pointer access operator", function () { let lines = grammar.tokenizeLines(`\ { a->b; @@ -852,8 +852,8 @@ some_t a[3] = { }); }); - describe("operators", function() { - it("tokenizes the sizeof operator", function() { + describe("operators", function () { + it("tokenizes the sizeof operator", function () { let {tokens} = grammar.tokenizeLine('sizeof unary_expression'); expect(tokens[0]).toEqual({value: 'sizeof', scopes: ['source.c', 'keyword.operator.sizeof.c']}); expect(tokens[1]).toEqual({value: ' unary_expression', scopes: ['source.c']}); @@ -875,7 +875,7 @@ some_t a[3] = { expect(tokens[0]).not.toEqual({value: 'sizeof', scopes: ['source.c', 'keyword.operator.sizeof.c']}); }); - it("tokenizes the increment operator", function() { + it("tokenizes the increment operator", function () { let {tokens} = grammar.tokenizeLine('i++'); expect(tokens[0]).toEqual({value: 'i', scopes: ['source.c']}); expect(tokens[1]).toEqual({value: '++', scopes: ['source.c', 'keyword.operator.increment.c']}); @@ -885,7 +885,7 @@ some_t a[3] = { expect(tokens[1]).toEqual({value: 'i', scopes: ['source.c']}); }); - it("tokenizes the decrement operator", function() { + it("tokenizes the decrement operator", function () { let {tokens} = grammar.tokenizeLine('i--'); expect(tokens[0]).toEqual({value: 'i', scopes: ['source.c']}); expect(tokens[1]).toEqual({value: '--', scopes: ['source.c', 'keyword.operator.decrement.c']}); @@ -895,7 +895,7 @@ some_t a[3] = { expect(tokens[1]).toEqual({value: 'i', scopes: ['source.c']}); }); - it("tokenizes logical operators", function() { + it("tokenizes logical operators", function () { let {tokens} = grammar.tokenizeLine('!a'); expect(tokens[0]).toEqual({value: '!', scopes: ['source.c', 'keyword.operator.logical.c']}); expect(tokens[1]).toEqual({value: 'a', scopes: ['source.c']}); @@ -913,7 +913,7 @@ some_t a[3] = { })(); }); - it("tokenizes comparison operators", function() { + it("tokenizes comparison operators", function () { const operators = ['<=', '>=', '!=', '==', '<', '>' ]; return (() => { @@ -928,7 +928,7 @@ some_t a[3] = { })(); }); - it("tokenizes arithmetic operators", function() { + it("tokenizes arithmetic operators", function () { const operators = ['+', '-', '*', '/', '%']; return (() => { @@ -943,7 +943,7 @@ some_t a[3] = { })(); }); - it("tokenizes ternary operators", function() { + it("tokenizes ternary operators", function () { const {tokens} = grammar.tokenizeLine('a ? b : c'); expect(tokens[0]).toEqual({value: 'a ', scopes: ['source.c']}); expect(tokens[1]).toEqual({value: '?', scopes: ['source.c', 'keyword.operator.ternary.c']}); @@ -952,7 +952,7 @@ some_t a[3] = { expect(tokens[4]).toEqual({value: ' c', scopes: ['source.c']}); }); - it("tokenizes ternary operators with member access", function() { + it("tokenizes ternary operators with member access", function () { const {tokens} = grammar.tokenizeLine('a ? b.c : d'); expect(tokens[0]).toEqual({value: 'a ', scopes: ['source.c']}); expect(tokens[1]).toEqual({value: '?', scopes: ['source.c', 'keyword.operator.ternary.c']}); @@ -964,7 +964,7 @@ some_t a[3] = { expect(tokens[7]).toEqual({value: ' d', scopes: ['source.c']}); }); - it("tokenizes ternary operators with pointer dereferencing", function() { + it("tokenizes ternary operators with pointer dereferencing", function () { const {tokens} = grammar.tokenizeLine('a ? b->c : d'); expect(tokens[0]).toEqual({value: 'a ', scopes: ['source.c']}); expect(tokens[1]).toEqual({value: '?', scopes: ['source.c', 'keyword.operator.ternary.c']}); @@ -976,7 +976,7 @@ some_t a[3] = { expect(tokens[7]).toEqual({value: ' d', scopes: ['source.c']}); }); - it("tokenizes ternary operators with function invocation", function() { + it("tokenizes ternary operators with function invocation", function () { const {tokens} = grammar.tokenizeLine('a ? f(b) : c'); expect(tokens[0]).toEqual({value: 'a ', scopes: ['source.c']}); expect(tokens[1]).toEqual({value: '?', scopes: ['source.c', 'keyword.operator.ternary.c']}); @@ -990,14 +990,14 @@ some_t a[3] = { expect(tokens[9]).toEqual({value: ' c', scopes: ['source.c']}); }); - describe("bitwise", function() { - it("tokenizes bitwise 'not'", function() { + describe("bitwise", function () { + it("tokenizes bitwise 'not'", function () { const {tokens} = grammar.tokenizeLine('~a'); expect(tokens[0]).toEqual({value: '~', scopes: ['source.c', 'keyword.operator.c']}); expect(tokens[1]).toEqual({value: 'a', scopes: ['source.c']}); }); - it("tokenizes shift operators", function() { + it("tokenizes shift operators", function () { let {tokens} = grammar.tokenizeLine('>>'); expect(tokens[0]).toEqual({value: '>>', scopes: ['source.c', 'keyword.operator.bitwise.shift.c']}); @@ -1005,7 +1005,7 @@ some_t a[3] = { expect(tokens[0]).toEqual({value: '<<', scopes: ['source.c', 'keyword.operator.bitwise.shift.c']}); }); - it("tokenizes them", function() { + it("tokenizes them", function () { const operators = ['|', '^', '&']; return (() => { @@ -1021,15 +1021,15 @@ some_t a[3] = { }); }); - describe("assignment", function() { - it("tokenizes the assignment operator", function() { + describe("assignment", function () { + it("tokenizes the assignment operator", function () { const {tokens} = grammar.tokenizeLine('a = b'); expect(tokens[0]).toEqual({value: 'a ', scopes: ['source.c']}); expect(tokens[1]).toEqual({value: '=', scopes: ['source.c', 'keyword.operator.assignment.c']}); expect(tokens[2]).toEqual({value: ' b', scopes: ['source.c']}); }); - it("tokenizes compound assignment operators", function() { + it("tokenizes compound assignment operators", function () { const operators = ['+=', '-=', '*=', '/=', '%=']; return (() => { const result = []; @@ -1043,7 +1043,7 @@ some_t a[3] = { })(); }); - it("tokenizes bitwise compound operators", function() { + it("tokenizes bitwise compound operators", function () { const operators = ['<<=', '>>=', '&=', '^=', '|=']; return (() => { const result = []; @@ -1060,20 +1060,20 @@ some_t a[3] = { }); }); - describe("C++", function() { + describe("C++", function () { beforeEach(() => grammar = atom.grammars.grammarForScopeName('source.cpp')); - it("parses the grammar", function() { + it("parses the grammar", function () { expect(grammar).toBeTruthy(); expect(grammar.scopeName).toBe('source.cpp'); }); - it("tokenizes this with `.this` class", function() { + it("tokenizes this with `.this` class", function () { const {tokens} = grammar.tokenizeLine('this.x'); expect(tokens[0]).toEqual({value: 'this', scopes: ['source.cpp', 'variable.language.this.cpp']}); }); - it("tokenizes classes", function() { + it("tokenizes classes", function () { const lines = grammar.tokenizeLines(`\ class Thing { int x; @@ -1084,7 +1084,7 @@ class Thing { expect(lines[0][2]).toEqual({value: 'Thing', scopes: ['source.cpp', 'meta.class-struct-block.cpp', 'entity.name.type.cpp']}); }); - it("tokenizes 'extern C'", function() { + it("tokenizes 'extern C'", function () { let lines = grammar.tokenizeLines(`\ extern "C" { #include "legacy_C_header.h" @@ -1132,7 +1132,7 @@ extern "C" { expect(lines[6][1]).toEqual({value: 'endif', scopes: ['source.cpp', 'meta.preprocessor.c', 'keyword.control.directive.conditional.c']}); }); - it("tokenizes UTF string escapes", function() { + it("tokenizes UTF string escapes", function () { const lines = grammar.tokenizeLines(`\ string str = U"\\U01234567\\u0123\\"\\0123\\x123";\ ` @@ -1151,7 +1151,7 @@ string str = U"\\U01234567\\u0123\\"\\0123\\x123";\ expect(lines[0][12]).toEqual({value: ';', scopes: ['source.cpp', 'punctuation.terminator.statement.c']}); }); - it("tokenizes % format specifiers", function() { + it("tokenizes % format specifiers", function () { let {tokens} = grammar.tokenizeLine('"%d"'); expect(tokens[0]).toEqual({value: '"', scopes: ['source.cpp', 'string.quoted.double.cpp', 'punctuation.definition.string.begin.cpp']}); expect(tokens[1]).toEqual({value: '%d', scopes: ['source.cpp', 'string.quoted.double.cpp', 'constant.other.placeholder.c']}); @@ -1168,13 +1168,15 @@ string str = U"\\U01234567\\u0123\\"\\0123\\x123";\ expect(tokens[2]).toEqual({value: '"', scopes: ['source.cpp', 'string.quoted.double.cpp', 'punctuation.definition.string.end.cpp']}); }); - it("tokenizes raw string literals", function() { + it("tokenizes raw string literals", function () { + /* eslint-disable no-useless-escape */ const lines = grammar.tokenizeLines(`\ string str = R"test( this is \"a\" test 'string' )test";\ ` ); + /* eslint-enable no-useless-escape */ expect(lines[0][0]).toEqual({value: 'string str ', scopes: ['source.cpp']}); expect(lines[0][3]).toEqual({value: 'R"test(', scopes: ['source.cpp', 'string.quoted.double.raw.cpp', 'punctuation.definition.string.begin.cpp']}); expect(lines[1][0]).toEqual({value: ' this is "a" test \'string\'', scopes: ['source.cpp', 'string.quoted.double.raw.cpp']}); @@ -1182,7 +1184,7 @@ string str = R"test( expect(lines[2][1]).toEqual({value: ';', scopes: ['source.cpp', 'punctuation.terminator.statement.c']}); }); - it("errors on long raw string delimiters", function() { + it("errors on long raw string delimiters", function () { const lines = grammar.tokenizeLines(`\ string str = R"01234567890123456()01234567890123456";\ ` @@ -1197,7 +1199,7 @@ string str = R"01234567890123456()01234567890123456";\ expect(lines[0][9]).toEqual({value: ';', scopes: ['source.cpp', 'punctuation.terminator.statement.c']}); }); - it("tokenizes destructors", function() { + it("tokenizes destructors", function () { let {tokens} = grammar.tokenizeLine('~Foo() {}'); expect(tokens[0]).toEqual({value: '~Foo', scopes: ['source.cpp', 'meta.function.destructor.cpp', 'entity.name.function.cpp']}); expect(tokens[1]).toEqual({value: '(', scopes: ['source.cpp', 'meta.function.destructor.cpp', 'punctuation.definition.parameters.begin.c']}); @@ -1213,8 +1215,8 @@ string str = R"01234567890123456()01234567890123456";\ expect(tokens[5]).toEqual({value: '}', scopes: ['source.cpp', 'meta.block.c', 'punctuation.section.block.end.bracket.curly.c']}); }); - describe("digit separators", function() { - it("recognizes numbers with digit separators", function() { + describe("digit separators", function () { + it("recognizes numbers with digit separators", function () { let {tokens} = grammar.tokenizeLine("1'000"); expect(tokens[0]).toEqual({value: "1'000", scopes: ['source.cpp', 'constant.numeric.c']}); @@ -1231,7 +1233,7 @@ string str = R"01234567890123456()01234567890123456";\ expect(tokens[0]).toEqual({value: "0b1100'0011'1111'0000", scopes: ['source.cpp', 'constant.numeric.c']}); }); - it("does not tokenize single quotes at the beginning or end of numbers as digit separators", function() { + it("does not tokenize single quotes at the beginning or end of numbers as digit separators", function () { let {tokens} = grammar.tokenizeLine("'1000"); expect(tokens[0]).toEqual({value: "'", scopes: ['source.cpp', 'string.quoted.single.c', 'punctuation.definition.string.begin.c']}); expect(tokens[1]).toEqual({value: "1000", scopes: ['source.cpp', 'string.quoted.single.c']}); @@ -1242,7 +1244,7 @@ string str = R"01234567890123456()01234567890123456";\ }); }); - describe("comments", () => it("tokenizes them", function() { + describe("comments", () => it("tokenizes them", function () { const {tokens} = grammar.tokenizeLine('// comment'); expect(tokens[0]).toEqual({value: '//', scopes: ['source.cpp', 'comment.line.double-slash.cpp', 'punctuation.definition.comment.cpp']}); expect(tokens[1]).toEqual({value: ' comment', scopes: ['source.cpp', 'comment.line.double-slash.cpp']}); @@ -1268,7 +1270,7 @@ comment\ expect(lines[2][0]).toEqual({value: 'comment', scopes: ['source.cpp']}); })); - describe("operators", () => it("tokenizes ternary operators with namespace resolution", function() { + describe("operators", () => it("tokenizes ternary operators with namespace resolution", function () { const {tokens} = grammar.tokenizeLine('a ? ns::b : ns::c'); expect(tokens[0]).toEqual({value: 'a ', scopes: ['source.cpp']}); expect(tokens[1]).toEqual({value: '?', scopes: ['source.cpp', 'keyword.operator.ternary.c']}); diff --git a/packages/language-c/spec/fixtures/sample.c b/packages/language-c/spec/fixtures/sample.c new file mode 100644 index 0000000000..3df22b5c68 --- /dev/null +++ b/packages/language-c/spec/fixtures/sample.c @@ -0,0 +1,92 @@ +#include +// <- keyword.control.directive.include + // ^ string.quoted.other.lt-gt.include + +#define FOO true +// <- keyword.control.directive.define + // ^ constant.preprocessor.c + // ^ constant.language.boolean.true + +int table[4]; +// <- support.storage.type.builtin + //^ variable.other.declaration.c + // ^ punctuation.definition.array.begin.bracket.square + +// THIS IS A COMMENT +// <- comment.line.double-slash +// <- punctuation.definition.comment + +/* This is also a comment. */ +// <- comment.block +// <- punctuation.definition.comment.begin + // ^^ punctuation.definition.comment.end + +int main() { +// <- support.storage.type.builtin + //^ entity.name.function + printf("Hello, World!"); + // <- support.function.c99 + // ^ string.quoted.double.c + + int32_t foo; + // <- support.storage.type.builtin.stdint + + int valueA = 2; + int valueB = 4; + // ^ keyword.operator.assignment + + valueA++; + // ^ keyword.operator.increment.c + + int product = valueA * valueB; + // ^ variable.other.declaration.c + // ^ keyword.operator.arithmetic + + return 0; + // <- keyword.control.return + // ^ constant.numeric.c +} +// <- punctuation.definition.block.end.bracket.curly + +int foo() { + // something + bool x = true; + bool y = false; + if (x && y) { + // ^ keyword.operator.logical.c + // something + } + + return rand(); + // ^ support.function.c99.c +} + +const char get_char() { + return 'x'; + // ^ punctuation.definition.string.begin + // ^^^ string.quoted.single.c +} + +const char *get_string() { + return "Hello, world!"; + // ^ punctuation.definition.string.begin + // ^^^^^^^^^^^^^^^ string.quoted.double.c +} + +enum Color { +// <- storage.type.enum + // ^ entity.name.type.enum.c + red, + // <- variable.declaration.enum + green, + // ^ meta.block.enum.c + blue +}; +enum Color r = red; + +struct MyStructure { +// <- storage.type.struct.c + // ^ entity.name.type.struct.c + int myNum; + char myLetter; +}; diff --git a/packages/language-c/spec/fixtures/sample.cpp b/packages/language-c/spec/fixtures/sample.cpp new file mode 100644 index 0000000000..255a4a2b3f --- /dev/null +++ b/packages/language-c/spec/fixtures/sample.cpp @@ -0,0 +1,109 @@ +#include +#include + +// THIS IS A COMMENT +// <- comment.line.double-slash +// <- punctuation.definition.comment + +/* This is also a comment. */ +// <- comment.block +// <- punctuation.definition.comment.begin + // ^^ punctuation.definition.comment.end + +using std::string; +// <- keyword.control.using + +#define FOO true +// <- keyword.control.directive.define + // ^ constant.preprocessor.cpp + // ^ constant.language.boolean.true + +int table[4]; +// <- support.storage.type.builtin + //^ variable.other.declaration.cpp + // ^ punctuation.definition.array.begin.bracket.square + +int *ptr; // ptr can point to an address which holds int data + +class vulkan_instance { +// <- storage.type.class-type.cpp +// ^ entity.name.class + +public: +// <- storage.modifier.public + int myNum; // Attribute (int variable) +//^ support.storage.type.builtin.cpp + // ^ variable.other.declaration.member + string myString; // Attribute (string variable) + something foo; + //^ support.storage.other.type + int32_t bar; + //^ support.storage.type.builtin.stdint + + ~vulkan_instance(); + // <- keyword.operator.destructor.cpp +}; +// <- punctuation.definition.block.end.bracket.curly + // <- punctuation.terminator.statement +struct queue_family_indices { +//^ storage.type.struct.cpp + // ^ entity.name.type.struct +}; + +int main() { +// <- support.storage.type.builtin +// ^ entity.name.function.cpp + std::cout << "Hello World!"; + // ^ keyword.operator.accessor.namespace + // ^ keyword.operator.bitwise.cpp + return 0; + // ^ keyword.control.return.cpp + // ^ constant.numeric +} + +int foo(int &bar) { + // ^ keyword.operator.pointer + // something + bool x = true; + bool y = false; + if (x and y) { + // ^ keyword.operator.logical.cpp + //^ punctuation.definition.expression.begin.bracket.round.cpp + // something + + } + std::cout << myMax(3, 7) << endl; + // ^ support.other.function.cpp + + std::cout << foo::myMax(3, 7) << endl; + // ^ support.other.function.cpp + + foo::bar(); + // ^ support.other.function.cpp + // ^ punctuation.definition.begin.bracket.round + return rand(); + // ^ support.function.c99.cpp +} + +const char get_char() { + return 'x'; + // ^ punctuation.definition.string.begin + // ^^^ string.quoted.single.cpp +} + +const char *get_string() { + return "Hello, world!"; + // ^ punctuation.definition.string.begin + // ^^^^^^^^^^^^^^^ string.quoted.double.cpp +} + +enum Color { +// <- storage.type.enum + // ^ entity.name.type.enum.cpp + red, + // <- variable.declaration.enum + green, + // ^ meta.block.enum.cpp + blue +}; +Color r = red; diff --git a/packages/language-c/spec/tree-sitter-grammar-spec.js b/packages/language-c/spec/tree-sitter-grammar-spec.js new file mode 100644 index 0000000000..8f10abc0c3 --- /dev/null +++ b/packages/language-c/spec/tree-sitter-grammar-spec.js @@ -0,0 +1,14 @@ +const path = require('path'); + +fdescribe('WASM Tree-sitter C grammar', () => { + + beforeEach(async () => { + await atom.packages.activatePackage('language-c'); + }); + + it('passes grammar tests', async () => { + await runGrammarTests(path.join(__dirname, 'fixtures', 'sample.c'), /\/\//) + await runGrammarTests(path.join(__dirname, 'fixtures', 'sample.cpp'), /\/\//) + }); + +}); From 67857f8875524a2d58e0548393df366ff2b1a9f8 Mon Sep 17 00:00:00 2001 From: Andrew Dupont Date: Sun, 22 Sep 2024 13:35:15 -0700 Subject: [PATCH 5/9] Un-focus a spec --- packages/language-c/spec/tree-sitter-grammar-spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/language-c/spec/tree-sitter-grammar-spec.js b/packages/language-c/spec/tree-sitter-grammar-spec.js index 8f10abc0c3..4ba930e429 100644 --- a/packages/language-c/spec/tree-sitter-grammar-spec.js +++ b/packages/language-c/spec/tree-sitter-grammar-spec.js @@ -1,6 +1,6 @@ const path = require('path'); -fdescribe('WASM Tree-sitter C grammar', () => { +describe('WASM Tree-sitter C grammar', () => { beforeEach(async () => { await atom.packages.activatePackage('language-c'); From f6ea101b87f4c0f8ef08cdb3647367b28b709d78 Mon Sep 17 00:00:00 2001 From: Andrew Dupont Date: Mon, 23 Sep 2024 11:40:49 -0700 Subject: [PATCH 6/9] =?UTF-8?q?[language-c]=20Restore=20some=20function=20?= =?UTF-8?q?highlighting=20in=20C++=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …that was inadvertently deleted. --- .../language-c/grammars/common/highlights.scm | 2 + .../grammars/tree-sitter-cpp/highlights.scm | 38 +++++++++++++++++-- packages/language-c/spec/fixtures/sample.c | 3 +- packages/language-c/spec/fixtures/sample.cpp | 19 +++++++++- 4 files changed, 57 insertions(+), 5 deletions(-) diff --git a/packages/language-c/grammars/common/highlights.scm b/packages/language-c/grammars/common/highlights.scm index 7c17086a9c..f1d80bd125 100644 --- a/packages/language-c/grammars/common/highlights.scm +++ b/packages/language-c/grammars/common/highlights.scm @@ -143,6 +143,7 @@ ; FUNCTIONS ; ========= +; The "foo" in `void foo() {`. (function_declarator (identifier) @entity.name.function._LANG_) @@ -158,6 +159,7 @@ field: (field_identifier) @support.other.function._LANG_) (#set! capture.final true)) +; The "foo" in `foo();`. (call_expression (identifier) @support.other.function._LANG_ (#set! capture.final true)) diff --git a/packages/language-c/grammars/tree-sitter-cpp/highlights.scm b/packages/language-c/grammars/tree-sitter-cpp/highlights.scm index e0d2343d8e..c819911905 100644 --- a/packages/language-c/grammars/tree-sitter-cpp/highlights.scm +++ b/packages/language-c/grammars/tree-sitter-cpp/highlights.scm @@ -87,21 +87,53 @@ (call_expression function: (qualified_identifier name: (identifier) @support.other.function.cpp) - (#set! capture.final true)) + (#set! capture.final)) + +; The "foo" in `void Bar::foo () {`. +(function_declarator + declarator: (qualified_identifier + name: (identifier) @entity.name.function.cpp) + (#set! capture.final)) + +; The "foo" in `void Bar::foo () {`. +(function_declarator + declarator: (qualified_identifier + name: (identifier) @entity.name.function.cpp) + (#set! capture.final)) + +; The "foo" in `void Bar::Baz::foo () {`, regardless of namespace depth. +(qualified_identifier + name: (identifier) @entity.name.function.cpp + (#set! capture.final) + (#is? test.descendantOfType "function_declarator")) + +; The "Bar" in `void Bar::~Bar () {`. +(function_declarator + declarator: (qualified_identifier + name: (destructor_name) @entity.name.function.cpp) + (#set! capture.final)) ; The "foo" in `foo(...)`. (call_expression function: (template_function name: (identifier) @support.other.function.cpp) - (#set! capture.final true)) + (#set! capture.final)) ; The "foo" in `troz::foo(...)`. (call_expression function: (qualified_identifier name: (template_function name: (identifier) @support.other.function.cpp)) - (#set! capture.final true)) + (#set! capture.final)) + +; A function name in a method declaration within a class specifier. +(function_declarator + (field_identifier) @entity.name.function.method.cpp) +; The "Foo" in `void Foo::bar() {` and `Foo::bar();`, regardless of namespace +; depth. +(qualified_identifier + scope: (namespace_identifier) @support.other.namespace.cpp) (string_literal (escape_sequence) @constant.character.escape.cpp) (char_literal (escape_sequence) @constant.character.escape.cpp) diff --git a/packages/language-c/spec/fixtures/sample.c b/packages/language-c/spec/fixtures/sample.c index 3df22b5c68..7b1a70e74f 100644 --- a/packages/language-c/spec/fixtures/sample.c +++ b/packages/language-c/spec/fixtures/sample.c @@ -24,7 +24,8 @@ int table[4]; int main() { // <- support.storage.type.builtin //^ entity.name.function - printf("Hello, World!"); + printf("Hello,\" World!"); + // ^ constant.character.escape.c // <- support.function.c99 // ^ string.quoted.double.c diff --git a/packages/language-c/spec/fixtures/sample.cpp b/packages/language-c/spec/fixtures/sample.cpp index 255a4a2b3f..49d94f9ff1 100644 --- a/packages/language-c/spec/fixtures/sample.cpp +++ b/packages/language-c/spec/fixtures/sample.cpp @@ -42,6 +42,9 @@ class vulkan_instance { ~vulkan_instance(); // <- keyword.operator.destructor.cpp + + void some_function(); + // ^ entity.name.function.method }; // <- punctuation.definition.block.end.bracket.curly // <- punctuation.terminator.statement @@ -53,9 +56,10 @@ struct queue_family_indices { int main() { // <- support.storage.type.builtin // ^ entity.name.function.cpp - std::cout << "Hello World!"; + std::cout << "Hello, \" World!"; // ^ keyword.operator.accessor.namespace // ^ keyword.operator.bitwise.cpp + // ^ constant.character.escape.cpp return 0; // ^ keyword.control.return.cpp // ^ constant.numeric @@ -97,6 +101,19 @@ const char *get_string() { // ^^^^^^^^^^^^^^^ string.quoted.double.cpp } +void vulkan_instance::some_function () { + // ^ support.other.namespace.cpp + // ^ entity.name.function +} + +void foo::bar::baz () { + // ^ support.other.namespace.cpp + // ^ support.other.namespace.cpp + // ^ entity.name.function + + vulkan_instance::some_function(); +} + enum Color { // <- storage.type.enum // ^ entity.name.type.enum.cpp From 0d0fcf49513ca381369dab6f268547039244dcb0 Mon Sep 17 00:00:00 2001 From: Andrew Dupont Date: Sat, 28 Sep 2024 14:34:17 -0700 Subject: [PATCH 7/9] Fix faulty point creation when resolving a divided fold --- src/wasm-tree-sitter-language-mode.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wasm-tree-sitter-language-mode.js b/src/wasm-tree-sitter-language-mode.js index 3bc5f253d0..37f686470c 100644 --- a/src/wasm-tree-sitter-language-mode.js +++ b/src/wasm-tree-sitter-language-mode.js @@ -1830,7 +1830,7 @@ class FoldResolver { // implied in the existing specs. return new Point(end.row - 1, Infinity); } else { - return new Point.fromObject(end, true); + return Point.fromObject(end, true); } } else { return null; From 2608d32d3529c7a619cfc7895b16a4a207b49cc2 Mon Sep 17 00:00:00 2001 From: Andrew Dupont Date: Tue, 1 Oct 2024 15:26:14 -0700 Subject: [PATCH 8/9] =?UTF-8?q?Fix=20issue=20with=20`editor.foldAllAtInden?= =?UTF-8?q?tLevel`=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …when two folds share a boundary. --- spec/wasm-tree-sitter-language-mode-spec.js | 33 ++++++++++++- src/wasm-tree-sitter-language-mode.js | 55 ++++++++++++++++++--- 2 files changed, 78 insertions(+), 10 deletions(-) diff --git a/spec/wasm-tree-sitter-language-mode-spec.js b/spec/wasm-tree-sitter-language-mode-spec.js index 60aee9b1d6..ba1ca7f20c 100644 --- a/spec/wasm-tree-sitter-language-mode-spec.js +++ b/spec/wasm-tree-sitter-language-mode-spec.js @@ -2288,8 +2288,8 @@ describe('WASMTreeSitterLanguageMode', () => { buffer.setText(dedent` - - + + `); @@ -2402,6 +2402,35 @@ describe('WASMTreeSitterLanguageMode', () => { `); }) + it('can handle folds that share boundaries with other folds', async () => { + const grammar = new WASMTreeSitterGrammar(atom.grammars, + pythonGrammarPath, + CSON.readFileSync(pythonGrammarPath) + ); + const languageMode = new WASMTreeSitterLanguageMode({ grammar, buffer }); + buffer.setLanguageMode(languageMode); + + buffer.setText(dedent` + class Example: + def get_dimension_values(self): + while True: + do_something() + + def wont_fold(self): + pass + `); + await languageMode.ready; + + editor.foldAllAtIndentLevel(1); + + expect(getDisplayText(editor)).toBe(dedent` + class Example: + def get_dimension_values(self):… + + def wont_fold(self):… + `); + }) + it('can target named vs anonymous nodes as fold boundaries', async () => { const grammar = new WASMTreeSitterGrammar(atom.grammars, rubyGrammarPath, rubyConfig); diff --git a/src/wasm-tree-sitter-language-mode.js b/src/wasm-tree-sitter-language-mode.js index 37f686470c..a4ad8098e5 100644 --- a/src/wasm-tree-sitter-language-mode.js +++ b/src/wasm-tree-sitter-language-mode.js @@ -121,6 +121,40 @@ function comparePoints(a, b) { } } +// A comparison function for a red-black tree that uses a point/range tuple as +// its key. +// +// We use this for folds. The primary indexing method of a fold is by point, +// but that point is one end of a range; and when two folds share a boundary, +// we need to be able to break the tie somehow. +function comparePointAndRangeBundles(bundleA, bundleB) { + let [pointA, rangeA] = bundleA; + let [pointB, rangeB] = bundleB; + let pointComparison = comparePoints(pointA, pointB); + if (pointComparison !== 0) { return pointComparison; } + + let aIsEnd = rangeA.end.isEqual(pointA); + let bIsEnd = rangeB.end.isEqual(pointB); + + // If one range ends at the given point and the other starts at the given + // point, then the one that ends should be handled earlier. + if (aIsEnd !== bIsEnd) { + return aIsEnd ? -1 : 1; + } + + // Otherwise, the larger of the two ranges should be considered to begin + // first and end last. (This comparison function does not envision ranges + // that overlap but where one is not contained by the other; it's up to the + // author of a `folds.scm` file to avoid those unusual scenarios, since they + // break the mental model of how folds work.) + if (rangeA.containsRange(rangeB)) { + return aIsEnd ? 1 : -1; + } else if (rangeB.containsRange(rangeA)) { + return bIsEnd ? -1 : 1; + } + return 0; +} + // Acts like `comparePoints`, but treats starting and ending boundaries // differently, making it so that ending boundaries are visited before starting // boundaries. @@ -1007,16 +1041,21 @@ class WASMTreeSitterLanguageMode { // violate that — perhaps most notably the C grammar in its use of nested // folds within `#ifdef` and its siblings. // - // Instead, a level of `0` means “all folds,” a level of `1` means “all folds - // that are contained by exactly one other fold,” and so on. This happens to - // work as expected if you're working in a language where nested folds are - // always indented relative to their enclosing fold, but it doesn't require - // it. + // Instead, a level of `0` means “all folds that are not contained by any + // other fold,” a level of `1` means “all folds that are contained by exactly + // one other fold,” and so on. This happens to work as expected if you're + // working in a language where nested folds are always indented relative to + // their enclosing fold, but it doesn't require it. // getFoldableRangesAtIndentLevel(goalLevel) { if (!this.tokenized) { return []; } - let rangeTree = createTree(comparePoints); + // The key for this red-black tree needs to be a combination of a point and + // a range. We do this because we want to order primarily by buffer + // position; but secondarily we need to consider whether the point is the + // start or the end of a range so that we can ensure that we visit the + // points in the order that properly expresses containment. + let rangeTree = createTree(comparePointAndRangeBundles); // No easy way around this. The way to pull it off is to get _all_ folds in // the document on all language layers, then place their boundaries into a @@ -1026,8 +1065,8 @@ class WASMTreeSitterLanguageMode { for (let layer of layers) { let folds = layer.foldResolver.getAllFoldRanges(); for (let fold of folds) { - rangeTree = rangeTree.insert(fold.start, { start: fold }); - rangeTree = rangeTree.insert(fold.end, { end: fold }); + rangeTree = rangeTree.insert([fold.start, fold], { start: fold }); + rangeTree = rangeTree.insert([fold.end, fold], { end: fold }); } } From 3b80398c4bcd70f7a69b4e21988645487e7de66c Mon Sep 17 00:00:00 2001 From: Andrew Dupont Date: Fri, 4 Oct 2024 13:07:00 -0700 Subject: [PATCH 9/9] Fix typos in `WASMTreeSitterLanguageMode::getSyntaxNodeAtPosition` --- src/wasm-tree-sitter-language-mode.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wasm-tree-sitter-language-mode.js b/src/wasm-tree-sitter-language-mode.js index a4ad8098e5..9374495940 100644 --- a/src/wasm-tree-sitter-language-mode.js +++ b/src/wasm-tree-sitter-language-mode.js @@ -966,7 +966,7 @@ class WASMTreeSitterLanguageMode { // includes this point. So let's just pretend that the root node covers // this area. if (where(rootNode, grammar)) { - results.push({ rootNode: node, depth }); + results.push({ node: rootNode, depth, grammar }); } continue; }