diff --git a/packages/language-c/.envrc b/packages/language-c/.envrc new file mode 100644 index 000000000..c42a8107e --- /dev/null +++ b/packages/language-c/.envrc @@ -0,0 +1 @@ +source "/Users/andrew/Code/JavaScript/emsdk/emsdk_env.sh" diff --git a/packages/language-c/.tool-versions b/packages/language-c/.tool-versions new file mode 100644 index 000000000..ab43e6ab2 --- /dev/null +++ b/packages/language-c/.tool-versions @@ -0,0 +1 @@ +nodejs 18.14.0 diff --git a/packages/language-c/grammars/modern-tree-sitter-c.cson b/packages/language-c/grammars/modern-tree-sitter-c.cson new file mode 100644 index 000000000..baa4d8baa --- /dev/null +++ b/packages/language-c/grammars/modern-tree-sitter-c.cson @@ -0,0 +1,13 @@ +name: 'C' +scopeName: 'source.c' +type: 'tree-sitter-2' +parser: 'tree-sitter-c' + +injectionRegex: '^(c|C)$' + +treeSitter: + grammar: 'tree-sitter-c/tree-sitter-c.wasm' + syntaxQuery: 'tree-sitter-c/highlights.scm' + localsQuery: 'tree-sitter-c/locals.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 new file mode 100644 index 000000000..0d064c63e --- /dev/null +++ b/packages/language-c/grammars/modern-tree-sitter-cpp.cson @@ -0,0 +1,13 @@ +name: 'C++' +scopeName: 'source.cpp' +type: 'tree-sitter-2' +parser: 'tree-sitter-cpp' + +injectionRegex: '^(cpp|CPP|cc|CC)$' + +treeSitter: + grammar: 'tree-sitter-cpp/tree-sitter-cpp.wasm' + syntaxQuery: 'tree-sitter-cpp/highlights.scm' + localsQuery: 'tree-sitter-cpp/locals.scm' + foldsQuery: 'tree-sitter-cpp/folds.scm' + indentsQuery: 'tree-sitter-cpp/indents.scm' diff --git a/packages/language-c/grammars/tree-sitter-c/folds.scm b/packages/language-c/grammars/tree-sitter-c/folds.scm new file mode 100644 index 000000000..a3de95821 --- /dev/null +++ b/packages/language-c/grammars/tree-sitter-c/folds.scm @@ -0,0 +1,24 @@ +; When we've got +; +; if (foo) { +; // something +; } else { +; // something else +; } +; +; we want the folds to work a little differently so that collapsing the `if` +; fold doesn't interfere with our ability to collapse the `else` fold. +(if_statement + consequence: (compound_statement) @fold + (#set! adjustToEndOfPreviousRow true)) + +[ + (field_declaration_list) + (enumerator_list) + (compound_statement) +] @fold + +; Divided folds for preprocessor statements because they can't reliably be +; expressed with simple folds. +["#ifndef" "#ifdef" "#elif" "#else" "#if"] @fold.start +["#elif" "#else" "#endif"] @fold.end diff --git a/packages/language-c/grammars/tree-sitter-c/highlights.scm b/packages/language-c/grammars/tree-sitter-c/highlights.scm new file mode 100644 index 000000000..677ddead2 --- /dev/null +++ b/packages/language-c/grammars/tree-sitter-c/highlights.scm @@ -0,0 +1,310 @@ +; PREPROCESSOR +; ============ + +[ + "#if" + "#ifdef" + "#ifndef" + "#endif" + "#elif" + "#else" +] @keyword.control.directive.conditional.c + +"#define" @keyword.control.directive.define.c +"#include" @keyword.control.directive.include.c + +; This will match if the more specific rules above haven't matched. The +; anonymous nodes will match under ideal conditions, but might not be present +; if the parser is flummoxed. +((preproc_directive) @keyword.control.directive.c + (#set! shy true)) + +((preproc_ifdef + (identifier) @entity.name.function.preprocessor.c + (#match? @entity.name.function.preprocessor.c "[a-zA-Z_$][\\w$]*"))) + +(preproc_function_def + (identifier) @entity.name.function.preprocessor.c + (#set! final true)) + +(system_lib_string) @string.quoted.other.lt-gt.include.c +((system_lib_string) @punctuation.definition.string.begin.c + (#set! endAfterFirstMatchOf "^<")) +((system_lib_string) @punctuation.definition.string.end.c + (#set! 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! final true)) + +(primitive_type) @storage.type.builtin.c +(type_identifier) @storage.type.other.c + +[ + "enum" + "long" + "short" + "signed" + "struct" + "typedef" + "union" + "unsigned" +] @storage.type.c + +[ + "const" + "extern" + "inline" + "register" + "restrict" + "static" + "volatile" +] @storage.modifier.c + +((primitive_type) @support.type.stdint.c + (#match? @support.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)$")) + + +; 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 + (#set! onlyIfFirst true)) + +(string_literal + "\"" @punctuation.definition.string.end.c + (#set! onlyIfLast true)) + +(char_literal "'") @string.quoted.single.c + +(char_literal + "'" @punctuation.definition.string.begin.c + (#set! onlyIfFirst true)) + +(char_literal + "'" @punctuation.definition.string.end.c + (#set! onlyIfLast true)) + +(string_literal (escape_sequence) @constant.character.escape.c) +(char_literal (escape_sequence) @constant.character.escape.c) + +; VARIABLES +; ========= + +; Declarations and assignments +; ---------------------------- + +(declaration + (identifier) @variable.declaration.c) + +(field_declaration + (field_identifier) @variable.declaration.c) + +(field_declaration + (pointer_declarator + (field_identifier) @variable.declaration.c)) + +(field_declaration + (array_declarator + (field_identifier) @variable.declaration.c)) + +(init_declarator + (identifier) @variable.declaration.c) + +(init_declarator + (pointer_declarator + (identifier) @variable.declaration.c)) + +(assignment_expression + left: (identifier) @variable.other.assignment.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. +(parameter_declaration + declarator: (pointer_declarator + declarator: (identifier) @variable.parameter.c)) + +; 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) + + +; 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! final true)) + +; The "foo" in `thing->troz->foo(...)`. +(call_expression + (field_expression + field: (field_identifier) @support.function.other.c) + (#set! final true)) + +(call_expression + (identifier) @support.function.other.c + (#set! final true)) + +; NUMBERS +; ======= + +(number_literal) @constant.numeric.c + + +; CONSTANTS +; ========= + +[ + (null) + (true) + (false) +] @constant.language._TYPE_.c + +((identifier) @constant.c + (#match? @constant.c "[_A-Z][_A-Z0-9]*$")) + + +; 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! startAndEndAroundFirstMatchOf "//")) + +; Match /* */ comments. +((comment) @comment.block.c + (#match? @comment.block.c "^/\\*")) + +((comment) @punctuation.definition.comment.begin.c + (#match? @punctuation.definition.comment.begin.c "^/\\*") + (#set! startAndEndAroundFirstMatchOf "^/\\*")) + +((comment) @punctuation.definition.comment.end.c + (#match? @punctuation.definition.comment.end.c "\\*/$") + (#set! 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) +(preproc_params "..." @keyword.operator.ellipsis.c) + +; PUNCTUATION +; =========== + +";" @punctuation.terminator.statement.c + +"," @punctuation.separator.comma.c +"->" @punctuation.separator.pointer-access.c + +"{" @punctuation.definition.begin.brace.curly.c +"}" @punctuation.definition.end.brace.curly.c +"(" @punctuation.definition.begin.brace.round.c +")" @punctuation.definition.end.brace.round.c +"[" @punctuation.definition.begin.brace.square.c +"]" @punctuation.definition.end.brace.square.c + +; TODO: +; +; * TM-style grammar has a lot of `mac-classic` scopes. I doubt they'd be +; present if this wasn't converted from a TextMate grammar, so I'm leaving +; them out for now. +; diff --git a/packages/language-c/grammars/tree-sitter-c/indents.scm b/packages/language-c/grammars/tree-sitter-c/indents.scm new file mode 100644 index 000000000..ba14e2493 --- /dev/null +++ b/packages/language-c/grammars/tree-sitter-c/indents.scm @@ -0,0 +1,20 @@ + + +["{" "(" "["] @indent + +["}" ")" "]"] @dedent + +; `switch` statements have a couple schools of thought, indentation-wise, and +; we might have to make this configurable somehow. +(switch_statement + body: (compound_statement "}" @match + (#set! onlyIfLast true)) + (#set! matchIndentOf parent.startPosition)) + +; 'case' and 'default' need to be indented one level more than their containing +; `switch`. +(["case" "default"] @match + (#set! matchIndentOf parent.parent.startPosition) + (#set! offsetIndent 1)) + +["case" "default"] @indent diff --git a/packages/language-c/grammars/tree-sitter-c/locals.scm b/packages/language-c/grammars/tree-sitter-c/locals.scm new file mode 100644 index 000000000..092bc2b04 --- /dev/null +++ b/packages/language-c/grammars/tree-sitter-c/locals.scm @@ -0,0 +1 @@ +; diff --git a/packages/language-c/grammars/tree-sitter-c/tree-sitter-c.wasm b/packages/language-c/grammars/tree-sitter-c/tree-sitter-c.wasm new file mode 100755 index 000000000..3f54a976c Binary files /dev/null and b/packages/language-c/grammars/tree-sitter-c/tree-sitter-c.wasm differ diff --git a/packages/language-c/grammars/tree-sitter-cpp/folds.scm b/packages/language-c/grammars/tree-sitter-cpp/folds.scm new file mode 100644 index 000000000..a3de95821 --- /dev/null +++ b/packages/language-c/grammars/tree-sitter-cpp/folds.scm @@ -0,0 +1,24 @@ +; When we've got +; +; if (foo) { +; // something +; } else { +; // something else +; } +; +; we want the folds to work a little differently so that collapsing the `if` +; fold doesn't interfere with our ability to collapse the `else` fold. +(if_statement + consequence: (compound_statement) @fold + (#set! adjustToEndOfPreviousRow true)) + +[ + (field_declaration_list) + (enumerator_list) + (compound_statement) +] @fold + +; Divided folds for preprocessor statements because they can't reliably be +; expressed with simple folds. +["#ifndef" "#ifdef" "#elif" "#else" "#if"] @fold.start +["#elif" "#else" "#endif"] @fold.end diff --git a/packages/language-c/grammars/tree-sitter-cpp/highlights.scm b/packages/language-c/grammars/tree-sitter-cpp/highlights.scm new file mode 100644 index 000000000..bd0ad102a --- /dev/null +++ b/packages/language-c/grammars/tree-sitter-cpp/highlights.scm @@ -0,0 +1,384 @@ +; PREPROCESSOR +; ============ + +[ + "#if" + "#ifdef" + "#ifndef" + "#endif" + "#elif" + "#else" +] @keyword.control.directive.conditional.cpp + +"#define" @keyword.control.directive.define.cpp +"#include" @keyword.control.directive.include.cpp + +; This will match if the more specific rules above haven't matched. The +; anonymous nodes will match under ideal conditions, but might not be present +; if the parser is flummoxed. +((preproc_directive) @keyword.control.directive.c + (#set! shy true)) + +((preproc_ifdef + (identifier) @entity.name.function.preprocessor.c + (#match? @entity.name.function.preprocessor.c "[a-zA-Z_$][\\w$]*"))) + +(preproc_function_def + (identifier) @entity.name.function.preprocessor.c + (#set! final true)) + +(preproc_function_def + (identifier) @entity.name.function.preprocessor.cpp + (#set! final true) +) + +(system_lib_string) @string.quoted.other.lt-gt.include.c +((system_lib_string) @punctuation.definition.string.begin.c + (#set! endAfterFirstMatchOf "^<")) +((system_lib_string) @punctuation.definition.string.end.c + (#set! 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! final true)) + + +(primitive_type) @storage.type.builtin.cpp + +(class_specifier + (type_identifier) @entity.name.class.cpp + (#set! final true)) + +(type_identifier) @storage.type.other.cpp +; (struct_specifier) @storage.type.cpp + +[ + "enum" + "long" + "short" + "signed" + "struct" + "typedef" + "union" + "unsigned" + + "template" +] @storage.type.cpp + +[ + "const" + "extern" + "inline" + "register" + "restrict" + "static" + "volatile" + + "private" + "protected" + "public" + + "friend" + "explicit" + "virtual" + "override" + "final" + "noexcept" +] @storage.modifier.cpp + +( + (primitive_type) @support.type.stdint.cpp + (#match? @support.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)$") +) + +"typename" @storage.modifier.typename.cpp + + +; FUNCTIONS +; ========= + +(function_declarator + (identifier) @entity.name.function.cpp) + +(function_declarator + (field_identifier) @entity.name.function.method.cpp) + +(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! final true)) + +; The "foo" in `thing->troz->foo(...)`. +(call_expression + (field_expression + field: (field_identifier) @support.function.other.cpp) + (#set! final true)) + +(call_expression + (identifier) @support.function.other.cpp + (#set! 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 + (#set! onlyIfFirst true)) + +(string_literal + "\"" @punctuation.definition.string.end.cpp + (#set! onlyIfLast true)) + +(char_literal "'") @string.quoted.single.cpp + +(char_literal + "'" @punctuation.definition.string.begin.cpp + (#set! onlyIfFirst true)) + +(char_literal + "'" @punctuation.definition.string.end.cpp + (#set! onlyIfLast 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 +; ---------------------------- + +(declaration + (identifier) @variable.declaration.cpp) + +(field_declaration + (field_identifier) @variable.declaration.cpp) + +(field_declaration + (pointer_declarator + (field_identifier) @variable.declaration.cpp)) + +(field_declaration + (array_declarator + (field_identifier) @variable.declaration.cpp)) + +(init_declarator + (identifier) @variable.declaration.cpp) + +(init_declarator + (pointer_declarator + (identifier) @variable.declaration.cpp)) + +(assignment_expression + left: (identifier) @variable.other.assignment.cpp) + +; Function parameters +; ------------------- + +(preproc_params + (identifier) @variable.parameter.preprocessor.cpp) + +(parameter_declaration + declarator: (identifier) @variable.parameter.cpp) + +(parameter_declaration + declarator: (pointer_declarator + 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) + +; 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) + (nullptr) +] @constant.language._TYPE_.cpp + +((identifier) @constant.cpp + (#match? @constant.cpp "[_A-Z][_A-Z0-9]*$")) + + +; 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! startAndEndAroundFirstMatchOf "//")) + +; Match /* */ comments. +((comment) @comment.block.cpp + (#match? @comment.block.cpp "^/\\*")) + +((comment) @punctuation.definition.comment.begin.cpp + (#match? @punctuation.definition.comment.begin.cpp "^/\\*") + (#set! startAndEndAroundFirstMatchOf "^/\\*")) + +((comment) @punctuation.definition.comment.end.cpp + (#match? @punctuation.definition.comment.end.cpp "\\*/$") + (#set! startAndEndAroundFirstMatchOf "\\*/$")) + + +; KEYWORDS +; ======== + +[ + "break" + "case" + "continue" + "default" + "do" + "else" + "for" + "goto" + "if" + "return" + "switch" + "while" + + "new" + "delete" +] @keyword.control._TYPE_.cpp + +[ + "catch" + "operator" + "try" + "throw" + "using" + "namespace" +] @keyword.control._TYPE_.cpp + +; OPERATORS +; ========= + +(pointer_declarator "*" @keyword.operator.pointer.cpp) +(abstract_pointer_declarator "*" @keyword.operator.pointer.cpp) +(pointer_expression "*" @keyword.operator.pointer.cpp) + + +"sizeof" @keyword.operator.sizeof.cpp +(pointer_expression "&" @keyword.operator.pointer.c) + + +"=" @keyword.operator.assignment.cpp + +[ + "%=" + "+=" + "-=" + "*=" + "/=" + "&=" + "^=" + "<<=" + ">>=" + "|=" +] @keyword.operator.assignment.compound.cpp + +(binary_expression + ["==" "!=" ">" "<" ">=" "<="] @keyword.operator.comparison.cpp) + + +["&&" "||"] @keyword.operator.logical.cpp + +"++" @keyword.operator.increment.cpp +"--" @keyword.operator.decrement.cpp + +(binary_expression + ["&" "|" "^" "~" "<<" ">>"] + @keyword.operator.bitwise.cpp) + +(binary_expression + ["<" ">"] @keyword.operator.comparison.cpp) + +(binary_expression + ["+" "-" "*" "/" "%"] @keyword.operator.arithmetic.cpp) + +(unary_expression + ["+" "-" "!"] @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 +; =========== + +";" @punctuation.terminator.statement.cpp + +"," @punctuation.separator.comma.cpp +"->" @punctuation.separator.pointer-access.cpp + +"{" @punctuation.definition.begin.brace.curly.cpp +"}" @punctuation.definition.end.brace.curly.cpp +"(" @punctuation.definition.begin.brace.round.cpp +")" @punctuation.definition.end.brace.round.cpp +"[" @punctuation.definition.begin.brace.square.cpp +"]" @punctuation.definition.end.brace.square.cpp + +; TODO: +; +; * TM-style grammar has a lot of `mac-classic` scopes. I doubt they'd be +; present if this wasn't converted from a TextMate grammar, so I'm leaving +; them out for now. +; + +; ( +; (compound_statement) @invalid.illegal +; ) + + +(compound_statement + (compound_statement) @invalid.illegal +) diff --git a/packages/language-c/grammars/tree-sitter-cpp/indents.scm b/packages/language-c/grammars/tree-sitter-cpp/indents.scm new file mode 100644 index 000000000..977f4b94c --- /dev/null +++ b/packages/language-c/grammars/tree-sitter-cpp/indents.scm @@ -0,0 +1,32 @@ + + +["{" "(" "["] @indent + +["}" ")" "]"] @dedent + + +; With access specifiers like… +; +; class Foo { +; public: +; void bar() {} +; } +; +; …prevailing style is to dedent the specifier so it matches the indentation of +; the line above. +(access_specifier) @dedent @indent + +; `switch` statements have a couple schools of thought, indentation-wise, and +; we might have to make this configurable somehow. +(switch_statement + body: (compound_statement "}" @match + (#set! onlyIfLast true)) + (#set! matchIndentOf parent.startPosition)) + +; 'case' and 'default' need to be indented one level more than their containing +; `switch`. +(["case" "default"] @match + (#set! matchIndentOf parent.parent.startPosition) + (#set! offsetIndent 1)) + +["case" "default"] @indent diff --git a/packages/language-c/grammars/tree-sitter-cpp/locals.scm b/packages/language-c/grammars/tree-sitter-cpp/locals.scm new file mode 100644 index 000000000..092bc2b04 --- /dev/null +++ b/packages/language-c/grammars/tree-sitter-cpp/locals.scm @@ -0,0 +1 @@ +; diff --git a/packages/language-c/grammars/tree-sitter-cpp/tree-sitter-cpp.wasm b/packages/language-c/grammars/tree-sitter-cpp/tree-sitter-cpp.wasm new file mode 100755 index 000000000..e84155965 Binary files /dev/null and b/packages/language-c/grammars/tree-sitter-cpp/tree-sitter-cpp.wasm differ