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 "CheckReturn.h"
7#include "Convert.h"
8
10
11#include <boost/asio/post.hpp>
12#include <llvm/ADT/StringRef.h>
13#include <lspserver/Protocol.h>
17
18#include <string>
19
20using namespace nixd;
21using namespace lspserver;
22using namespace nixf;
23
24namespace {
25
26std::string getLambdaName(const ExprLambda &Lambda) {
27 if (!Lambda.arg() || !Lambda.arg()->id())
28 return "(anonymous lambda)";
29 return Lambda.arg()->id()->name();
30}
31
32lspserver::Range getLambdaSelectionRage(llvm::StringRef Src,
33 const ExprLambda &Lambda) {
34 if (!Lambda.arg()) {
35 return toLSPRange(Src, Lambda.range());
36 }
37
38 if (!Lambda.arg()->id()) {
39 assert(Lambda.arg()->formals());
40 return toLSPRange(Src, Lambda.arg()->formals()->range());
41 }
42 return toLSPRange(Src, Lambda.arg()->id()->range());
43}
44
45lspserver::Range getAttrRange(llvm::StringRef Src, const Attribute &Attr) {
46 auto LCur = toLSPPosition(Src, Attr.key().lCur());
47 if (Attr.value())
48 return {LCur, toLSPPosition(Src, Attr.value()->rCur())};
49 return {LCur, toLSPPosition(Src, Attr.key().rCur())};
50}
51
52/// Make variable's entry rich.
53void richVar(const ExprVar &Var, DocumentSymbol &Sym,
54 const VariableLookupAnalysis &VLA) {
55 if (Var.id().name() == "true" || Var.id().name() == "false") {
56 Sym.kind = SymbolKind::Boolean;
57 Sym.detail = "builtin boolean";
58 return;
59 }
60
61 if (Var.id().name() == "null") {
62 Sym.kind = SymbolKind::Null;
63 Sym.detail = "null";
64 return;
65 }
66
67 auto Result = VLA.query(Var);
69 if (Result.Kind == ResultKind::Defined)
70 Sym.kind = SymbolKind::Constant;
71 else if (Result.Kind == ResultKind::FromWith)
72 Sym.kind = SymbolKind::Variable;
73 else {
74 Sym.deprecated = true;
75 return;
76 }
77
78 if (Result.Def->isBuiltin())
79 Sym.kind = SymbolKind::Event;
80}
81
82/// Collect document symbol on AST.
83void collect(const Node *AST, std::vector<DocumentSymbol> &Symbols,
84 const VariableLookupAnalysis &VLA, llvm::StringRef Src) {
85 if (!AST)
86 return;
87 switch (AST->kind()) {
88
89 case Node::NK_ExprString: {
90 const auto &Str = static_cast<const ExprString &>(*AST);
92 .name = Str.isLiteral() ? Str.literal() : "(dynamic string)",
93 .detail = "string",
94 .kind = SymbolKind::String,
95 .deprecated = false,
96 .range = toLSPRange(Src, Str.range()),
97 .selectionRange = toLSPRange(Src, Str.range()),
98 .children = {},
99 };
100 Symbols.emplace_back(std::move(Sym));
101 break;
102 }
103 case Node::NK_ExprInt: {
104 const auto &Int = static_cast<const ExprInt &>(*AST);
105 DocumentSymbol Sym{
106 .name = std::to_string(Int.value()),
107 .detail = "integer",
108 .kind = SymbolKind::Number,
109 .deprecated = false,
110 .range = toLSPRange(Src, Int.range()),
111 .selectionRange = toLSPRange(Src, Int.range()),
112 .children = {},
113 };
114 Symbols.emplace_back(std::move(Sym));
115 break;
116 }
117 case Node::NK_ExprFloat: {
118 const auto &Float = static_cast<const ExprFloat &>(*AST);
119 DocumentSymbol Sym{
120 .name = std::to_string(Float.value()),
121 .detail = "float",
122 .kind = SymbolKind::Number,
123 .deprecated = false,
124 .range = toLSPRange(Src, Float.range()),
125 .selectionRange = toLSPRange(Src, Float.range()),
126 .children = {},
127 };
128 Symbols.emplace_back(std::move(Sym));
129 break;
130 }
131 case Node::NK_AttrName: {
132 const auto &AN = static_cast<const AttrName &>(*AST);
133 DocumentSymbol Sym{
134 .name = AN.isStatic() ? AN.staticName() : "(dynamic attribute name)",
135 .detail = "attribute name",
136 .kind = SymbolKind::Property,
137 .deprecated = false,
138 .range = toLSPRange(Src, AN.range()),
139 .selectionRange = toLSPRange(Src, AN.range()),
140 .children = {},
141 };
142 Symbols.emplace_back(std::move(Sym));
143 break;
144 }
145 case Node::NK_ExprVar: {
146 const auto &Var = static_cast<const ExprVar &>(*AST);
147 DocumentSymbol Sym{
148 .name = Var.id().name(),
149 .detail = "identifier",
150 .kind = SymbolKind::Variable,
151 .deprecated = false,
152 .range = toLSPRange(Src, Var.range()),
153 .selectionRange = toLSPRange(Src, Var.range()),
154 .children = {},
155 };
156 richVar(Var, Sym, VLA);
157 Symbols.emplace_back(std::move(Sym));
158 break;
159 }
160 case Node::NK_ExprLambda: {
161 std::vector<DocumentSymbol> Children;
162 const auto &Lambda = static_cast<const ExprLambda &>(*AST);
163 collect(Lambda.body(), Children, VLA, Src);
164 DocumentSymbol Sym{
165 .name = getLambdaName(Lambda),
166 .detail = "lambda",
167 .kind = SymbolKind::Function,
168 .deprecated = false,
169 .range = toLSPRange(Src, Lambda.range()),
170 .selectionRange = getLambdaSelectionRage(Src, Lambda),
171 .children = std::move(Children),
172 };
173 Symbols.emplace_back(std::move(Sym));
174 break;
175 }
176 case Node::NK_ExprList: {
177 std::vector<DocumentSymbol> Children;
178 const auto &List = static_cast<const ExprList &>(*AST);
179 for (const Node *Ch : AST->children())
180 collect(Ch, Children, VLA, Src);
181
182 DocumentSymbol Sym{
183 .name = "{anonymous}",
184 .detail = "list",
185 .kind = SymbolKind::Array,
186 .deprecated = false,
187 .range = toLSPRange(Src, List.range()),
188 .selectionRange = toLSPRange(Src, List.range()),
189 .children = std::move(Children),
190 };
191 Symbols.emplace_back(std::move(Sym));
192 break;
193 }
194 case Node::NK_ExprAttrs: {
195 const SemaAttrs &SA = static_cast<const ExprAttrs &>(*AST).sema();
196 for (const auto &[Name, Attr] : SA.staticAttrs()) {
197 if (!Attr.value())
198 continue;
199 std::vector<DocumentSymbol> Children;
200 collect(Attr.value(), Children, VLA, Src);
201 DocumentSymbol Sym{
202 .name = Name,
203 .detail = "attribute",
204 .kind = SymbolKind::Field,
205 .deprecated = false,
206 .range = getAttrRange(Src, Attr),
207 .selectionRange = toLSPRange(Src, Attr.key().range()),
208 .children = std::move(Children),
209 };
210 Symbols.emplace_back(std::move(Sym));
211 }
212 for (const nixf::Attribute &Attr : SA.dynamicAttrs()) {
213 std::vector<DocumentSymbol> Children;
214 collect(Attr.value(), Children, VLA, Src);
215 DocumentSymbol Sym{
216 .name = "${dynamic attribute}",
217 .detail = "attribute",
218 .kind = SymbolKind::Field,
219 .deprecated = false,
220 .range = getAttrRange(Src, Attr),
221 .selectionRange = toLSPRange(Src, Attr.key().range()),
222 .children = std::move(Children),
223 };
224 Symbols.emplace_back(std::move(Sym));
225 }
226 break;
227 }
228 default:
229 // Trivial dispatch. Treat these symbol as same as this level.
230 for (const Node *Ch : AST->children())
231 collect(Ch, Symbols, VLA, Src);
232 break;
233 }
234}
235
236} // namespace
237
238void Controller::onDocumentSymbol(const DocumentSymbolParams &Params,
239 Callback<std::vector<DocumentSymbol>> Reply) {
240 using CheckTy = std::vector<DocumentSymbol>;
241 auto Action = [Reply = std::move(Reply), URI = Params.textDocument.uri,
242 this]() mutable {
243 return Reply([&]() -> llvm::Expected<CheckTy> {
244 const auto TU = CheckDefault(getTU(URI.file().str()));
245 const auto AST = CheckDefault(getAST(*TU));
246 auto Symbols = std::vector<DocumentSymbol>();
247 collect(AST.get(), Symbols, *TU->variableLookup(), TU->src());
248 return Symbols;
249 }());
250 };
251 boost::asio::post(Pool, std::move(Action));
252}
#define CheckDefault(x)
Variant of CheckReturn, but returns default constructed CheckTy
Definition CheckReturn.h:16
Convert between LSP and nixf types.
Lookup variable names, from it's parent scope.
Node & key() const
Definition Attrs.h:212
Expr * value() const
Definition Attrs.h:214
const SemaAttrs & sema() const
Definition Attrs.h:284
Expr * body() const
Definition Lambda.h:119
LambdaArg * arg() const
Definition Lambda.h:118
const Identifier & id() const
Definition Simple.h:200
const std::string & name() const
Definition Basic.h:120
Formals * formals() const
Definition Lambda.h:101
Identifier * id() const
Definition Lambda.h:99
LexerCursor lCur() const
Definition Basic.h:37
NodeKind kind() const
Definition Basic.h:34
std::string_view src(std::string_view Src) const
Definition Basic.h:63
LexerCursor rCur() const
Definition Basic.h:38
LexerCursorRange range() const
Definition Basic.h:35
virtual ChildVector children() const =0
Attribute set after deduplication.
Definition Attrs.h:231
const std::vector< Attribute > & dynamicAttrs() const
Dynamic attributes, require evaluation to get the key.
Definition Attrs.h:259
const std::map< std::string, Attribute > & staticAttrs() const
Static attributes, do not require evaluation to get the key.
Definition Attrs.h:252
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
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
SymbolKind kind
The kind of this symbol.
bool deprecated
Indicates if this symbol is deprecated.
std::string detail
More detail for this symbol, e.g the signature of a function.