├── .github └── workflows │ └── CI.yml ├── COPYING ├── PROGRESS.md ├── README.md ├── meson.build ├── parser ├── README.md ├── include │ └── tree_sitter │ │ ├── api.h │ │ └── parser.h ├── meson.build ├── parser.c └── src │ ├── alloc.c │ ├── alloc.h │ ├── array.h │ ├── atomic.h │ ├── clock.h │ ├── error_costs.h │ ├── get_changed_ranges.c │ ├── get_changed_ranges.h │ ├── host.h │ ├── language.c │ ├── language.h │ ├── length.h │ ├── lexer.c │ ├── lexer.h │ ├── lib.c │ ├── node.c │ ├── parser.c │ ├── point.h │ ├── query.c │ ├── reduce_action.h │ ├── reusable_node.h │ ├── scanner.c │ ├── stack.c │ ├── stack.h │ ├── subtree.c │ ├── subtree.h │ ├── tree.c │ ├── tree.h │ ├── tree_cursor.c │ ├── tree_cursor.h │ ├── unicode.h │ └── unicode │ ├── ICU_SHA │ ├── LICENSE │ ├── README.md │ ├── ptypes.h │ ├── umachine.h │ ├── urename.h │ ├── utf.h │ ├── utf16.h │ └── utf8.h ├── src ├── ast │ ├── ast.vala │ ├── astvisitor.vala │ └── dataextractor.vala ├── colors.json ├── docs.json ├── functions.json ├── gtkcsslangserver.vala ├── ids.c.in ├── load.c ├── main.vala ├── meson.build ├── parsecontext.vala ├── properties.json ├── protocol.vala ├── selectors.json └── util.vala └── vapi └── ts.vapi /.github/workflows/CI.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: [ "main" ] 5 | pull_request: 6 | branches: [ "main" ] 7 | workflow_dispatch: 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v3 13 | - name: Build 14 | run: | 15 | sudo apt install -y libgtk-4-dev meson ninja-build libjsonrpc-glib-1.0-dev valac 16 | meson _build 17 | ninja -C _build 18 | -------------------------------------------------------------------------------- /PROGRESS.md: -------------------------------------------------------------------------------- 1 | - [x] Basic diagnostics 2 | - [ ] Autocompletion 3 | - [x] Autocomplete properties 4 | - [ ] Autocomplete values 5 | - [ ] Formatting using prettier. 6 | - [x] Docs on hover 7 | - [x] Add CI 8 | - [x] Figure out attribution for stuff from MDN 9 | - [x] Fork [tree-sitter-css](https://github.com/tree-sitter/tree-sitter-css) and adjust to GTK syntax. 10 | - [x] Jump to definition 11 | - [x] Better hovering 12 | - [ ] Better auto-completion 13 | - [ ] Documentsymbols 14 | - [x] Colors defined using `@define-color` 15 | - [ ] How to represent RuleSets? 16 | 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gtkcsslanguageserver 2 | 3 | A language server for the GTK CSS flavor. Written for [Workbench](https://github.com/sonnyp/workbench), probably supported 4 | at some point in GNOME Builder. 5 | 6 | ## Usage 7 | ### GNOME Builder 8 | ``` 9 | git clone https://github.com/JCWasmx86/GNOME-Builder-Plugins 10 | cd GNOME-Builder-Plugins 11 | meson _build -Dc_args="-O2" \ 12 | -Dplugin_clangd=disabled -Dplugin_gitgui=disabled \ 13 | -Dplugin_icon_installer=disabled -Dplugin_scriptdir=disabled \ 14 | -Dplugin_shfmt=disabled -Dplugin_swift_templates=disabled \ 15 | -Dplugin_texlab=disabled -Dplugin_callhierarchy=disabled \ 16 | -Dplugin_meson=disabled 17 | ninja -C _build install # Without root 18 | ``` 19 | For a better experience, please disable the "HTML Autocompletion" plugin. 20 | ### Workbench 21 | Is integrated with Workbench, so it should work OOTB. 22 | 23 | ## License 24 | - src/: GNU General Public License v3.0 25 | - parser/{src,include}: MIT License (MIT) (Vendored code from https://github.com/tree-sitter/tree-sitter): 26 | ``` 27 | The MIT License (MIT) 28 | 29 | Copyright (c) 2018-2023 Max Brunsfeld 30 | 31 | Permission is hereby granted, free of charge, to any person obtaining a copy 32 | of this software and associated documentation files (the "Software"), to deal 33 | in the Software without restriction, including without limitation the rights 34 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 35 | copies of the Software, and to permit persons to whom the Software is 36 | furnished to do so, subject to the following conditions: 37 | 38 | The above copyright notice and this permission notice shall be included in all 39 | copies or substantial portions of the Software. 40 | 41 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 42 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 43 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 44 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 45 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 46 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 47 | SOFTWARE. 48 | ``` 49 | - src/*.json: Documentation is from MDN: [Attributions and copyright licensing](https://developer.mozilla.org/en-US/docs/MDN/Writing_guidelines/Attrib_copyright_license) by Mozilla Contributors is licensed under [CC-BY-SA 2.5](https://creativecommons.org/licenses/by-sa/2.5/). 50 | ## Other language servers by this author: 51 | - [Swift-MesonLSP](https://github.com/JCWasmx86/Swift-MesonLSP): A meson language server written in Swift. 52 | 53 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project('gtkcsslanguageserver', ['c', 'vala'], 2 | version: '0.1.0', 3 | meson_version: '>= 0.60.0', 4 | default_options: [ 'warning_level=2', 'werror=false', ], 5 | ) 6 | 7 | vapi_dir = meson.current_source_dir() / 'vapi' 8 | add_project_arguments(['--vapidir', vapi_dir], language: 'vala') 9 | 10 | subdir('parser') 11 | subdir('src') 12 | 13 | -------------------------------------------------------------------------------- /parser/README.md: -------------------------------------------------------------------------------- 1 | # Tree-sitter 2 | This library consists of: 3 | - https://github.com/JCWasmx86/tree-sitter-css (MIT, forked from tree-sitter/tree-sitter-css) 4 | - https://github.com/tree-sitter/tree-sitter (MIT) 5 | 6 | -------------------------------------------------------------------------------- /parser/include/tree_sitter/parser.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_PARSER_H_ 2 | #define TREE_SITTER_PARSER_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #define ts_builtin_sym_error ((TSSymbol)-1) 13 | #define ts_builtin_sym_end 0 14 | #define TREE_SITTER_SERIALIZATION_BUFFER_SIZE 1024 15 | 16 | typedef uint16_t TSStateId; 17 | 18 | #ifndef TREE_SITTER_API_H_ 19 | typedef uint16_t TSSymbol; 20 | typedef uint16_t TSFieldId; 21 | typedef struct TSLanguage TSLanguage; 22 | #endif 23 | 24 | typedef struct { 25 | TSFieldId field_id; 26 | uint8_t child_index; 27 | bool inherited; 28 | } TSFieldMapEntry; 29 | 30 | typedef struct { 31 | uint16_t index; 32 | uint16_t length; 33 | } TSFieldMapSlice; 34 | 35 | typedef struct { 36 | bool visible; 37 | bool named; 38 | bool supertype; 39 | } TSSymbolMetadata; 40 | 41 | typedef struct TSLexer TSLexer; 42 | 43 | struct TSLexer { 44 | int32_t lookahead; 45 | TSSymbol result_symbol; 46 | void (*advance)(TSLexer *, bool); 47 | void (*mark_end)(TSLexer *); 48 | uint32_t (*get_column)(TSLexer *); 49 | bool (*is_at_included_range_start)(const TSLexer *); 50 | bool (*eof)(const TSLexer *); 51 | }; 52 | 53 | typedef enum { 54 | TSParseActionTypeShift, 55 | TSParseActionTypeReduce, 56 | TSParseActionTypeAccept, 57 | TSParseActionTypeRecover, 58 | } TSParseActionType; 59 | 60 | typedef union { 61 | struct { 62 | uint8_t type; 63 | TSStateId state; 64 | bool extra; 65 | bool repetition; 66 | } shift; 67 | struct { 68 | uint8_t type; 69 | uint8_t child_count; 70 | TSSymbol symbol; 71 | int16_t dynamic_precedence; 72 | uint16_t production_id; 73 | } reduce; 74 | uint8_t type; 75 | } TSParseAction; 76 | 77 | typedef struct { 78 | uint16_t lex_state; 79 | uint16_t external_lex_state; 80 | } TSLexMode; 81 | 82 | typedef union { 83 | TSParseAction action; 84 | struct { 85 | uint8_t count; 86 | bool reusable; 87 | } entry; 88 | } TSParseActionEntry; 89 | 90 | struct TSLanguage { 91 | uint32_t version; 92 | uint32_t symbol_count; 93 | uint32_t alias_count; 94 | uint32_t token_count; 95 | uint32_t external_token_count; 96 | uint32_t state_count; 97 | uint32_t large_state_count; 98 | uint32_t production_id_count; 99 | uint32_t field_count; 100 | uint16_t max_alias_sequence_length; 101 | const uint16_t *parse_table; 102 | const uint16_t *small_parse_table; 103 | const uint32_t *small_parse_table_map; 104 | const TSParseActionEntry *parse_actions; 105 | const char * const *symbol_names; 106 | const char * const *field_names; 107 | const TSFieldMapSlice *field_map_slices; 108 | const TSFieldMapEntry *field_map_entries; 109 | const TSSymbolMetadata *symbol_metadata; 110 | const TSSymbol *public_symbol_map; 111 | const uint16_t *alias_map; 112 | const TSSymbol *alias_sequences; 113 | const TSLexMode *lex_modes; 114 | bool (*lex_fn)(TSLexer *, TSStateId); 115 | bool (*keyword_lex_fn)(TSLexer *, TSStateId); 116 | TSSymbol keyword_capture_token; 117 | struct { 118 | const bool *states; 119 | const TSSymbol *symbol_map; 120 | void *(*create)(void); 121 | void (*destroy)(void *); 122 | bool (*scan)(void *, TSLexer *, const bool *symbol_whitelist); 123 | unsigned (*serialize)(void *, char *); 124 | void (*deserialize)(void *, const char *, unsigned); 125 | } external_scanner; 126 | const TSStateId *primary_state_ids; 127 | }; 128 | 129 | /* 130 | * Lexer Macros 131 | */ 132 | 133 | #define START_LEXER() \ 134 | bool result = false; \ 135 | bool skip = false; \ 136 | bool eof = false; \ 137 | int32_t lookahead; \ 138 | goto start; \ 139 | next_state: \ 140 | lexer->advance(lexer, skip); \ 141 | start: \ 142 | skip = false; \ 143 | lookahead = lexer->lookahead; 144 | 145 | #define ADVANCE(state_value) \ 146 | { \ 147 | state = state_value; \ 148 | goto next_state; \ 149 | } 150 | 151 | #define SKIP(state_value) \ 152 | { \ 153 | skip = true; \ 154 | state = state_value; \ 155 | goto next_state; \ 156 | } 157 | 158 | #define ACCEPT_TOKEN(symbol_value) \ 159 | result = true; \ 160 | lexer->result_symbol = symbol_value; \ 161 | lexer->mark_end(lexer); 162 | 163 | #define END_STATE() return result; 164 | 165 | /* 166 | * Parse Table Macros 167 | */ 168 | 169 | #define SMALL_STATE(id) id - LARGE_STATE_COUNT 170 | 171 | #define STATE(id) id 172 | 173 | #define ACTIONS(id) id 174 | 175 | #define SHIFT(state_value) \ 176 | {{ \ 177 | .shift = { \ 178 | .type = TSParseActionTypeShift, \ 179 | .state = state_value \ 180 | } \ 181 | }} 182 | 183 | #define SHIFT_REPEAT(state_value) \ 184 | {{ \ 185 | .shift = { \ 186 | .type = TSParseActionTypeShift, \ 187 | .state = state_value, \ 188 | .repetition = true \ 189 | } \ 190 | }} 191 | 192 | #define SHIFT_EXTRA() \ 193 | {{ \ 194 | .shift = { \ 195 | .type = TSParseActionTypeShift, \ 196 | .extra = true \ 197 | } \ 198 | }} 199 | 200 | #define REDUCE(symbol_val, child_count_val, ...) \ 201 | {{ \ 202 | .reduce = { \ 203 | .type = TSParseActionTypeReduce, \ 204 | .symbol = symbol_val, \ 205 | .child_count = child_count_val, \ 206 | __VA_ARGS__ \ 207 | }, \ 208 | }} 209 | 210 | #define RECOVER() \ 211 | {{ \ 212 | .type = TSParseActionTypeRecover \ 213 | }} 214 | 215 | #define ACCEPT_INPUT() \ 216 | {{ \ 217 | .type = TSParseActionTypeAccept \ 218 | }} 219 | 220 | #ifdef __cplusplus 221 | } 222 | #endif 223 | 224 | #endif // TREE_SITTER_PARSER_H_ 225 | -------------------------------------------------------------------------------- /parser/meson.build: -------------------------------------------------------------------------------- 1 | incdirs = [ 2 | include_directories('include'), 3 | include_directories('src'), 4 | include_directories('src/unicode'), 5 | ] 6 | ts_sources = [ 7 | 'parser.c', 8 | 'src/alloc.c', 9 | 'src/get_changed_ranges.c', 10 | 'src/language.c', 11 | 'src/lexer.c', 12 | 'src/node.c', 13 | 'parser.c', 14 | 'src/query.c', 15 | 'src/scanner.c', 16 | 'src/stack.c', 17 | 'src/subtree.c', 18 | 'src/tree.c', 19 | 'src/tree_cursor.c', 20 | 'src/parser.c', 21 | ] 22 | ts_lib = static_library('ts', ts_sources, include_directories: incdirs) 23 | 24 | ts_dep = declare_dependency( 25 | link_with: ts_lib, 26 | include_directories: incdirs, 27 | ) 28 | 29 | -------------------------------------------------------------------------------- /parser/src/alloc.c: -------------------------------------------------------------------------------- 1 | #include "alloc.h" 2 | #include 3 | 4 | static void *ts_malloc_default(size_t size) { 5 | void *result = malloc(size); 6 | if (size > 0 && !result) { 7 | fprintf(stderr, "tree-sitter failed to allocate %zu bytes", size); 8 | exit(1); 9 | } 10 | return result; 11 | } 12 | 13 | static void *ts_calloc_default(size_t count, size_t size) { 14 | void *result = calloc(count, size); 15 | if (count > 0 && !result) { 16 | fprintf(stderr, "tree-sitter failed to allocate %zu bytes", count * size); 17 | exit(1); 18 | } 19 | return result; 20 | } 21 | 22 | static void *ts_realloc_default(void *buffer, size_t size) { 23 | void *result = realloc(buffer, size); 24 | if (size > 0 && !result) { 25 | fprintf(stderr, "tree-sitter failed to reallocate %zu bytes", size); 26 | exit(1); 27 | } 28 | return result; 29 | } 30 | 31 | // Allow clients to override allocation functions dynamically 32 | void *(*ts_current_malloc)(size_t) = ts_malloc_default; 33 | void *(*ts_current_calloc)(size_t, size_t) = ts_calloc_default; 34 | void *(*ts_current_realloc)(void *, size_t) = ts_realloc_default; 35 | void (*ts_current_free)(void *) = free; 36 | 37 | void ts_set_allocator( 38 | void *(*new_malloc)(size_t), 39 | void *(*new_calloc)(size_t, size_t), 40 | void *(*new_realloc)(void *, size_t), 41 | void (*new_free)(void *) 42 | ) { 43 | ts_current_malloc = new_malloc ? new_malloc : ts_malloc_default; 44 | ts_current_calloc = new_calloc ? new_calloc : ts_calloc_default; 45 | ts_current_realloc = new_realloc ? new_realloc : ts_realloc_default; 46 | ts_current_free = new_free ? new_free : free; 47 | } 48 | 49 | -------------------------------------------------------------------------------- /parser/src/alloc.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_ALLOC_H_ 2 | #define TREE_SITTER_ALLOC_H_ 3 | 4 | #include "tree_sitter/api.h" 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | extern void *(*ts_current_malloc)(size_t); 15 | extern void *(*ts_current_calloc)(size_t, size_t); 16 | extern void *(*ts_current_realloc)(void *, size_t); 17 | extern void (*ts_current_free)(void *); 18 | 19 | // Allow clients to override allocation functions 20 | #ifndef ts_malloc 21 | #define ts_malloc ts_current_malloc 22 | #endif 23 | #ifndef ts_calloc 24 | #define ts_calloc ts_current_calloc 25 | #endif 26 | #ifndef ts_realloc 27 | #define ts_realloc ts_current_realloc 28 | #endif 29 | #ifndef ts_free 30 | #define ts_free ts_current_free 31 | #endif 32 | 33 | #ifdef __cplusplus 34 | } 35 | #endif 36 | 37 | #endif // TREE_SITTER_ALLOC_H_ 38 | -------------------------------------------------------------------------------- /parser/src/array.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_ARRAY_H_ 2 | #define TREE_SITTER_ARRAY_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "./alloc.h" 14 | 15 | #define Array(T) \ 16 | struct { \ 17 | T *contents; \ 18 | uint32_t size; \ 19 | uint32_t capacity; \ 20 | } 21 | 22 | #define array_init(self) \ 23 | ((self)->size = 0, (self)->capacity = 0, (self)->contents = NULL) 24 | 25 | #define array_new() \ 26 | { NULL, 0, 0 } 27 | 28 | #define array_get(self, index) \ 29 | (assert((uint32_t)index < (self)->size), &(self)->contents[index]) 30 | 31 | #define array_front(self) array_get(self, 0) 32 | 33 | #define array_back(self) array_get(self, (self)->size - 1) 34 | 35 | #define array_clear(self) ((self)->size = 0) 36 | 37 | #define array_reserve(self, new_capacity) \ 38 | array__reserve((VoidArray *)(self), array__elem_size(self), new_capacity) 39 | 40 | // Free any memory allocated for this array. 41 | #define array_delete(self) array__delete((VoidArray *)self) 42 | 43 | #define array_push(self, element) \ 44 | (array__grow((VoidArray *)(self), 1, array__elem_size(self)), \ 45 | (self)->contents[(self)->size++] = (element)) 46 | 47 | // Increase the array's size by a given number of elements, reallocating 48 | // if necessary. New elements are zero-initialized. 49 | #define array_grow_by(self, count) \ 50 | (array__grow((VoidArray *)(self), count, array__elem_size(self)), \ 51 | memset((self)->contents + (self)->size, 0, (count) * array__elem_size(self)), \ 52 | (self)->size += (count)) 53 | 54 | #define array_push_all(self, other) \ 55 | array_extend((self), (other)->size, (other)->contents) 56 | 57 | // Append `count` elements to the end of the array, reading their values from the 58 | // `contents` pointer. 59 | #define array_extend(self, count, contents) \ 60 | array__splice( \ 61 | (VoidArray *)(self), array__elem_size(self), (self)->size, \ 62 | 0, count, contents \ 63 | ) 64 | 65 | // Remove `old_count` elements from the array starting at the given `index`. At 66 | // the same index, insert `new_count` new elements, reading their values from the 67 | // `new_contents` pointer. 68 | #define array_splice(self, index, old_count, new_count, new_contents) \ 69 | array__splice( \ 70 | (VoidArray *)(self), array__elem_size(self), index, \ 71 | old_count, new_count, new_contents \ 72 | ) 73 | 74 | // Insert one `element` into the array at the given `index`. 75 | #define array_insert(self, index, element) \ 76 | array__splice((VoidArray *)(self), array__elem_size(self), index, 0, 1, &element) 77 | 78 | // Remove one `element` from the array at the given `index`. 79 | #define array_erase(self, index) \ 80 | array__erase((VoidArray *)(self), array__elem_size(self), index) 81 | 82 | #define array_pop(self) ((self)->contents[--(self)->size]) 83 | 84 | #define array_assign(self, other) \ 85 | array__assign((VoidArray *)(self), (const VoidArray *)(other), array__elem_size(self)) 86 | 87 | #define array_swap(self, other) \ 88 | array__swap((VoidArray *)(self), (VoidArray *)(other)) 89 | 90 | // Search a sorted array for a given `needle` value, using the given `compare` 91 | // callback to determine the order. 92 | // 93 | // If an existing element is found to be equal to `needle`, then the `index` 94 | // out-parameter is set to the existing value's index, and the `exists` 95 | // out-parameter is set to true. Otherwise, `index` is set to an index where 96 | // `needle` should be inserted in order to preserve the sorting, and `exists` 97 | // is set to false. 98 | #define array_search_sorted_with(self, compare, needle, index, exists) \ 99 | array__search_sorted(self, 0, compare, , needle, index, exists) 100 | 101 | // Search a sorted array for a given `needle` value, using integer comparisons 102 | // of a given struct field (specified with a leading dot) to determine the order. 103 | // 104 | // See also `array_search_sorted_with`. 105 | #define array_search_sorted_by(self, field, needle, index, exists) \ 106 | array__search_sorted(self, 0, _compare_int, field, needle, index, exists) 107 | 108 | // Insert a given `value` into a sorted array, using the given `compare` 109 | // callback to determine the order. 110 | #define array_insert_sorted_with(self, compare, value) \ 111 | do { \ 112 | unsigned index, exists; \ 113 | array_search_sorted_with(self, compare, &(value), &index, &exists); \ 114 | if (!exists) array_insert(self, index, value); \ 115 | } while (0) 116 | 117 | // Insert a given `value` into a sorted array, using integer comparisons of 118 | // a given struct field (specified with a leading dot) to determine the order. 119 | // 120 | // See also `array_search_sorted_by`. 121 | #define array_insert_sorted_by(self, field, value) \ 122 | do { \ 123 | unsigned index, exists; \ 124 | array_search_sorted_by(self, field, (value) field, &index, &exists); \ 125 | if (!exists) array_insert(self, index, value); \ 126 | } while (0) 127 | 128 | // Private 129 | 130 | typedef Array(void) VoidArray; 131 | 132 | #define array__elem_size(self) sizeof(*(self)->contents) 133 | 134 | static inline void array__delete(VoidArray *self) { 135 | ts_free(self->contents); 136 | self->contents = NULL; 137 | self->size = 0; 138 | self->capacity = 0; 139 | } 140 | 141 | static inline void array__erase(VoidArray *self, size_t element_size, 142 | uint32_t index) { 143 | assert(index < self->size); 144 | char *contents = (char *)self->contents; 145 | memmove(contents + index * element_size, contents + (index + 1) * element_size, 146 | (self->size - index - 1) * element_size); 147 | self->size--; 148 | } 149 | 150 | static inline void array__reserve(VoidArray *self, size_t element_size, uint32_t new_capacity) { 151 | if (new_capacity > self->capacity) { 152 | if (self->contents) { 153 | self->contents = ts_realloc(self->contents, new_capacity * element_size); 154 | } else { 155 | self->contents = ts_malloc(new_capacity * element_size); 156 | } 157 | self->capacity = new_capacity; 158 | } 159 | } 160 | 161 | static inline void array__assign(VoidArray *self, const VoidArray *other, size_t element_size) { 162 | array__reserve(self, element_size, other->size); 163 | self->size = other->size; 164 | memcpy(self->contents, other->contents, self->size * element_size); 165 | } 166 | 167 | static inline void array__swap(VoidArray *self, VoidArray *other) { 168 | VoidArray swap = *other; 169 | *other = *self; 170 | *self = swap; 171 | } 172 | 173 | static inline void array__grow(VoidArray *self, uint32_t count, size_t element_size) { 174 | uint32_t new_size = self->size + count; 175 | if (new_size > self->capacity) { 176 | uint32_t new_capacity = self->capacity * 2; 177 | if (new_capacity < 8) new_capacity = 8; 178 | if (new_capacity < new_size) new_capacity = new_size; 179 | array__reserve(self, element_size, new_capacity); 180 | } 181 | } 182 | 183 | static inline void array__splice(VoidArray *self, size_t element_size, 184 | uint32_t index, uint32_t old_count, 185 | uint32_t new_count, const void *elements) { 186 | uint32_t new_size = self->size + new_count - old_count; 187 | uint32_t old_end = index + old_count; 188 | uint32_t new_end = index + new_count; 189 | assert(old_end <= self->size); 190 | 191 | array__reserve(self, element_size, new_size); 192 | 193 | char *contents = (char *)self->contents; 194 | if (self->size > old_end) { 195 | memmove( 196 | contents + new_end * element_size, 197 | contents + old_end * element_size, 198 | (self->size - old_end) * element_size 199 | ); 200 | } 201 | if (new_count > 0) { 202 | if (elements) { 203 | memcpy( 204 | (contents + index * element_size), 205 | elements, 206 | new_count * element_size 207 | ); 208 | } else { 209 | memset( 210 | (contents + index * element_size), 211 | 0, 212 | new_count * element_size 213 | ); 214 | } 215 | } 216 | self->size += new_count - old_count; 217 | } 218 | 219 | // A binary search routine, based on Rust's `std::slice::binary_search_by`. 220 | #define array__search_sorted(self, start, compare, suffix, needle, index, exists) \ 221 | do { \ 222 | *(index) = start; \ 223 | *(exists) = false; \ 224 | uint32_t size = (self)->size - *(index); \ 225 | if (size == 0) break; \ 226 | int comparison; \ 227 | while (size > 1) { \ 228 | uint32_t half_size = size / 2; \ 229 | uint32_t mid_index = *(index) + half_size; \ 230 | comparison = compare(&((self)->contents[mid_index] suffix), (needle)); \ 231 | if (comparison <= 0) *(index) = mid_index; \ 232 | size -= half_size; \ 233 | } \ 234 | comparison = compare(&((self)->contents[*(index)] suffix), (needle)); \ 235 | if (comparison == 0) *(exists) = true; \ 236 | else if (comparison < 0) *(index) += 1; \ 237 | } while (0) 238 | 239 | // Helper macro for the `_sorted_by` routines below. This takes the left (existing) 240 | // parameter by reference in order to work with the generic sorting function above. 241 | #define _compare_int(a, b) ((int)*(a) - (int)(b)) 242 | 243 | #ifdef __cplusplus 244 | } 245 | #endif 246 | 247 | #endif // TREE_SITTER_ARRAY_H_ 248 | -------------------------------------------------------------------------------- /parser/src/atomic.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_ATOMIC_H_ 2 | #define TREE_SITTER_ATOMIC_H_ 3 | 4 | #include 5 | 6 | #ifdef __TINYC__ 7 | 8 | static inline size_t atomic_load(const volatile size_t *p) { 9 | return *p; 10 | } 11 | 12 | static inline uint32_t atomic_inc(volatile uint32_t *p) { 13 | *p += 1; 14 | return *p; 15 | } 16 | 17 | static inline uint32_t atomic_dec(volatile uint32_t *p) { 18 | *p-= 1; 19 | return *p; 20 | } 21 | 22 | #elif defined(_WIN32) 23 | 24 | #include 25 | 26 | static inline size_t atomic_load(const volatile size_t *p) { 27 | return *p; 28 | } 29 | 30 | static inline uint32_t atomic_inc(volatile uint32_t *p) { 31 | return InterlockedIncrement((long volatile *)p); 32 | } 33 | 34 | static inline uint32_t atomic_dec(volatile uint32_t *p) { 35 | return InterlockedDecrement((long volatile *)p); 36 | } 37 | 38 | #else 39 | 40 | static inline size_t atomic_load(const volatile size_t *p) { 41 | #ifdef __ATOMIC_RELAXED 42 | return __atomic_load_n(p, __ATOMIC_RELAXED); 43 | #else 44 | return __sync_fetch_and_add((volatile size_t *)p, 0); 45 | #endif 46 | } 47 | 48 | static inline uint32_t atomic_inc(volatile uint32_t *p) { 49 | return __sync_add_and_fetch(p, 1u); 50 | } 51 | 52 | static inline uint32_t atomic_dec(volatile uint32_t *p) { 53 | return __sync_sub_and_fetch(p, 1u); 54 | } 55 | 56 | #endif 57 | 58 | #endif // TREE_SITTER_ATOMIC_H_ 59 | -------------------------------------------------------------------------------- /parser/src/clock.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_CLOCK_H_ 2 | #define TREE_SITTER_CLOCK_H_ 3 | 4 | #include 5 | #include 6 | 7 | typedef uint64_t TSDuration; 8 | 9 | #ifdef _WIN32 10 | 11 | // Windows: 12 | // * Represent a time as a performance counter value. 13 | // * Represent a duration as a number of performance counter ticks. 14 | 15 | #include 16 | typedef uint64_t TSClock; 17 | 18 | static inline TSDuration duration_from_micros(uint64_t micros) { 19 | LARGE_INTEGER frequency; 20 | QueryPerformanceFrequency(&frequency); 21 | return micros * (uint64_t)frequency.QuadPart / 1000000; 22 | } 23 | 24 | static inline uint64_t duration_to_micros(TSDuration self) { 25 | LARGE_INTEGER frequency; 26 | QueryPerformanceFrequency(&frequency); 27 | return self * 1000000 / (uint64_t)frequency.QuadPart; 28 | } 29 | 30 | static inline TSClock clock_null(void) { 31 | return 0; 32 | } 33 | 34 | static inline TSClock clock_now(void) { 35 | LARGE_INTEGER result; 36 | QueryPerformanceCounter(&result); 37 | return (uint64_t)result.QuadPart; 38 | } 39 | 40 | static inline TSClock clock_after(TSClock base, TSDuration duration) { 41 | return base + duration; 42 | } 43 | 44 | static inline bool clock_is_null(TSClock self) { 45 | return !self; 46 | } 47 | 48 | static inline bool clock_is_gt(TSClock self, TSClock other) { 49 | return self > other; 50 | } 51 | 52 | #elif defined(CLOCK_MONOTONIC) && !defined(__APPLE__) 53 | 54 | // POSIX with monotonic clock support (Linux) 55 | // * Represent a time as a monotonic (seconds, nanoseconds) pair. 56 | // * Represent a duration as a number of microseconds. 57 | // 58 | // On these platforms, parse timeouts will correspond accurately to 59 | // real time, regardless of what other processes are running. 60 | 61 | #include 62 | typedef struct timespec TSClock; 63 | 64 | static inline TSDuration duration_from_micros(uint64_t micros) { 65 | return micros; 66 | } 67 | 68 | static inline uint64_t duration_to_micros(TSDuration self) { 69 | return self; 70 | } 71 | 72 | static inline TSClock clock_now(void) { 73 | TSClock result; 74 | clock_gettime(CLOCK_MONOTONIC, &result); 75 | return result; 76 | } 77 | 78 | static inline TSClock clock_null(void) { 79 | return (TSClock) {0, 0}; 80 | } 81 | 82 | static inline TSClock clock_after(TSClock base, TSDuration duration) { 83 | TSClock result = base; 84 | result.tv_sec += duration / 1000000; 85 | result.tv_nsec += (duration % 1000000) * 1000; 86 | if (result.tv_nsec >= 1000000000) { 87 | result.tv_nsec -= 1000000000; 88 | ++(result.tv_sec); 89 | } 90 | return result; 91 | } 92 | 93 | static inline bool clock_is_null(TSClock self) { 94 | return !self.tv_sec; 95 | } 96 | 97 | static inline bool clock_is_gt(TSClock self, TSClock other) { 98 | if (self.tv_sec > other.tv_sec) return true; 99 | if (self.tv_sec < other.tv_sec) return false; 100 | return self.tv_nsec > other.tv_nsec; 101 | } 102 | 103 | #else 104 | 105 | // macOS or POSIX without monotonic clock support 106 | // * Represent a time as a process clock value. 107 | // * Represent a duration as a number of process clock ticks. 108 | // 109 | // On these platforms, parse timeouts may be affected by other processes, 110 | // which is not ideal, but is better than using a non-monotonic time API 111 | // like `gettimeofday`. 112 | 113 | #include 114 | typedef uint64_t TSClock; 115 | 116 | static inline TSDuration duration_from_micros(uint64_t micros) { 117 | return micros * (uint64_t)CLOCKS_PER_SEC / 1000000; 118 | } 119 | 120 | static inline uint64_t duration_to_micros(TSDuration self) { 121 | return self * 1000000 / (uint64_t)CLOCKS_PER_SEC; 122 | } 123 | 124 | static inline TSClock clock_null(void) { 125 | return 0; 126 | } 127 | 128 | static inline TSClock clock_now(void) { 129 | return (uint64_t)clock(); 130 | } 131 | 132 | static inline TSClock clock_after(TSClock base, TSDuration duration) { 133 | return base + duration; 134 | } 135 | 136 | static inline bool clock_is_null(TSClock self) { 137 | return !self; 138 | } 139 | 140 | static inline bool clock_is_gt(TSClock self, TSClock other) { 141 | return self > other; 142 | } 143 | 144 | #endif 145 | 146 | #endif // TREE_SITTER_CLOCK_H_ 147 | -------------------------------------------------------------------------------- /parser/src/error_costs.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_ERROR_COSTS_H_ 2 | #define TREE_SITTER_ERROR_COSTS_H_ 3 | 4 | #define ERROR_STATE 0 5 | #define ERROR_COST_PER_RECOVERY 500 6 | #define ERROR_COST_PER_MISSING_TREE 110 7 | #define ERROR_COST_PER_SKIPPED_TREE 100 8 | #define ERROR_COST_PER_SKIPPED_LINE 30 9 | #define ERROR_COST_PER_SKIPPED_CHAR 1 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /parser/src/get_changed_ranges.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_GET_CHANGED_RANGES_H_ 2 | #define TREE_SITTER_GET_CHANGED_RANGES_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include "./tree_cursor.h" 9 | #include "./subtree.h" 10 | 11 | typedef Array(TSRange) TSRangeArray; 12 | 13 | void ts_range_array_get_changed_ranges( 14 | const TSRange *old_ranges, unsigned old_range_count, 15 | const TSRange *new_ranges, unsigned new_range_count, 16 | TSRangeArray *differences 17 | ); 18 | 19 | bool ts_range_array_intersects( 20 | const TSRangeArray *self, unsigned start_index, 21 | uint32_t start_byte, uint32_t end_byte 22 | ); 23 | 24 | unsigned ts_subtree_get_changed_ranges( 25 | const Subtree *old_tree, const Subtree *new_tree, 26 | TreeCursor *cursor1, TreeCursor *cursor2, 27 | const TSLanguage *language, 28 | const TSRangeArray *included_range_differences, 29 | TSRange **ranges 30 | ); 31 | 32 | #ifdef __cplusplus 33 | } 34 | #endif 35 | 36 | #endif // TREE_SITTER_GET_CHANGED_RANGES_H_ 37 | -------------------------------------------------------------------------------- /parser/src/host.h: -------------------------------------------------------------------------------- 1 | 2 | // Determine endian and pointer size based on known defines. 3 | // TS_BIG_ENDIAN and TS_PTR_SIZE can be set as -D compiler arguments 4 | // to override this. 5 | 6 | #if !defined(TS_BIG_ENDIAN) 7 | #if (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) \ 8 | || (defined( __APPLE_CC__) && (defined(__ppc__) || defined(__ppc64__))) 9 | #define TS_BIG_ENDIAN 1 10 | #else 11 | #define TS_BIG_ENDIAN 0 12 | #endif 13 | #endif 14 | 15 | #if !defined(TS_PTR_SIZE) 16 | #if UINTPTR_MAX == 0xFFFFFFFF 17 | #define TS_PTR_SIZE 32 18 | #else 19 | #define TS_PTR_SIZE 64 20 | #endif 21 | #endif 22 | -------------------------------------------------------------------------------- /parser/src/language.c: -------------------------------------------------------------------------------- 1 | #include "./language.h" 2 | #include "./subtree.h" 3 | #include "./error_costs.h" 4 | #include 5 | 6 | uint32_t ts_language_symbol_count(const TSLanguage *self) { 7 | return self->symbol_count + self->alias_count; 8 | } 9 | 10 | uint32_t ts_language_version(const TSLanguage *self) { 11 | return self->version; 12 | } 13 | 14 | uint32_t ts_language_field_count(const TSLanguage *self) { 15 | return self->field_count; 16 | } 17 | 18 | void ts_language_table_entry( 19 | const TSLanguage *self, 20 | TSStateId state, 21 | TSSymbol symbol, 22 | TableEntry *result 23 | ) { 24 | if (symbol == ts_builtin_sym_error || symbol == ts_builtin_sym_error_repeat) { 25 | result->action_count = 0; 26 | result->is_reusable = false; 27 | result->actions = NULL; 28 | } else { 29 | assert(symbol < self->token_count); 30 | uint32_t action_index = ts_language_lookup(self, state, symbol); 31 | const TSParseActionEntry *entry = &self->parse_actions[action_index]; 32 | result->action_count = entry->entry.count; 33 | result->is_reusable = entry->entry.reusable; 34 | result->actions = (const TSParseAction *)(entry + 1); 35 | } 36 | } 37 | 38 | TSSymbolMetadata ts_language_symbol_metadata( 39 | const TSLanguage *self, 40 | TSSymbol symbol 41 | ) { 42 | if (symbol == ts_builtin_sym_error) { 43 | return (TSSymbolMetadata) {.visible = true, .named = true}; 44 | } else if (symbol == ts_builtin_sym_error_repeat) { 45 | return (TSSymbolMetadata) {.visible = false, .named = false}; 46 | } else { 47 | return self->symbol_metadata[symbol]; 48 | } 49 | } 50 | 51 | TSSymbol ts_language_public_symbol( 52 | const TSLanguage *self, 53 | TSSymbol symbol 54 | ) { 55 | if (symbol == ts_builtin_sym_error) return symbol; 56 | return self->public_symbol_map[symbol]; 57 | } 58 | 59 | const char *ts_language_symbol_name( 60 | const TSLanguage *self, 61 | TSSymbol symbol 62 | ) { 63 | if (symbol == ts_builtin_sym_error) { 64 | return "ERROR"; 65 | } else if (symbol == ts_builtin_sym_error_repeat) { 66 | return "_ERROR"; 67 | } else if (symbol < ts_language_symbol_count(self)) { 68 | return self->symbol_names[symbol]; 69 | } else { 70 | return NULL; 71 | } 72 | } 73 | 74 | TSSymbol ts_language_symbol_for_name( 75 | const TSLanguage *self, 76 | const char *string, 77 | uint32_t length, 78 | bool is_named 79 | ) { 80 | if (!strncmp(string, "ERROR", length)) return ts_builtin_sym_error; 81 | uint32_t count = ts_language_symbol_count(self); 82 | for (TSSymbol i = 0; i < count; i++) { 83 | TSSymbolMetadata metadata = ts_language_symbol_metadata(self, i); 84 | if ((!metadata.visible && !metadata.supertype) || metadata.named != is_named) continue; 85 | const char *symbol_name = self->symbol_names[i]; 86 | if (!strncmp(symbol_name, string, length) && !symbol_name[length]) { 87 | return self->public_symbol_map[i]; 88 | } 89 | } 90 | return 0; 91 | } 92 | 93 | TSSymbolType ts_language_symbol_type( 94 | const TSLanguage *self, 95 | TSSymbol symbol 96 | ) { 97 | TSSymbolMetadata metadata = ts_language_symbol_metadata(self, symbol); 98 | if (metadata.named && metadata.visible) { 99 | return TSSymbolTypeRegular; 100 | } else if (metadata.visible) { 101 | return TSSymbolTypeAnonymous; 102 | } else { 103 | return TSSymbolTypeAuxiliary; 104 | } 105 | } 106 | 107 | const char *ts_language_field_name_for_id( 108 | const TSLanguage *self, 109 | TSFieldId id 110 | ) { 111 | uint32_t count = ts_language_field_count(self); 112 | if (count && id <= count) { 113 | return self->field_names[id]; 114 | } else { 115 | return NULL; 116 | } 117 | } 118 | 119 | TSFieldId ts_language_field_id_for_name( 120 | const TSLanguage *self, 121 | const char *name, 122 | uint32_t name_length 123 | ) { 124 | uint32_t count = ts_language_field_count(self); 125 | for (TSSymbol i = 1; i < count + 1; i++) { 126 | switch (strncmp(name, self->field_names[i], name_length)) { 127 | case 0: 128 | if (self->field_names[i][name_length] == 0) return i; 129 | break; 130 | case -1: 131 | return 0; 132 | default: 133 | break; 134 | } 135 | } 136 | return 0; 137 | } 138 | -------------------------------------------------------------------------------- /parser/src/language.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_LANGUAGE_H_ 2 | #define TREE_SITTER_LANGUAGE_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include "./subtree.h" 9 | #include "tree_sitter/parser.h" 10 | 11 | #define ts_builtin_sym_error_repeat (ts_builtin_sym_error - 1) 12 | 13 | typedef struct { 14 | const TSParseAction *actions; 15 | uint32_t action_count; 16 | bool is_reusable; 17 | } TableEntry; 18 | 19 | typedef struct { 20 | const TSLanguage *language; 21 | const uint16_t *data; 22 | const uint16_t *group_end; 23 | TSStateId state; 24 | uint16_t table_value; 25 | uint16_t section_index; 26 | uint16_t group_count; 27 | bool is_small_state; 28 | 29 | const TSParseAction *actions; 30 | TSSymbol symbol; 31 | TSStateId next_state; 32 | uint16_t action_count; 33 | } LookaheadIterator; 34 | 35 | void ts_language_table_entry(const TSLanguage *, TSStateId, TSSymbol, TableEntry *); 36 | 37 | TSSymbolMetadata ts_language_symbol_metadata(const TSLanguage *, TSSymbol); 38 | 39 | TSSymbol ts_language_public_symbol(const TSLanguage *, TSSymbol); 40 | 41 | static inline bool ts_language_is_symbol_external(const TSLanguage *self, TSSymbol symbol) { 42 | return 0 < symbol && symbol < self->external_token_count + 1; 43 | } 44 | 45 | static inline const TSParseAction *ts_language_actions( 46 | const TSLanguage *self, 47 | TSStateId state, 48 | TSSymbol symbol, 49 | uint32_t *count 50 | ) { 51 | TableEntry entry; 52 | ts_language_table_entry(self, state, symbol, &entry); 53 | *count = entry.action_count; 54 | return entry.actions; 55 | } 56 | 57 | static inline bool ts_language_has_reduce_action( 58 | const TSLanguage *self, 59 | TSStateId state, 60 | TSSymbol symbol 61 | ) { 62 | TableEntry entry; 63 | ts_language_table_entry(self, state, symbol, &entry); 64 | return entry.action_count > 0 && entry.actions[0].type == TSParseActionTypeReduce; 65 | } 66 | 67 | // Lookup the table value for a given symbol and state. 68 | // 69 | // For non-terminal symbols, the table value represents a successor state. 70 | // For terminal symbols, it represents an index in the actions table. 71 | // For 'large' parse states, this is a direct lookup. For 'small' parse 72 | // states, this requires searching through the symbol groups to find 73 | // the given symbol. 74 | static inline uint16_t ts_language_lookup( 75 | const TSLanguage *self, 76 | TSStateId state, 77 | TSSymbol symbol 78 | ) { 79 | if (state >= self->large_state_count) { 80 | uint32_t index = self->small_parse_table_map[state - self->large_state_count]; 81 | const uint16_t *data = &self->small_parse_table[index]; 82 | uint16_t group_count = *(data++); 83 | for (unsigned i = 0; i < group_count; i++) { 84 | uint16_t section_value = *(data++); 85 | uint16_t symbol_count = *(data++); 86 | for (unsigned i = 0; i < symbol_count; i++) { 87 | if (*(data++) == symbol) return section_value; 88 | } 89 | } 90 | return 0; 91 | } else { 92 | return self->parse_table[state * self->symbol_count + symbol]; 93 | } 94 | } 95 | 96 | static inline bool ts_language_has_actions( 97 | const TSLanguage *self, 98 | TSStateId state, 99 | TSSymbol symbol 100 | ) { 101 | return ts_language_lookup(self, state, symbol) != 0; 102 | } 103 | 104 | // Iterate over all of the symbols that are valid in the given state. 105 | // 106 | // For 'large' parse states, this just requires iterating through 107 | // all possible symbols and checking the parse table for each one. 108 | // For 'small' parse states, this exploits the structure of the 109 | // table to only visit the valid symbols. 110 | static inline LookaheadIterator ts_language_lookaheads( 111 | const TSLanguage *self, 112 | TSStateId state 113 | ) { 114 | bool is_small_state = state >= self->large_state_count; 115 | const uint16_t *data; 116 | const uint16_t *group_end = NULL; 117 | uint16_t group_count = 0; 118 | if (is_small_state) { 119 | uint32_t index = self->small_parse_table_map[state - self->large_state_count]; 120 | data = &self->small_parse_table[index]; 121 | group_end = data + 1; 122 | group_count = *data; 123 | } else { 124 | data = &self->parse_table[state * self->symbol_count] - 1; 125 | } 126 | return (LookaheadIterator) { 127 | .language = self, 128 | .data = data, 129 | .group_end = group_end, 130 | .group_count = group_count, 131 | .is_small_state = is_small_state, 132 | .symbol = UINT16_MAX, 133 | .next_state = 0, 134 | }; 135 | } 136 | 137 | static inline bool ts_lookahead_iterator_next(LookaheadIterator *self) { 138 | // For small parse states, valid symbols are listed explicitly, 139 | // grouped by their value. There's no need to look up the actions 140 | // again until moving to the next group. 141 | if (self->is_small_state) { 142 | self->data++; 143 | if (self->data == self->group_end) { 144 | if (self->group_count == 0) return false; 145 | self->group_count--; 146 | self->table_value = *(self->data++); 147 | unsigned symbol_count = *(self->data++); 148 | self->group_end = self->data + symbol_count; 149 | self->symbol = *self->data; 150 | } else { 151 | self->symbol = *self->data; 152 | return true; 153 | } 154 | } 155 | 156 | // For large parse states, iterate through every symbol until one 157 | // is found that has valid actions. 158 | else { 159 | do { 160 | self->data++; 161 | self->symbol++; 162 | if (self->symbol >= self->language->symbol_count) return false; 163 | self->table_value = *self->data; 164 | } while (!self->table_value); 165 | } 166 | 167 | // Depending on if the symbols is terminal or non-terminal, the table value either 168 | // represents a list of actions or a successor state. 169 | if (self->symbol < self->language->token_count) { 170 | const TSParseActionEntry *entry = &self->language->parse_actions[self->table_value]; 171 | self->action_count = entry->entry.count; 172 | self->actions = (const TSParseAction *)(entry + 1); 173 | self->next_state = 0; 174 | } else { 175 | self->action_count = 0; 176 | self->next_state = self->table_value; 177 | } 178 | return true; 179 | } 180 | 181 | static inline TSStateId ts_language_next_state( 182 | const TSLanguage *self, 183 | TSStateId state, 184 | TSSymbol symbol 185 | ) { 186 | if (symbol == ts_builtin_sym_error || symbol == ts_builtin_sym_error_repeat) { 187 | return 0; 188 | } else if (symbol < self->token_count) { 189 | uint32_t count; 190 | const TSParseAction *actions = ts_language_actions(self, state, symbol, &count); 191 | if (count > 0) { 192 | TSParseAction action = actions[count - 1]; 193 | if (action.type == TSParseActionTypeShift) { 194 | return action.shift.extra ? state : action.shift.state; 195 | } 196 | } 197 | return 0; 198 | } else { 199 | return ts_language_lookup(self, state, symbol); 200 | } 201 | } 202 | 203 | // Whether the state is a "primary state". If this returns false, it indicates that there exists 204 | // another state that behaves identically to this one with respect to query analysis. 205 | static inline bool ts_language_state_is_primary( 206 | const TSLanguage *self, 207 | TSStateId state 208 | ) { 209 | if (self->version >= 14) { 210 | return state == self->primary_state_ids[state]; 211 | } else { 212 | return true; 213 | } 214 | } 215 | 216 | static inline const bool *ts_language_enabled_external_tokens( 217 | const TSLanguage *self, 218 | unsigned external_scanner_state 219 | ) { 220 | if (external_scanner_state == 0) { 221 | return NULL; 222 | } else { 223 | return self->external_scanner.states + self->external_token_count * external_scanner_state; 224 | } 225 | } 226 | 227 | static inline const TSSymbol *ts_language_alias_sequence( 228 | const TSLanguage *self, 229 | uint32_t production_id 230 | ) { 231 | return production_id ? 232 | &self->alias_sequences[production_id * self->max_alias_sequence_length] : 233 | NULL; 234 | } 235 | 236 | static inline TSSymbol ts_language_alias_at( 237 | const TSLanguage *self, 238 | uint32_t production_id, 239 | uint32_t child_index 240 | ) { 241 | return production_id ? 242 | self->alias_sequences[production_id * self->max_alias_sequence_length + child_index] : 243 | 0; 244 | } 245 | 246 | static inline void ts_language_field_map( 247 | const TSLanguage *self, 248 | uint32_t production_id, 249 | const TSFieldMapEntry **start, 250 | const TSFieldMapEntry **end 251 | ) { 252 | if (self->field_count == 0) { 253 | *start = NULL; 254 | *end = NULL; 255 | return; 256 | } 257 | 258 | TSFieldMapSlice slice = self->field_map_slices[production_id]; 259 | *start = &self->field_map_entries[slice.index]; 260 | *end = &self->field_map_entries[slice.index] + slice.length; 261 | } 262 | 263 | static inline void ts_language_aliases_for_symbol( 264 | const TSLanguage *self, 265 | TSSymbol original_symbol, 266 | const TSSymbol **start, 267 | const TSSymbol **end 268 | ) { 269 | *start = &self->public_symbol_map[original_symbol]; 270 | *end = *start + 1; 271 | 272 | unsigned i = 0; 273 | for (;;) { 274 | TSSymbol symbol = self->alias_map[i++]; 275 | if (symbol == 0 || symbol > original_symbol) break; 276 | uint16_t count = self->alias_map[i++]; 277 | if (symbol == original_symbol) { 278 | *start = &self->alias_map[i]; 279 | *end = &self->alias_map[i + count]; 280 | break; 281 | } 282 | i += count; 283 | } 284 | } 285 | 286 | static inline void ts_language_write_symbol_as_dot_string( 287 | const TSLanguage *self, 288 | FILE *f, 289 | TSSymbol symbol 290 | ) { 291 | const char *name = ts_language_symbol_name(self, symbol); 292 | for (const char *c = name; *c; c++) { 293 | switch (*c) { 294 | case '"': 295 | case '\\': 296 | fputc('\\', f); 297 | fputc(*c, f); 298 | break; 299 | case '\n': 300 | fputs("\\n", f); 301 | break; 302 | case '\t': 303 | fputs("\\n", f); 304 | break; 305 | default: 306 | fputc(*c, f); 307 | break; 308 | } 309 | } 310 | } 311 | 312 | #ifdef __cplusplus 313 | } 314 | #endif 315 | 316 | #endif // TREE_SITTER_LANGUAGE_H_ 317 | -------------------------------------------------------------------------------- /parser/src/length.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_LENGTH_H_ 2 | #define TREE_SITTER_LENGTH_H_ 3 | 4 | #include 5 | #include 6 | #include "./point.h" 7 | #include "tree_sitter/api.h" 8 | 9 | typedef struct { 10 | uint32_t bytes; 11 | TSPoint extent; 12 | } Length; 13 | 14 | static const Length LENGTH_UNDEFINED = {0, {0, 1}}; 15 | static const Length LENGTH_MAX = {UINT32_MAX, {UINT32_MAX, UINT32_MAX}}; 16 | 17 | static inline bool length_is_undefined(Length length) { 18 | return length.bytes == 0 && length.extent.column != 0; 19 | } 20 | 21 | static inline Length length_min(Length len1, Length len2) { 22 | return (len1.bytes < len2.bytes) ? len1 : len2; 23 | } 24 | 25 | static inline Length length_add(Length len1, Length len2) { 26 | Length result; 27 | result.bytes = len1.bytes + len2.bytes; 28 | result.extent = point_add(len1.extent, len2.extent); 29 | return result; 30 | } 31 | 32 | static inline Length length_sub(Length len1, Length len2) { 33 | Length result; 34 | result.bytes = len1.bytes - len2.bytes; 35 | result.extent = point_sub(len1.extent, len2.extent); 36 | return result; 37 | } 38 | 39 | static inline Length length_zero(void) { 40 | Length result = {0, {0, 0}}; 41 | return result; 42 | } 43 | 44 | static inline Length length_saturating_sub(Length len1, Length len2) { 45 | if (len1.bytes > len2.bytes) { 46 | return length_sub(len1, len2); 47 | } else { 48 | return length_zero(); 49 | } 50 | } 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /parser/src/lexer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "./lexer.h" 3 | #include "./subtree.h" 4 | #include "./length.h" 5 | #include "./unicode.h" 6 | 7 | #define LOG(message, character) \ 8 | if (self->logger.log) { \ 9 | snprintf( \ 10 | self->debug_buffer, \ 11 | TREE_SITTER_SERIALIZATION_BUFFER_SIZE, \ 12 | 32 <= character && character < 127 ? \ 13 | message " character:'%c'" : \ 14 | message " character:%d", \ 15 | character \ 16 | ); \ 17 | self->logger.log( \ 18 | self->logger.payload, \ 19 | TSLogTypeLex, \ 20 | self->debug_buffer \ 21 | ); \ 22 | } 23 | 24 | static const int32_t BYTE_ORDER_MARK = 0xFEFF; 25 | 26 | static const TSRange DEFAULT_RANGE = { 27 | .start_point = { 28 | .row = 0, 29 | .column = 0, 30 | }, 31 | .end_point = { 32 | .row = UINT32_MAX, 33 | .column = UINT32_MAX, 34 | }, 35 | .start_byte = 0, 36 | .end_byte = UINT32_MAX 37 | }; 38 | 39 | // Check if the lexer has reached EOF. This state is stored 40 | // by setting the lexer's `current_included_range_index` such that 41 | // it has consumed all of its available ranges. 42 | static bool ts_lexer__eof(const TSLexer *_self) { 43 | Lexer *self = (Lexer *)_self; 44 | return self->current_included_range_index == self->included_range_count; 45 | } 46 | 47 | // Clear the currently stored chunk of source code, because the lexer's 48 | // position has changed. 49 | static void ts_lexer__clear_chunk(Lexer *self) { 50 | self->chunk = NULL; 51 | self->chunk_size = 0; 52 | self->chunk_start = 0; 53 | } 54 | 55 | // Call the lexer's input callback to obtain a new chunk of source code 56 | // for the current position. 57 | static void ts_lexer__get_chunk(Lexer *self) { 58 | self->chunk_start = self->current_position.bytes; 59 | self->chunk = self->input.read( 60 | self->input.payload, 61 | self->current_position.bytes, 62 | self->current_position.extent, 63 | &self->chunk_size 64 | ); 65 | if (!self->chunk_size) { 66 | self->current_included_range_index = self->included_range_count; 67 | self->chunk = NULL; 68 | } 69 | } 70 | 71 | // Decode the next unicode character in the current chunk of source code. 72 | // This assumes that the lexer has already retrieved a chunk of source 73 | // code that spans the current position. 74 | static void ts_lexer__get_lookahead(Lexer *self) { 75 | uint32_t position_in_chunk = self->current_position.bytes - self->chunk_start; 76 | uint32_t size = self->chunk_size - position_in_chunk; 77 | 78 | if (size == 0) { 79 | self->lookahead_size = 1; 80 | self->data.lookahead = '\0'; 81 | return; 82 | } 83 | 84 | const uint8_t *chunk = (const uint8_t *)self->chunk + position_in_chunk; 85 | UnicodeDecodeFunction decode = self->input.encoding == TSInputEncodingUTF8 86 | ? ts_decode_utf8 87 | : ts_decode_utf16; 88 | 89 | self->lookahead_size = decode(chunk, size, &self->data.lookahead); 90 | 91 | // If this chunk ended in the middle of a multi-byte character, 92 | // try again with a fresh chunk. 93 | if (self->data.lookahead == TS_DECODE_ERROR && size < 4) { 94 | ts_lexer__get_chunk(self); 95 | chunk = (const uint8_t *)self->chunk; 96 | size = self->chunk_size; 97 | self->lookahead_size = decode(chunk, size, &self->data.lookahead); 98 | } 99 | 100 | if (self->data.lookahead == TS_DECODE_ERROR) { 101 | self->lookahead_size = 1; 102 | } 103 | } 104 | 105 | static void ts_lexer_goto(Lexer *self, Length position) { 106 | self->current_position = position; 107 | 108 | // Move to the first valid position at or after the given position. 109 | bool found_included_range = false; 110 | for (unsigned i = 0; i < self->included_range_count; i++) { 111 | TSRange *included_range = &self->included_ranges[i]; 112 | if ( 113 | included_range->end_byte > self->current_position.bytes && 114 | included_range->end_byte > included_range->start_byte 115 | ) { 116 | if (included_range->start_byte >= self->current_position.bytes) { 117 | self->current_position = (Length) { 118 | .bytes = included_range->start_byte, 119 | .extent = included_range->start_point, 120 | }; 121 | } 122 | 123 | self->current_included_range_index = i; 124 | found_included_range = true; 125 | break; 126 | } 127 | } 128 | 129 | if (found_included_range) { 130 | // If the current position is outside of the current chunk of text, 131 | // then clear out the current chunk of text. 132 | if (self->chunk && ( 133 | self->current_position.bytes < self->chunk_start || 134 | self->current_position.bytes >= self->chunk_start + self->chunk_size 135 | )) { 136 | ts_lexer__clear_chunk(self); 137 | } 138 | 139 | self->lookahead_size = 0; 140 | self->data.lookahead = '\0'; 141 | } 142 | 143 | // If the given position is beyond any of included ranges, move to the EOF 144 | // state - past the end of the included ranges. 145 | else { 146 | self->current_included_range_index = self->included_range_count; 147 | TSRange *last_included_range = &self->included_ranges[self->included_range_count - 1]; 148 | self->current_position = (Length) { 149 | .bytes = last_included_range->end_byte, 150 | .extent = last_included_range->end_point, 151 | }; 152 | ts_lexer__clear_chunk(self); 153 | self->lookahead_size = 1; 154 | self->data.lookahead = '\0'; 155 | } 156 | } 157 | 158 | // Intended to be called only from functions that control logging. 159 | static void ts_lexer__do_advance(Lexer *self, bool skip) { 160 | if (self->lookahead_size) { 161 | self->current_position.bytes += self->lookahead_size; 162 | if (self->data.lookahead == '\n') { 163 | self->current_position.extent.row++; 164 | self->current_position.extent.column = 0; 165 | } else { 166 | self->current_position.extent.column += self->lookahead_size; 167 | } 168 | } 169 | 170 | const TSRange *current_range = &self->included_ranges[self->current_included_range_index]; 171 | while ( 172 | self->current_position.bytes >= current_range->end_byte || 173 | current_range->end_byte == current_range->start_byte 174 | ) { 175 | self->current_included_range_index++; 176 | if (self->current_included_range_index < self->included_range_count) { 177 | current_range++; 178 | self->current_position = (Length) { 179 | current_range->start_byte, 180 | current_range->start_point, 181 | }; 182 | } else { 183 | current_range = NULL; 184 | break; 185 | } 186 | } 187 | 188 | if (skip) self->token_start_position = self->current_position; 189 | 190 | if (current_range) { 191 | if ( 192 | self->current_position.bytes < self->chunk_start || 193 | self->current_position.bytes >= self->chunk_start + self->chunk_size 194 | ) { 195 | ts_lexer__get_chunk(self); 196 | } 197 | ts_lexer__get_lookahead(self); 198 | } else { 199 | ts_lexer__clear_chunk(self); 200 | self->data.lookahead = '\0'; 201 | self->lookahead_size = 1; 202 | } 203 | } 204 | 205 | // Advance to the next character in the source code, retrieving a new 206 | // chunk of source code if needed. 207 | static void ts_lexer__advance(TSLexer *_self, bool skip) { 208 | Lexer *self = (Lexer *)_self; 209 | if (!self->chunk) return; 210 | 211 | if (skip) { 212 | LOG("skip", self->data.lookahead); 213 | } else { 214 | LOG("consume", self->data.lookahead); 215 | } 216 | 217 | ts_lexer__do_advance(self, skip); 218 | } 219 | 220 | // Mark that a token match has completed. This can be called multiple 221 | // times if a longer match is found later. 222 | static void ts_lexer__mark_end(TSLexer *_self) { 223 | Lexer *self = (Lexer *)_self; 224 | if (!ts_lexer__eof(&self->data)) { 225 | // If the lexer is right at the beginning of included range, 226 | // then the token should be considered to end at the *end* of the 227 | // previous included range, rather than here. 228 | TSRange *current_included_range = &self->included_ranges[ 229 | self->current_included_range_index 230 | ]; 231 | if ( 232 | self->current_included_range_index > 0 && 233 | self->current_position.bytes == current_included_range->start_byte 234 | ) { 235 | TSRange *previous_included_range = current_included_range - 1; 236 | self->token_end_position = (Length) { 237 | previous_included_range->end_byte, 238 | previous_included_range->end_point, 239 | }; 240 | return; 241 | } 242 | } 243 | self->token_end_position = self->current_position; 244 | } 245 | 246 | static uint32_t ts_lexer__get_column(TSLexer *_self) { 247 | Lexer *self = (Lexer *)_self; 248 | 249 | uint32_t goal_byte = self->current_position.bytes; 250 | 251 | self->did_get_column = true; 252 | self->current_position.bytes -= self->current_position.extent.column; 253 | self->current_position.extent.column = 0; 254 | 255 | if (self->current_position.bytes < self->chunk_start) { 256 | ts_lexer__get_chunk(self); 257 | } 258 | 259 | uint32_t result = 0; 260 | ts_lexer__get_lookahead(self); 261 | while (self->current_position.bytes < goal_byte && !ts_lexer__eof(_self) && self->chunk) { 262 | ts_lexer__do_advance(self, false); 263 | result++; 264 | } 265 | 266 | return result; 267 | } 268 | 269 | // Is the lexer at a boundary between two disjoint included ranges of 270 | // source code? This is exposed as an API because some languages' external 271 | // scanners need to perform custom actions at these boundaries. 272 | static bool ts_lexer__is_at_included_range_start(const TSLexer *_self) { 273 | const Lexer *self = (const Lexer *)_self; 274 | if (self->current_included_range_index < self->included_range_count) { 275 | TSRange *current_range = &self->included_ranges[self->current_included_range_index]; 276 | return self->current_position.bytes == current_range->start_byte; 277 | } else { 278 | return false; 279 | } 280 | } 281 | 282 | void ts_lexer_init(Lexer *self) { 283 | *self = (Lexer) { 284 | .data = { 285 | // The lexer's methods are stored as struct fields so that generated 286 | // parsers can call them without needing to be linked against this 287 | // library. 288 | .advance = ts_lexer__advance, 289 | .mark_end = ts_lexer__mark_end, 290 | .get_column = ts_lexer__get_column, 291 | .is_at_included_range_start = ts_lexer__is_at_included_range_start, 292 | .eof = ts_lexer__eof, 293 | .lookahead = 0, 294 | .result_symbol = 0, 295 | }, 296 | .chunk = NULL, 297 | .chunk_size = 0, 298 | .chunk_start = 0, 299 | .current_position = {0, {0, 0}}, 300 | .logger = { 301 | .payload = NULL, 302 | .log = NULL 303 | }, 304 | .included_ranges = NULL, 305 | .included_range_count = 0, 306 | .current_included_range_index = 0, 307 | }; 308 | ts_lexer_set_included_ranges(self, NULL, 0); 309 | } 310 | 311 | void ts_lexer_delete(Lexer *self) { 312 | ts_free(self->included_ranges); 313 | } 314 | 315 | void ts_lexer_set_input(Lexer *self, TSInput input) { 316 | self->input = input; 317 | ts_lexer__clear_chunk(self); 318 | ts_lexer_goto(self, self->current_position); 319 | } 320 | 321 | // Move the lexer to the given position. This doesn't do any work 322 | // if the parser is already at the given position. 323 | void ts_lexer_reset(Lexer *self, Length position) { 324 | if (position.bytes != self->current_position.bytes) { 325 | ts_lexer_goto(self, position); 326 | } 327 | } 328 | 329 | void ts_lexer_start(Lexer *self) { 330 | self->token_start_position = self->current_position; 331 | self->token_end_position = LENGTH_UNDEFINED; 332 | self->data.result_symbol = 0; 333 | self->did_get_column = false; 334 | if (!ts_lexer__eof(&self->data)) { 335 | if (!self->chunk_size) ts_lexer__get_chunk(self); 336 | if (!self->lookahead_size) ts_lexer__get_lookahead(self); 337 | if ( 338 | self->current_position.bytes == 0 && 339 | self->data.lookahead == BYTE_ORDER_MARK 340 | ) ts_lexer__advance(&self->data, true); 341 | } 342 | } 343 | 344 | void ts_lexer_finish(Lexer *self, uint32_t *lookahead_end_byte) { 345 | if (length_is_undefined(self->token_end_position)) { 346 | ts_lexer__mark_end(&self->data); 347 | } 348 | 349 | // If the token ended at an included range boundary, then its end position 350 | // will have been reset to the end of the preceding range. Reset the start 351 | // position to match. 352 | if (self->token_end_position.bytes < self->token_start_position.bytes) { 353 | self->token_start_position = self->token_end_position; 354 | } 355 | 356 | uint32_t current_lookahead_end_byte = self->current_position.bytes + 1; 357 | 358 | // In order to determine that a byte sequence is invalid UTF8 or UTF16, 359 | // the character decoding algorithm may have looked at the following byte. 360 | // Therefore, the next byte *after* the current (invalid) character 361 | // affects the interpretation of the current character. 362 | if (self->data.lookahead == TS_DECODE_ERROR) { 363 | current_lookahead_end_byte++; 364 | } 365 | 366 | if (current_lookahead_end_byte > *lookahead_end_byte) { 367 | *lookahead_end_byte = current_lookahead_end_byte; 368 | } 369 | } 370 | 371 | void ts_lexer_advance_to_end(Lexer *self) { 372 | while (self->chunk) { 373 | ts_lexer__advance(&self->data, false); 374 | } 375 | } 376 | 377 | void ts_lexer_mark_end(Lexer *self) { 378 | ts_lexer__mark_end(&self->data); 379 | } 380 | 381 | bool ts_lexer_set_included_ranges( 382 | Lexer *self, 383 | const TSRange *ranges, 384 | uint32_t count 385 | ) { 386 | if (count == 0 || !ranges) { 387 | ranges = &DEFAULT_RANGE; 388 | count = 1; 389 | } else { 390 | uint32_t previous_byte = 0; 391 | for (unsigned i = 0; i < count; i++) { 392 | const TSRange *range = &ranges[i]; 393 | if ( 394 | range->start_byte < previous_byte || 395 | range->end_byte < range->start_byte 396 | ) return false; 397 | previous_byte = range->end_byte; 398 | } 399 | } 400 | 401 | size_t size = count * sizeof(TSRange); 402 | self->included_ranges = ts_realloc(self->included_ranges, size); 403 | memcpy(self->included_ranges, ranges, size); 404 | self->included_range_count = count; 405 | ts_lexer_goto(self, self->current_position); 406 | return true; 407 | } 408 | 409 | TSRange *ts_lexer_included_ranges(const Lexer *self, uint32_t *count) { 410 | *count = self->included_range_count; 411 | return self->included_ranges; 412 | } 413 | 414 | #undef LOG 415 | -------------------------------------------------------------------------------- /parser/src/lexer.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_LEXER_H_ 2 | #define TREE_SITTER_LEXER_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include "./length.h" 9 | #include "./subtree.h" 10 | #include "tree_sitter/api.h" 11 | #include "tree_sitter/parser.h" 12 | 13 | typedef struct { 14 | TSLexer data; 15 | Length current_position; 16 | Length token_start_position; 17 | Length token_end_position; 18 | 19 | TSRange *included_ranges; 20 | const char *chunk; 21 | TSInput input; 22 | TSLogger logger; 23 | 24 | uint32_t included_range_count; 25 | uint32_t current_included_range_index; 26 | uint32_t chunk_start; 27 | uint32_t chunk_size; 28 | uint32_t lookahead_size; 29 | bool did_get_column; 30 | 31 | char debug_buffer[TREE_SITTER_SERIALIZATION_BUFFER_SIZE]; 32 | } Lexer; 33 | 34 | void ts_lexer_init(Lexer *); 35 | void ts_lexer_delete(Lexer *); 36 | void ts_lexer_set_input(Lexer *, TSInput); 37 | void ts_lexer_reset(Lexer *, Length); 38 | void ts_lexer_start(Lexer *); 39 | void ts_lexer_finish(Lexer *, uint32_t *); 40 | void ts_lexer_advance_to_end(Lexer *); 41 | void ts_lexer_mark_end(Lexer *); 42 | bool ts_lexer_set_included_ranges(Lexer *self, const TSRange *ranges, uint32_t count); 43 | TSRange *ts_lexer_included_ranges(const Lexer *self, uint32_t *count); 44 | 45 | #ifdef __cplusplus 46 | } 47 | #endif 48 | 49 | #endif // TREE_SITTER_LEXER_H_ 50 | -------------------------------------------------------------------------------- /parser/src/lib.c: -------------------------------------------------------------------------------- 1 | // The Tree-sitter library can be built by compiling this one source file. 2 | // 3 | // The following directories must be added to the include path: 4 | // - include 5 | 6 | #define _POSIX_C_SOURCE 200112L 7 | 8 | #include "./alloc.c" 9 | #include "./get_changed_ranges.c" 10 | #include "./language.c" 11 | #include "./lexer.c" 12 | #include "./node.c" 13 | #include "./parser.c" 14 | #include "./query.c" 15 | #include "./stack.c" 16 | #include "./subtree.c" 17 | #include "./tree_cursor.c" 18 | #include "./tree.c" 19 | -------------------------------------------------------------------------------- /parser/src/point.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_POINT_H_ 2 | #define TREE_SITTER_POINT_H_ 3 | 4 | #include "tree_sitter/api.h" 5 | 6 | #define POINT_ZERO ((TSPoint) {0, 0}) 7 | #define POINT_MAX ((TSPoint) {UINT32_MAX, UINT32_MAX}) 8 | 9 | static inline TSPoint point__new(unsigned row, unsigned column) { 10 | TSPoint result = {row, column}; 11 | return result; 12 | } 13 | 14 | static inline TSPoint point_add(TSPoint a, TSPoint b) { 15 | if (b.row > 0) 16 | return point__new(a.row + b.row, b.column); 17 | else 18 | return point__new(a.row, a.column + b.column); 19 | } 20 | 21 | static inline TSPoint point_sub(TSPoint a, TSPoint b) { 22 | if (a.row > b.row) 23 | return point__new(a.row - b.row, a.column); 24 | else 25 | return point__new(0, a.column - b.column); 26 | } 27 | 28 | static inline bool point_lte(TSPoint a, TSPoint b) { 29 | return (a.row < b.row) || (a.row == b.row && a.column <= b.column); 30 | } 31 | 32 | static inline bool point_lt(TSPoint a, TSPoint b) { 33 | return (a.row < b.row) || (a.row == b.row && a.column < b.column); 34 | } 35 | 36 | static inline bool point_gt(TSPoint a, TSPoint b) { 37 | return (a.row > b.row) || (a.row == b.row && a.column > b.column); 38 | } 39 | 40 | static inline bool point_gte(TSPoint a, TSPoint b) { 41 | return (a.row > b.row) || (a.row == b.row && a.column >= b.column); 42 | } 43 | 44 | static inline bool point_eq(TSPoint a, TSPoint b) { 45 | return a.row == b.row && a.column == b.column; 46 | } 47 | 48 | static inline TSPoint point_min(TSPoint a, TSPoint b) { 49 | if (a.row < b.row || (a.row == b.row && a.column < b.column)) 50 | return a; 51 | else 52 | return b; 53 | } 54 | 55 | static inline TSPoint point_max(TSPoint a, TSPoint b) { 56 | if (a.row > b.row || (a.row == b.row && a.column > b.column)) 57 | return a; 58 | else 59 | return b; 60 | } 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /parser/src/reduce_action.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_REDUCE_ACTION_H_ 2 | #define TREE_SITTER_REDUCE_ACTION_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include "./array.h" 9 | #include "tree_sitter/api.h" 10 | 11 | typedef struct { 12 | uint32_t count; 13 | TSSymbol symbol; 14 | int dynamic_precedence; 15 | unsigned short production_id; 16 | } ReduceAction; 17 | 18 | typedef Array(ReduceAction) ReduceActionSet; 19 | 20 | static inline void ts_reduce_action_set_add(ReduceActionSet *self, 21 | ReduceAction new_action) { 22 | for (uint32_t i = 0; i < self->size; i++) { 23 | ReduceAction action = self->contents[i]; 24 | if (action.symbol == new_action.symbol && action.count == new_action.count) 25 | return; 26 | } 27 | array_push(self, new_action); 28 | } 29 | 30 | #ifdef __cplusplus 31 | } 32 | #endif 33 | 34 | #endif // TREE_SITTER_REDUCE_ACTION_H_ 35 | -------------------------------------------------------------------------------- /parser/src/reusable_node.h: -------------------------------------------------------------------------------- 1 | #include "./subtree.h" 2 | 3 | typedef struct { 4 | Subtree tree; 5 | uint32_t child_index; 6 | uint32_t byte_offset; 7 | } StackEntry; 8 | 9 | typedef struct { 10 | Array(StackEntry) stack; 11 | Subtree last_external_token; 12 | } ReusableNode; 13 | 14 | static inline ReusableNode reusable_node_new(void) { 15 | return (ReusableNode) {array_new(), NULL_SUBTREE}; 16 | } 17 | 18 | static inline void reusable_node_clear(ReusableNode *self) { 19 | array_clear(&self->stack); 20 | self->last_external_token = NULL_SUBTREE; 21 | } 22 | 23 | static inline Subtree reusable_node_tree(ReusableNode *self) { 24 | return self->stack.size > 0 25 | ? self->stack.contents[self->stack.size - 1].tree 26 | : NULL_SUBTREE; 27 | } 28 | 29 | static inline uint32_t reusable_node_byte_offset(ReusableNode *self) { 30 | return self->stack.size > 0 31 | ? self->stack.contents[self->stack.size - 1].byte_offset 32 | : UINT32_MAX; 33 | } 34 | 35 | static inline void reusable_node_delete(ReusableNode *self) { 36 | array_delete(&self->stack); 37 | } 38 | 39 | static inline void reusable_node_advance(ReusableNode *self) { 40 | StackEntry last_entry = *array_back(&self->stack); 41 | uint32_t byte_offset = last_entry.byte_offset + ts_subtree_total_bytes(last_entry.tree); 42 | if (ts_subtree_has_external_tokens(last_entry.tree)) { 43 | self->last_external_token = ts_subtree_last_external_token(last_entry.tree); 44 | } 45 | 46 | Subtree tree; 47 | uint32_t next_index; 48 | do { 49 | StackEntry popped_entry = array_pop(&self->stack); 50 | next_index = popped_entry.child_index + 1; 51 | if (self->stack.size == 0) return; 52 | tree = array_back(&self->stack)->tree; 53 | } while (ts_subtree_child_count(tree) <= next_index); 54 | 55 | array_push(&self->stack, ((StackEntry) { 56 | .tree = ts_subtree_children(tree)[next_index], 57 | .child_index = next_index, 58 | .byte_offset = byte_offset, 59 | })); 60 | } 61 | 62 | static inline bool reusable_node_descend(ReusableNode *self) { 63 | StackEntry last_entry = *array_back(&self->stack); 64 | if (ts_subtree_child_count(last_entry.tree) > 0) { 65 | array_push(&self->stack, ((StackEntry) { 66 | .tree = ts_subtree_children(last_entry.tree)[0], 67 | .child_index = 0, 68 | .byte_offset = last_entry.byte_offset, 69 | })); 70 | return true; 71 | } else { 72 | return false; 73 | } 74 | } 75 | 76 | static inline void reusable_node_advance_past_leaf(ReusableNode *self) { 77 | while (reusable_node_descend(self)) {} 78 | reusable_node_advance(self); 79 | } 80 | 81 | static inline void reusable_node_reset(ReusableNode *self, Subtree tree) { 82 | reusable_node_clear(self); 83 | array_push(&self->stack, ((StackEntry) { 84 | .tree = tree, 85 | .child_index = 0, 86 | .byte_offset = 0, 87 | })); 88 | 89 | // Never reuse the root node, because it has a non-standard internal structure 90 | // due to transformations that are applied when it is accepted: adding the EOF 91 | // child and any extra children. 92 | if (!reusable_node_descend(self)) { 93 | reusable_node_clear(self); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /parser/src/scanner.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | enum TokenType { 5 | DESCENDANT_OP, 6 | }; 7 | 8 | void *tree_sitter_css_external_scanner_create() { return NULL; } 9 | void tree_sitter_css_external_scanner_destroy(void *p) {} 10 | void tree_sitter_css_external_scanner_reset(void *p) {} 11 | unsigned tree_sitter_css_external_scanner_serialize(void *p, char *buffer) { return 0; } 12 | void tree_sitter_css_external_scanner_deserialize(void *p, const char *b, unsigned n) {} 13 | 14 | bool tree_sitter_css_external_scanner_scan(void *payload, TSLexer *lexer, const bool *valid_symbols) { 15 | if (iswspace(lexer->lookahead) && valid_symbols[DESCENDANT_OP]) { 16 | lexer->result_symbol = DESCENDANT_OP; 17 | 18 | lexer->advance(lexer, true); 19 | while (iswspace(lexer->lookahead)) { 20 | lexer->advance(lexer, true); 21 | } 22 | lexer->mark_end(lexer); 23 | 24 | if ( 25 | lexer->lookahead == '#' || 26 | lexer->lookahead == '.' || 27 | lexer->lookahead == '[' || 28 | lexer->lookahead == '-' || 29 | iswalnum(lexer->lookahead) 30 | ) { 31 | return true; 32 | } 33 | 34 | if (lexer->lookahead == ':') { 35 | lexer->advance(lexer, false); 36 | if (iswspace(lexer->lookahead)) return false; 37 | for (;;) { 38 | if ( 39 | lexer->lookahead == ';' || 40 | lexer->lookahead == '}' || 41 | lexer->eof(lexer) 42 | ) return false; 43 | if (lexer->lookahead == '{') { 44 | return true; 45 | } 46 | lexer->advance(lexer, false); 47 | } 48 | } 49 | } 50 | 51 | return false; 52 | } 53 | -------------------------------------------------------------------------------- /parser/src/stack.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_PARSE_STACK_H_ 2 | #define TREE_SITTER_PARSE_STACK_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include "./array.h" 9 | #include "./subtree.h" 10 | #include "./error_costs.h" 11 | #include 12 | 13 | typedef struct Stack Stack; 14 | 15 | typedef unsigned StackVersion; 16 | #define STACK_VERSION_NONE ((StackVersion)-1) 17 | 18 | typedef struct { 19 | SubtreeArray subtrees; 20 | StackVersion version; 21 | } StackSlice; 22 | typedef Array(StackSlice) StackSliceArray; 23 | 24 | typedef struct { 25 | Length position; 26 | unsigned depth; 27 | TSStateId state; 28 | } StackSummaryEntry; 29 | typedef Array(StackSummaryEntry) StackSummary; 30 | 31 | // Create a stack. 32 | Stack *ts_stack_new(SubtreePool *); 33 | 34 | // Release the memory reserved for a given stack. 35 | void ts_stack_delete(Stack *); 36 | 37 | // Get the stack's current number of versions. 38 | uint32_t ts_stack_version_count(const Stack *); 39 | 40 | // Get the state at the top of the given version of the stack. If the stack is 41 | // empty, this returns the initial state, 0. 42 | TSStateId ts_stack_state(const Stack *, StackVersion); 43 | 44 | // Get the last external token associated with a given version of the stack. 45 | Subtree ts_stack_last_external_token(const Stack *, StackVersion); 46 | 47 | // Set the last external token associated with a given version of the stack. 48 | void ts_stack_set_last_external_token(Stack *, StackVersion, Subtree ); 49 | 50 | // Get the position of the given version of the stack within the document. 51 | Length ts_stack_position(const Stack *, StackVersion); 52 | 53 | // Push a tree and state onto the given version of the stack. 54 | // 55 | // This transfers ownership of the tree to the Stack. Callers that 56 | // need to retain ownership of the tree for their own purposes should 57 | // first retain the tree. 58 | void ts_stack_push(Stack *, StackVersion, Subtree , bool, TSStateId); 59 | 60 | // Pop the given number of entries from the given version of the stack. This 61 | // operation can increase the number of stack versions by revealing multiple 62 | // versions which had previously been merged. It returns an array that 63 | // specifies the index of each revealed version and the trees that were 64 | // removed from that version. 65 | StackSliceArray ts_stack_pop_count(Stack *, StackVersion, uint32_t count); 66 | 67 | // Remove an error at the top of the given version of the stack. 68 | SubtreeArray ts_stack_pop_error(Stack *, StackVersion); 69 | 70 | // Remove any pending trees from the top of the given version of the stack. 71 | StackSliceArray ts_stack_pop_pending(Stack *, StackVersion); 72 | 73 | // Remove any all trees from the given version of the stack. 74 | StackSliceArray ts_stack_pop_all(Stack *, StackVersion); 75 | 76 | // Get the maximum number of tree nodes reachable from this version of the stack 77 | // since the last error was detected. 78 | unsigned ts_stack_node_count_since_error(const Stack *, StackVersion); 79 | 80 | int ts_stack_dynamic_precedence(Stack *, StackVersion); 81 | 82 | bool ts_stack_has_advanced_since_error(const Stack *, StackVersion); 83 | 84 | // Compute a summary of all the parse states near the top of the given 85 | // version of the stack and store the summary for later retrieval. 86 | void ts_stack_record_summary(Stack *, StackVersion, unsigned max_depth); 87 | 88 | // Retrieve a summary of all the parse states near the top of the 89 | // given version of the stack. 90 | StackSummary *ts_stack_get_summary(Stack *, StackVersion); 91 | 92 | // Get the total cost of all errors on the given version of the stack. 93 | unsigned ts_stack_error_cost(const Stack *, StackVersion version); 94 | 95 | // Merge the given two stack versions if possible, returning true 96 | // if they were successfully merged and false otherwise. 97 | bool ts_stack_merge(Stack *, StackVersion, StackVersion); 98 | 99 | // Determine whether the given two stack versions can be merged. 100 | bool ts_stack_can_merge(Stack *, StackVersion, StackVersion); 101 | 102 | Subtree ts_stack_resume(Stack *, StackVersion); 103 | 104 | void ts_stack_pause(Stack *, StackVersion, Subtree); 105 | 106 | void ts_stack_halt(Stack *, StackVersion); 107 | 108 | bool ts_stack_is_active(const Stack *, StackVersion); 109 | 110 | bool ts_stack_is_paused(const Stack *, StackVersion); 111 | 112 | bool ts_stack_is_halted(const Stack *, StackVersion); 113 | 114 | void ts_stack_renumber_version(Stack *, StackVersion, StackVersion); 115 | 116 | void ts_stack_swap_versions(Stack *, StackVersion, StackVersion); 117 | 118 | StackVersion ts_stack_copy_version(Stack *, StackVersion); 119 | 120 | // Remove the given version from the stack. 121 | void ts_stack_remove_version(Stack *, StackVersion); 122 | 123 | void ts_stack_clear(Stack *); 124 | 125 | bool ts_stack_print_dot_graph(Stack *, const TSLanguage *, FILE *); 126 | 127 | typedef void (*StackIterateCallback)(void *, TSStateId, uint32_t); 128 | 129 | #ifdef __cplusplus 130 | } 131 | #endif 132 | 133 | #endif // TREE_SITTER_PARSE_STACK_H_ 134 | -------------------------------------------------------------------------------- /parser/src/subtree.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_SUBTREE_H_ 2 | #define TREE_SITTER_SUBTREE_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | #include "./length.h" 12 | #include "./array.h" 13 | #include "./error_costs.h" 14 | #include "./host.h" 15 | #include "tree_sitter/api.h" 16 | #include "tree_sitter/parser.h" 17 | 18 | #define TS_TREE_STATE_NONE USHRT_MAX 19 | #define NULL_SUBTREE ((Subtree) {.ptr = NULL}) 20 | 21 | // The serialized state of an external scanner. 22 | // 23 | // Every time an external token subtree is created after a call to an 24 | // external scanner, the scanner's `serialize` function is called to 25 | // retrieve a serialized copy of its state. The bytes are then copied 26 | // onto the subtree itself so that the scanner's state can later be 27 | // restored using its `deserialize` function. 28 | // 29 | // Small byte arrays are stored inline, and long ones are allocated 30 | // separately on the heap. 31 | typedef struct { 32 | union { 33 | char *long_data; 34 | char short_data[24]; 35 | }; 36 | uint32_t length; 37 | } ExternalScannerState; 38 | 39 | // A compact representation of a subtree. 40 | // 41 | // This representation is used for small leaf nodes that are not 42 | // errors, and were not created by an external scanner. 43 | // 44 | // The idea behind the layout of this struct is that the `is_inline` 45 | // bit will fall exactly into the same location as the least significant 46 | // bit of the pointer in `Subtree` or `MutableSubtree`, respectively. 47 | // Because of alignment, for any valid pointer this will be 0, giving 48 | // us the opportunity to make use of this bit to signify whether to use 49 | // the pointer or the inline struct. 50 | typedef struct SubtreeInlineData SubtreeInlineData; 51 | 52 | #define SUBTREE_BITS \ 53 | bool visible : 1; \ 54 | bool named : 1; \ 55 | bool extra : 1; \ 56 | bool has_changes : 1; \ 57 | bool is_missing : 1; \ 58 | bool is_keyword : 1; 59 | 60 | #define SUBTREE_SIZE \ 61 | uint8_t padding_columns; \ 62 | uint8_t padding_rows : 4; \ 63 | uint8_t lookahead_bytes : 4; \ 64 | uint8_t padding_bytes; \ 65 | uint8_t size_bytes; 66 | 67 | #if TS_BIG_ENDIAN 68 | #if TS_PTR_SIZE == 32 69 | 70 | struct SubtreeInlineData { 71 | uint16_t parse_state; 72 | uint8_t symbol; 73 | SUBTREE_BITS 74 | bool unused : 1; 75 | bool is_inline : 1; 76 | SUBTREE_SIZE 77 | }; 78 | 79 | #else 80 | 81 | struct SubtreeInlineData { 82 | SUBTREE_SIZE 83 | uint16_t parse_state; 84 | uint8_t symbol; 85 | SUBTREE_BITS 86 | bool unused : 1; 87 | bool is_inline : 1; 88 | }; 89 | 90 | #endif 91 | #else 92 | 93 | struct SubtreeInlineData { 94 | bool is_inline : 1; 95 | SUBTREE_BITS 96 | uint8_t symbol; 97 | uint16_t parse_state; 98 | SUBTREE_SIZE 99 | }; 100 | 101 | #endif 102 | 103 | #undef SUBTREE_BITS 104 | #undef SUBTREE_SIZE 105 | 106 | // A heap-allocated representation of a subtree. 107 | // 108 | // This representation is used for parent nodes, external tokens, 109 | // errors, and other leaf nodes whose data is too large to fit into 110 | // the inline representation. 111 | typedef struct { 112 | volatile uint32_t ref_count; 113 | Length padding; 114 | Length size; 115 | uint32_t lookahead_bytes; 116 | uint32_t error_cost; 117 | uint32_t child_count; 118 | TSSymbol symbol; 119 | TSStateId parse_state; 120 | 121 | bool visible : 1; 122 | bool named : 1; 123 | bool extra : 1; 124 | bool fragile_left : 1; 125 | bool fragile_right : 1; 126 | bool has_changes : 1; 127 | bool has_external_tokens : 1; 128 | bool has_external_scanner_state_change : 1; 129 | bool depends_on_column: 1; 130 | bool is_missing : 1; 131 | bool is_keyword : 1; 132 | 133 | union { 134 | // Non-terminal subtrees (`child_count > 0`) 135 | struct { 136 | uint32_t visible_child_count; 137 | uint32_t named_child_count; 138 | uint32_t node_count; 139 | int32_t dynamic_precedence; 140 | uint16_t repeat_depth; 141 | uint16_t production_id; 142 | struct { 143 | TSSymbol symbol; 144 | TSStateId parse_state; 145 | } first_leaf; 146 | }; 147 | 148 | // External terminal subtrees (`child_count == 0 && has_external_tokens`) 149 | ExternalScannerState external_scanner_state; 150 | 151 | // Error terminal subtrees (`child_count == 0 && symbol == ts_builtin_sym_error`) 152 | int32_t lookahead_char; 153 | }; 154 | } SubtreeHeapData; 155 | 156 | // The fundamental building block of a syntax tree. 157 | typedef union { 158 | SubtreeInlineData data; 159 | const SubtreeHeapData *ptr; 160 | } Subtree; 161 | 162 | // Like Subtree, but mutable. 163 | typedef union { 164 | SubtreeInlineData data; 165 | SubtreeHeapData *ptr; 166 | } MutableSubtree; 167 | 168 | typedef Array(Subtree) SubtreeArray; 169 | typedef Array(MutableSubtree) MutableSubtreeArray; 170 | 171 | typedef struct { 172 | MutableSubtreeArray free_trees; 173 | MutableSubtreeArray tree_stack; 174 | } SubtreePool; 175 | 176 | void ts_external_scanner_state_init(ExternalScannerState *, const char *, unsigned); 177 | const char *ts_external_scanner_state_data(const ExternalScannerState *); 178 | bool ts_external_scanner_state_eq(const ExternalScannerState *a, const char *, unsigned); 179 | void ts_external_scanner_state_delete(ExternalScannerState *self); 180 | 181 | void ts_subtree_array_copy(SubtreeArray, SubtreeArray *); 182 | void ts_subtree_array_clear(SubtreePool *, SubtreeArray *); 183 | void ts_subtree_array_delete(SubtreePool *, SubtreeArray *); 184 | void ts_subtree_array_remove_trailing_extras(SubtreeArray *, SubtreeArray *); 185 | void ts_subtree_array_reverse(SubtreeArray *); 186 | 187 | SubtreePool ts_subtree_pool_new(uint32_t capacity); 188 | void ts_subtree_pool_delete(SubtreePool *); 189 | 190 | Subtree ts_subtree_new_leaf( 191 | SubtreePool *, TSSymbol, Length, Length, uint32_t, 192 | TSStateId, bool, bool, bool, const TSLanguage * 193 | ); 194 | Subtree ts_subtree_new_error( 195 | SubtreePool *, int32_t, Length, Length, uint32_t, TSStateId, const TSLanguage * 196 | ); 197 | MutableSubtree ts_subtree_new_node(TSSymbol, SubtreeArray *, unsigned, const TSLanguage *); 198 | Subtree ts_subtree_new_error_node(SubtreeArray *, bool, const TSLanguage *); 199 | Subtree ts_subtree_new_missing_leaf(SubtreePool *, TSSymbol, Length, uint32_t, const TSLanguage *); 200 | MutableSubtree ts_subtree_make_mut(SubtreePool *, Subtree); 201 | void ts_subtree_retain(Subtree); 202 | void ts_subtree_release(SubtreePool *, Subtree); 203 | int ts_subtree_compare(Subtree, Subtree); 204 | void ts_subtree_set_symbol(MutableSubtree *, TSSymbol, const TSLanguage *); 205 | void ts_subtree_summarize(MutableSubtree, const Subtree *, uint32_t, const TSLanguage *); 206 | void ts_subtree_summarize_children(MutableSubtree, const TSLanguage *); 207 | void ts_subtree_balance(Subtree, SubtreePool *, const TSLanguage *); 208 | Subtree ts_subtree_edit(Subtree, const TSInputEdit *edit, SubtreePool *); 209 | char *ts_subtree_string(Subtree, const TSLanguage *, bool include_all); 210 | void ts_subtree_print_dot_graph(Subtree, const TSLanguage *, FILE *); 211 | Subtree ts_subtree_last_external_token(Subtree); 212 | const ExternalScannerState *ts_subtree_external_scanner_state(Subtree self); 213 | bool ts_subtree_external_scanner_state_eq(Subtree, Subtree); 214 | 215 | #define SUBTREE_GET(self, name) (self.data.is_inline ? self.data.name : self.ptr->name) 216 | 217 | static inline TSSymbol ts_subtree_symbol(Subtree self) { return SUBTREE_GET(self, symbol); } 218 | static inline bool ts_subtree_visible(Subtree self) { return SUBTREE_GET(self, visible); } 219 | static inline bool ts_subtree_named(Subtree self) { return SUBTREE_GET(self, named); } 220 | static inline bool ts_subtree_extra(Subtree self) { return SUBTREE_GET(self, extra); } 221 | static inline bool ts_subtree_has_changes(Subtree self) { return SUBTREE_GET(self, has_changes); } 222 | static inline bool ts_subtree_missing(Subtree self) { return SUBTREE_GET(self, is_missing); } 223 | static inline bool ts_subtree_is_keyword(Subtree self) { return SUBTREE_GET(self, is_keyword); } 224 | static inline TSStateId ts_subtree_parse_state(Subtree self) { return SUBTREE_GET(self, parse_state); } 225 | static inline uint32_t ts_subtree_lookahead_bytes(Subtree self) { return SUBTREE_GET(self, lookahead_bytes); } 226 | 227 | #undef SUBTREE_GET 228 | 229 | // Get the size needed to store a heap-allocated subtree with the given 230 | // number of children. 231 | static inline size_t ts_subtree_alloc_size(uint32_t child_count) { 232 | return child_count * sizeof(Subtree) + sizeof(SubtreeHeapData); 233 | } 234 | 235 | // Get a subtree's children, which are allocated immediately before the 236 | // tree's own heap data. 237 | #define ts_subtree_children(self) \ 238 | ((self).data.is_inline ? NULL : (Subtree *)((self).ptr) - (self).ptr->child_count) 239 | 240 | static inline void ts_subtree_set_extra(MutableSubtree *self, bool is_extra) { 241 | if (self->data.is_inline) { 242 | self->data.extra = is_extra; 243 | } else { 244 | self->ptr->extra = is_extra; 245 | } 246 | } 247 | 248 | static inline TSSymbol ts_subtree_leaf_symbol(Subtree self) { 249 | if (self.data.is_inline) return self.data.symbol; 250 | if (self.ptr->child_count == 0) return self.ptr->symbol; 251 | return self.ptr->first_leaf.symbol; 252 | } 253 | 254 | static inline TSStateId ts_subtree_leaf_parse_state(Subtree self) { 255 | if (self.data.is_inline) return self.data.parse_state; 256 | if (self.ptr->child_count == 0) return self.ptr->parse_state; 257 | return self.ptr->first_leaf.parse_state; 258 | } 259 | 260 | static inline Length ts_subtree_padding(Subtree self) { 261 | if (self.data.is_inline) { 262 | Length result = {self.data.padding_bytes, {self.data.padding_rows, self.data.padding_columns}}; 263 | return result; 264 | } else { 265 | return self.ptr->padding; 266 | } 267 | } 268 | 269 | static inline Length ts_subtree_size(Subtree self) { 270 | if (self.data.is_inline) { 271 | Length result = {self.data.size_bytes, {0, self.data.size_bytes}}; 272 | return result; 273 | } else { 274 | return self.ptr->size; 275 | } 276 | } 277 | 278 | static inline Length ts_subtree_total_size(Subtree self) { 279 | return length_add(ts_subtree_padding(self), ts_subtree_size(self)); 280 | } 281 | 282 | static inline uint32_t ts_subtree_total_bytes(Subtree self) { 283 | return ts_subtree_total_size(self).bytes; 284 | } 285 | 286 | static inline uint32_t ts_subtree_child_count(Subtree self) { 287 | return self.data.is_inline ? 0 : self.ptr->child_count; 288 | } 289 | 290 | static inline uint32_t ts_subtree_repeat_depth(Subtree self) { 291 | return self.data.is_inline ? 0 : self.ptr->repeat_depth; 292 | } 293 | 294 | static inline uint32_t ts_subtree_is_repetition(Subtree self) { 295 | return self.data.is_inline 296 | ? 0 297 | : !self.ptr->named && !self.ptr->visible && self.ptr->child_count != 0; 298 | } 299 | 300 | static inline uint32_t ts_subtree_node_count(Subtree self) { 301 | return (self.data.is_inline || self.ptr->child_count == 0) ? 1 : self.ptr->node_count; 302 | } 303 | 304 | static inline uint32_t ts_subtree_visible_child_count(Subtree self) { 305 | if (ts_subtree_child_count(self) > 0) { 306 | return self.ptr->visible_child_count; 307 | } else { 308 | return 0; 309 | } 310 | } 311 | 312 | static inline uint32_t ts_subtree_error_cost(Subtree self) { 313 | if (ts_subtree_missing(self)) { 314 | return ERROR_COST_PER_MISSING_TREE + ERROR_COST_PER_RECOVERY; 315 | } else { 316 | return self.data.is_inline ? 0 : self.ptr->error_cost; 317 | } 318 | } 319 | 320 | static inline int32_t ts_subtree_dynamic_precedence(Subtree self) { 321 | return (self.data.is_inline || self.ptr->child_count == 0) ? 0 : self.ptr->dynamic_precedence; 322 | } 323 | 324 | static inline uint16_t ts_subtree_production_id(Subtree self) { 325 | if (ts_subtree_child_count(self) > 0) { 326 | return self.ptr->production_id; 327 | } else { 328 | return 0; 329 | } 330 | } 331 | 332 | static inline bool ts_subtree_fragile_left(Subtree self) { 333 | return self.data.is_inline ? false : self.ptr->fragile_left; 334 | } 335 | 336 | static inline bool ts_subtree_fragile_right(Subtree self) { 337 | return self.data.is_inline ? false : self.ptr->fragile_right; 338 | } 339 | 340 | static inline bool ts_subtree_has_external_tokens(Subtree self) { 341 | return self.data.is_inline ? false : self.ptr->has_external_tokens; 342 | } 343 | 344 | static inline bool ts_subtree_has_external_scanner_state_change(Subtree self) { 345 | return self.data.is_inline ? false : self.ptr->has_external_scanner_state_change; 346 | } 347 | 348 | static inline bool ts_subtree_depends_on_column(Subtree self) { 349 | return self.data.is_inline ? false : self.ptr->depends_on_column; 350 | } 351 | 352 | static inline bool ts_subtree_is_fragile(Subtree self) { 353 | return self.data.is_inline ? false : (self.ptr->fragile_left || self.ptr->fragile_right); 354 | } 355 | 356 | static inline bool ts_subtree_is_error(Subtree self) { 357 | return ts_subtree_symbol(self) == ts_builtin_sym_error; 358 | } 359 | 360 | static inline bool ts_subtree_is_eof(Subtree self) { 361 | return ts_subtree_symbol(self) == ts_builtin_sym_end; 362 | } 363 | 364 | static inline Subtree ts_subtree_from_mut(MutableSubtree self) { 365 | Subtree result; 366 | result.data = self.data; 367 | return result; 368 | } 369 | 370 | static inline MutableSubtree ts_subtree_to_mut_unsafe(Subtree self) { 371 | MutableSubtree result; 372 | result.data = self.data; 373 | return result; 374 | } 375 | 376 | #ifdef __cplusplus 377 | } 378 | #endif 379 | 380 | #endif // TREE_SITTER_SUBTREE_H_ 381 | -------------------------------------------------------------------------------- /parser/src/tree.c: -------------------------------------------------------------------------------- 1 | #include "tree_sitter/api.h" 2 | #include "./array.h" 3 | #include "./get_changed_ranges.h" 4 | #include "./length.h" 5 | #include "./subtree.h" 6 | #include "./tree_cursor.h" 7 | #include "./tree.h" 8 | 9 | TSTree *ts_tree_new( 10 | Subtree root, const TSLanguage *language, 11 | const TSRange *included_ranges, unsigned included_range_count 12 | ) { 13 | TSTree *result = ts_malloc(sizeof(TSTree)); 14 | result->root = root; 15 | result->language = language; 16 | result->included_ranges = ts_calloc(included_range_count, sizeof(TSRange)); 17 | memcpy(result->included_ranges, included_ranges, included_range_count * sizeof(TSRange)); 18 | result->included_range_count = included_range_count; 19 | return result; 20 | } 21 | 22 | TSTree *ts_tree_copy(const TSTree *self) { 23 | ts_subtree_retain(self->root); 24 | return ts_tree_new(self->root, self->language, self->included_ranges, self->included_range_count); 25 | } 26 | 27 | void ts_tree_delete(TSTree *self) { 28 | if (!self) return; 29 | 30 | SubtreePool pool = ts_subtree_pool_new(0); 31 | ts_subtree_release(&pool, self->root); 32 | ts_subtree_pool_delete(&pool); 33 | ts_free(self->included_ranges); 34 | ts_free(self); 35 | } 36 | 37 | TSNode ts_tree_root_node(const TSTree *self) { 38 | return ts_node_new(self, &self->root, ts_subtree_padding(self->root), 0); 39 | } 40 | 41 | TSNode ts_tree_root_node_with_offset( 42 | const TSTree *self, 43 | uint32_t offset_bytes, 44 | TSPoint offset_extent 45 | ) { 46 | Length offset = {offset_bytes, offset_extent}; 47 | return ts_node_new(self, &self->root, length_add(offset, ts_subtree_padding(self->root)), 0); 48 | } 49 | 50 | const TSLanguage *ts_tree_language(const TSTree *self) { 51 | return self->language; 52 | } 53 | 54 | void ts_tree_edit(TSTree *self, const TSInputEdit *edit) { 55 | for (unsigned i = 0; i < self->included_range_count; i++) { 56 | TSRange *range = &self->included_ranges[i]; 57 | if (range->end_byte >= edit->old_end_byte) { 58 | if (range->end_byte != UINT32_MAX) { 59 | range->end_byte = edit->new_end_byte + (range->end_byte - edit->old_end_byte); 60 | range->end_point = point_add( 61 | edit->new_end_point, 62 | point_sub(range->end_point, edit->old_end_point) 63 | ); 64 | if (range->end_byte < edit->new_end_byte) { 65 | range->end_byte = UINT32_MAX; 66 | range->end_point = POINT_MAX; 67 | } 68 | } 69 | } else if (range->end_byte > edit->start_byte) { 70 | range->end_byte = edit->start_byte; 71 | range->end_point = edit->start_point; 72 | } 73 | if (range->start_byte >= edit->old_end_byte) { 74 | range->start_byte = edit->new_end_byte + (range->start_byte - edit->old_end_byte); 75 | range->start_point = point_add( 76 | edit->new_end_point, 77 | point_sub(range->start_point, edit->old_end_point) 78 | ); 79 | if (range->start_byte < edit->new_end_byte) { 80 | range->start_byte = UINT32_MAX; 81 | range->start_point = POINT_MAX; 82 | } 83 | } else if (range->start_byte > edit->start_byte) { 84 | range->start_byte = edit->start_byte; 85 | range->start_point = edit->start_point; 86 | } 87 | } 88 | 89 | SubtreePool pool = ts_subtree_pool_new(0); 90 | self->root = ts_subtree_edit(self->root, edit, &pool); 91 | ts_subtree_pool_delete(&pool); 92 | } 93 | 94 | TSRange *ts_tree_included_ranges(const TSTree *self, uint32_t *length) { 95 | *length = self->included_range_count; 96 | TSRange *ranges = ts_calloc(self->included_range_count, sizeof(TSRange)); 97 | memcpy(ranges, self->included_ranges, self->included_range_count * sizeof(TSRange)); 98 | return ranges; 99 | } 100 | 101 | TSRange *ts_tree_get_changed_ranges(const TSTree *self, const TSTree *other, uint32_t *count) { 102 | TreeCursor cursor1 = {NULL, array_new()}; 103 | TreeCursor cursor2 = {NULL, array_new()}; 104 | ts_tree_cursor_init(&cursor1, ts_tree_root_node(self)); 105 | ts_tree_cursor_init(&cursor2, ts_tree_root_node(other)); 106 | 107 | TSRangeArray included_range_differences = array_new(); 108 | ts_range_array_get_changed_ranges( 109 | self->included_ranges, self->included_range_count, 110 | other->included_ranges, other->included_range_count, 111 | &included_range_differences 112 | ); 113 | 114 | TSRange *result; 115 | *count = ts_subtree_get_changed_ranges( 116 | &self->root, &other->root, &cursor1, &cursor2, 117 | self->language, &included_range_differences, &result 118 | ); 119 | 120 | array_delete(&included_range_differences); 121 | array_delete(&cursor1.stack); 122 | array_delete(&cursor2.stack); 123 | return result; 124 | } 125 | 126 | #ifdef _WIN32 127 | 128 | void ts_tree_print_dot_graph(const TSTree *self, int fd) { 129 | (void)self; 130 | (void)fd; 131 | } 132 | 133 | #else 134 | 135 | #include 136 | 137 | void ts_tree_print_dot_graph(const TSTree *self, int fd) { 138 | FILE *file = fdopen(dup(fd), "a"); 139 | ts_subtree_print_dot_graph(self->root, self->language, file); 140 | fclose(file); 141 | } 142 | 143 | #endif 144 | -------------------------------------------------------------------------------- /parser/src/tree.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_TREE_H_ 2 | #define TREE_SITTER_TREE_H_ 3 | 4 | #include "./subtree.h" 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | typedef struct { 11 | const Subtree *child; 12 | const Subtree *parent; 13 | Length position; 14 | TSSymbol alias_symbol; 15 | } ParentCacheEntry; 16 | 17 | struct TSTree { 18 | Subtree root; 19 | const TSLanguage *language; 20 | TSRange *included_ranges; 21 | unsigned included_range_count; 22 | }; 23 | 24 | TSTree *ts_tree_new(Subtree root, const TSLanguage *language, const TSRange *, unsigned); 25 | TSNode ts_node_new(const TSTree *, const Subtree *, Length, TSSymbol); 26 | 27 | #ifdef __cplusplus 28 | } 29 | #endif 30 | 31 | #endif // TREE_SITTER_TREE_H_ 32 | -------------------------------------------------------------------------------- /parser/src/tree_cursor.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_TREE_CURSOR_H_ 2 | #define TREE_SITTER_TREE_CURSOR_H_ 3 | 4 | #include "./subtree.h" 5 | 6 | typedef struct { 7 | const Subtree *subtree; 8 | Length position; 9 | uint32_t child_index; 10 | uint32_t structural_child_index; 11 | } TreeCursorEntry; 12 | 13 | typedef struct { 14 | const TSTree *tree; 15 | Array(TreeCursorEntry) stack; 16 | } TreeCursor; 17 | 18 | typedef enum { 19 | TreeCursorStepNone, 20 | TreeCursorStepHidden, 21 | TreeCursorStepVisible, 22 | } TreeCursorStep; 23 | 24 | void ts_tree_cursor_init(TreeCursor *, TSNode); 25 | void ts_tree_cursor_current_status( 26 | const TSTreeCursor *, 27 | TSFieldId *, 28 | bool *, 29 | bool *, 30 | bool *, 31 | TSSymbol *, 32 | unsigned * 33 | ); 34 | 35 | TreeCursorStep ts_tree_cursor_goto_first_child_internal(TSTreeCursor *); 36 | TreeCursorStep ts_tree_cursor_goto_next_sibling_internal(TSTreeCursor *); 37 | 38 | static inline Subtree ts_tree_cursor_current_subtree(const TSTreeCursor *_self) { 39 | const TreeCursor *self = (const TreeCursor *)_self; 40 | TreeCursorEntry *last_entry = array_back(&self->stack); 41 | return *last_entry->subtree; 42 | } 43 | 44 | TSNode ts_tree_cursor_parent_node(const TSTreeCursor *); 45 | 46 | #endif // TREE_SITTER_TREE_CURSOR_H_ 47 | -------------------------------------------------------------------------------- /parser/src/unicode.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_UNICODE_H_ 2 | #define TREE_SITTER_UNICODE_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | #include 10 | 11 | #define U_EXPORT 12 | #define U_EXPORT2 13 | #include "unicode/utf8.h" 14 | #include "unicode/utf16.h" 15 | 16 | static const int32_t TS_DECODE_ERROR = U_SENTINEL; 17 | 18 | // These functions read one unicode code point from the given string, 19 | // returning the number of bytes consumed. 20 | typedef uint32_t (*UnicodeDecodeFunction)( 21 | const uint8_t *string, 22 | uint32_t length, 23 | int32_t *code_point 24 | ); 25 | 26 | static inline uint32_t ts_decode_utf8( 27 | const uint8_t *string, 28 | uint32_t length, 29 | int32_t *code_point 30 | ) { 31 | uint32_t i = 0; 32 | U8_NEXT(string, i, length, *code_point); 33 | return i; 34 | } 35 | 36 | static inline uint32_t ts_decode_utf16( 37 | const uint8_t *string, 38 | uint32_t length, 39 | int32_t *code_point 40 | ) { 41 | uint32_t i = 0; 42 | U16_NEXT(((uint16_t *)string), i, length, *code_point); 43 | return i * 2; 44 | } 45 | 46 | #ifdef __cplusplus 47 | } 48 | #endif 49 | 50 | #endif // TREE_SITTER_UNICODE_H_ 51 | -------------------------------------------------------------------------------- /parser/src/unicode/ICU_SHA: -------------------------------------------------------------------------------- 1 | 552b01f61127d30d6589aa4bf99468224979b661 2 | -------------------------------------------------------------------------------- /parser/src/unicode/README.md: -------------------------------------------------------------------------------- 1 | # ICU Parts 2 | 3 | This directory contains a small subset of files from the Unicode organization's [ICU repository](https://github.com/unicode-org/icu). 4 | 5 | ### License 6 | 7 | The license for these files is contained in the `LICENSE` file within this directory. 8 | 9 | ### Contents 10 | 11 | * Source files taken from the [`icu4c/source/common/unicode`](https://github.com/unicode-org/icu/tree/552b01f61127d30d6589aa4bf99468224979b661/icu4c/source/common/unicode) directory: 12 | * `utf8.h` 13 | * `utf16.h` 14 | * `umachine.h` 15 | * Empty source files that are referenced by the above source files, but whose original contents in `libicu` are not needed: 16 | * `ptypes.h` 17 | * `urename.h` 18 | * `utf.h` 19 | * `ICU_SHA` - File containing the Git SHA of the commit in the `icu` repository from which the files were obtained. 20 | * `LICENSE` - The license file from the [`icu4c`](https://github.com/unicode-org/icu/tree/552b01f61127d30d6589aa4bf99468224979b661/icu4c) directory of the `icu` repository. 21 | * `README.md` - This text file. 22 | 23 | ### Updating ICU 24 | 25 | To incorporate changes from the upstream `icu` repository: 26 | 27 | * Update `ICU_SHA` with the new Git SHA. 28 | * Update `LICENSE` with the license text from the directory mentioned above. 29 | * Update `utf8.h`, `utf16.h`, and `umachine.h` with their new contents in the `icu` repository. 30 | -------------------------------------------------------------------------------- /parser/src/unicode/ptypes.h: -------------------------------------------------------------------------------- 1 | // This file must exist in order for `utf8.h` and `utf16.h` to be used. 2 | -------------------------------------------------------------------------------- /parser/src/unicode/umachine.h: -------------------------------------------------------------------------------- 1 | // © 2016 and later: Unicode, Inc. and others. 2 | // License & terms of use: http://www.unicode.org/copyright.html 3 | /* 4 | ****************************************************************************** 5 | * 6 | * Copyright (C) 1999-2015, International Business Machines 7 | * Corporation and others. All Rights Reserved. 8 | * 9 | ****************************************************************************** 10 | * file name: umachine.h 11 | * encoding: UTF-8 12 | * tab size: 8 (not used) 13 | * indentation:4 14 | * 15 | * created on: 1999sep13 16 | * created by: Markus W. Scherer 17 | * 18 | * This file defines basic types and constants for ICU to be 19 | * platform-independent. umachine.h and utf.h are included into 20 | * utypes.h to provide all the general definitions for ICU. 21 | * All of these definitions used to be in utypes.h before 22 | * the UTF-handling macros made this unmaintainable. 23 | */ 24 | 25 | #ifndef __UMACHINE_H__ 26 | #define __UMACHINE_H__ 27 | 28 | 29 | /** 30 | * \file 31 | * \brief Basic types and constants for UTF 32 | * 33 | *

Basic types and constants for UTF

34 | * This file defines basic types and constants for utf.h to be 35 | * platform-independent. umachine.h and utf.h are included into 36 | * utypes.h to provide all the general definitions for ICU. 37 | * All of these definitions used to be in utypes.h before 38 | * the UTF-handling macros made this unmaintainable. 39 | * 40 | */ 41 | /*==========================================================================*/ 42 | /* Include platform-dependent definitions */ 43 | /* which are contained in the platform-specific file platform.h */ 44 | /*==========================================================================*/ 45 | 46 | #include "unicode/ptypes.h" /* platform.h is included in ptypes.h */ 47 | 48 | /* 49 | * ANSI C headers: 50 | * stddef.h defines wchar_t 51 | */ 52 | #include 53 | 54 | /*==========================================================================*/ 55 | /* For C wrappers, we use the symbol U_STABLE. */ 56 | /* This works properly if the includer is C or C++. */ 57 | /* Functions are declared U_STABLE return-type U_EXPORT2 function-name()... */ 58 | /*==========================================================================*/ 59 | 60 | /** 61 | * \def U_CFUNC 62 | * This is used in a declaration of a library private ICU C function. 63 | * @stable ICU 2.4 64 | */ 65 | 66 | /** 67 | * \def U_CDECL_BEGIN 68 | * This is used to begin a declaration of a library private ICU C API. 69 | * @stable ICU 2.4 70 | */ 71 | 72 | /** 73 | * \def U_CDECL_END 74 | * This is used to end a declaration of a library private ICU C API 75 | * @stable ICU 2.4 76 | */ 77 | 78 | #ifdef __cplusplus 79 | # define U_CFUNC extern "C" 80 | # define U_CDECL_BEGIN extern "C" { 81 | # define U_CDECL_END } 82 | #else 83 | # define U_CFUNC extern 84 | # define U_CDECL_BEGIN 85 | # define U_CDECL_END 86 | #endif 87 | 88 | #ifndef U_ATTRIBUTE_DEPRECATED 89 | /** 90 | * \def U_ATTRIBUTE_DEPRECATED 91 | * This is used for GCC specific attributes 92 | * @internal 93 | */ 94 | #if U_GCC_MAJOR_MINOR >= 302 95 | # define U_ATTRIBUTE_DEPRECATED __attribute__ ((deprecated)) 96 | /** 97 | * \def U_ATTRIBUTE_DEPRECATED 98 | * This is used for Visual C++ specific attributes 99 | * @internal 100 | */ 101 | #elif defined(_MSC_VER) && (_MSC_VER >= 1400) 102 | # define U_ATTRIBUTE_DEPRECATED __declspec(deprecated) 103 | #else 104 | # define U_ATTRIBUTE_DEPRECATED 105 | #endif 106 | #endif 107 | 108 | /** This is used to declare a function as a public ICU C API @stable ICU 2.0*/ 109 | #define U_CAPI U_CFUNC U_EXPORT 110 | /** This is used to declare a function as a stable public ICU C API*/ 111 | #define U_STABLE U_CAPI 112 | /** This is used to declare a function as a draft public ICU C API */ 113 | #define U_DRAFT U_CAPI 114 | /** This is used to declare a function as a deprecated public ICU C API */ 115 | #define U_DEPRECATED U_CAPI U_ATTRIBUTE_DEPRECATED 116 | /** This is used to declare a function as an obsolete public ICU C API */ 117 | #define U_OBSOLETE U_CAPI 118 | /** This is used to declare a function as an internal ICU C API */ 119 | #define U_INTERNAL U_CAPI 120 | 121 | /** 122 | * \def U_OVERRIDE 123 | * Defined to the C++11 "override" keyword if available. 124 | * Denotes a class or member which is an override of the base class. 125 | * May result in an error if it applied to something not an override. 126 | * @internal 127 | */ 128 | #ifndef U_OVERRIDE 129 | #define U_OVERRIDE override 130 | #endif 131 | 132 | /** 133 | * \def U_FINAL 134 | * Defined to the C++11 "final" keyword if available. 135 | * Denotes a class or member which may not be overridden in subclasses. 136 | * May result in an error if subclasses attempt to override. 137 | * @internal 138 | */ 139 | #if !defined(U_FINAL) || defined(U_IN_DOXYGEN) 140 | #define U_FINAL final 141 | #endif 142 | 143 | // Before ICU 65, function-like, multi-statement ICU macros were just defined as 144 | // series of statements wrapped in { } blocks and the caller could choose to 145 | // either treat them as if they were actual functions and end the invocation 146 | // with a trailing ; creating an empty statement after the block or else omit 147 | // this trailing ; using the knowledge that the macro would expand to { }. 148 | // 149 | // But doing so doesn't work well with macros that look like functions and 150 | // compiler warnings about empty statements (ICU-20601) and ICU 65 therefore 151 | // switches to the standard solution of wrapping such macros in do { } while. 152 | // 153 | // This will however break existing code that depends on being able to invoke 154 | // these macros without a trailing ; so to be able to remain compatible with 155 | // such code the wrapper is itself defined as macros so that it's possible to 156 | // build ICU 65 and later with the old macro behaviour, like this: 157 | // 158 | // CPPFLAGS='-DUPRV_BLOCK_MACRO_BEGIN="" -DUPRV_BLOCK_MACRO_END=""' 159 | // runConfigureICU ... 160 | 161 | /** 162 | * \def UPRV_BLOCK_MACRO_BEGIN 163 | * Defined as the "do" keyword by default. 164 | * @internal 165 | */ 166 | #ifndef UPRV_BLOCK_MACRO_BEGIN 167 | #define UPRV_BLOCK_MACRO_BEGIN do 168 | #endif 169 | 170 | /** 171 | * \def UPRV_BLOCK_MACRO_END 172 | * Defined as "while (FALSE)" by default. 173 | * @internal 174 | */ 175 | #ifndef UPRV_BLOCK_MACRO_END 176 | #define UPRV_BLOCK_MACRO_END while (FALSE) 177 | #endif 178 | 179 | /*==========================================================================*/ 180 | /* limits for int32_t etc., like in POSIX inttypes.h */ 181 | /*==========================================================================*/ 182 | 183 | #ifndef INT8_MIN 184 | /** The smallest value an 8 bit signed integer can hold @stable ICU 2.0 */ 185 | # define INT8_MIN ((int8_t)(-128)) 186 | #endif 187 | #ifndef INT16_MIN 188 | /** The smallest value a 16 bit signed integer can hold @stable ICU 2.0 */ 189 | # define INT16_MIN ((int16_t)(-32767-1)) 190 | #endif 191 | #ifndef INT32_MIN 192 | /** The smallest value a 32 bit signed integer can hold @stable ICU 2.0 */ 193 | # define INT32_MIN ((int32_t)(-2147483647-1)) 194 | #endif 195 | 196 | #ifndef INT8_MAX 197 | /** The largest value an 8 bit signed integer can hold @stable ICU 2.0 */ 198 | # define INT8_MAX ((int8_t)(127)) 199 | #endif 200 | #ifndef INT16_MAX 201 | /** The largest value a 16 bit signed integer can hold @stable ICU 2.0 */ 202 | # define INT16_MAX ((int16_t)(32767)) 203 | #endif 204 | #ifndef INT32_MAX 205 | /** The largest value a 32 bit signed integer can hold @stable ICU 2.0 */ 206 | # define INT32_MAX ((int32_t)(2147483647)) 207 | #endif 208 | 209 | #ifndef UINT8_MAX 210 | /** The largest value an 8 bit unsigned integer can hold @stable ICU 2.0 */ 211 | # define UINT8_MAX ((uint8_t)(255U)) 212 | #endif 213 | #ifndef UINT16_MAX 214 | /** The largest value a 16 bit unsigned integer can hold @stable ICU 2.0 */ 215 | # define UINT16_MAX ((uint16_t)(65535U)) 216 | #endif 217 | #ifndef UINT32_MAX 218 | /** The largest value a 32 bit unsigned integer can hold @stable ICU 2.0 */ 219 | # define UINT32_MAX ((uint32_t)(4294967295U)) 220 | #endif 221 | 222 | #if defined(U_INT64_T_UNAVAILABLE) 223 | # error int64_t is required for decimal format and rule-based number format. 224 | #else 225 | # ifndef INT64_C 226 | /** 227 | * Provides a platform independent way to specify a signed 64-bit integer constant. 228 | * note: may be wrong for some 64 bit platforms - ensure your compiler provides INT64_C 229 | * @stable ICU 2.8 230 | */ 231 | # define INT64_C(c) c ## LL 232 | # endif 233 | # ifndef UINT64_C 234 | /** 235 | * Provides a platform independent way to specify an unsigned 64-bit integer constant. 236 | * note: may be wrong for some 64 bit platforms - ensure your compiler provides UINT64_C 237 | * @stable ICU 2.8 238 | */ 239 | # define UINT64_C(c) c ## ULL 240 | # endif 241 | # ifndef U_INT64_MIN 242 | /** The smallest value a 64 bit signed integer can hold @stable ICU 2.8 */ 243 | # define U_INT64_MIN ((int64_t)(INT64_C(-9223372036854775807)-1)) 244 | # endif 245 | # ifndef U_INT64_MAX 246 | /** The largest value a 64 bit signed integer can hold @stable ICU 2.8 */ 247 | # define U_INT64_MAX ((int64_t)(INT64_C(9223372036854775807))) 248 | # endif 249 | # ifndef U_UINT64_MAX 250 | /** The largest value a 64 bit unsigned integer can hold @stable ICU 2.8 */ 251 | # define U_UINT64_MAX ((uint64_t)(UINT64_C(18446744073709551615))) 252 | # endif 253 | #endif 254 | 255 | /*==========================================================================*/ 256 | /* Boolean data type */ 257 | /*==========================================================================*/ 258 | 259 | /** The ICU boolean type @stable ICU 2.0 */ 260 | typedef int8_t UBool; 261 | 262 | #ifndef TRUE 263 | /** The TRUE value of a UBool @stable ICU 2.0 */ 264 | # define TRUE 1 265 | #endif 266 | #ifndef FALSE 267 | /** The FALSE value of a UBool @stable ICU 2.0 */ 268 | # define FALSE 0 269 | #endif 270 | 271 | 272 | /*==========================================================================*/ 273 | /* Unicode data types */ 274 | /*==========================================================================*/ 275 | 276 | /* wchar_t-related definitions -------------------------------------------- */ 277 | 278 | /* 279 | * \def U_WCHAR_IS_UTF16 280 | * Defined if wchar_t uses UTF-16. 281 | * 282 | * @stable ICU 2.0 283 | */ 284 | /* 285 | * \def U_WCHAR_IS_UTF32 286 | * Defined if wchar_t uses UTF-32. 287 | * 288 | * @stable ICU 2.0 289 | */ 290 | #if !defined(U_WCHAR_IS_UTF16) && !defined(U_WCHAR_IS_UTF32) 291 | # ifdef __STDC_ISO_10646__ 292 | # if (U_SIZEOF_WCHAR_T==2) 293 | # define U_WCHAR_IS_UTF16 294 | # elif (U_SIZEOF_WCHAR_T==4) 295 | # define U_WCHAR_IS_UTF32 296 | # endif 297 | # elif defined __UCS2__ 298 | # if (U_PF_OS390 <= U_PLATFORM && U_PLATFORM <= U_PF_OS400) && (U_SIZEOF_WCHAR_T==2) 299 | # define U_WCHAR_IS_UTF16 300 | # endif 301 | # elif defined(__UCS4__) || (U_PLATFORM == U_PF_OS400 && defined(__UTF32__)) 302 | # if (U_SIZEOF_WCHAR_T==4) 303 | # define U_WCHAR_IS_UTF32 304 | # endif 305 | # elif U_PLATFORM_IS_DARWIN_BASED || (U_SIZEOF_WCHAR_T==4 && U_PLATFORM_IS_LINUX_BASED) 306 | # define U_WCHAR_IS_UTF32 307 | # elif U_PLATFORM_HAS_WIN32_API 308 | # define U_WCHAR_IS_UTF16 309 | # endif 310 | #endif 311 | 312 | /* UChar and UChar32 definitions -------------------------------------------- */ 313 | 314 | /** Number of bytes in a UChar. @stable ICU 2.0 */ 315 | #define U_SIZEOF_UCHAR 2 316 | 317 | /** 318 | * \def U_CHAR16_IS_TYPEDEF 319 | * If 1, then char16_t is a typedef and not a real type (yet) 320 | * @internal 321 | */ 322 | #if (U_PLATFORM == U_PF_AIX) && defined(__cplusplus) &&(U_CPLUSPLUS_VERSION < 11) 323 | // for AIX, uchar.h needs to be included 324 | # include 325 | # define U_CHAR16_IS_TYPEDEF 1 326 | #elif defined(_MSC_VER) && (_MSC_VER < 1900) 327 | // Versions of Visual Studio/MSVC below 2015 do not support char16_t as a real type, 328 | // and instead use a typedef. https://msdn.microsoft.com/library/bb531344.aspx 329 | # define U_CHAR16_IS_TYPEDEF 1 330 | #else 331 | # define U_CHAR16_IS_TYPEDEF 0 332 | #endif 333 | 334 | 335 | /** 336 | * \var UChar 337 | * 338 | * The base type for UTF-16 code units and pointers. 339 | * Unsigned 16-bit integer. 340 | * Starting with ICU 59, C++ API uses char16_t directly, while C API continues to use UChar. 341 | * 342 | * UChar is configurable by defining the macro UCHAR_TYPE 343 | * on the preprocessor or compiler command line: 344 | * -DUCHAR_TYPE=uint16_t or -DUCHAR_TYPE=wchar_t (if U_SIZEOF_WCHAR_T==2) etc. 345 | * (The UCHAR_TYPE can also be \#defined earlier in this file, for outside the ICU library code.) 346 | * This is for transitional use from application code that uses uint16_t or wchar_t for UTF-16. 347 | * 348 | * The default is UChar=char16_t. 349 | * 350 | * C++11 defines char16_t as bit-compatible with uint16_t, but as a distinct type. 351 | * 352 | * In C, char16_t is a simple typedef of uint_least16_t. 353 | * ICU requires uint_least16_t=uint16_t for data memory mapping. 354 | * On macOS, char16_t is not available because the uchar.h standard header is missing. 355 | * 356 | * @stable ICU 4.4 357 | */ 358 | 359 | #if 1 360 | // #if 1 is normal. UChar defaults to char16_t in C++. 361 | // For configuration testing of UChar=uint16_t temporarily change this to #if 0. 362 | // The intltest Makefile #defines UCHAR_TYPE=char16_t, 363 | // so we only #define it to uint16_t if it is undefined so far. 364 | #elif !defined(UCHAR_TYPE) 365 | # define UCHAR_TYPE uint16_t 366 | #endif 367 | 368 | #if defined(U_COMBINED_IMPLEMENTATION) || defined(U_COMMON_IMPLEMENTATION) || \ 369 | defined(U_I18N_IMPLEMENTATION) || defined(U_IO_IMPLEMENTATION) 370 | // Inside the ICU library code, never configurable. 371 | typedef char16_t UChar; 372 | #elif defined(UCHAR_TYPE) 373 | typedef UCHAR_TYPE UChar; 374 | #elif defined(__cplusplus) 375 | typedef char16_t UChar; 376 | #else 377 | typedef uint16_t UChar; 378 | #endif 379 | 380 | /** 381 | * \var OldUChar 382 | * Default ICU 58 definition of UChar. 383 | * A base type for UTF-16 code units and pointers. 384 | * Unsigned 16-bit integer. 385 | * 386 | * Define OldUChar to be wchar_t if that is 16 bits wide. 387 | * If wchar_t is not 16 bits wide, then define UChar to be uint16_t. 388 | * 389 | * This makes the definition of OldUChar platform-dependent 390 | * but allows direct string type compatibility with platforms with 391 | * 16-bit wchar_t types. 392 | * 393 | * This is how UChar was defined in ICU 58, for transition convenience. 394 | * Exception: ICU 58 UChar was defined to UCHAR_TYPE if that macro was defined. 395 | * The current UChar responds to UCHAR_TYPE but OldUChar does not. 396 | * 397 | * @stable ICU 59 398 | */ 399 | #if U_SIZEOF_WCHAR_T==2 400 | typedef wchar_t OldUChar; 401 | #elif defined(__CHAR16_TYPE__) 402 | typedef __CHAR16_TYPE__ OldUChar; 403 | #else 404 | typedef uint16_t OldUChar; 405 | #endif 406 | 407 | /** 408 | * Define UChar32 as a type for single Unicode code points. 409 | * UChar32 is a signed 32-bit integer (same as int32_t). 410 | * 411 | * The Unicode code point range is 0..0x10ffff. 412 | * All other values (negative or >=0x110000) are illegal as Unicode code points. 413 | * They may be used as sentinel values to indicate "done", "error" 414 | * or similar non-code point conditions. 415 | * 416 | * Before ICU 2.4 (Jitterbug 2146), UChar32 was defined 417 | * to be wchar_t if that is 32 bits wide (wchar_t may be signed or unsigned) 418 | * or else to be uint32_t. 419 | * That is, the definition of UChar32 was platform-dependent. 420 | * 421 | * @see U_SENTINEL 422 | * @stable ICU 2.4 423 | */ 424 | typedef int32_t UChar32; 425 | 426 | /** 427 | * This value is intended for sentinel values for APIs that 428 | * (take or) return single code points (UChar32). 429 | * It is outside of the Unicode code point range 0..0x10ffff. 430 | * 431 | * For example, a "done" or "error" value in a new API 432 | * could be indicated with U_SENTINEL. 433 | * 434 | * ICU APIs designed before ICU 2.4 usually define service-specific "done" 435 | * values, mostly 0xffff. 436 | * Those may need to be distinguished from 437 | * actual U+ffff text contents by calling functions like 438 | * CharacterIterator::hasNext() or UnicodeString::length(). 439 | * 440 | * @return -1 441 | * @see UChar32 442 | * @stable ICU 2.4 443 | */ 444 | #define U_SENTINEL (-1) 445 | 446 | #include "unicode/urename.h" 447 | 448 | #endif 449 | -------------------------------------------------------------------------------- /parser/src/unicode/urename.h: -------------------------------------------------------------------------------- 1 | // This file must exist in order for `utf8.h` and `utf16.h` to be used. 2 | -------------------------------------------------------------------------------- /parser/src/unicode/utf.h: -------------------------------------------------------------------------------- 1 | // This file must exist in order for `utf8.h` and `utf16.h` to be used. 2 | -------------------------------------------------------------------------------- /src/ast/astvisitor.vala: -------------------------------------------------------------------------------- 1 | /* astvisitor.vala 2 | * 3 | * Copyright 2023 JCWasmx86 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * SPDX-License-Identifier: GPL-3.0-or-later 19 | */ 20 | namespace GtkCssLangServer { 21 | public interface ASTVisitor { 22 | public abstract void visitStyleSheet (StyleSheet node); 23 | public abstract void visitImportStatement (ImportStatement node); 24 | public abstract void visitMediaStatement (MediaStatement node); 25 | public abstract void visitCharsetStatement (CharsetStatement node); 26 | public abstract void visitNamespaceStatement (NamespaceStatement node); 27 | public abstract void visitDefineColorStatement (DefineColorStatement node); 28 | public abstract void visitKeyframesStatement (KeyframesStatement node); 29 | public abstract void visitKeyFrameBlockList (KeyFrameBlockList node); 30 | public abstract void visitKeyFrame (KeyFrame node); 31 | public abstract void visitFrom (From node); 32 | public abstract void visitTo (To node); 33 | public abstract void visitSupportsStatement (SupportsStatement node); 34 | public abstract void visitAtRule (AtRule node); 35 | public abstract void visitRuleSet (RuleSet node); 36 | public abstract void visitSelectors (Selectors node); 37 | public abstract void visitBlock (Block node); 38 | public abstract void visitNestingSelector (NestingSelector node); 39 | public abstract void visitUniversalSelector (UniversalSelector node); 40 | public abstract void visitClassSelector (ClassSelector node); 41 | public abstract void visitPseudoClassSelector (PseudoClassSelector node); 42 | public abstract void visitPseudoElementSelector (PseudoElementSelector node); 43 | public abstract void visitIdSelector (IdSelector node); 44 | public abstract void visitAttributeSelector (AttributeSelector node); 45 | public abstract void visitChildSelector (ChildSelector node); 46 | public abstract void visitDescendantSelector (DescendantSelector node); 47 | public abstract void visitSibilingSelector (SibilingSelector node); 48 | public abstract void visitAdjacentSibilingSelector (AdjacentSibilingSelector node); 49 | public abstract void visitPseudoClassArguments (PseudoClassArguments node); 50 | public abstract void visitDeclaration (Declaration node); 51 | public abstract void visitLastDeclaration (LastDeclaration node); 52 | public abstract void visitImportant (Important node); 53 | public abstract void visitFeatureQuery (FeatureQuery node); 54 | public abstract void visitParenthesizedQuery (ParenthesizedQuery node); 55 | public abstract void visitBinaryQuery (BinaryQuery node); 56 | public abstract void visitUnaryQuery (UnaryQuery node); 57 | public abstract void visitSelectorQuery (SelectorQuery node); 58 | public abstract void visitParenthesizedValue (ParenthesizedValue node); 59 | public abstract void visitColorValue (ColorValue node); 60 | public abstract void visitStringValue (StringValue node); 61 | public abstract void visitIntegerValue (IntegerValue node); 62 | public abstract void visitFloatValue (FloatValue node); 63 | public abstract void visitUnit (Unit node); 64 | public abstract void visitCallExpression (CallExpression node); 65 | public abstract void visitBinaryExpression (BinaryExpression node); 66 | public abstract void visitArguments (Arguments node); 67 | public abstract void visitIdentifier (Identifier node); 68 | public abstract void visitAtKeyword (AtKeyword node); 69 | public abstract void visitPlainValue (PlainValue node); 70 | } 71 | } 72 | 73 | -------------------------------------------------------------------------------- /src/ast/dataextractor.vala: -------------------------------------------------------------------------------- 1 | /* dataextractor.vala 2 | * 3 | * Copyright 2023 JCWasmx86 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * SPDX-License-Identifier: GPL-3.0-or-later 19 | */ 20 | 21 | namespace GtkCssLangServer { 22 | public class ColorReference { 23 | public string name; 24 | public Range range; 25 | } 26 | 27 | public class CallReference { 28 | public string name; 29 | public Range range; 30 | } 31 | 32 | public class PropertyReference { 33 | public string name; 34 | public Range range; 35 | public Declaration node; 36 | } 37 | 38 | public class KeyframeReference { 39 | public string name; 40 | public Range range; 41 | } 42 | 43 | public class DataExtractor : ASTVisitor { 44 | // @define-color colors 45 | internal GLib.HashTable colors; 46 | internal RuleSet[] sets; 47 | // Access to colors from e.g. libadwaita 48 | internal ColorReference[] external_color_references; 49 | // @foo and there is a @define-color foo #123123 50 | internal ColorReference[] color_references; 51 | // E.g. calls to lighter(color) or mix(a,b) 52 | internal CallReference[] calls; 53 | // Property-references like min-width: 5px; 54 | internal PropertyReference[] property_uses; 55 | // @keyframes foo {} 56 | internal KeyframeReference[] keyframes; 57 | 58 | internal string[] lines; 59 | 60 | public DataExtractor (string text) { 61 | this.colors = new GLib.HashTable (GLib.str_hash, GLib.str_equal); 62 | this.sets = new RuleSet[0]; 63 | this.external_color_references = new ColorReference[0]; 64 | this.color_references = new ColorReference[0]; 65 | this.calls = new CallReference[0]; 66 | this.property_uses = new PropertyReference[0]; 67 | this.keyframes = new KeyframeReference[0]; 68 | this.lines = text.split ("\n"); 69 | } 70 | 71 | private string extract (Node node) { 72 | if (node.range.start.line != node.range.end.line) 73 | return "<<>>"; 74 | var line = node.range.start.line; 75 | return this.lines[line].substring (node.range.start.character, node.range.end.character - node.range.start.character); 76 | } 77 | 78 | public void visitStyleSheet (StyleSheet node) { node.visit_children (this); } 79 | public void visitImportStatement (ImportStatement node) { node.visit_children (this); } 80 | public void visitMediaStatement (MediaStatement node) { node.visit_children (this); } 81 | public void visitCharsetStatement (CharsetStatement node) { node.visit_children (this); } 82 | public void visitNamespaceStatement (NamespaceStatement node) { node.visit_children (this); } 83 | public void visitDefineColorStatement (DefineColorStatement node) { 84 | node.visit_children (this); 85 | if (node.identifier is Identifier) { 86 | var name = this.extract (node.identifier); 87 | this.colors[name] = node.identifier.range.start; 88 | info ("Extracted color %s [%u:%u:%u]", name, node.identifier.range.start.line, node.identifier.range.start.character, node.identifier.range.end.character); 89 | } 90 | } 91 | 92 | public void visitKeyframesStatement (KeyframesStatement node) { 93 | if (node.name != null && node.name is Identifier) { 94 | var name = this.extract (node.name); 95 | this.keyframes += new KeyframeReference () { 96 | name = name, 97 | range = node.name.range 98 | }; 99 | info ("Extracted keyframe %s [%u:%u:%u]", name, node.name.range.start.line, node.name.range.start.character, node.name.range.end.character); 100 | } 101 | node.visit_children (this); 102 | } 103 | public void visitKeyFrameBlockList (KeyFrameBlockList node) { node.visit_children (this); } 104 | public void visitKeyFrame (KeyFrame node) { node.visit_children (this); } 105 | public void visitFrom (From node) { node.visit_children (this); } 106 | public void visitTo (To node) { node.visit_children (this); } 107 | public void visitSupportsStatement (SupportsStatement node) { node.visit_children (this); } 108 | public void visitAtRule (AtRule node) { node.visit_children (this); } 109 | public void visitRuleSet (RuleSet node) { 110 | this.sets += node; 111 | node.visit_children (this); 112 | } 113 | 114 | public void visitSelectors (Selectors node) { node.visit_children (this); } 115 | public void visitBlock (Block node) { node.visit_children (this); } 116 | public void visitNestingSelector (NestingSelector node) { node.visit_children (this); } 117 | public void visitUniversalSelector (UniversalSelector node) { node.visit_children (this); } 118 | public void visitClassSelector (ClassSelector node) { node.visit_children (this); } 119 | public void visitPseudoClassSelector (PseudoClassSelector node) { node.visit_children (this); } 120 | public void visitPseudoElementSelector (PseudoElementSelector node) { node.visit_children (this); } 121 | public void visitIdSelector (IdSelector node) { node.visit_children (this); } 122 | public void visitAttributeSelector (AttributeSelector node) { node.visit_children (this); } 123 | public void visitChildSelector (ChildSelector node) { node.visit_children (this); } 124 | public void visitDescendantSelector (DescendantSelector node) { node.visit_children (this); } 125 | public void visitSibilingSelector (SibilingSelector node) { node.visit_children (this); } 126 | public void visitAdjacentSibilingSelector (AdjacentSibilingSelector node) { node.visit_children (this); } 127 | public void visitPseudoClassArguments (PseudoClassArguments node) { node.visit_children (this); } 128 | public void visitDeclaration (Declaration node) { 129 | if (node.id is Identifier) { 130 | var name = this.extract (node.id); 131 | this.property_uses += new PropertyReference () { 132 | name = name, 133 | range = node.id.range, 134 | node = node 135 | }; 136 | info ("Extracted property %s [%u:%u:%u]", name, node.id.range.start.line, node.id.range.start.character, node.id.range.end.character); 137 | } 138 | node.visit_children (this); 139 | } 140 | 141 | public void visitLastDeclaration (LastDeclaration node) { 142 | if (node.id is Identifier) { 143 | var name = this.extract (node.id); 144 | this.property_uses += new PropertyReference () { 145 | name = name, 146 | range = node.id.range, 147 | node = node 148 | }; 149 | info ("Extracted property %s [%u:%u:%u]", name, node.id.range.start.line, node.id.range.start.character, node.id.range.end.character); 150 | } 151 | node.visit_children (this); 152 | } 153 | 154 | public void visitImportant (Important node) { node.visit_children (this); } 155 | public void visitFeatureQuery (FeatureQuery node) { node.visit_children (this); } 156 | public void visitParenthesizedQuery (ParenthesizedQuery node) { node.visit_children (this); } 157 | public void visitBinaryQuery (BinaryQuery node) { node.visit_children (this); } 158 | public void visitUnaryQuery (UnaryQuery node) { node.visit_children (this); } 159 | public void visitSelectorQuery (SelectorQuery node) { node.visit_children (this); } 160 | public void visitParenthesizedValue (ParenthesizedValue node) { node.visit_children (this); } 161 | public void visitColorValue (ColorValue node) { node.visit_children (this); } 162 | public void visitStringValue (StringValue node) { node.visit_children (this); } 163 | public void visitIntegerValue (IntegerValue node) { node.visit_children (this); } 164 | public void visitFloatValue (FloatValue node) { node.visit_children (this); } 165 | public void visitUnit (Unit node) { node.visit_children (this); } 166 | public void visitCallExpression (CallExpression node) { 167 | if (node.id is Identifier) { 168 | var name = this.extract (node.id); 169 | this.calls += new CallReference () { 170 | name = name, 171 | range = node.id.range 172 | }; 173 | info ("Extracted call %s [%u:%u:%u]", name, node.id.range.start.line, node.id.range.start.character, node.id.range.end.character); 174 | } 175 | node.visit_children (this); 176 | } 177 | 178 | public void visitBinaryExpression (BinaryExpression node) { node.visit_children (this); } 179 | public void visitArguments (Arguments node) { node.visit_children (this); } 180 | public void visitIdentifier (Identifier node) { 181 | node.id = this.extract (node); 182 | node.visit_children (this); 183 | } 184 | public void visitAtKeyword (AtKeyword node) { 185 | info ("Found probable color reference: %s", this.extract (node)); 186 | this.color_references += new ColorReference () { 187 | name = this.extract (node).substring (1), 188 | range = node.range 189 | }; 190 | node.visit_children (this); 191 | } 192 | 193 | public void visitPlainValue (PlainValue node) { 194 | node.id = this.extract (node); 195 | node.visit_children (this); 196 | } 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /src/colors.json: -------------------------------------------------------------------------------- 1 | { 2 | "accent": { 3 | "colors": [ 4 | "@accent_color", 5 | "@accent_bg_color", 6 | "@accent_fg_color" 7 | ], 8 | "docs": "The accent color is used across many different widgets, often to indicate that a widget is important, interactive, or currently active. Try to avoid using it on large surfaces, or on too many items on the same view. The `.accent` style class allows to use it for widgets such as `GtkLabel`." 9 | }, 10 | "destructive": { 11 | "colors": [ 12 | "@destructive_color", 13 | "@destructive_bg_color", 14 | "@destructive_fg_color" 15 | ], 16 | "docs": "The destructive color indicates a dangerous action such as deleting a file. It's used by `GtkButton` with the `.destructive-action` style class." 17 | }, 18 | "success": { 19 | "colors": [ 20 | "@success_color", 21 | "@success_bg_color", 22 | "@success_fg_color" 23 | ], 24 | "docs": "This color is used with the `.success` style class, or in a `GtkLevelBar` with the `GTK_LEVEL_BAR_OFFSET_HIGH` offset." 25 | }, 26 | "warning": { 27 | "colors": [ 28 | "@warning_color", 29 | "@warning_bg_color", 30 | "@warning_fg_color" 31 | ], 32 | "docs": "This color is used with the `.warning` style class, or in a `GtkLevelBar` with the `GTK_LEVEL_BAR_OFFSET_LOW` offset." 33 | }, 34 | "error": { 35 | "colors": [ 36 | "@error_color", 37 | "@error_bg_color", 38 | "@error_fg_color" 39 | ], 40 | "docs": "This color is used with the `.error` style class." 41 | }, 42 | "window": { 43 | "colors": [ 44 | "@window_bg_color", 45 | "@window_fg_color" 46 | ], 47 | "docs": "These colors are used on `GtkWindow`, as well as with the `.background` style class." 48 | }, 49 | "view": { 50 | "colors": [ 51 | "@view_bg_color", 52 | "@view_fg_color" 53 | ], 54 | "docs": "These colors are used in a variety of widgets such as `GtkTextView`, as well as with the `.view` style class." 55 | }, 56 | "header_bar": { 57 | "colors": [ 58 | "@headerbar_bg_color", 59 | "@headerbar_fg_color", 60 | "@headerbar_border_color", 61 | "@headerbar_backdrop_color", 62 | "@headerbar_shade_color" 63 | ], 64 | "docs": "These colors are used for header bars and similar widgets, generally attached to the top or bottom sides of a window." 65 | }, 66 | "sidebar": { 67 | "colors": [ 68 | "@sidebar_bg_color", 69 | "@sidebar_fg_color", 70 | "@sidebar_backdrop_color", 71 | "@sidebar_shade_color" 72 | ], 73 | "docs": "These colors are used for sidebars, generally attached to the left or right sides of a window." 74 | }, 75 | "secondary_sidebar": { 76 | "colors": [ 77 | "@secondary_sidebar_bg_color", 78 | "@secondary_sidebar_fg_color", 79 | "@secondary_sidebar_backdrop_color", 80 | "@secondary_sidebar_shade_color" 81 | ], 82 | "docs": "These colors are used for middle panes in triple-pane layouts, created via nesting two split views within one another." 83 | }, 84 | "card": { 85 | "colors": [ 86 | "@card_bg_color", 87 | "@card_fg_color", 88 | "@card_shade_color" 89 | ], 90 | "docs": "These colors are used for cards and boxed lists." 91 | }, 92 | "thumbnail": { 93 | "colors": [ 94 | "@thumbnail_bg_color", 95 | "@thumbnail_fg_color" 96 | ], 97 | "docs": "These colors are used for `AdwTabOverview` thumbnails." 98 | }, 99 | "dialog": { 100 | "colors": [ 101 | "@dialog_bg_color", 102 | "@dialog_fg_color" 103 | ], 104 | "docs": "These colors are used for `AdwMessageDialog`." 105 | }, 106 | "popover": { 107 | "colors": [ 108 | "@popover_bg_color", 109 | "@popover_fg_color" 110 | ], 111 | "docs": "These colors are used for GtkPopover." 112 | }, 113 | "shade_color": { 114 | "colors": [ 115 | "@shade_color" 116 | ], 117 | "docs": "`@shade_color` is used for scroll undershoots, as well as transitions in `AdwNavigationView`, `AdwOverlaySplitView`, `AdwLeaflet` and `AdwFlap`. This color should always be partially transparent black, with the opacity tuned to be well visible on top of `@window_bg_color`." 118 | }, 119 | "scrollbar_outline": { 120 | "colors": [ 121 | "@scrollbar_outline_color" 122 | ], 123 | "docs": "`@scrollbar_outline_color` is used by `GtkScrollbar` to ensure that overlay scrollbars are visible regardless of the content color. It should always be the opposite of the scrollbar color - light with a dark scrollbar and dark with a light scrollbar." 124 | }, 125 | "borders": { 126 | "colors": [ 127 | "@borders" 128 | ], 129 | "docs": "This color should be used for supporting high contrast mode automatically." 130 | }, 131 | "palette": { 132 | "colors": [ 133 | "@blue_1", 134 | "@blue_2", 135 | "@blue_3", 136 | "@blue_4", 137 | "@blue_5", 138 | "@green_1", 139 | "@green_2", 140 | "@green_3", 141 | "@green_4", 142 | "@green_5", 143 | "@yellow_1", 144 | "@yellow_2", 145 | "@yellow_3", 146 | "@yellow_4", 147 | "@yellow_5", 148 | "@orange_1", 149 | "@orange_2", 150 | "@orange_3", 151 | "@orange_4", 152 | "@orange_5", 153 | "@red_1", 154 | "@red_2", 155 | "@red_3", 156 | "@red_4", 157 | "@red_5", 158 | "@purple_1", 159 | "@purple_2", 160 | "@purple_3", 161 | "@purple_4", 162 | "@purple_5", 163 | "@brown_1", 164 | "@brown_2", 165 | "@brown_3", 166 | "@brown_4", 167 | "@brown_5", 168 | "@light_1", 169 | "@light_2", 170 | "@light_3", 171 | "@light_4", 172 | "@light_5", 173 | "@dark_1", 174 | "@dark_2", 175 | "@dark_3", 176 | "@dark_4", 177 | "@dark_5" 178 | ], 179 | "docs": "The GNOME color palette is intended for use in app icons and illustrations." 180 | }, 181 | "compat": { 182 | "colors": [ 183 | "@theme_bg_color", 184 | "@theme_fg_color", 185 | "@theme_base_color", 186 | "@theme_text_color", 187 | "@theme_selected_bg_color", 188 | "@theme_selected_fg_color", 189 | "@insensitive_bg_color", 190 | "@insensitive_fg_color", 191 | "@insensitive_base_color", 192 | "@theme_unfocused_bg_color", 193 | "@theme_unfocused_fg_color", 194 | "@theme_unfocused_base_color", 195 | "@theme_unfocused_text_color", 196 | "@theme_unfocused_selected_bg_color", 197 | "@theme_unfocused_selected_fg_color", 198 | "@unfocused_insensitive_color", 199 | "@unfocused_borders" 200 | ], 201 | "docs": "Alias of UI colors derived from GTK3" 202 | } 203 | } -------------------------------------------------------------------------------- /src/docs.json: -------------------------------------------------------------------------------- 1 | { 2 | "animation-delay": "Specifies the amount of time to wait from applying the animation to an element before beginning to perform the animation. The animation can start later, immediately from its beginning, or immediately and partway through the animation.", 3 | "animation-direction": "Sets whether an animation should play forward, backward, or alternate back and forth between playing the sequence forward and backward.", 4 | "animation-duration": "Sets the length of time that an animation takes to complete one cycle.", 5 | "animation-fill-mode": "Sets how a CSS animation applies styles to its target before and after its execution.", 6 | "animation-iteration-count": "Sets the number of times an animation sequence should be played before stopping.", 7 | "animation": "The `animation` shorthand CSS property applies an animation between styles.", 8 | "animation-name": "Specifies the names of one or more `@keyframes` at-rules that describe the animation to apply to an element.", 9 | "animation-play-state": "Sets whether an animation is running or paused.", 10 | "animation-timing-function": "Sets how an animation progresses through the duration of each cycle.", 11 | "background-blend-mode": "Sets how an element's background images should blend with each other and with the element's background color.", 12 | "background-clip": "Sets whether an element's background extends underneath its border box, padding box, or content box.", 13 | "background-color": "Sets the background color of an element.", 14 | "background-image": "Sets one or more background images on an element.", 15 | "background": "Ssets all background style properties at once, such as color, image, origin and size, or repeat method. Component properties not set in the background shorthand property value declaration are set to their default values.", 16 | "background-origin": "Sets the background's origin: from the border start, inside the border, or inside the padding.", 17 | "background-position": "Sets the initial position for each background image. The position is relative to the position layer set by background-origin.", 18 | "background-repeat": "Sets how background images are repeated. A background image can be repeated along the horizontal and vertical axes, or not repeated at all.", 19 | "background-size": "Sets the size of the element's background image. The image can be left to its natural size, stretched, or constrained to fit the available space.", 20 | "border-bottom-color": "Sets the color of an element's bottom border. It can also be set with the shorthand CSS properties border-color or border-bottom.", 21 | "border-bottom-left-radius": "Rounds the bottom-left corner of an element by specifying the radius (or the radius of the semi-major and semi-minor axes) of the ellipse defining the curvature of the corner.", 22 | "border-bottom": "Sets an element's bottom border. It sets the values of border-bottom-width, border-bottom-style and border-bottom-color.", 23 | "border-bottom-right-radius": "Rounds the bottom-right corner of an element by specifying the radius (or the radius of the semi-major and semi-minor axes) of the ellipse defining the curvature of the corner.", 24 | "border-bottom-style": "Sets the line style of an element's bottom border.", 25 | "border-bottom-width": "Sets the width of the bottom border of an element.", 26 | "border-color": "Sets the color of an element's border.", 27 | "border-image": "Draws an image around a given element. ", 28 | "border-image-repeat": "Defines how the edge regions and middle region of a source image are adjusted to fit the dimensions of an element's border image. The middle region can be displayed by using the keyword `fill` in the border-image-slice property.", 29 | "border-image-slice": "Divides the image specified by border-image-source into regions. These regions form the components of an element's border image.", 30 | "border-image-source": "Sets the source image used to create an element's border image.", 31 | "border-image-width": "Sets the width of an element's border image.", 32 | "border-left-color": "Sets the color of an element's left border. It can also be set with the shorthand CSS properties border-color or border-left.", 33 | "border-left": "Sets all the properties of an element's left border.", 34 | "border-left-style": "Sets the line style of an element's left border.", 35 | "border-left-width": "Sets the width of the left border of an element.", 36 | "border": "Sets an element's border. It sets the values of border-width, border-style, and border-color.", 37 | "border-radius": "Rounds the corners of an element's outer border edge. You can set a single radius to make circular corners, or two radii to make elliptical corners.", 38 | "border-right-color": "Sets the color of an element's right border. It can also be set with the shorthand CSS properties border-color or border-right.", 39 | "border-right": "Sets all the properties of an element's right border.", 40 | "border-right-style": "Sets the line style of an element's right border.", 41 | "border-right-width": "Sets the width of the right border of an element.", 42 | "border-spacing": "Sets the distance between the borders of adjacent cells in a . This property applies only when border-collapse is separate.", 43 | "border-style": "Sets the line style for all four sides of an element's border.", 44 | "border-top-color": "Sets the color of an element's top border. It can also be set with the shorthand CSS properties border-color or border-top.", 45 | "border-top-left-radius": "Rounds the top-left corner of an element by specifying the radius (or the radius of the semi-major and semi-minor axes) of the ellipse defining the curvature of the corner.", 46 | "border-top": "Sets all the properties of an element's top border.", 47 | "border-top-right-radius": "Rounds the top-right corner of an element by specifying the radius (or the radius of the semi-major and semi-minor axes) of the ellipse defining the curvature of the corner.", 48 | "border-top-style": "Sets the line style of an element's top border.", 49 | "border-top-width": "Sets the width of the top border of an element.", 50 | "border-width": "Sets the width of an element's border.", 51 | "box-shadow": "Attaches one or more drop-shadows to the box.", 52 | "caret-color": "The caret is a visible indicator of the insertion point in an element where text (and potentially other content) is inserted by the user. This property controls the color of that visible indicator.", 53 | "color": "This property describes the foreground color of an element's text content.", 54 | "filter": "Defines visual effects (like blur and saturation) to an element", 55 | "font-family": "This property specifies a prioritized list of font family names or generic family names. A font family defines a set of faces that vary in weight, width or slope.", 56 | "font-feature-settings": "This property provides low-level control over OpenType font features. It is intended as a way of providing access to font features that are not widely used but are needed for a particular use case.", 57 | "font-kerning": "Kerning is the contextual adjustment of inter-glyph spacing. This property controls metric kerning, kerning that utilizes adjustment data contained in the font", 58 | "font": "The font property is a shorthand property for:font-style,font-variant,font-weight,font-size/line-height,font-family", 59 | "font-size": "This property indicates the desired height of glyphs from the font. For scalable fonts, the font-size is a scale factor applied to the EM unit of the font.", 60 | "font-stretch": "This property selects a normal, condensed, or expanded face from a font family.", 61 | "font-style": "This property allows italic or oblique faces to be selected. Italic forms are generally cursive in nature while oblique faces are typically sloped versions of the regular face. ", 62 | "font-variant-alternates": "The font-variant-alternates property is used to specifically select alternate glyphs.", 63 | "font-variant-caps": "This property is used to enable typographic subscript and superscript glyphs.", 64 | "font-variant-east-asian": "Allows control of glyph substitution and sizing in East Asian text. ", 65 | "font-variant-ligatures": "Ligatures and contextual forms are ways of combining glyphs to produce more harmonized forms", 66 | "font-variant": "The font-variant property specifies whether or not a text should be displayed in a small-caps font.", 67 | "font-variant-numeric": "Specifies control over numerical forms.", 68 | "font-variant-position": "This property controls the use of alternate, smaller glyphs that are positioned as superscript or subscript", 69 | "font-variation-settings": "This property provides low-level control over OpenType or TrueType font variations.", 70 | "font-weight": "This property specifies the weight of glyphs in the font, their degree of blackness or stroke thickness. ", 71 | "-gtk-dpi": "Dimension values default for screen resolution", 72 | "-gtk-icon-filter": "Filter applied to builtin and application-loaded icons", 73 | "-gtk-icon-palette": "Defines a color palette for recoloring symbolic icons.", 74 | "-gtk-icon-shadow": "Shadow applied to builtin and application-loaded icons", 75 | "-gtk-icon-size": " Size used for builtin icons in buttons and expanders", 76 | "-gtk-icon-source": "Used by widgets that are rendering ‘built-in’ icons, such as arrows, expanders, spinners, checks or radios.", 77 | "-gtk-icon-style": "Determines the preferred style for application-loaded icons", 78 | "-gtk-icon-transform": "Transform list applied to builtin and application-loaded icons", 79 | "-gtk-secondary-caret-color": "This property controls the color of the secondary caret indicator", 80 | "letter-spacing": "This property specifies additional spacing (commonly called tracking) between adjacent typographic character units", 81 | "line-height": "This property specifies the box’s preferred line height, which is used in calculating its “layout bounds", 82 | "margin-bottom": "Sets the margin area on the bottom of an element. A positive value places it farther from its neighbors, while a negative value places it closer.", 83 | "margin-left": "Sets the margin area on the left side of an element. A positive value places it farther from its neighbors, while a negative value places it closer.", 84 | "margin": "sets the margin area on all four sides of an element.", 85 | "margin-right": "Sets the margin area on the right side of an element. A positive value places it farther from its neighbors, while a negative value places it closer.", 86 | "margin-top": "Sets the margin area on the top of an element. A positive value places it farther from its neighbors, while a negative value places it closer.", 87 | "min-height": "Sets the minimum height of an element. It prevents the used value of the height property from becoming smaller than the value specified for min-height.", 88 | "min-width": "Sets the minimum width of an element. It prevents the used value of the width property from becoming smaller than the value specified for min-width.", 89 | "opacity": "Sets the opacity of an element. Opacity is the degree to which content behind an element is hidden, and is the opposite of transparency.", 90 | "outline-color": "Sets the color of an element's outline.", 91 | "outline": "Sets most of the outline properties in a single declaration.", 92 | "outline-offset": "Sets the amount of space between an outline and the edge or border of an element.", 93 | "outline-style": "Sets the style of an element's outline. An outline is a line that is drawn around an element, outside the border.", 94 | "outline-width": "Sets the thickness of an element's outline. An outline is a line that is drawn around an element, outside the border.", 95 | "padding-bottom": "Sets the height of the padding area on the bottom of an element.", 96 | "padding-left": "Sets the width of the padding area to the left of an element.", 97 | "padding": "Sets the padding area on all four sides of an element at once.", 98 | "padding-right": "Sets the width of the padding area on the right of an element.", 99 | "padding-top": "Sets the height of the padding area on the top of an element.", 100 | "text-decoration-color": "Sets the color of decorations added to text by text-decoration-line.\nThe color applies to decorations, such as underlines, overlines, strikethroughs, and wavy lines like those used to mark misspellings, in the scope of the property's value.", 101 | "text-decoration-line": "Sets the kind of decoration that is used on text in an element, such as an underline or overline.", 102 | "text-decoration": "Sets the appearance of decorative lines on text. It is a shorthand for text-decoration-line, text-decoration-color, text-decoration-style, and the newer text-decoration-thickness property.", 103 | "text-decoration-style": "Sets the style of the lines specified by text-decoration-line. The style applies to all lines that are set with text-decoration-line.", 104 | "text-shadow": "Adds shadows to text. It accepts a comma-separated list of shadows to be applied to the text and any of its decorations. Each shadow is described by some combination of X and Y offsets from the element, blur radius, and color.", 105 | "text-transform": "Specifies how to capitalize an element's text. It can be used to make text appear in all-uppercase or all-lowercase, or with each word capitalized.", 106 | "transform": "Lets you rotate, scale, skew, or translate an element. It modifies the coordinate space of the CSS visual formatting model.", 107 | "transform-origin": "Sets the origin for an element's transformations.", 108 | "transition-delay": "Specifies the duration to wait before starting a property's transition effect when its value changes.", 109 | "transition-duration": "Sets the length of time a transition animation should take to complete. By default, the value is 0s, meaning that no animation will occur.", 110 | "transition": "A shorthand property for transition-property, transition-duration, transition-timing-function, and transition-delay.", 111 | "transition-property": "Sets the CSS properties to which a transition effect should be applied.", 112 | "transition-timing-function": "Sets how intermediate values are calculated for CSS properties being affected by a transition effect." 113 | } -------------------------------------------------------------------------------- /src/functions.json: -------------------------------------------------------------------------------- 1 | { 2 | "lighter": { 3 | "docs": "Produces a brighter variant of the passed color" 4 | }, 5 | "darker": { 6 | "docs": "Produces a darker variant of the passed color." 7 | }, 8 | "shade": { 9 | "docs": "Changes the lightness of the passed color. The number ranges from 0 for black to 2 for white" 10 | }, 11 | "alpha": { 12 | "docs": "Replaces the alpha values of the passed color with the double in range 0 to 1." 13 | }, 14 | "mix": { 15 | "docs": "Interpolates between the two colors." 16 | }, 17 | "hsl": { 18 | "docs": "The `hsl()` functional notation expresses an sRGB color according to its hue, saturation, and lightness components." 19 | }, 20 | "hsla": { 21 | "docs": "The `hsla()` functional notation expresses an sRGB color according to its hue, saturation, lightness and alpha components." 22 | }, 23 | "rgb": { 24 | "docs": "The `rgb()` functional notation expresses a color according to its red, green, and blue components." 25 | }, 26 | "rgba": { 27 | "docs": "The `rgba()` functional notation expresses a color according to its red, green, blue and alpha components." 28 | }, 29 | "brightness": { 30 | "docs": "Applies a linear multiplier value on an element or an input image, making the image appear brighter or darker." 31 | }, 32 | "contrast": { 33 | "docs": "Adjusts the contrast of the input image." 34 | }, 35 | "grayscale": { 36 | "docs": "Converts the input image to grayscale." 37 | }, 38 | "hue-rotate": { 39 | "docs": "Rotates the hue of an element and its contents." 40 | }, 41 | "invert": { 42 | "docs": "Inverts the color samples in the input image." 43 | }, 44 | "opacity": { 45 | "docs": "Applies transparency to the samples in the input image." 46 | }, 47 | "saturate": { 48 | "docs": "Super-saturates or desaturates the input image" 49 | }, 50 | "sepia": { 51 | "docs": "Converts the input image to sepia, giving tit a warmer, more yellow/brown appearance." 52 | }, 53 | "drop-shadow": { 54 | "docs": "Applies a drop shadow effect to the input image." 55 | }, 56 | "linear": { 57 | "docs": "Defines a piecewise linear function that interpolates linearly between its points, allowing you to approximate more complex animations like bounce and elastic effects. The interpolation is done at a constant rate from beginning to end. A typical use of the `linear()` function is to provide many points to approximate any curve." 58 | }, 59 | "ease-in-out": { 60 | "docs": "Missing function docs. Please contribute." 61 | }, 62 | "ease-in": { 63 | "docs": "Missing function docs. Please contribute." 64 | }, 65 | "ease": { 66 | "docs": "Missing function docs. Please contribute." 67 | }, 68 | "step-start": { 69 | "docs": "Missing function docs. Please contribute." 70 | }, 71 | "step-end": { 72 | "docs": "Missing function docs. Please contribute." 73 | }, 74 | "steps": { 75 | "docs": "Missing function docs. Please contribute." 76 | }, 77 | "cubic-bezier": { 78 | "docs": "Missing function docs. Please contribute." 79 | }, 80 | "-gtk-recolor": { 81 | "docs": "Recolors a icon from a given URI with the selected palette." 82 | }, 83 | "cross-fade": { 84 | "docs": "Missing function docs. Please contribute." 85 | }, 86 | "conic-gradient": { 87 | "docs": "Missing function docs. Please contribute." 88 | }, 89 | "image": { 90 | "docs": "Missing function docs. Please contribute." 91 | }, 92 | "calc": { 93 | "docs": "Missing function docs. Please contribute." 94 | }, 95 | "-gtk-icontheme": { 96 | "docs": "The specified icon name is used to look up a themed icon, while taking into account the values of the -gtk-icon-palette property." 97 | }, 98 | "radial-gradient": { 99 | "docs": "Creates an image consisting of a progressive transition between two or more colors that radiate from an origin. Its shape may be a circle or an ellipse." 100 | }, 101 | "repeating-radial-gradient": { 102 | "docs": "Creates an image consisting of repeating gradients that radiate from an origin." 103 | }, 104 | "linear-gradient": { 105 | "docs": "Creates an image consisting of a progressive transition between two or more colors along a straight line." 106 | }, 107 | "repeating-linear-gradient": { 108 | "docs": "Creates an image consisting of repeating linear gradients." 109 | }, 110 | "url": { 111 | "docs": "Is used to include a file. The parameter is an absolute URL, a relative URL, a blob URL, or a data URL." 112 | }, 113 | "-gtk-scaled": { 114 | "docs": "`-gtk-scaled` can be used to provide a normal and a hi-resolution variant of an image." 115 | }, 116 | "matrix": { 117 | "docs": "Missing function docs. Please contribute." 118 | }, 119 | "matrix3d": { 120 | "docs": "Missing function docs. Please contribute." 121 | }, 122 | "perspective": { 123 | "docs": "Missing function docs. Please contribute." 124 | }, 125 | "rotate": { 126 | "docs": "Missing function docs. Please contribute." 127 | }, 128 | "rotateZ": { 129 | "docs": "Missing function docs. Please contribute." 130 | }, 131 | "rotate3d": { 132 | "docs": "Missing function docs. Please contribute." 133 | }, 134 | "rotateX": { 135 | "docs": "Missing function docs. Please contribute." 136 | }, 137 | "rotateY": { 138 | "docs": "Missing function docs. Please contribute." 139 | }, 140 | "scale": { 141 | "docs": "Missing function docs. Please contribute." 142 | }, 143 | "scale3d": { 144 | "docs": "Missing function docs. Please contribute." 145 | }, 146 | "scaleX": { 147 | "docs": "Missing function docs. Please contribute." 148 | }, 149 | "scaleY": { 150 | "docs": "Missing function docs. Please contribute." 151 | }, 152 | "scaleZ": { 153 | "docs": "Missing function docs. Please contribute." 154 | }, 155 | "skew": { 156 | "docs": "Missing function docs. Please contribute." 157 | }, 158 | "skewX": { 159 | "docs": "Missing function docs. Please contribute." 160 | }, 161 | "skewY": { 162 | "docs": "Missing function docs. Please contribute." 163 | }, 164 | "translate": { 165 | "docs": "Missing function docs. Please contribute." 166 | }, 167 | "translate3d": { 168 | "docs": "Missing function docs. Please contribute." 169 | }, 170 | "translateX": { 171 | "docs": "Missing function docs. Please contribute." 172 | }, 173 | "translateY": { 174 | "docs": "Missing function docs. Please contribute." 175 | }, 176 | "translateZ": { 177 | "docs": "Missing function docs. Please contribute." 178 | } 179 | } -------------------------------------------------------------------------------- /src/gtkcsslangserver.vala: -------------------------------------------------------------------------------- 1 | /* gtkcsslangserver.vala 2 | * 3 | * Copyright 2023 JCWasmx86 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * SPDX-License-Identifier: GPL-3.0-or-later 19 | */ 20 | [CCode (cname = "gtkcssprovider_load_from_data")] 21 | public static extern void gtkcssprovider_load_from_data (Gtk.CssProvider provider, string data); 22 | 23 | errordomain FormattingError { 24 | FORMAT, 25 | READ 26 | } 27 | 28 | namespace GtkCssLangServer { 29 | public class Server : Jsonrpc.Server { 30 | Uri? base_uri; 31 | MainLoop loop; 32 | GLib.HashTable ctxs; 33 | GLib.Mutex mutex; 34 | 35 | internal Server (MainLoop l) { 36 | this.loop = l; 37 | this.mutex = Mutex (); 38 | this.ctxs = new GLib.HashTable (GLib.str_hash, GLib.str_equal); 39 | critical ("Documentation comes from MDN: Attributions and copyright licensing by Mozilla Contributors is licensed under CC-BY-SA 2.5."); 40 | } 41 | 42 | protected override void notification (Jsonrpc.Client client, string method, Variant parameters) { 43 | info ("Received notification %s", method); 44 | try { 45 | switch (method) { 46 | case "textDocument/didOpen": 47 | this.did_open (client, parameters); 48 | break; 49 | case "textDocument/didChange": 50 | this.did_change (client, parameters); 51 | break; 52 | case "textDocument/didSave": 53 | this.did_save (client, parameters); 54 | break; 55 | } 56 | } catch (Error e) { 57 | critical ("Failed to handle [%s]: %s", method, e.message); 58 | } 59 | } 60 | 61 | void did_change (Jsonrpc.Client client, Variant @params) throws Error { 62 | var document = @params.lookup_value ("textDocument", VariantType.VARDICT); 63 | var changes = @params.lookup_value ("contentChanges", VariantType.ARRAY); 64 | var uri = (string) document.lookup_value ("uri", VariantType.STRING); 65 | var iter = changes.iterator (); 66 | var elem = iter.next_value (); 67 | var ce = Util.parse_variant (elem); 68 | var text = ce.text; 69 | this.ctxs[uri] = this.parse (text, uri, client); 70 | } 71 | 72 | void did_open (Jsonrpc.Client client, Variant @params) throws Error { 73 | var document = @params.lookup_value ("textDocument", VariantType.VARDICT); 74 | var uri = (string) document.lookup_value ("uri", VariantType.STRING); 75 | var path = Uri.parse (uri, UriFlags.NONE).get_path (); 76 | this.ctxs[uri] = this.parse_path (path, uri, client); 77 | } 78 | 79 | void did_save (Jsonrpc.Client client, Variant @params) throws Error { 80 | var document = @params.lookup_value ("textDocument", VariantType.VARDICT); 81 | var uri = (string) document.lookup_value ("uri", VariantType.STRING); 82 | var path = Uri.parse (uri, UriFlags.NONE).get_path (); 83 | this.ctxs[uri] = this.parse_path (path, uri, client); 84 | } 85 | 86 | ParseContext ? parse (string text, string uri, Jsonrpc.Client client) throws Error { 87 | this.mutex.lock (); 88 | client.send_notification ( 89 | "textDocument/publishDiagnostics", 90 | build_dict ( 91 | uri: new Variant.string (uri), 92 | diagnostics: new Variant.array (VariantType.VARIANT, {}) 93 | ) 94 | ); 95 | var gtkcssprovider = new Gtk.CssProvider (); 96 | var diags = new Diagnostic[0]; 97 | gtkcssprovider.parsing_error.connect ((section, error) => { 98 | var start = section.get_start_location (); 99 | var s_pos = new Position () { 100 | line = (uint) start.lines, 101 | character = (uint) start.line_bytes 102 | }; 103 | var end = section.get_end_location (); 104 | var e_pos = new Position () { 105 | line = (uint) end.lines, 106 | character = (uint) end.line_bytes 107 | }; 108 | var sev = error.domain == Gtk.CssParserWarning.quark () ? DiagnosticSeverity.Warning : DiagnosticSeverity.Error; 109 | var range = new Range () { 110 | start = s_pos, 111 | end = e_pos 112 | }; 113 | diags += new Diagnostic () { 114 | range = range, 115 | severity = sev, 116 | message = error.message, 117 | file = uri 118 | }; 119 | }); 120 | gtkcssprovider_load_from_data (gtkcssprovider, text); 121 | var p = new ParseContext (diags, text, uri); 122 | var arr = new Json.Array (); 123 | foreach (var d in diags) { 124 | arr.add_element (Json.gobject_serialize (d)); 125 | } 126 | foreach (var d in p.enhanced_diags) { 127 | arr.add_element (Json.gobject_serialize (d)); 128 | } 129 | client.send_notification ( 130 | "textDocument/publishDiagnostics", 131 | build_dict ( 132 | uri : new Variant.string (uri), 133 | diagnostics: Json.gvariant_deserialize (new Json.Node.alloc ().init_array (arr), null) 134 | )); 135 | this.mutex.unlock (); 136 | return p; 137 | } 138 | 139 | ParseContext ? parse_path (string file, string uri, Jsonrpc.Client client) throws Error { 140 | string contents; 141 | size_t len; 142 | FileUtils.get_contents (file, out contents, out len); 143 | return this.parse (contents, uri, client); 144 | } 145 | 146 | protected override bool handle_call (Jsonrpc.Client client, string method, Variant id, Variant parameters) { 147 | info ("Received call %s", method); 148 | try { 149 | switch (method) { 150 | case "initialize": 151 | this.initialize (client, id, parameters); 152 | break; 153 | case "textDocument/hover": 154 | this.hover (client, id, parameters); 155 | break; 156 | case "textDocument/documentSymbol": 157 | this.symbols (client, id, parameters); 158 | break; 159 | case "textDocument/declaration": 160 | case "textDocument/definition": 161 | this.definition (client, id, parameters); 162 | break; 163 | case "textDocument/completion": 164 | this.completion (client, method, id, parameters); 165 | break; 166 | case "textDocument/formatting": 167 | this.formatting (client, method, id, parameters); 168 | break; 169 | } 170 | } catch (Error e) { 171 | client.reply_error_async (id, Jsonrpc.ClientError.INTERNAL_ERROR, "Error: %s".printf (e.message), null); 172 | return false; 173 | } 174 | return true; 175 | } 176 | 177 | void formatting (Jsonrpc.Client client, string method, Variant id, Variant @params) throws Error { 178 | var p = Util.parse_variant (@params); 179 | var uri = p.textDocument.uri; 180 | var ctx = this.ctxs[uri]; 181 | var sin = ctx.text; 182 | var launcher = new SubprocessLauncher (SubprocessFlags.STDERR_PIPE | SubprocessFlags.STDOUT_PIPE | SubprocessFlags.STDIN_PIPE); 183 | launcher.set_environ (Environ.get ()); 184 | var options = p.options; 185 | var path = Uri.parse (p.textDocument.uri, UriFlags.NONE).get_path (); 186 | var cmd = new string[] { "prettier", "--stdin-filepath", path }; 187 | if (!options.insertSpaces) 188 | cmd += "--use-tabs"; 189 | cmd += "--tab-width"; 190 | cmd += "%u".printf (options.tabSize); 191 | string? stdout_buf = null, stderr_buf = null; 192 | var subprocess = launcher.spawnv (cmd); 193 | subprocess.communicate_utf8 (sin, null, out stdout_buf, out stderr_buf); 194 | if (!subprocess.get_successful ()) { 195 | if (stderr_buf != null && stderr_buf.strip ().length > 0) { 196 | throw new FormattingError.FORMAT ("%s", stderr_buf); 197 | } else { 198 | var sb = new StringBuilder (); 199 | foreach (var arg in cmd) 200 | sb.append (arg).append (" "); 201 | throw new FormattingError.READ ("prettier failed with error code %d: %s", subprocess.get_exit_status (), sb.str.strip ()); 202 | } 203 | } 204 | int last_nl_pos; 205 | uint nl_count = Util.count_chars_in_string (sin, '\n', out last_nl_pos); 206 | var edit_range = new Range () { 207 | start = new Position () { 208 | line = 0, 209 | character = 0 210 | }, 211 | end = new Position () { 212 | line = nl_count + 1, 213 | // handle trailing newline 214 | character = last_nl_pos == sin.length - 1 ? 1 : 0 215 | } 216 | }; 217 | var edit = new TextEdit (edit_range, stdout_buf); 218 | var json_array = new Json.Array (); 219 | json_array.add_element (Json.gobject_serialize (edit)); 220 | var variant_array = Json.gvariant_deserialize (new Json.Node.alloc ().init_array (json_array), null); 221 | client.reply (id, variant_array); 222 | } 223 | 224 | void completion (Jsonrpc.Client client, string method, Variant id, Variant @params) throws Error { 225 | var p = Util.parse_variant (@params); 226 | var ctx = this.ctxs[p.textDocument.uri]; 227 | if (ctx == null) { 228 | var json_array = new Json.Array (); 229 | var variant_array = Json.gvariant_deserialize (new Json.Node.alloc ().init_array (json_array), null); 230 | client.reply (id, variant_array); 231 | return; 232 | } 233 | info ("Completing at %s:[%u:%u]", p.textDocument.uri, p.position.line, p.position.character); 234 | var completions = ctx.complete (p); 235 | var json_array = new Json.Array (); 236 | foreach (var comp in completions) 237 | json_array.add_element (Json.gobject_serialize (comp)); 238 | var variant_array = Json.gvariant_deserialize (new Json.Node.alloc ().init_array (json_array), null); 239 | client.reply (id, variant_array); 240 | } 241 | 242 | void definition (Jsonrpc.Client client, Variant id, Variant params) throws Error { 243 | var p = Util.parse_variant (@params); 244 | var uri = p.textDocument.uri; 245 | var ctx = this.ctxs[uri]; 246 | if (ctx == null) { 247 | client.reply (id, null); 248 | return; 249 | } 250 | var location = ctx.find_declaration (p.position); 251 | client.reply (id, ((location == null) ? null : Util.object_to_variant (location))); 252 | } 253 | 254 | void symbols (Jsonrpc.Client client, Variant id, Variant params) throws Error { 255 | var p = Util.parse_variant (@params); 256 | var uri = p.textDocument.uri; 257 | var ctx = this.ctxs[uri]; 258 | var array = new Json.Array (); 259 | if (ctx != null) { 260 | foreach (var sym in ctx.symbols ()) { 261 | array.add_element (Json.gobject_serialize (sym)); 262 | } 263 | } 264 | Variant result = Json.gvariant_deserialize (new Json.Node.alloc ().init_array (array), null); 265 | client.reply (id, result); 266 | } 267 | 268 | void hover (Jsonrpc.Client client, Variant id, Variant params) throws Error { 269 | var p = Util.parse_variant (@params); 270 | var uri = p.textDocument.uri; 271 | var ctx = this.ctxs[uri]; 272 | info ("Hovering at %s:[%u:%u]", uri, p.position.line, p.position.character); 273 | var response = ctx.hover (p.position.line, p.position.character); 274 | if (response == null) { 275 | client.reply (id, null); 276 | return; 277 | } 278 | client.reply (id, Util.object_to_variant (response)); 279 | } 280 | 281 | void initialize (Jsonrpc.Client client, Variant id, Variant @params) throws Error { 282 | var init = Util.parse_variant (@params); 283 | this.base_uri = Uri.parse (init.rootUri, UriFlags.NONE); 284 | client.reply (id, build_dict ( 285 | capabilities : build_dict ( 286 | textDocumentSync: new Variant.int32 (1 /* Full*/), 287 | diagnosticProvider: new Variant.boolean (true), 288 | hoverProvider: new Variant.boolean (true), 289 | documentSymbolProvider: new Variant.boolean (true), 290 | declarationProvider: new Variant.boolean (true), 291 | definitionProvider: new Variant.boolean (true), 292 | completionProvider: build_dict ( 293 | triggerCharacters: new Variant.strv (new string[] { "@", ":", "-" }) 294 | ) 295 | ), 296 | serverInfo : build_dict ( 297 | name: new Variant.string ("GTK CSS Language Server"), 298 | version: new Variant.string ("0.0.1-alpha") 299 | ) 300 | )); 301 | } 302 | 303 | private Variant build_dict (...) { 304 | var builder = new VariantBuilder (new VariantType ("a{sv}")); 305 | var l = va_list (); 306 | while (true) { 307 | string? key = l.arg (); 308 | if (key == null) { 309 | break; 310 | } 311 | Variant val = l.arg (); 312 | builder.add ("{sv}", key, val); 313 | } 314 | return builder.end (); 315 | } 316 | } 317 | } 318 | -------------------------------------------------------------------------------- /src/ids.c.in: -------------------------------------------------------------------------------- 1 | const char *VCS_TAG = "@VCS_TAG@"; 2 | 3 | -------------------------------------------------------------------------------- /src/load.c: -------------------------------------------------------------------------------- 1 | /* load.c 2 | * 3 | * Copyright 2023 JCWasmx86 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * SPDX-License-Identifier: GPL-3.0-or-later 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | extern char _binary_src_docs_json_start; 28 | extern char _binary_src_docs_json_end; 29 | 30 | extern char _binary_src_colors_json_start; 31 | extern char _binary_src_colors_json_end; 32 | 33 | extern char _binary_src_functions_json_start; 34 | extern char _binary_src_functions_json_end; 35 | 36 | extern char _binary_src_properties_json_start; 37 | extern char _binary_src_properties_json_end; 38 | 39 | extern char _binary_src_selectors_json_start; 40 | extern char _binary_src_selectors_json_end; 41 | 42 | const char * 43 | load_docs (void) 44 | { 45 | size_t len = (size_t)(&_binary_src_docs_json_end - &_binary_src_docs_json_start); 46 | void *mem = calloc (1, len + 1); 47 | assert (mem); 48 | memcpy (mem, &_binary_src_docs_json_start, len); 49 | return mem; 50 | } 51 | 52 | const char * 53 | load_colors (void) 54 | { 55 | size_t len = (size_t)(&_binary_src_colors_json_end - &_binary_src_colors_json_start); 56 | void *mem = calloc (1, len + 1); 57 | assert (mem); 58 | memcpy (mem, &_binary_src_colors_json_start, len); 59 | return mem; 60 | } 61 | 62 | const char * 63 | load_functions (void) 64 | { 65 | size_t len = (size_t)(&_binary_src_functions_json_end - &_binary_src_functions_json_start); 66 | void *mem = calloc (1, len + 1); 67 | assert (mem); 68 | memcpy (mem, &_binary_src_functions_json_start, len); 69 | return mem; 70 | } 71 | 72 | const char * 73 | load_properties (void) 74 | { 75 | size_t len = (size_t)(&_binary_src_properties_json_end - &_binary_src_properties_json_start); 76 | void *mem = calloc (1, len + 1); 77 | assert (mem); 78 | memcpy (mem, &_binary_src_properties_json_start, len); 79 | return mem; 80 | } 81 | 82 | const char * 83 | load_selectors (void) 84 | { 85 | size_t len = (size_t)(&_binary_src_selectors_json_end - &_binary_src_selectors_json_start); 86 | void *mem = calloc (1, len + 1); 87 | assert (mem); 88 | memcpy (mem, &_binary_src_selectors_json_start, len); 89 | return mem; 90 | } 91 | 92 | extern void *ts_parser_new (); 93 | extern void *tree_sitter_css (); 94 | extern unsigned int ts_parser_set_language (void *, void *); 95 | 96 | void* 97 | get_parser (void) 98 | { 99 | void *parser = ts_parser_new(); 100 | ts_parser_set_language(parser, tree_sitter_css()); 101 | return parser; 102 | } 103 | 104 | void 105 | gtkcssprovider_load_from_data (GtkCssProvider *provider, 106 | char *data) 107 | { 108 | gtk_css_provider_load_from_data (provider, data, -1); 109 | } 110 | -------------------------------------------------------------------------------- /src/main.vala: -------------------------------------------------------------------------------- 1 | /* main.vala 2 | * 3 | * Copyright 2023 JCWasmx86 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * SPDX-License-Identifier: GPL-3.0-or-later 19 | */ 20 | 21 | int main (string[] args) { 22 | if (args.length == 2) { 23 | var file = args[1]; 24 | string contents; 25 | FileUtils.get_contents (file, out contents); 26 | var p = new GtkCssLangServer.ParseContext (new GtkCssLangServer.Diagnostic[0], contents, file); 27 | var gtkcssprovider = new Gtk.CssProvider (); 28 | var diags = new GtkCssLangServer.Diagnostic[0]; 29 | gtkcssprovider.parsing_error.connect ((section, error) => { 30 | var start = section.get_start_location (); 31 | var s_pos = new GtkCssLangServer.Position () { 32 | line = (uint) start.lines, 33 | character = (uint) start.line_bytes 34 | }; 35 | var end = section.get_end_location (); 36 | var e_pos = new GtkCssLangServer.Position () { 37 | line = (uint) end.lines, 38 | character = (uint) end.line_bytes 39 | }; 40 | var sev = error.domain == Gtk.CssParserWarning.quark () ? GtkCssLangServer.DiagnosticSeverity.Warning : GtkCssLangServer.DiagnosticSeverity.Error; 41 | var range = new GtkCssLangServer.Range () { 42 | start = s_pos, 43 | end = e_pos 44 | }; 45 | diags += new GtkCssLangServer.Diagnostic () { 46 | range = range, 47 | severity = sev, 48 | message = error.message, 49 | file = file, 50 | }; 51 | }); 52 | gtkcssprovider_load_from_data (gtkcssprovider, contents); 53 | foreach (var d in p.enhanced_diags) { 54 | diags += d; 55 | } 56 | foreach (var diag in diags) { 57 | var range = "%u:%u".printf (diag.range.start.line, diag.range.start.character); 58 | stdout.printf((diag.severity == GtkCssLangServer.DiagnosticSeverity.Error ? "[ERROR] " : "[WARNING] ") + diag.file + ":["+ range + "]: "+ diag.message + "\n"); 59 | } 60 | return (diags.length == 0 ? 0 : 1); 61 | } else if (args.length == 1) { 62 | GLib.Log.writer_default_set_use_stderr (true); 63 | GLib.Log.set_debug_enabled (true); 64 | var main_loop = new MainLoop (); 65 | var s = new GtkCssLangServer.Server (main_loop); 66 | var new_stdout_fd = Posix.dup (Posix.STDOUT_FILENO); 67 | Posix.close (Posix.STDOUT_FILENO); 68 | Posix.dup2 (Posix.STDERR_FILENO, Posix.STDOUT_FILENO); 69 | var input_stream = new UnixInputStream (Posix.STDIN_FILENO, false); 70 | var output_stream = new UnixOutputStream (new_stdout_fd, false); 71 | var b = Unix.set_fd_nonblocking (Posix.STDIN_FILENO, true) && Unix.set_fd_nonblocking (new_stdout_fd, true); 72 | if (!b) { 73 | error ("Unable to make pipes non-blocking"); 74 | } 75 | s.accept_io_stream (new SimpleIOStream (input_stream, output_stream)); 76 | main_loop.run (); 77 | return 0; 78 | } else { 79 | stderr.printf("Error: Run either with one(!) CSS file as argument for linting this file or run gtkcsslanguageserver without args for starting the server!\n"); 80 | return 1; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/meson.build: -------------------------------------------------------------------------------- 1 | gtkcsslanguageserver_sources = [ 2 | 'ast/ast.vala', 3 | 'ast/astvisitor.vala', 4 | 'ast/dataextractor.vala', 5 | 'gtkcsslangserver.vala', 6 | 'load.c', 7 | 'main.vala', 8 | 'parsecontext.vala', 9 | 'protocol.vala', 10 | 'util.vala', 11 | ] 12 | 13 | gtkcsslanguageserver_deps = [ 14 | dependency('gio-2.0'), 15 | dependency('gio-unix-2.0'), 16 | dependency('glib-2.0'), 17 | dependency('gobject-2.0'), 18 | dependency('gtk4'), 19 | dependency('jsonrpc-glib-1.0'), 20 | meson.get_compiler('vala').find_library('posix'), 21 | meson.get_compiler('vala').find_library('ts', dirs: vapi_dir), 22 | ts_dep, 23 | ] 24 | 25 | ld = find_program('ld') 26 | 27 | jsons = [ 28 | 'colors.json', 29 | 'docs.json', 30 | 'functions.json', 31 | 'properties.json', 32 | 'selectors.json', 33 | ] 34 | objs = [] 35 | foreach j : jsons 36 | data_local = configure_file( 37 | input: j, 38 | output: j, 39 | copy: true, 40 | ) 41 | objs += custom_target( 42 | 'objectify-' + j, 43 | output: j + '.o', 44 | input: data_local, 45 | command: [ld, '--relocatable', '--format', 'binary', '--output', '@OUTPUT@', '@INPUT@'], 46 | ) 47 | endforeach 48 | 49 | r = run_command(['git', 'log', 'HEAD^..HEAD', '--format=%H']) 50 | commit = r.stdout().strip().split('\n')[0] 51 | config = { 52 | 'VCS_TAG': commit, 53 | } 54 | 55 | ids_c = configure_file( 56 | input: 'ids.c.in', 57 | output: 'ids.c', 58 | configuration: config, 59 | ) 60 | 61 | executable( 62 | 'gtkcsslanguageserver', 63 | gtkcsslanguageserver_sources + objs + [ids_c], 64 | vala_args: '--target-glib=2.58', 65 | dependencies: gtkcsslanguageserver_deps, 66 | install: true, 67 | link_with: ts_lib, 68 | ) 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /src/parsecontext.vala: -------------------------------------------------------------------------------- 1 | /* parsecontext.vala 2 | * 3 | * Copyright 2023 JCWasmx86 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * SPDX-License-Identifier: GPL-3.0-or-later 19 | */ 20 | 21 | [CCode (cname = "load_colors")] 22 | public static extern string load_colors (); 23 | 24 | [CCode (cname = "get_parser")] 25 | public static extern TreeSitter.TSParser get_parser (); 26 | 27 | [CCode (cname = "load_docs")] 28 | public static extern string load_docs (); 29 | 30 | [CCode (cname = "load_functions")] 31 | public static extern string load_functions (); 32 | 33 | [CCode (cname = "load_selectors")] 34 | public static extern string load_selectors (); 35 | 36 | namespace GtkCssLangServer { 37 | internal class ParseContext { 38 | Diagnostic[] diags; 39 | public Diagnostic[] enhanced_diags; 40 | public string text; 41 | string uri; 42 | string[] lines; 43 | Node sheet; 44 | DataExtractor? extractor; 45 | Json.Object color_docs; 46 | Json.Object function_docs; 47 | Json.Object selector_docs; 48 | GLib.HashTable property_docs; 49 | 50 | internal ParseContext (Diagnostic[] diags, string text, string uri) { 51 | this.property_docs = new GLib.HashTable (GLib.str_hash, GLib.str_equal); 52 | this.diags = diags; 53 | this.text = text; 54 | this.uri = uri; 55 | this.lines = this.text.split ("\n"); 56 | var t = get_parser (); 57 | var tree = t.parse_string (null, text, text.length); 58 | if (tree != null) { 59 | var root = tree.root_node (); 60 | this.sheet = to_node (root, text); 61 | this.sheet.set_parents (); 62 | tree.free (); 63 | this.extractor = new DataExtractor (text); 64 | this.sheet.visit (this.extractor); 65 | } 66 | var p = new Json.Parser (); 67 | p.load_from_data (load_colors ()); 68 | this.color_docs = p.get_root ().get_object (); 69 | p = new Json.Parser (); 70 | p.load_from_data (load_docs ()); 71 | var n = p.get_root ().get_object (); 72 | foreach (var k in n.get_members ()) { 73 | this.property_docs[k] = n.get_string_member (k); 74 | } 75 | p = new Json.Parser (); 76 | p.load_from_data (load_functions ()); 77 | this.function_docs = p.get_root ().get_object (); 78 | p = new Json.Parser (); 79 | p.load_from_data (load_selectors ()); 80 | this.selector_docs = p.get_root ().get_object (); 81 | this.enhanced_diags = new Diagnostic[0]; 82 | this.enhanced_diagnostics (); 83 | } 84 | 85 | void enhanced_diagnostics () { 86 | if (this.sheet == null) 87 | return; 88 | var diags = new Diagnostic[0]; 89 | foreach (var p in this.extractor.property_uses) { 90 | if (p.name == "animation-name") { 91 | var r = p.node; 92 | var name = r.value; 93 | if (name is Identifier) { 94 | var i = (Identifier) name; 95 | info ("Found property-reference called animation-name with a name %s", i.id); 96 | var found = false; 97 | foreach (var keyframe in this.extractor.keyframes) { 98 | if (keyframe.name == i.id) { 99 | found = true; 100 | break; 101 | } 102 | } 103 | if (!found) { 104 | diags += new Diagnostic () { 105 | range = r.value.range, 106 | severity = DiagnosticSeverity.Error, 107 | message = "Unknown animation name \'" + i.id + "\'", 108 | file = this.uri 109 | }; 110 | } 111 | } 112 | } 113 | } 114 | this.enhanced_diags = diags; 115 | } 116 | 117 | internal DocumentSymbol[] symbols () { 118 | var r = new DocumentSymbol[0]; 119 | if (this.extractor == null) 120 | return r; 121 | foreach (var color in this.extractor.colors.get_keys ()) { 122 | var p = this.extractor.colors[color]; 123 | r += new DocumentSymbol () { 124 | name = color, 125 | kind = SymbolKind.Field, 126 | range = new Range () { 127 | start = p, 128 | end = p 129 | } 130 | }; 131 | } 132 | foreach (var kf in this.extractor.keyframes) { 133 | var range = kf.range; 134 | r += new DocumentSymbol () { 135 | name = "@keyframes " + kf.name, 136 | kind = SymbolKind.Field, 137 | range = range 138 | }; 139 | } 140 | return r; 141 | } 142 | 143 | internal Location ? find_declaration (Position p) { 144 | foreach (var ca in this.extractor.color_references) { 145 | if (ca.range.contains (p) && this.extractor.colors[ca.name] != null) { 146 | return new Location () { 147 | uri = this.uri, 148 | range = new Range () { 149 | start = this.extractor.colors[ca.name], 150 | end = this.extractor.colors[ca.name], 151 | } 152 | }; 153 | } 154 | } 155 | foreach (var pu in this.extractor.property_uses) { 156 | if (pu.name == "animation-name") { 157 | var r = pu.node; 158 | var name = r.value; 159 | if (name.range.contains (p) && name is Identifier) { 160 | var i = (Identifier) name; 161 | foreach (var keyframe in this.extractor.keyframes) { 162 | if (keyframe.name == i.id) { 163 | return new Location () { 164 | uri = this.uri, 165 | range = keyframe.range 166 | }; 167 | } 168 | } 169 | } 170 | } 171 | } 172 | return null; 173 | } 174 | 175 | internal Hover ? hover (uint line, uint character) { 176 | var p = new Position () { 177 | line = line, 178 | character = character 179 | }; 180 | var hover_response = new Hover (); 181 | hover_response.contents = new MarkupContent (); 182 | hover_response.contents.kind = "markdown"; 183 | foreach (var color in this.extractor.color_references) { 184 | if (color.range.contains (p)) { 185 | var c = extract_color_docs (color.name); 186 | if (c != null) { 187 | hover_response.range = color.range; 188 | hover_response.contents.value = c; 189 | return hover_response; 190 | } 191 | } 192 | } 193 | foreach (var pu in this.extractor.property_uses) { 194 | if (pu.range.contains (p)) { 195 | var v = this.property_docs[pu.name]; 196 | if (v != null) { 197 | hover_response.range = pu.range; 198 | hover_response.contents.value = v; 199 | return hover_response; 200 | } 201 | } 202 | } 203 | foreach (var c in this.extractor.calls) { 204 | if (c.range.contains (p)) { 205 | var v = this.function_docs.get_object_member (c.name); 206 | if (v != null) { 207 | hover_response.range = c.range; 208 | hover_response.contents.value = v.get_string_member ("docs"); 209 | return hover_response; 210 | } 211 | } 212 | } 213 | return null; 214 | } 215 | 216 | internal string ? extract_color_docs (string name) { 217 | foreach (var k in this.color_docs.get_members ()) { 218 | var obj = this.color_docs.get_object_member (k); 219 | var arr = obj.get_array_member ("colors"); 220 | for (var i = 0; i < arr.get_length (); i++) { 221 | var s = arr.get_string_element (i); 222 | if (s == "@" + name) 223 | return k + " colors:\n\n" + obj.get_string_member ("docs"); 224 | } 225 | } 226 | return null; 227 | } 228 | 229 | private bool is_color (string line, uint pos) { 230 | while (pos > 0) { 231 | if (line[pos] == '@') 232 | return true; 233 | if (line[pos] != '-' && !line[pos].isalnum () && line[pos] != '_') 234 | break; 235 | pos--; 236 | } 237 | return false; 238 | } 239 | 240 | private bool is_property (string line, uint pos) { 241 | var in_streak = false; 242 | while (pos > 0) { 243 | if (line[pos] == 0) { 244 | pos -= 1; 245 | continue; 246 | } 247 | // E.g. if there is: 248 | // prop-name 1px; 249 | // Without the streaks, it would match 250 | // With it, we would go till the space between prop-name and 1px 251 | if (in_streak && !line[pos].isspace ()) 252 | return false; 253 | if (line[pos].isspace ()) { 254 | if (!in_streak) { 255 | in_streak = true; 256 | } 257 | } 258 | if (!(line[pos].isspace () || line[pos] == '-' || line[pos].isalnum ())) 259 | return false; 260 | pos--; 261 | } 262 | return true; 263 | } 264 | 265 | private bool is_selector (string line, uint pos) { 266 | while (pos > 0 && line[pos].isspace ()) 267 | pos--; 268 | while (pos > 0) { 269 | if (line[pos] == ':') 270 | return true; 271 | if (!line[pos].isalpha () && line[pos] != '-') 272 | break; 273 | pos--; 274 | } 275 | return false; 276 | } 277 | 278 | private string ? fix_property (string line, uint pos, string old) { 279 | var old_pos = pos; 280 | while (pos > 0) { 281 | if (line[pos] == 0) { 282 | pos -= 1; 283 | continue; 284 | } 285 | if (line[pos] != '-' && !line[pos].isalnum ()) { 286 | var s = line.substring (pos + 1, old_pos - pos - 1); 287 | if (!old.has_prefix (s)) 288 | return null; 289 | var offset = old_pos - pos - 1; 290 | if (offset > old.length) 291 | return null; 292 | return old.substring (offset); 293 | } 294 | pos--; 295 | } 296 | return null; 297 | } 298 | 299 | internal CompletionItem[] complete (CompletionParams p) { 300 | if (p.position.line > this.lines.length) 301 | return new CompletionItem[0]; 302 | var l = this.lines[p.position.line]; 303 | if (p.position.character > l.length) 304 | return new CompletionItem[0]; 305 | var ret = new CompletionItem[0]; 306 | var c = p.position.character; 307 | if (c == 1 && l[0] == '@') { 308 | info ("Completing @define-color"); 309 | ret += new CompletionItem ("@define-color", "define-color ${1:name} ${2:color};$0"); 310 | } else if (c != 0 && (l[c - 1] == '@' || is_color (l, c))) { 311 | info ("Completing @color"); 312 | foreach (var color in this.extractor.colors.get_keys ()) { 313 | ret += new CompletionItem ("@" + color, color); 314 | } 315 | foreach (var k in this.color_docs.get_members ()) { 316 | var obj = this.color_docs.get_object_member (k); 317 | var arr = obj.get_array_member ("colors"); 318 | for (var i = 0; i < arr.get_length (); i++) { 319 | var color = arr.get_string_element (i); 320 | ret += new CompletionItem (color, color.substring (1)); 321 | } 322 | } 323 | } else if (is_property (l, c)) { 324 | info ("Completing properties"); 325 | foreach (var k in this.property_docs.get_keys ()) { 326 | // TODO: Add parameters to auto-completion. 327 | var fixed = fix_property (l, c, k); 328 | if (fixed == null) 329 | continue; 330 | ret += new CompletionItem (k, fixed + ": ${1:args};$0"); 331 | } 332 | } else if (l[c] == ')' && c > 4 && l[c - 1] == '(' && l[c - 2] == 'r' && l[c - 3] == 'i' && l[c - 4] == 'd') { 333 | info ("Completing :dir(ltr|rtl)"); 334 | ret += new CompletionItem ("ltr", "ltr"); 335 | ret += new CompletionItem ("rtl", "rtl"); 336 | } else if (l[c] == ')' && c > 5 && l[c - 1] == '(' && l[c - 2] == 'p' && l[c - 3] == 'o' && l[c - 4] == 'r' && l[c - 5] == 'd') { 337 | info ("Completing :drop(active)"); 338 | ret += new CompletionItem ("active", "active"); 339 | } else if (l[c] == ')' && c > 4 && l[c - 1] == '(' && l[c - 2] == 't' && l[c - 3] == 'o' && l[c - 4] == 'n') { 340 | info ("Completing :not(*)"); 341 | ret += new CompletionItem ("*", "*"); 342 | } else if (l.substring (0, c).has_suffix (":nth-last-child(") || l.substring (0, c).has_suffix (":nth-child(")) { 343 | info ("Completing :nth-(last-)child(even|odd)"); 344 | ret += new CompletionItem ("even", "even"); 345 | ret += new CompletionItem ("odd", "odd"); 346 | } else if (is_selector (l, c)) { 347 | info ("Completing selectors"); 348 | foreach (var sc in this.selector_docs.get_members ()) { 349 | var obj = this.selector_docs.get_object_member (sc); 350 | var is_func = obj.get_boolean_member ("function"); 351 | if (is_func) { 352 | ret += new CompletionItem (":" + sc + "()", sc + "()"); 353 | } else { 354 | ret += new CompletionItem (":" + sc, sc); 355 | } 356 | } 357 | } 358 | return ret; 359 | } 360 | } 361 | } 362 | -------------------------------------------------------------------------------- /src/properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "animation-delay": {}, 3 | "animation-direction": {}, 4 | "animation-duration": {}, 5 | "animation-fill-mode": {}, 6 | "animation-iteration-count": {}, 7 | "animation": {}, 8 | "animation-name": {}, 9 | "animation-play-state": {}, 10 | "animation-timing-function": {}, 11 | "background-blend-mode": {}, 12 | "background-clip": {}, 13 | "background-color": { 14 | "argList": [ 15 | [ 16 | { 17 | "type": "color", 18 | "name": "color", 19 | "docs": "The uniform color of the background" 20 | } 21 | ] 22 | ] 23 | }, 24 | "background-image": {}, 25 | "background": {}, 26 | "background-origin": {}, 27 | "background-position": {}, 28 | "background-repeat": {}, 29 | "background-size": {}, 30 | "border-bottom-color": {}, 31 | "border-bottom-left-radius": {}, 32 | "border-bottom": {}, 33 | "border-bottom-right-radius": {}, 34 | "border-bottom-style": {}, 35 | "border-bottom-width": {}, 36 | "border-color": {}, 37 | "border-image": {}, 38 | "border-image-repeat": {}, 39 | "border-image-slice": {}, 40 | "border-image-source": {}, 41 | "border-image-width": {}, 42 | "border-left-color": {}, 43 | "border-left": {}, 44 | "border-left-style": {}, 45 | "border-left-width": {}, 46 | "border": {}, 47 | "border-radius": {}, 48 | "border-right-color": {}, 49 | "border-right": {}, 50 | "border-right-style": {}, 51 | "border-right-width": {}, 52 | "border-spacing": {}, 53 | "border-style": {}, 54 | "border-top-color": {}, 55 | "border-top-left-radius": {}, 56 | "border-top": {}, 57 | "border-top-right-radius": {}, 58 | "border-top-style": {}, 59 | "border-top-width": {}, 60 | "border-width": {}, 61 | "box-shadow": {}, 62 | "caret-color": {}, 63 | "color": {}, 64 | "filter": {}, 65 | "font-family": {}, 66 | "font-feature-settings": {}, 67 | "font-kerning": {}, 68 | "font": {}, 69 | "font-size": {}, 70 | "font-stretch": {}, 71 | "font-style": {}, 72 | "font-variant-alternates": {}, 73 | "font-variant-caps": {}, 74 | "font-variant-east-asian": {}, 75 | "font-variant-ligatures": {}, 76 | "font-variant": {}, 77 | "font-variant-numeric": {}, 78 | "font-variant-position": {}, 79 | "font-variation-settings": {}, 80 | "font-weight": {}, 81 | "-gtk-dpi": {}, 82 | "-gtk-icon-filter": {}, 83 | "-gtk-icon-palette": {}, 84 | "-gtk-icon-shadow": {}, 85 | "-gtk-icon-size": {}, 86 | "-gtk-icon-source": {}, 87 | "-gtk-icon-style": {}, 88 | "-gtk-icon-transform": {}, 89 | "-gtk-secondary-caret-color": {}, 90 | "letter-spacing": {}, 91 | "line-height": {}, 92 | "margin-bottom": {}, 93 | "margin-left": {}, 94 | "margin": {}, 95 | "margin-right": {}, 96 | "margin-top": {}, 97 | "min-height": {}, 98 | "min-width": {}, 99 | "opacity": {}, 100 | "outline-color": {}, 101 | "outline": {}, 102 | "outline-offset": {}, 103 | "outline-style": {}, 104 | "outline-width": {}, 105 | "padding-bottom": { 106 | "argList": [ 107 | [ 108 | { 109 | "type": "length", 110 | "name": "length", 111 | "docs": "The size of the padding as a fixed value. Must be nonnegative." 112 | } 113 | ], 114 | [ 115 | { 116 | "type": "percentage", 117 | "name": "percentage", 118 | "docs": "The size of the padding as a percentage, relative to the inline size" 119 | } 120 | ], 121 | [ 122 | { 123 | "type": "constant", 124 | "values": [ 125 | "inherit", 126 | "initial", 127 | "unset" 128 | ] 129 | } 130 | ] 131 | ] 132 | }, 133 | "padding-left": {}, 134 | "padding": {}, 135 | "padding-right": {}, 136 | "padding-top": {}, 137 | "text-decoration-color": {}, 138 | "text-decoration-line": {}, 139 | "text-decoration": {}, 140 | "text-decoration-style": {}, 141 | "text-shadow": { 142 | "argList": [ 143 | [ 144 | { 145 | "type": "block", 146 | "repeating": true, 147 | "block": [ 148 | { 149 | "type": "color", 150 | "optional": true, 151 | "name": "color", 152 | "docs": "The color of the shadow. It can be specified either before or after the offset values." 153 | }, 154 | { 155 | "type": "length", 156 | "name": "offset-x", 157 | "docs": "Specifies the horizontal distance to the text" 158 | }, 159 | { 160 | "type": "length", 161 | "name": "offset-y", 162 | "docs": "Specifies the vertical distance to the text" 163 | }, 164 | { 165 | "type": "length", 166 | "name": "blur-radius", 167 | "docs": "The higher the value, the bigger the blur; the shadow becomes wider and lighter. If not specified, it defaults to 0." 168 | } 169 | ] 170 | } 171 | ], 172 | [ 173 | { 174 | "type": "constant", 175 | "values": [ 176 | "inherit", 177 | "initial", 178 | "unset", 179 | "none" 180 | ] 181 | } 182 | ] 183 | ] 184 | }, 185 | "text-transform": {}, 186 | "transform": {}, 187 | "transform-origin": {}, 188 | "transition-delay": {}, 189 | "transition-duration": {}, 190 | "transition": {}, 191 | "transition-property": {}, 192 | "transition-timing-function": {} 193 | } -------------------------------------------------------------------------------- /src/protocol.vala: -------------------------------------------------------------------------------- 1 | /* protocol.vala 2 | * 3 | * Copyright 2023 JCWasmx86 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * SPDX-License-Identifier: GPL-3.0-or-later 19 | */ 20 | 21 | namespace GtkCssLangServer { 22 | class InitializeParams : Object { 23 | public int processId { get; set; } 24 | public string? rootPath { get; set; } 25 | public string? rootUri { get; set; } 26 | public ClientCapabilities capabilities { get; set; default = new ClientCapabilities (); } 27 | } 28 | class ClientCapabilities : Object { 29 | public TextDocumentClientCapabilities textDocument { get; set; default = new TextDocumentClientCapabilities (); } 30 | } 31 | class TextDocumentClientCapabilities : Object { 32 | public DocumentSymbolCapabilities documentSymbol { get; set; default = new DocumentSymbolCapabilities (); } 33 | public RenameClientCapabilities rename { get; set; default = new RenameClientCapabilities (); } 34 | } 35 | class RenameClientCapabilities : Object { 36 | public bool prepareSupport { get; set; } 37 | } 38 | class DocumentSymbolCapabilities : Object { 39 | public bool hierarchicalDocumentSymbolSupport { get; set; } 40 | } 41 | class TextDocumentPositionParams : Object { 42 | public TextDocumentIdentifier textDocument { get; set; } 43 | public Position position { get; set; } 44 | } 45 | class TextDocumentIdentifier : Object { 46 | public string uri { get; set; } 47 | } 48 | public class Position : Object { 49 | public uint line { get; set; default = -1; } 50 | public uint character { get; set; default = -1; } 51 | 52 | public int compare_to (Position other) { 53 | return line > other.line ? 1 : 54 | (line == other.line ? 55 | (character > other.character ? 1 : 56 | (character == other.character ? 0 : -1)) : -1); 57 | } 58 | } 59 | 60 | class DocumentSymbolParams : Object { 61 | public TextDocumentIdentifier textDocument { get; set; } 62 | } 63 | 64 | class DocumentSymbol : Object { 65 | public string? name { get; set; } 66 | public string? detail { get; set; } 67 | public int kind { get; set; } 68 | public bool deprecated { get; set; } 69 | public Range range { get; set; } 70 | } 71 | 72 | [CCode (default_value = "GTK_CSS_LANG_SERVER_SYMBOL_KIND_Variable")] 73 | enum SymbolKind { 74 | File = 1, 75 | Module = 2, 76 | Namespace = 3, 77 | Package = 4, 78 | Class = 5, 79 | Method = 6, 80 | Property = 7, 81 | Field = 8, 82 | Constructor = 9, 83 | Enum = 10, 84 | Interface = 11, 85 | Function = 12, 86 | Variable = 13, 87 | Constant = 14, 88 | String = 15, 89 | Number = 16, 90 | Boolean = 17, 91 | Array = 18, 92 | Object = 19, 93 | Key = 20, 94 | Null = 21, 95 | EnumMember = 22, 96 | Struct = 23, 97 | Event = 24, 98 | Operator = 25, 99 | TypeParameter = 26 100 | } 101 | public class Range : Object { 102 | public Position start { get; set; } 103 | public Position end { get; set; } 104 | 105 | public bool contains (Position pos) { 106 | return start.compare_to (pos) <= 0 && pos.compare_to (end) <= 0; 107 | } 108 | } 109 | 110 | class Location : Object { 111 | public string uri { get; set; } 112 | public Range range { get; set; } 113 | } 114 | 115 | class TextDocumentContentChangeEvent : Object { 116 | public Range? range { get; set; } 117 | public int rangeLength { get; set; } 118 | public string text { get; set; } 119 | } 120 | 121 | class MarkupContent : Object { 122 | public string kind { get; set; } 123 | public string value { get; set; } 124 | } 125 | 126 | class Hover : Object { 127 | public MarkupContent contents { get; set; } 128 | public Range range { get; set; } 129 | } 130 | 131 | class Diagnostic : Object { 132 | public Range range { get; set; } 133 | public DiagnosticSeverity severity { get; set; } 134 | public string? code { get; set; } 135 | public string? source { get; set; } 136 | public string message { get; set; } 137 | public string file { get; set; } 138 | } 139 | enum DiagnosticSeverity { 140 | Unset = 0, 141 | Error = 1, 142 | Warning = 2, 143 | Information = 3, 144 | Hint = 4 145 | } 146 | 147 | [CCode (default_value = "GTK_CSS_LANG_SERVER_COMPLETION_TRIGGER_KIND_Invoked")] 148 | enum CompletionTriggerKind { 149 | Invoked = 1, 150 | TriggerCharacter = 2, 151 | TriggerForIncompleteCompletions = 3 152 | } 153 | 154 | class CompletionContext : Object { 155 | public CompletionTriggerKind triggerKind { get; set; } 156 | public string? triggerCharacter { get; set; } 157 | } 158 | 159 | class CompletionParams : TextDocumentPositionParams { 160 | public CompletionContext? context { get; set; } 161 | } 162 | 163 | enum CompletionItemTag { 164 | Deprecated = 1, 165 | } 166 | 167 | [CCode (default_value = "GTK_CSS_LANG_SERVER_INSERT_TEXT_FORMAT_PlainText")] 168 | enum InsertTextFormat { 169 | PlainText = 1, 170 | Snippet = 2, 171 | } 172 | 173 | class CompletionItem : Object { 174 | public string label { get; set; } 175 | public CompletionItemKind kind { get; set; } 176 | public string detail { get; set; } 177 | public string insertText { get; set; } 178 | public InsertTextFormat insertTextFormat { get; set; default = InsertTextFormat.PlainText; } 179 | 180 | public CompletionItem (string label, string insert_text) { 181 | this.label = label; 182 | this.kind = CompletionItemKind.Keyword; 183 | this.insertText = insert_text; 184 | this.insertTextFormat = InsertTextFormat.Snippet; 185 | } 186 | } 187 | 188 | class DocumentRangeFormattingParams : Object { 189 | public TextDocumentIdentifier textDocument { get; set; } 190 | public Range? range { get; set; } 191 | public FormattingOptions options { get; set; } 192 | } 193 | 194 | 195 | class FormattingOptions : Object { 196 | public uint tabSize { get; set; } 197 | public bool insertSpaces { get; set; } 198 | public bool trimTrailingWhitespace { get; set; } 199 | public bool insertFinalNewline { get; set; } 200 | public bool trimFinalNewlines { get; set; } 201 | } 202 | 203 | class TextEdit : Object { 204 | public Range range { get; set; } 205 | public string newText { get; set; } 206 | 207 | public TextEdit (Range range, string new_text = "") { 208 | this.range = range; 209 | this.newText = new_text; 210 | } 211 | } 212 | 213 | [CCode (default_value = "GTK_CSS_LANG_SERVER_COMPLETION_ITEM_KIND_Text")] 214 | enum CompletionItemKind { 215 | Text = 1, 216 | Method = 2, 217 | Function = 3, 218 | Constructor = 4, 219 | Field = 5, 220 | Variable = 6, 221 | Class = 7, 222 | Interface = 8, 223 | Module = 9, 224 | Property = 10, 225 | Unit = 11, 226 | Value = 12, 227 | Enum = 13, 228 | Keyword = 14, 229 | Snippet = 15, 230 | Color = 16, 231 | File = 17, 232 | Reference = 18, 233 | Folder = 19, 234 | EnumMember = 20, 235 | Constant = 21, 236 | Struct = 22, 237 | Event = 23, 238 | Operator = 24, 239 | TypeParameter = 25 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /src/selectors.json: -------------------------------------------------------------------------------- 1 | { 2 | "first-child": { 3 | "function": false, 4 | "docs": "The `:first-child` CSS pseudo-class represents the first element among a group of sibling elements." 5 | }, 6 | "last-child": { 7 | "function": false, 8 | "docs": "The `:last-child` CSS pseudo-class represents the last element among a group of sibling elements." 9 | }, 10 | "only-child": { 11 | "function": false, 12 | "docs": "The `:only-child` CSS pseudo-class represents an element without any siblings. This is the same as `:first-child:last-child` or `:nth-child(1):nth-last-child(1)`, but with a lower specificity." 13 | }, 14 | "active": { 15 | "function": false, 16 | "docs": "The `:active` CSS pseudo-class represents an element (such as a button) that is being activated by the user. When using a mouse, \"activation\" typically starts when the user presses down the primary mouse button." 17 | }, 18 | "hover": { 19 | "function": false, 20 | "docs": "The `:hover` CSS pseudo-class matches when the user interacts with an element with a pointing device, but does not necessarily activate it. It is generally triggered when the user hovers over an element with the cursor (mouse pointer)." 21 | }, 22 | "selected": { 23 | "function": false, 24 | "docs": "..." 25 | }, 26 | "disabled": { 27 | "function": false, 28 | "docs": "The `:disabled` CSS pseudo-class represents any disabled element. An element is disabled if it can't be activated (selected, clicked on, typed into, etc.) or accept focus. The element also has an enabled state, in which it can be activated or accept focus." 29 | }, 30 | "indeterminate": { 31 | "function": false, 32 | "docs": "The `:indeterminate` CSS pseudo-class represents any form element whose state is indeterminate." 33 | }, 34 | "focus": { 35 | "function": false, 36 | "docs": "The `:focus` CSS pseudo-class represents an element (such as a form input) that has received focus. It is generally triggered when the user clicks or taps on an element or selects it with the keyboard's Tab key." 37 | }, 38 | "backdrop": { 39 | "function": false, 40 | "docs": "..." 41 | }, 42 | "link": { 43 | "function": false, 44 | "docs": "The `:link` CSS pseudo-class represents an element that has not yet been visited." 45 | }, 46 | "visited": { 47 | "function": false, 48 | "docs": "The `:visited` CSS pseudo-class applies once the link has been visited by the user." 49 | }, 50 | "checked": { 51 | "function": false, 52 | "docs": "The `:checked` CSS pseudo-class selector represents any radio-button or checkbox in a element that is checked or toggled to an `on` state." 53 | }, 54 | "focus-visible": { 55 | "function": false, 56 | "docs": "The `:focus-visible` pseudo-class applies while an element matches the `:focus` pseudo-class" 57 | }, 58 | "focus-within": { 59 | "function": false, 60 | "docs": "The `:focus-within` CSS pseudo-class matches an element if the element or any of its descendants are focused. In other words, it represents an element that is itself matched by the `:focus` pseudo-class or has a descendant that is matched by `:focus`." 61 | }, 62 | "nth-child": { 63 | "function": true, 64 | "docs": "The `:nth-child()` CSS pseudo-class matches elements based on their position among a group of siblings.", 65 | "argList": [ 66 | [ 67 | { 68 | "type": "constant", 69 | "values": [ 70 | "even", 71 | "odd" 72 | ] 73 | } 74 | ] 75 | ] 76 | }, 77 | "nth-last-child": { 78 | "function": true, 79 | "docs": "The `:nth-last-child()` CSS pseudo-class matches elements based on their position among a group of siblings, counting from the end.", 80 | "argList": [ 81 | [ 82 | { 83 | "type": "constant", 84 | "values": [ 85 | "even", 86 | "odd" 87 | ] 88 | } 89 | ] 90 | ] 91 | }, 92 | "not": { 93 | "function": true, 94 | "docs": "The `:not()` CSS pseudo-class represents elements that do not match a list of selectors. Since it prevents specific items from being selected, it is known as the negation pseudo-class.", 95 | "argList": [ 96 | [ 97 | { 98 | "type": "constant", 99 | "values": [ 100 | "*" 101 | ] 102 | } 103 | ] 104 | ] 105 | }, 106 | "dir": { 107 | "function": true, 108 | "docs": "The `:dir()` CSS pseudo-class matches elements based on the directionality of the text contained in them.", 109 | "argList": [ 110 | [ 111 | { 112 | "type": "constant", 113 | "values": [ 114 | "ltr", 115 | "rtl" 116 | ] 117 | } 118 | ] 119 | ] 120 | }, 121 | "drop": { 122 | "function": true, 123 | "docs": "...", 124 | "argList": [ 125 | [ 126 | { 127 | "type": "constant", 128 | "values": [ 129 | "active" 130 | ] 131 | } 132 | ] 133 | ] 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/util.vala: -------------------------------------------------------------------------------- 1 | /* util.vala 2 | * 3 | * Copyright 2023 JCWasmx86 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * SPDX-License-Identifier: GPL-3.0-or-later 19 | */ 20 | 21 | namespace GtkCssLangServer { 22 | internal class Util { 23 | internal static T ? parse_variant (Variant variant) { 24 | var json = Json.gvariant_serialize (variant); 25 | return Json.gobject_deserialize (typeof (T), json); 26 | } 27 | 28 | internal static Variant object_to_variant (Object object) throws Error { 29 | var json = Json.gobject_serialize (object); 30 | return Json.gvariant_deserialize (json, null); 31 | } 32 | 33 | public static uint count_chars_in_string (string str, char character, out int last_char_pos = null) { 34 | uint count = 0; 35 | last_char_pos = -1; 36 | for (int i = 0; i < str.length; i++) { 37 | if (str[i] == character) { 38 | count++; 39 | last_char_pos = i; 40 | } 41 | } 42 | return count; 43 | } 44 | } 45 | internal class IIdentifier { 46 | internal string name; 47 | internal Range range; 48 | 49 | internal IIdentifier (string name, Range range) { 50 | this.name = name; 51 | this.range = range; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /vapi/ts.vapi: -------------------------------------------------------------------------------- 1 | [CCode (cheader_filename = "tree_sitter/api.h,tree_sitter/parser.h")] 2 | namespace TreeSitter { 3 | [CCode (cname = "tree_sitter_css")] 4 | public static TSLanguage tree_sitter_css (); 5 | 6 | [Compact] 7 | [CCode (has_type_id = false, cname = "TSLanguage", free_function = "")] 8 | public class TSLanguage { 9 | 10 | } 11 | 12 | [Compact] 13 | [CCode (has_type_id = false, cname = "TSParser", free_function = "ts_parser_delete")] 14 | public class TSParser { 15 | [CCode (cname = "ts_parser_new")] 16 | public TSParser (); 17 | 18 | [CCode (cname = "ts_parser_set_language")] 19 | public void set_language (TSLanguage tsl); 20 | 21 | [CCode (cname = "ts_parser_parse_string")] 22 | public TSTree? parse_string (TSTree? old_tree, string s, uint32 length); 23 | } 24 | 25 | [Compact] 26 | [CCode (has_type_id = false, cname = "TSTree", free_function = "")] 27 | public class TSTree { 28 | [CCode (cname = "ts_tree_root_node")] 29 | public TSNode root_node (); 30 | 31 | [CCode (cname = "ts_tree_delete")] 32 | public void free (); 33 | } 34 | 35 | [SimpleType] 36 | [CCode (has_type_id = false, cname = "TSNode", free_function = "")] 37 | public struct TSNode { 38 | [CCode (cname = "ts_node_named_child")] 39 | public TSNode named_child (uint index); 40 | 41 | [CCode (cname = "ts_node_child")] 42 | public TSNode child (uint index); 43 | 44 | [CCode (cname = "ts_node_type")] 45 | public unowned string type (); 46 | 47 | [CCode (cname = "ts_node_string")] 48 | public string to_string (); 49 | 50 | [CCode (cname = "ts_node_named_child_count")] 51 | public uint32 named_child_count (); 52 | 53 | [CCode (cname = "ts_node_child_count")] 54 | public uint32 child_count (); 55 | 56 | [CCode (cname = "ts_node_start_byte")] 57 | public uint32 start_byte (); 58 | 59 | [CCode (cname = "ts_node_end_byte")] 60 | public uint32 end_byte (); 61 | 62 | [CCode (cname = "ts_node_start_point")] 63 | public TSPoint start_point (); 64 | 65 | [CCode (cname = "ts_node_end_point")] 66 | public TSPoint end_point (); 67 | } 68 | 69 | [SimpleType] 70 | [CCode (has_type_id = false, cname = "TSPoint", free_function = "")] 71 | public struct TSPoint { 72 | public uint32 row; 73 | public uint32 column; 74 | } 75 | } 76 | --------------------------------------------------------------------------------