diff options
| author | Riley Bruins <ribru17@hotmail.com> | 2024-11-01 13:34:02 -0700 |
|---|---|---|
| committer | Christian Clason <ch.clason+github@icloud.com> | 2024-11-04 08:36:41 +0100 |
| commit | 8fadb18430df7025cad37f2b6b5138fe00eea545 (patch) | |
| tree | 59ac3e41f3723350fca380033794b866b7066ac9 | |
| parent | fix(cooklang): apply correct filetype (diff) | |
| download | nvim-treesitter-8fadb18430df7025cad37f2b6b5138fe00eea545.tar nvim-treesitter-8fadb18430df7025cad37f2b6b5138fe00eea545.tar.gz nvim-treesitter-8fadb18430df7025cad37f2b6b5138fe00eea545.tar.bz2 nvim-treesitter-8fadb18430df7025cad37f2b6b5138fe00eea545.tar.lz nvim-treesitter-8fadb18430df7025cad37f2b6b5138fe00eea545.tar.xz nvim-treesitter-8fadb18430df7025cad37f2b6b5138fe00eea545.tar.zst nvim-treesitter-8fadb18430df7025cad37f2b6b5138fe00eea545.zip | |
feat: sway programming language
| -rw-r--r-- | lockfile.json | 3 | ||||
| -rw-r--r-- | lua/nvim-treesitter/parsers.lua | 8 | ||||
| -rw-r--r-- | queries/sway/folds.scm | 20 | ||||
| -rw-r--r-- | queries/sway/highlights.scm | 336 | ||||
| -rw-r--r-- | queries/sway/indents.scm | 95 | ||||
| -rw-r--r-- | queries/sway/injections.scm | 5 | ||||
| -rw-r--r-- | queries/sway/locals.scm | 16 | ||||
| -rw-r--r-- | tests/indent/sway/main.sw | 345 | ||||
| -rw-r--r-- | tests/indent/sway_spec.lua | 20 |
9 files changed, 848 insertions, 0 deletions
diff --git a/lockfile.json b/lockfile.json index 35bc2ec8e..689451739 100644 --- a/lockfile.json +++ b/lockfile.json @@ -746,6 +746,9 @@ "svelte": { "revision": "ae5199db47757f785e43a14b332118a5474de1a2" }, + "sway": { + "revision": "03d97aad336ecc6b302f23bdd9b695ddc937160b" + }, "swift": { "revision": "5098007f58f4663a5613b2fecb6b866e3d41e37b" }, diff --git a/lua/nvim-treesitter/parsers.lua b/lua/nvim-treesitter/parsers.lua index 8fc5e555e..cd9b42cc9 100644 --- a/lua/nvim-treesitter/parsers.lua +++ b/lua/nvim-treesitter/parsers.lua @@ -2164,6 +2164,14 @@ list.svelte = { maintainers = { "@amaanq" }, } +list.sway = { + install_info = { + url = "https://github.com/FuelLabs/tree-sitter-sway.git", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@ribru17" }, +} + list.swift = { install_info = { url = "https://github.com/alex-pinkus/tree-sitter-swift", diff --git a/queries/sway/folds.scm b/queries/sway/folds.scm new file mode 100644 index 000000000..ba8474aa2 --- /dev/null +++ b/queries/sway/folds.scm @@ -0,0 +1,20 @@ +[ + (mod_item) + (function_item) + (struct_item) + (trait_item) + (enum_item) + (impl_item) + (type_item) + (const_item) + (let_declaration) + (for_expression) + (while_expression) + (if_expression) + (match_expression) + (call_expression) + (array_expression) + (attribute_item) + (block) + (use_declaration)+ +] @fold diff --git a/queries/sway/highlights.scm b/queries/sway/highlights.scm new file mode 100644 index 000000000..e408464fe --- /dev/null +++ b/queries/sway/highlights.scm @@ -0,0 +1,336 @@ +(type_identifier) @type + +(identifier) @variable + +(field_identifier) @variable.member + +(escape_sequence) @string.escape + +(primitive_type) @type.builtin + +(boolean_literal) @boolean + +(integer_literal) @number + +(float_literal) @number.float + +(char_literal) @character + +; ------- +; Paths +; ------- +(use_declaration + argument: (identifier) @module) + +(use_wildcard + (identifier) @module) + +(mod_item + name: (identifier) @module) + +(scoped_use_list + path: (identifier)? @module) + +(use_list + (identifier) @module) + +(use_as_clause + path: (identifier)? @module + alias: (identifier) @module) + +; --- +; Remaining Paths +; --- +(scoped_identifier + path: (identifier)? @module + name: (identifier) @module) + +(scoped_type_identifier + path: (identifier) @module) + +[ + "*" + "'" + "->" + "=>" + "<=" + "=" + "==" + "!" + "!=" + "%" + "%=" + "&" + "&=" + "&&" + "|" + "|=" + "||" + "^" + "^=" + "*" + "*=" + "-" + "-=" + "+" + "+=" + "/" + "/=" + ">" + "<" + ">=" + ">>" + "<<" + ">>=" + "<<=" + "@" + ".." + "..=" + "'" + "?" +] @operator + +(use_wildcard + "*" @character.special) + +[ + (string_literal) + (raw_string_literal) +] @string + +[ + (line_comment) + (block_comment) +] @comment + +; --- +; Extraneous +; --- +(self) @variable.builtin + +(enum_variant + (identifier) @constant) + +(field_initializer + (field_identifier) @variable.member) + +(shorthand_field_initializer + (identifier) @variable.member) + +(shorthand_field_identifier) @variable.member + +(loop_label + "'" @label + (identifier) @label) + +; --- +; Punctuation +; --- +[ + "::" + ":" + "." + ";" + "," +] @punctuation.delimiter + +[ + "(" + ")" + "[" + "]" + "{" + "}" + "#" +] @punctuation.bracket + +(type_arguments + [ + "<" + ">" + ] @punctuation.bracket) + +(type_parameters + [ + "<" + ">" + ] @punctuation.bracket) + +(closure_parameters + "|" @punctuation.bracket) + +(let_declaration + pattern: [ + (identifier) @variable + (tuple_pattern + (identifier) @variable) + ]) + +; It needs to be anonymous to not conflict with `call_expression` further below. +(_ + value: (field_expression + value: (identifier)? @variable + field: (field_identifier) @variable.member)) + +(parameter + pattern: (identifier) @variable.parameter) + +(parameter + pattern: (ref_pattern + [ + (mut_pattern + (identifier) @variable.parameter) + (identifier) @variable.parameter + ])) + +(closure_parameters + (identifier) @variable.parameter) + +(for_expression + "for" @keyword.repeat) + +"in" @keyword.repeat + +[ + "match" + "if" + "else" +] @keyword.conditional + +"while" @keyword.repeat + +[ + "break" + "continue" + "return" + "yield" +] @keyword.return + +"use" @keyword.import + +(mod_item + "mod" @keyword.import + !body) + +(use_as_clause + "as" @keyword.import) + +(type_cast_expression + "as" @keyword.operator) + +[ + "as" + "mod" + "abi" + "impl" + "where" + "trait" + "for" + "let" + "contract" + "script" + "predicate" + "library" +] @keyword + +[ + "struct" + "enum" + "storage" + "configurable" + "type" +] @keyword.type + +[ + "fn" + "abi" +] @keyword.function + +[ + (mutable_specifier) + "const" + "ref" + "deref" + "move" + "pub" +] @keyword.modifier + +(reference_type + "&" @keyword.modifier) + +(self_parameter + "&" @keyword.modifier) + +; ------- +; Guess Other Types +; ------- +((identifier) @constant + (#lua-match? @constant "^[A-Z][A-Z%d_]*$")) + +; --- +; PascalCase identifiers in call_expressions (e.g. `Ok()`) +; are assumed to be enum constructors. +; --- +(call_expression + function: [ + ((identifier) @constant + (#lua-match? @constant "^[A-Z]")) + (scoped_identifier + name: ((identifier) @constant + (#lua-match? @constant "^[A-Z]"))) + ]) + +; --- +; Assume that types in match arms are enums and not +; tuple structs. Same for `if let` expressions. +; --- +(match_pattern + (scoped_identifier + name: (identifier) @constructor)) + +(tuple_struct_pattern + type: [ + (identifier) @constructor + (scoped_identifier + name: (identifier) @constructor) + ]) + +(struct_pattern + type: [ + (type_identifier) @constructor + (scoped_type_identifier + name: (type_identifier) @constructor) + ]) + +; --- +; Other PascalCase identifiers are assumed to be structs. +; --- +((identifier) @type + (#lua-match? @type "^[A-Z]")) + +; ------- +; Functions +; ------- +(call_expression + function: [ + (identifier) @function.call + (scoped_identifier + name: (identifier) @function.call) + (field_expression + field: (field_identifier) @function.method.call) + ]) + +(generic_function + function: [ + (identifier) @function.call + (scoped_identifier + name: (identifier) @function.call) + (field_expression + field: (field_identifier) @function.method.call) + ]) + +(function_item + name: (identifier) @function) + +(function_signature_item + name: (identifier) @function) diff --git a/queries/sway/indents.scm b/queries/sway/indents.scm new file mode 100644 index 000000000..151d8b20b --- /dev/null +++ b/queries/sway/indents.scm @@ -0,0 +1,95 @@ +[ + (mod_item) + (struct_item) + (enum_item) + (impl_item) + (struct_expression) + (struct_pattern) + (tuple_struct_pattern) + (tuple_expression) + (tuple_type) + (tuple_pattern) + (match_block) + (call_expression) + (asm_block) + (asm_parameters) + (assignment_expression) + (arguments) + (block) + (where_clause) + (use_list) + (array_expression) + (ordered_field_declaration_list) + (field_declaration_list) + (enum_variant_list) + (parameters) + (token_tree) +] @indent.begin + +(trait_item + body: (_) @indent.begin) + +(string_literal + (escape_sequence)) @indent.begin + +(block + "}" @indent.end) + +(asm_block + "}" @indent.end) + +(enum_item + body: (enum_variant_list + "}" @indent.end)) + +(impl_item + body: (declaration_list + "}" @indent.end)) + +(match_expression + body: (match_block + "}" @indent.end)) + +(struct_item + body: (field_declaration_list + "}" @indent.end)) + +(struct_expression + body: (field_initializer_list + "}" @indent.end)) + +(struct_pattern + "}" @indent.end) + +(tuple_struct_pattern + ")" @indent.end) + +(tuple_type + ")" @indent.end) + +(tuple_pattern + ")" @indent.end) + +(trait_item + body: (declaration_list + "}" @indent.end)) + +(impl_item + (where_clause) @indent.dedent) + +[ + "where" + ")" + "]" + "}" +] @indent.branch + +(impl_item + (declaration_list) @indent.branch) + +[ + (line_comment) + (string_literal) +] @indent.ignore + +(raw_string_literal) @indent.auto diff --git a/queries/sway/injections.scm b/queries/sway/injections.scm new file mode 100644 index 000000000..3cd6aac8e --- /dev/null +++ b/queries/sway/injections.scm @@ -0,0 +1,5 @@ +([ + (line_comment) + (block_comment) +] @injection.content + (#set! injection.language "comment")) diff --git a/queries/sway/locals.scm b/queries/sway/locals.scm new file mode 100644 index 000000000..5c2ade83a --- /dev/null +++ b/queries/sway/locals.scm @@ -0,0 +1,16 @@ +; Scopes +[ + (function_item) + (closure_expression) + (block) +] @local.scope + +; Definitions +(parameter + (identifier) @local.definition) + +(closure_parameters + (identifier) @local.definition) + +; References +(identifier) @local.reference diff --git a/tests/indent/sway/main.sw b/tests/indent/sway/main.sw new file mode 100644 index 000000000..d917895c3 --- /dev/null +++ b/tests/indent/sway/main.sw @@ -0,0 +1,345 @@ +library; + +use ::alloc::{alloc, realloc}; +use ::assert::assert; +use ::option::Option::{self, *}; +use ::convert::From; +use ::iterator::*; + +struct RawVec<T> { + ptr: raw_ptr, + cap: u64, +} + +pub fn tx_witness_data<T>(index: u64) -> Option<T> { + if index >= tx_witnesses_count() { + return None + } + + let length = match tx_witness_data_length(index) { + Some(len) => len, + None => return None, + }; + + if __is_reference_type::<T>() { + let witness_data_ptr = __gtf::<raw_ptr>(index, GTF_WITNESS_DATA); + let new_ptr = alloc_bytes(length); + witness_data_ptr.copy_bytes_to(new_ptr, length); + + Some(asm(ptr: new_ptr) { + ptr: T + }) + } else { + // u8 is the only value type that is less than 8 bytes and should be handled separately + if __size_of::<T>() == 1 { + Some(__gtf::<raw_ptr>(index, GTF_WITNESS_DATA).add::<u8>(7).read::<T>()) + } else { + Some(__gtf::<raw_ptr>(index, GTF_WITNESS_DATA).read::<T>()) + } + } +} + +impl<T> RawVec<T> { + pub fn new() -> Self { + Self { + ptr: alloc::<T>(0), + cap: 0, + } + } + + pub fn with_capacity(capacity: u64) -> Self { + Self { + ptr: alloc::<T>(capacity), + cap: capacity, + } + } + + pub fn ptr(self) -> raw_ptr { + self.ptr + } + + pub fn capacity(self) -> u64 { + self::cap() + } + + pub fn grow(ref mut self) { + let new_cap = if self.cap == 0 { 1 } else { 2 * self.cap }; + + self.ptr = realloc::<T>(self.ptr, self.cap, new_cap); + self.cap = new_cap; + } +} + +impl<T> From<raw_slice> for RawVec<T> { + fn from(slice: raw_slice) -> Self { + let cap = slice.len::<T>(); + let ptr = alloc::<T>(cap); + if cap > 0 { + slice.ptr().copy_to::<T>(ptr, cap); + } + Self { ptr, cap } + } +} + +pub struct Vec<T> { + buf: RawVec<T>, + len: u64, +} + +impl<T> Vec<T> { + pub fn new() -> Self { + 'hey: while true { + + } + Self { + buf: RawVec::new(), + len: 0, + } + } + + pub fn with_capacity(capacity: u64) -> Self { + Self { + buf: RawVec::with_capacity(capacity), + len: 0, + } + } + + pub fn push(ref mut self, value: T) { + // If there is insufficient capacity, grow the buffer. + if self.len == self.buf.capacity() { + self.buf.grow(); + }; + + // Get a pointer to the end of the buffer, where the new element will + // be inserted. + let end = self.buf.ptr().add::<T>(self.len); + + // Write `value` at pointer `end` + end.write::<T>(value); + + // Increment length. + self.len += 1; + } + + pub fn capacity(self) -> u64 { + self.buf.capacity() + } + + pub fn clear(ref mut self) { + self.len = 0; + } + + pub fn get(self, index: u64) -> Option<T> { + // First check that index is within bounds. + if self.len <= index { + return None; + }; + + // Get a pointer to the desired element using `index` + let ptr = self.buf.ptr().add::<T>(index); + + // Read from `ptr` + Some(ptr.read::<T>()) + } + + pub fn len(self) -> u64 { + self.len + } + + pub fn is_empty(self) -> bool { + self.len == 0 + } + + pub fn remove(ref mut self, index: u64) -> T { + assert(index < self.len); + + let buf_start = self.buf.ptr(); + + // Read the value at `index` + let ptr = buf_start.add::<T>(index); + let ret = ptr.read::<T>(); + + // Shift everything down to fill in that spot. + let mut i = index; + if self.len > 1 { + while i < self.len - 1 { + let ptr = buf_start.add::<T>(i); + ptr.add::<T>(1).copy_to::<T>(ptr, 1); + i += 1; + } + } + + // Decrease length. + self.len -= 1; + ret + } + + pub fn insert(ref mut self, index: u64, element: T) { + assert(index <= self.len); + + // If there is insufficient capacity, grow the buffer. + if self.len == self.buf.capacity() { + self.buf.grow(); + } + + let buf_start = self.buf.ptr(); + + // The spot to put the new value + let index_ptr = buf_start.add::<T>(index); + + // Shift everything over to make space. + let mut i = self.len; + while i > index { + let ptr = buf_start.add::<T>(i); + ptr.sub::<T>(1).copy_to::<T>(ptr, 1); + i -= 1; + } + + // Write `element` at pointer `index` + index_ptr.write::<T>(element); + + // Increment length. + self.len += 1; + } + + pub fn pop(ref mut self) -> Option<T> { + if self.len == 0 { + return None; + } + self.len -= 1; + Some(self.buf.ptr().add::<T>(self.len).read::<T>()) + } + + pub fn swap(ref mut self, element1_index: u64, element2_index: u64) { + assert(element1_index < self.len); + assert(element2_index < self.len); + + if element1_index == element2_index { + return; + } + + let element1_ptr = self.buf.ptr().add::<T>(element1_index); + let element2_ptr = self.buf.ptr().add::<T>(element2_index); + + let element1_val: T = element1_ptr.read::<T>(); + element2_ptr.copy_to::<T>(element1_ptr, 1); + element2_ptr.write::<T>(element1_val); + } + + pub fn set(ref mut self, index: u64, value: T) { + assert(index < self.len); + + let index_ptr = self.buf.ptr().add::<T>(index); + + index_ptr.write::<T>(value); + } + + pub fn iter(self) -> VecIter<T> { + VecIter { + values: self, + index: 0, + } + } + + pub fn ptr(self) -> raw_ptr { + self.buf.ptr() + } +} + +impl<T> AsRawSlice for Vec<T> { + fn as_raw_slice(self) -> raw_slice { + raw_slice::from_parts::<T>(self.buf.ptr(), self.len) + } +} + +impl<T> From<raw_slice> for Vec<T> { + fn from(slice: raw_slice) -> Self { + Self { + buf: RawVec::from(slice), + len: slice.len::<T>(), + } + } +} + +impl<T> From<Vec<T>> for raw_slice { + fn from(vec: Vec<T>) -> Self { + asm(ptr: (vec.ptr(), vec.len())) { + ptr: raw_slice + } + } + + pub fn sha256(self) -> b256 { + let mut result_buffer = b256::min(); + asm( + hash: result_buffer, + ptr: p, + bytes: p, + ) { + s256 hash ptr bytes; + hash: b256 + } + } +} + +impl<T> AbiEncode for Vec<T> +where + T: AbiEncode, +{ + fn abi_encode(self, buffer: Buffer) -> Buffer { + let len = self.len(); + let mut buffer = len.abi_encode(buffer); + + let mut i = 0; + while i < len { + let item = self.get(i).unwrap(); + buffer = item.abi_encode(buffer); + i += 1; + } + + buffer + } +} + +impl<T> AbiDecode for Vec<T> +where + T: AbiDecode, +{ + fn abi_decode(ref buffer: BufferReader) -> Vec<T> { + let len = u64::abi_decode(buffer); + + let mut v = Vec::with_capacity(len); + + let mut i = 0; + while i < len { + let item = T::abi_decode(buffer); + v.push(item); + i += 1; + } + + yield 5; + + v + } +} + +pub struct VecIter<T> { + values: Vec<T>, + index: u64, +} + +impl<T> Iterator for VecIter<T> { + type Item = T; + fn next(ref mut self) -> Option<Self::Item> { + if self.index >= self.values.len()? { + return None + } + + self.index += 1; + self.values.get(self.index - 1) + } +} + +fn hello<T>(hi: T) { + println("{}", hi); +} +// vim: ft=sway diff --git a/tests/indent/sway_spec.lua b/tests/indent/sway_spec.lua new file mode 100644 index 000000000..d37573ae9 --- /dev/null +++ b/tests/indent/sway_spec.lua @@ -0,0 +1,20 @@ +local Runner = require("tests.indent.common").Runner + +local run = Runner:new(it, "tests/indent/sway", { + tabstop = 4, + shiftwidth = 4, + softtabstop = 4, + expandtab = true, +}) + +describe("indent Sway:", function() + describe("whole file:", function() + run:whole_file(".", {}) + end) + describe("new line:", function() + run:new_line("main.sw", { on_line = 12, text = "const CONST: u32 = 2;", indent = 0 }) + run:new_line("main.sw", { on_line = 14, text = "let hi = 5;", indent = 4 }) + run:new_line("main.sw", { on_line = 15, text = "let hi = 5;", indent = 8 }) + run:new_line("main.sw", { on_line = 92, text = "let hi = 5;", indent = 12 }) + end) +end) |
