aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRiley Bruins <ribru17@hotmail.com>2024-11-01 13:34:02 -0700
committerChristian Clason <ch.clason+github@icloud.com>2024-11-04 08:36:41 +0100
commit8fadb18430df7025cad37f2b6b5138fe00eea545 (patch)
tree59ac3e41f3723350fca380033794b866b7066ac9
parentfix(cooklang): apply correct filetype (diff)
downloadnvim-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.json3
-rw-r--r--lua/nvim-treesitter/parsers.lua8
-rw-r--r--queries/sway/folds.scm20
-rw-r--r--queries/sway/highlights.scm336
-rw-r--r--queries/sway/indents.scm95
-rw-r--r--queries/sway/injections.scm5
-rw-r--r--queries/sway/locals.scm16
-rw-r--r--tests/indent/sway/main.sw345
-rw-r--r--tests/indent/sway_spec.lua20
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)