Skip to content

Commit

Permalink
feat(ts): Add support for elixir
Browse files Browse the repository at this point in the history
  • Loading branch information
stevearc committed Jun 27, 2022
1 parent 17773f8 commit 897d4bd
Show file tree
Hide file tree
Showing 7 changed files with 287 additions and 1 deletion.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ automatically fetch symbols from treesitter.
- c_sharp
- cpp
- dart
- elixir
- go
- java
- javascript
Expand Down
34 changes: 34 additions & 0 deletions lua/aerial/backends/treesitter/extensions.lua
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,40 @@ local function set_end_range(bufnr, items, last_line)
end
end

M.elixir = {
kind_map = {
callback = "Function",
def = "Function",
defguard = "Function",
defimpl = "Class",
defmacro = "Function",
defmacrop = "Function",
defmodule = "Module",
defp = "Function",
defprotocol = "Interface",
defstruct = "Struct",
module_attribute = "Field",
spec = "TypeParameter",
},
postprocess = function(bufnr, item, match)
local identifier = (utils.get_at_path(match, "identifier") or {}).node
if identifier then
local name = get_node_text(identifier, bufnr) or "<parse error>"
item.kind = M.elixir.kind_map[name] or item.kind
if name == "callback" and item.parent then
item.parent.kind = "Interface"
elseif name == "defstruct" and item.parent then
item.parent.kind = "Struct"
return false
elseif name == "defimpl" then
local protocol = (utils.get_at_path(match, "protocol") or {}).node
local protocol_name = get_node_text(protocol, bufnr) or "<parse error>"
item.name = string.format("%s > %s", item.name, protocol_name)
end
end
end,
}

M.markdown = {
get_parent = function(stack, match, node)
local level_node = (utils.get_at_path(match, "level") or {}).node
Expand Down
4 changes: 3 additions & 1 deletion lua/aerial/backends/treesitter/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,9 @@ M.fetch_symbols_sync = function(bufnr)
col = col,
end_col = end_col,
}
ext.postprocess(bufnr, item, match)
if ext.postprocess(bufnr, item, match) == false then
goto continue
end
if item.parent then
if not item.parent.children then
item.parent.children = {}
Expand Down
3 changes: 3 additions & 0 deletions lua/aerial/backends/treesitter/language_kind_map.lua
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ return {
method_signature = "Method",
enum_declaration = "Enum",
},
elixir = {
call = "Function",
},
go = {
function_declaration = "Function",
method_declaration = "Method",
Expand Down
38 changes: 38 additions & 0 deletions queries/elixir/aerial.scm
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
(call
target: (identifier) @identifier (#any-of? @identifier "defmodule" "defprotocol")
(arguments) @name) @type

(call
target: (identifier) @identifier (#eq? @identifier "defimpl")
(arguments
(alias) @protocol
(keywords (pair
key: (keyword) @kw (#match? @kw "^for:")
value: (alias) @name))
)) @type

(call
target: (identifier) @identifier (#any-of? @identifier "def" "defp" "defguard" "defmacro" "defmacrop")
(arguments [
(call target: (identifier) @name)
(binary_operator left: (call target: (identifier) @name))
])) @type

(unary_operator
operand: (call
target: (identifier) @identifier (#any-of? @identifier "callback" "spec")
(arguments [
(call target: (identifier) @name)
(binary_operator left: (call target: (identifier) @name))
])) @type) @start

(unary_operator
operand: (call
target: (identifier) @identifier (#eq? @identifier "module_attribute")
(arguments) @name
) @type
) @start

(do_block
(call
target: (identifier) @identifier (#eq? @identifier "defstruct")) @type) @start
169 changes: 169 additions & 0 deletions tests/treesitter/elixir_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
local util = require("tests.test_util")

describe("treesitter elixir", function()
it("parses all symbols correctly", function()
util.test_file_symbols("treesitter", "./tests/treesitter/elixir_test.exs", {
{
kind = "Module",
name = "Example.Module",
level = 0,
lnum = 1,
col = 0,
end_lnum = 19,
end_col = 3,
children = {
{
kind = "Field",
name = ":value",
level = 1,
lnum = 2,
col = 2,
end_lnum = 2,
end_col = 26,
},
{
kind = "Function",
name = "public_function",
level = 1,
lnum = 4,
col = 2,
end_lnum = 5,
end_col = 5,
},
{
kind = "Function",
name = "private_function",
level = 1,
lnum = 7,
col = 2,
end_lnum = 8,
end_col = 5,
},
{
kind = "Function",
name = "public_guard",
level = 1,
lnum = 10,
col = 2,
end_lnum = 10,
end_col = 42,
},
{
kind = "Function",
name = "private_guard",
level = 1,
lnum = 12,
col = 2,
end_lnum = 12,
end_col = 43,
},
{
kind = "Function",
name = "public_macro",
level = 1,
lnum = 14,
col = 2,
end_lnum = 15,
end_col = 5,
},
{
kind = "Function",
name = "private_macro",
level = 1,
lnum = 17,
col = 2,
end_lnum = 18,
end_col = 5,
},
},
},
{
kind = "Interface",
name = "Example.Behaviour",
level = 0,
lnum = 21,
col = 0,
end_lnum = 23,
end_col = 3,
children = {
{
kind = "Function",
name = "example_function",
level = 1,
lnum = 22,
col = 2,
end_lnum = 22,
end_col = 46,
},
},
},
{
kind = "Struct",
name = "Example.Struct",
level = 0,
lnum = 25,
col = 0,
end_lnum = 27,
end_col = 3,
},
{
kind = "Interface",
name = "Example.Protocol",
level = 0,
lnum = 29,
col = 0,
end_lnum = 32,
end_col = 3,
children = {
{
kind = "TypeParameter",
name = "public_function_head",
level = 1,
lnum = 30,
col = 2,
end_lnum = 30,
end_col = 50,
},
{
kind = "Function",
name = "public_function_head",
level = 1,
lnum = 31,
col = 2,
end_lnum = 31,
end_col = 39,
},
},
},
{
kind = "Class",
name = "Map > Example.Protocol",
level = 0,
lnum = 34,
col = 0,
end_lnum = 39,
end_col = 3,
children = {
{
kind = "TypeParameter",
name = "public_function_head",
level = 1,
lnum = 35,
col = 2,
end_lnum = 35,
end_col = 56,
},
{
kind = "Function",
name = "public_function_head",
level = 1,
lnum = 36,
col = 2,
end_lnum = 38,
end_col = 5,
},
},
},
})
end)
end)
39 changes: 39 additions & 0 deletions tests/treesitter/elixir_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
defmodule Example.Module do
@module_attribute :value

def public_function() do
end

defp private_function() do
end

defguard public_guard(x) when is_atom(x)

defguard private_guard(x) when is_atom(x)

defmacro public_macro() do
end

defmacrop private_macro() do
end
end

defmodule Example.Behaviour do
@callback example_function(atom()) :: atom()
end

defmodule Example.Struct do
defstruct name: nil, age: nil
end

defprotocol Example.Protocol do
@spec public_function_head(t, atom()) :: boolean
def public_function_head(target, opt)
end

defimpl Example.Protocol, for: Map do
@spec public_function_head(Map.t(), atom()) :: boolean
def public_function_head(target, opt) do
true
end
end

0 comments on commit 897d4bd

Please sign in to comment.