nixd
Loading...
Searching...
No Matches
DocumentSymbol.cpp
Go to the documentation of this file.
1/// \file
2/// \brief Implementation of [Document Symbol].
3/// [Document Symbol]:
4/// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_documentSymbol
5
6#include "Convert.h"
7
9
10#include <boost/asio/post.hpp>
11#include <llvm/ADT/StringRef.h>
12#include <lspserver/Protocol.h>
16
17#include <string>
18
19using namespace nixd;
20using namespace lspserver;
21using namespace nixf;
22
23namespace {
24
25std::string getLambdaName(const ExprLambda &Lambda) {
26 if (!Lambda.arg() || !Lambda.arg()->id())
27 return "(anonymous lambda)";
28 return Lambda.arg()->id()->name();
29}
30
32 const ExprLambda &Lambda) {
33 if (!Lambda.arg()) {
34 return toLSPRange(Src, Lambda.range());
35 }
36
37 if (!Lambda.arg()->id()) {
38 assert(Lambda.arg()->formals());
39 return toLSPRange(Src, Lambda.arg()->formals()->range());
40 }
41 return toLSPRange(Src, Lambda.arg()->id()->range());
42}
43
44lspserver::Range getAttrRange(llvm::StringRef Src, const Attribute &Attr) {
45 auto LCur = toLSPPosition(Src, Attr.key().lCur());
46 if (Attr.value())
47 return {LCur, toLSPPosition(Src, Attr.value()->rCur())};
48 return {LCur, toLSPPosition(Src, Attr.key().rCur())};
49}
50
51/// Make variable's entry rich.
53 const VariableLookupAnalysis &VLA) {
54 if (Var.id().name() == "true" || Var.id().name() == "false") {
55 Sym.kind = SymbolKind::Boolean;
56 Sym.detail = "builtin boolean";
57 return;
58 }
59
60 if (Var.id().name() == "null") {
61 Sym.kind = SymbolKind::Null;
62 Sym.detail = "null";
63 return;
64 }
65
66 auto Result = VLA.query(Var);
68 if (Result.Kind == ResultKind::Defined)
69 Sym.kind = SymbolKind::Constant;
70 else if (Result.Kind == ResultKind::FromWith)
71 Sym.kind = SymbolKind::Variable;
72 else {
73 Sym.deprecated = true;
74 return;
75 }
76
77 if (Result.Def->isBuiltin())
78 Sym.kind = SymbolKind::Event;
79}
80
81/// Collect document symbol on AST.
82void collect(const Node *AST, std::vector<DocumentSymbol> &Symbols,
83 const VariableLookupAnalysis &VLA, llvm::StringRef Src) {
84 if (!AST)
85 return;
86 switch (AST->kind()) {
87
88 case Node::NK_ExprString: {
89 const auto &Str = static_cast<const ExprString &>(*AST);
91 .name = Str.isLiteral() ? Str.literal() : "(dynamic string)",
92 .detail = "string",
93 .kind = SymbolKind::String,
94 .deprecated = false,
95 .range = toLSPRange(Src, Str.range()),
96 .selectionRange = toLSPRange(Src, Str.range()),
97 .children = {},
98 };
99 Symbols.emplace_back(std::move(Sym));
100 break;
101 }
102 case Node::NK_ExprInt: {
103 const auto &Int = static_cast<const ExprInt &>(*AST);
105 .name = std::to_string(Int.value()),
106 .detail = "integer",
107 .kind = SymbolKind::Number,
108 .deprecated = false,
109 .range = toLSPRange(Src, Int.range()),
110 .selectionRange = toLSPRange(Src, Int.range()),
111 .children = {},
112 };
113 Symbols.emplace_back(std::move(Sym));
114 break;
115 }
116 case Node::NK_ExprFloat: {
117 const auto &Float = static_cast<const ExprFloat &>(*AST);
119 .name = std::to_string(Float.value()),
120 .detail = "float",
121 .kind = SymbolKind::Number,
122 .deprecated = false,
123 .range = toLSPRange(Src, Float.range()),
124 .selectionRange = toLSPRange(Src, Float.range()),
125 .children = {},
126 };
127 Symbols.emplace_back(std::move(Sym));
128 break;
129 }
130 case Node::NK_AttrName: {
131 const auto &AN = static_cast<const AttrName &>(*AST);
133 .name = AN.isStatic() ? AN.staticName() : "(dynamic attribute name)",
134 .detail = "attribute name",
135 .kind = SymbolKind::Property,
136 .deprecated = false,
137 .range = toLSPRange(Src, AN.range()),
138 .selectionRange = toLSPRange(Src, AN.range()),
139 .children = {},
140 };
141 Symbols.emplace_back(std::move(Sym));
142 break;
143 }
144 case Node::NK_ExprVar: {
145 const auto &Var = static_cast<const ExprVar &>(*AST);
147 .name = Var.id().name(),
148 .detail = "identifier",
149 .kind = SymbolKind::Variable,
150 .deprecated = false,
151 .range = toLSPRange(Src, Var.range()),
152 .selectionRange = toLSPRange(Src, Var.range()),
153 .children = {},
154 };
155 richVar(Var, Sym, VLA);
156 Symbols.emplace_back(std::move(Sym));
157 break;
158 }
159 case Node::NK_ExprLambda: {
160 std::vector<DocumentSymbol> Children;
161 const auto &Lambda = static_cast<const ExprLambda &>(*AST);
162 collect(Lambda.body(), Children, VLA, Src);
165 .detail = "lambda",
166 .kind = SymbolKind::Function,
167 .deprecated = false,
168 .range = toLSPRange(Src, Lambda.range()),
169 .selectionRange = getLambdaSelectionRage(Src, Lambda),
170 .children = std::move(Children),
171 };
172 Symbols.emplace_back(std::move(Sym));
173 break;
174 }
175 case Node::NK_ExprList: {
176 std::vector<DocumentSymbol> Children;
177 const auto &List = static_cast<const ExprList &>(*AST);
178 for (const Node *Ch : AST->children())
179 collect(Ch, Children, VLA, Src);
180
182 .name = "{anonymous}",
183 .detail = "list",
184 .kind = SymbolKind::Array,
185 .deprecated = false,
186 .range = toLSPRange(Src, List.range()),
187 .selectionRange = toLSPRange(Src, List.range()),
188 .children = std::move(Children),
189 };
190 Symbols.emplace_back(std::move(Sym));
191 break;
192 }
193 case Node::NK_ExprAttrs: {
194 const SemaAttrs &SA = static_cast<const ExprAttrs &>(*AST).sema();
195 for (const auto &[Name, Attr] : SA.staticAttrs()) {
196 if (!Attr.value())
197 continue;
198 std::vector<DocumentSymbol> Children;
199 collect(Attr.value(), Children, VLA, Src);
201 .name = Name,
202 .detail = "attribute",
203 .kind = SymbolKind::Field,
204 .deprecated = false,
205 .range = getAttrRange(Src, Attr),
206 .selectionRange = toLSPRange(Src, Attr.key().range()),
207 .children = std::move(Children),
208 };
209 Symbols.emplace_back(std::move(Sym));
210 }
211 for (const nixf::Attribute &Attr : SA.dynamicAttrs()) {
212 std::vector<DocumentSymbol> Children;
213 collect(Attr.value(), Children, VLA, Src);
215 .name = "${dynamic attribute}",
216 .detail = "attribute",
217 .kind = SymbolKind::Field,
218 .deprecated = false,
219 .range = getAttrRange(Src, Attr),
220 .selectionRange = toLSPRange(Src, Attr.key().range()),
221 .children = std::move(Children),
222 };
223 Symbols.emplace_back(std::move(Sym));
224 }
225 break;
226 }
227 default:
228 // Trivial dispatch. Treat these symbol as same as this level.
229 for (const Node *Ch : AST->children())
230 collect(Ch, Symbols, VLA, Src);
231 break;
232 }
233}
234
235} // namespace
236
237void Controller::onDocumentSymbol(const DocumentSymbolParams &Params,
238 Callback<std::vector<DocumentSymbol>> Reply) {
239 auto Action = [Reply = std::move(Reply), URI = Params.textDocument.uri,
240 this]() mutable {
241 if (std::shared_ptr<NixTU> TU = getTU(URI.file().str(), Reply)) {
242 if (std::shared_ptr<Node> AST = getAST(*TU, Reply)) {
243 std::vector<DocumentSymbol> Symbols;
244 collect(AST.get(), Symbols, *TU->variableLookup(), TU->src());
245 Reply(std::move(Symbols));
246 }
247 }
248 };
249 boost::asio::post(Pool, std::move(Action));
250}
Convert between LSP and nixf types.
Lookup variable names, from it's parent scope.
const SemaAttrs & sema() const
Definition Attrs.h:284
NodeKind kind() const
Definition Basic.h:34
std::string_view src(std::string_view Src) const
Definition Basic.h:63
Attribute set after deduplication.
Definition Attrs.h:231
LookupResult query(const ExprVar &Var) const
Query the which name/with binds to specific varaible.
Whether current platform treats paths case insensitively.
Definition Connection.h:11
llvm::unique_function< void(llvm::Expected< T >)> Callback
Definition Function.h:14
bool fromJSON(const llvm::json::Value &Params, Configuration::Diagnostic &R, llvm::json::Path P)
lspserver::Position toLSPPosition(llvm::StringRef Code, const nixf::LexerCursor &P)
Definition Convert.cpp:27
lspserver::Range toLSPRange(llvm::StringRef Code, const nixf::LexerCursorRange &R)
Definition Convert.cpp:40
std::string name
The name of this symbol.