Skip to content
This repository was archived by the owner on Jul 7, 2022. It is now read-only.

Commit a89f265

Browse files
authored
Dart outline support (#8)
* Add Dart Outline support * Add documentation for dart outline
1 parent 873ec05 commit a89f265

File tree

3 files changed

+250
-0
lines changed

3 files changed

+250
-0
lines changed

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,13 @@ autocmd InsertLeave,BufEnter,BufWinEnter,TabEnter,BufWritePost *.rs :lua require
6363

6464
Check out the [example file](examples/dart/closing_labels.lua) for setup
6565

66+
## Outline (dartls)
67+
![outline](https://raw.githubusercontent.com/tjdevries/media.repo/b27a8366b460cac2629d5fdb81862e5bd1d0a553/lsp_extensions/dart-outline.png)
68+
69+
[Outline Documentation](https://github.com/dart-lang/sdk/blob/master/pkg/analysis_server/tool/lsp_spec/README.md#darttextdocumentpublishoutline-notification)
70+
71+
Check out the [example file](examples/dart/outline.lua) for setup
72+
6673
## Clips
6774

6875
- Showing Line Diagnostics: https://clips.twitch.tv/ProductiveBoxyPastaCoolStoryBro

examples/dart/outline.lua

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
-- First, register the callback with your LSP config. This minimal config shows
2+
-- what that looks like.
3+
local nvim_lsp = require('nvim_lsp')
4+
nvim_lsp.dartls.setup{
5+
init_options = {
6+
outline = true,
7+
},
8+
callbacks = {
9+
-- get_callback can be called with or without arguments
10+
['dart/textDocument/publishOutline'] = require('lsp_extensions.dart.outline').get_callback(),
11+
},
12+
}
13+
14+
-- Next, when you want to actually show the outline, you will need to call one
15+
-- of the display methods (either loclist() or custom().
16+
require('lsp_extensions.dart.outline').loclist({})
17+
18+
-- If you want to handle the display yourself you can use the `custom()` function.
19+
require('lsp_extensions.dart.outline').custom({}, function(items) print(items) end)
20+
21+
-- The outline categorizes the entries by `kind`s. By default, the outline will
22+
-- Prefix each entry with it's kind. However, if you prefer to define your own
23+
-- prefixes you can do that by passing `kind_prefixes` into the opts. If you
24+
-- pair this with a patched [Nerdfont](https://www.nerdfonts.com/) you can
25+
-- define a very custom experience. You can define a function that looks like:
26+
DART_SHOW_OUTLINE = function()
27+
require('lsp_extensions.dart.outline').loclist({kind_prefixes={
28+
CLASS = "",
29+
CLASS_TYPE_ALIAS = "",
30+
COMPILATION_UNIT = "",
31+
CONSTRUCTOR = "",
32+
CONSTRUCTOR_INVOCATION = "",
33+
ENUM = "",
34+
ENUM_CONSTANT = "",
35+
EXTENSION = "",
36+
FIELD = "",
37+
FILE = "",
38+
FUNCTION = "",
39+
FUNCTION_INVOCATION = "",
40+
FUNCTION_TYPE_ALIAS = "",
41+
GETTER = "",
42+
LABEL = "",
43+
LIBRARY = "",
44+
LOCAL_VARIABLE = "",
45+
METHOD = "",
46+
MIXIN = "",
47+
PARAMETER = "",
48+
PREFIX = "",
49+
SETTER = "",
50+
TOP_LEVEL_VARIABLE = "",
51+
TYPE_PARAMETER = "",
52+
UNIT_TEST_GROUP = "",
53+
UNIT_TEST_TEST = "",
54+
UNKNOWN = "",
55+
}})
56+
end
57+
58+
-- And then call it from neovim with :lua DART_SHOW_OUTLINE()
59+

lua/lsp_extensions/dart/outline.lua

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
--[[
2+
--
3+
## dart/textDocument/publishOutline Notification
4+
5+
Direction: Server -> Client
6+
Params: `{ uri: string, outline: Outline }`
7+
Outline: `{ element: Element, range: Range, codeRange: Range, children: Outline[] }`
8+
Element: `{ name: string, range: Range, kind: string, parameters: string | undefined, typeParameters: string | undefined, returnType: string | undefined }`
9+
10+
Notifies the client when outline information is available (or updated) for a file.
11+
12+
Nodes contains multiple ranges:
13+
14+
- `element.range` - the range of the name in the declaration of the element
15+
- `range` - the entire range of the declaration including dartdocs
16+
- `codeRange` - the range of code part of the declaration (excluding dartdocs and annotations) - typically used when navigating to the declaration
17+
18+
https://github.com/dart-lang/sdk/blob/master/pkg/analysis_server/tool/lsp_spec/README.md#darttextdocumentpublishoutline-notification
19+
20+
## Usage
21+
Since this is a notification, the callback needs to be registered in the client's callbacks table.
22+
This can be achieved with nvim_lspconfig with this minimal config.
23+
```lua
24+
nvim_lsp.dartls.setup{
25+
init_options = {
26+
outline = true,
27+
},
28+
callbacks = {
29+
['dart/textDocument/publishOutline'] = require('lsp_extensions.dart.outline').get_callback(),
30+
},
31+
}
32+
```
33+
34+
Then from nvim you can call loclist() or custom() to show the outline.
35+
```
36+
:lua require('lsp_extensions.dart.outline').loclist()
37+
```
38+
39+
--]]
40+
41+
local M = {}
42+
43+
-- The most recent published outline.
44+
local current_outline = {}
45+
46+
-- https://github.com/dart-lang/sdk/blob/93313eb2449099e20ade80d4760f76a325a4e176/pkg/analysis_server/tool/spec/generated/java/types/ElementKind.java#L16
47+
local default_kind_prefixes = {
48+
CLASS = "CLASS",
49+
CLASS_TYPE_ALIAS = "CLASS_TYPE_ALIAS",
50+
COMPILATION_UNIT = "COMPILATION_UNIT",
51+
CONSTRUCTOR = "CONSTRUCTOR",
52+
CONSTRUCTOR_INVOCATION = "CONSTRUCTOR_INVOCATION",
53+
ENUM = "ENUM",
54+
ENUM_CONSTANT = "ENUM_CONSTANT",
55+
EXTENSION = "EXTENSION",
56+
FIELD = "FIELD",
57+
FILE = "FILE",
58+
FUNCTION = "FUNCTION",
59+
FUNCTION_INVOCATION = "FUNCTION_INVOCATION",
60+
FUNCTION_TYPE_ALIAS = "FUNCTION_TYPE_ALIAS",
61+
GETTER = "GETTER",
62+
LABEL = "LABEL",
63+
LIBRARY = "LIBRARY",
64+
LOCAL_VARIABLE = "LOCAL_VARIABLE",
65+
METHOD = "METHOD",
66+
MIXIN = "MIXIN",
67+
PARAMETER = "PARAMETER",
68+
PREFIX = "PREFIX",
69+
SETTER = "SETTER",
70+
TOP_LEVEL_VARIABLE = "TOP_LEVEL_VARIABLE",
71+
TYPE_PARAMETER = "TYPE_PARAMETER",
72+
UNIT_TEST_GROUP = "UNIT_TEST_GROUP",
73+
UNIT_TEST_TEST = "UNIT_TEST_TEST",
74+
UNKNOWN = "UNKNOWN",
75+
}
76+
77+
-- A global function that recursively traverses the outline tree depth first
78+
-- and adds items to the items table.
79+
--
80+
-- @tparam table opts is a table used to mutate items
81+
-- @tparam string fname is the filename of the buffer that the outline belongs to
82+
-- @tparam table items is the in progress table of items
83+
-- @tparam table node is the current `Element` that is being traversed
84+
_DART_OUTLINE_APPEND_CHILDREN = function(opts, fname, items, node)
85+
if node == nil then
86+
return
87+
end
88+
local stringBuilder = {}
89+
local range = node.codeRange
90+
local elem = node.element
91+
92+
93+
table.insert(stringBuilder, opts.kind_prefixes[elem.kind] or opts.kind_prefixes.UNKNOWN)
94+
95+
if elem.returnType ~= nil then
96+
table.insert(stringBuilder, elem.returnType)
97+
end
98+
99+
if elem.typeParameters ~= nil and elem.parameters ~= nil then
100+
table.insert(stringBuilder, elem.name .. elem.typeParameters .. elem.parameters)
101+
elseif elem.typeParameters ~= nil then
102+
table.insert(stringBuilder, elem.name .. elem.typeParameters)
103+
elseif elem.parameters ~= nil then
104+
table.insert(stringBuilder, elem.name .. elem.parameters)
105+
else
106+
table.insert(stringBuilder, elem.name)
107+
end
108+
109+
local text = table.concat(stringBuilder, ' ')
110+
table.insert(items, {filename = fname, lnum = range.start.line + 1, col = range.start.character + 1, text = text})
111+
for _, child in ipairs(node.children or {}) do
112+
_DART_OUTLINE_APPEND_CHILDREN(opts, fname, items, child)
113+
end
114+
end
115+
116+
-- Rudimentary validation for the outlines before trying to do anything with
117+
-- them.
118+
--
119+
-- @tparam table outline the outline for the current request
120+
-- @treturn bool a bool describing if the outline is valid
121+
local validate = function(outline)
122+
if vim.tbl_isempty(outline) then
123+
print('No outline available for ' .. vim.api.nvim_buf_get_name(0))
124+
return false
125+
end
126+
return true
127+
end
128+
129+
-- Constructs a list of items that can be used to build the UI of the outline.
130+
--
131+
-- @tparam table opts is table used to mutate items
132+
-- @tparam table outline the outline for the current request
133+
-- @treturn table {{filename = string, lnum = number, col = number, text = string}, ...}
134+
local build_items = function(opts, outline)
135+
local fname = vim.api.nvim_buf_get_name(0)
136+
local items = {}
137+
for _, node in ipairs(outline.children or {}) do
138+
_DART_OUTLINE_APPEND_CHILDREN(opts, fname, items, node)
139+
end
140+
return items
141+
end
142+
143+
-- This function allows you to specify your own outline handler to do whatever
144+
-- you want. Check out the loclist implementation as an example.
145+
--
146+
-- @tparam table opts is table used to mutate items. opts.kind_prefixes is a
147+
-- table that allows specifying a prefix per kind type. This can be especially
148+
-- useful if you want to display unicode or patched font icons.
149+
-- @tparam function(items) handler is a function which takes a list of items
150+
M.custom = function(opts, handler)
151+
opts = opts or {}
152+
local kind_prefixes = opts.kind_prefixes or default_kind_prefixes
153+
opts.kind_prefixes = vim.tbl_extend("keep", kind_prefixes, default_kind_prefixes)
154+
local outline = current_outline
155+
if not validate(outline) then
156+
return
157+
end
158+
local items = build_items(opts, outline)
159+
handler(items)
160+
end
161+
162+
-- This function displays the outline in the loclist.
163+
--
164+
-- @tparam table opts is table used to mutate items. opts.kind_prefixes is a
165+
-- table that allows specifying a prefix per kind type. This can be especially
166+
-- useful if you want to display unicode or patched font icons.
167+
M.loclist = function(opts)
168+
M.custom(opts, function(items)
169+
vim.fn.setloclist(0, {}, ' ', {
170+
title = 'Outline';
171+
items = items;
172+
})
173+
vim.cmd[[lopen]]
174+
end)
175+
end
176+
177+
-- Gets a callback to register to the dartls outline notification.
178+
M.get_callback = function()
179+
return function(_, _, result, _, _)
180+
current_outline = result.outline
181+
end
182+
end
183+
184+
return M

0 commit comments

Comments
 (0)