nixd
Loading...
Searching...
No Matches
SemanticTokens.cpp
Go to the documentation of this file.
1/// \file
2/// \brief Implementation of [Semantic Tokens].
3/// [Semantic Tokens]:
4/// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_semanticTokens
5
6#include "CheckReturn.h"
7#include "Convert.h"
8
10
14#include <nixf/Basic/Range.h>
16
17#include <lspserver/Protocol.h>
18
19#include <boost/asio/post.hpp>
20
21using namespace nixd;
22using namespace lspserver;
23using namespace nixf;
24
25namespace {
26
27enum SemaType {
28 ST_Function,
29 ST_String,
30 ST_Number,
31 ST_Select,
32 ST_Builtin,
33 ST_Defined,
34 ST_FromWith,
35 ST_Undefined,
36 ST_Null,
37 ST_Bool,
38 ST_AttrName,
39 ST_LambdaArg,
40 ST_LambdaFormal,
41};
42
43enum SemaModifiers {
44 SM_Builtin = 1 << 0,
45 SM_Deprecated = 1 << 1,
46 SM_Dynamic = 1 << 2,
47};
48
49struct RawSemanticToken {
51 bool operator<(const RawSemanticToken &Other) const {
52 return Pos < Other.Pos;
53 }
54 unsigned Length;
55 unsigned TokenType;
56 unsigned TokenModifiers;
57};
58
59class SemanticTokenBuilder {
60
61 const VariableLookupAnalysis &VLA;
62
63 nixf::Position Previous = {0, 0};
64
65 std::vector<RawSemanticToken> Raw;
66
67 llvm::StringRef Src;
68
69public:
70 SemanticTokenBuilder(const VariableLookupAnalysis &VLA, llvm::StringRef Src)
71 : VLA(VLA), Src(Src) {}
72 void addImpl(nixf::LexerCursor Pos, unsigned Length, unsigned TokenType,
73 unsigned TokenModifiers) {
74 auto P = toLSPPosition(Src, Pos);
75 Raw.emplace_back(RawSemanticToken{P, Length, TokenType, TokenModifiers});
76 }
77
78 void add(const Node &N, unsigned TokenType, unsigned TokenModifiers) {
79 if (skip(N))
80 return;
81 addImpl(N.lCur(), len(N), TokenType, TokenModifiers);
82 }
83
84 static bool skip(const Node &N) {
85 // Skip cross-line strings.
86 return N.range().lCur().line() != N.range().rCur().line();
87 }
88
89 static unsigned len(const Node &N) {
90 return N.range().rCur().offset() - N.range().lCur().offset();
91 }
92
93 void dfs(const ExprString &Str) {
94 unsigned Modifers = 0;
95 if (!Str.isLiteral())
96 return;
97 add(Str, ST_String, Modifers);
98 }
99
100 void dfs(const ExprVar &Var) {
101 if (Var.id().name() == "true" || Var.id().name() == "false") {
102 add(Var, ST_Bool, SM_Builtin);
103 return;
104 }
105
106 if (Var.id().name() == "null") {
107 add(Var, ST_Null, 0);
108 return;
109 }
110
111 auto Result = VLA.query(Var);
113 if (Result.Def && Result.Def->isBuiltin()) {
114 add(Var, ST_Builtin, SM_Builtin);
115 return;
116 }
117 if (Result.Kind == ResultKind::Defined) {
118 add(Var, ST_Defined, 0);
119 return;
120 }
121 if (Result.Kind == ResultKind::FromWith) {
122 add(Var, ST_FromWith, SM_Dynamic);
123 return;
124 }
125
126 add(Var, ST_Defined, SM_Deprecated);
127 }
128
129 void dfs(const ExprSelect &Select) {
130 dfs(&Select.expr());
131 dfs(Select.defaultExpr());
132 if (!Select.path())
133 return;
134 for (const std::shared_ptr<nixf::AttrName> &Name : Select.path()->names()) {
135 if (!Name)
136 continue;
137 const AttrName &AN = *Name;
138 if (AN.isStatic()) {
139 if (AN.kind() == AttrName::ANK_ID) {
140 add(AN, ST_Select, 0);
141 }
142 }
143 }
144 }
145
146 void dfs(const SemaAttrs &SA) {
147 for (const auto &[Name, Attr] : SA.staticAttrs()) {
148 if (!Attr.value())
149 continue;
150 // If this attribute comes from "inherit", don't mark it as "AttrName"
151 // The rationale behind this is that we don't want to mark same token
152 // twice because "inherit"ed names also create variables.
153 if (Attr.fromInherit())
154 continue;
155 add(Attr.key(), ST_AttrName, 0);
156 dfs(Attr.value());
157 }
158 for (const auto &Attr : SA.dynamicAttrs()) {
159 dfs(Attr.value());
160 }
161 }
162
163 void dfs(const LambdaArg &Arg) {
164 if (Arg.id())
165 add(*Arg.id(), ST_LambdaArg, 0);
166 // Color deduplicated formals.
167 if (Arg.formals())
168 for (const auto &[_, Formal] : Arg.formals()->dedup()) {
169 if (Formal->id()) {
170 add(*Formal->id(), ST_LambdaFormal, 0);
171 }
172 }
173 }
174
175 void dfs(const ExprLambda &Lambda) {
176 if (Lambda.arg()) {
177 dfs(*Lambda.arg());
178 }
179 dfs(Lambda.body());
180 }
181
182 void dfs(const Node *AST) {
183 if (!AST)
184 return;
185 switch (AST->kind()) {
186 case Node::NK_ExprLambda: {
187 const auto &Lambda = static_cast<const ExprLambda &>(*AST);
188 dfs(Lambda);
189 break;
190 }
191 case Node::NK_ExprString: {
192 const auto &Str = static_cast<const ExprString &>(*AST);
193 dfs(Str);
194 break;
195 }
196 case Node::NK_ExprVar: {
197 const auto &Var = static_cast<const ExprVar &>(*AST);
198 dfs(Var);
199 break;
200 }
201 case Node::NK_ExprSelect: {
202 const auto &Select = static_cast<const ExprSelect &>(*AST);
203 dfs(Select);
204 break;
205 }
206 case Node::NK_ExprAttrs: {
207 const SemaAttrs &SA = static_cast<const ExprAttrs &>(*AST).sema();
208 dfs(SA);
209 break;
210 }
211 default:
212 for (const Node *Ch : AST->children()) {
213 dfs(Ch);
214 }
215 }
216 };
217
218 std::vector<SemanticToken> finish() {
219 std::vector<SemanticToken> Tokens;
220 std::sort(Raw.begin(), Raw.end());
221 lspserver::Position Prev{0, 0};
222 for (auto Elm : Raw) {
223 assert(Elm.Pos.line - Prev.line >= 0);
224 unsigned DeltaLine = Elm.Pos.line - Prev.line;
225 unsigned DeltaCol =
226 DeltaLine ? Elm.Pos.character : Elm.Pos.character - Prev.character;
227 Prev = Elm.Pos;
228 Tokens.emplace_back(DeltaLine, DeltaCol, Elm.Length, Elm.TokenType,
229 Elm.TokenModifiers);
230 }
231 return Tokens;
232 }
233};
234
235} // namespace
236
237void Controller::onSemanticTokens(const SemanticTokensParams &Params,
239 using CheckTy = SemanticTokens;
240 auto Action = [Reply = std::move(Reply), URI = Params.textDocument.uri,
241 this]() mutable {
242 const auto File = URI.file();
243 return Reply([&]() -> llvm::Expected<CheckTy> {
244 const auto TU = CheckDefault(getTU(File));
245 const auto AST = CheckDefault(getAST(*TU));
246 SemanticTokenBuilder Builder(*TU->variableLookup(), TU->src());
247 Builder.dfs(AST.get());
248 return SemanticTokens{.tokens = Builder.finish()};
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.
AttrNameKind kind() const
Definition Attrs.h:21
bool isStatic() const
Definition Attrs.h:40
const std::vector< std::shared_ptr< AttrName > > & names() const
Definition Attrs.h:100
const SemaAttrs & sema() const
Definition Attrs.h:284
Expr * body() const
Definition Lambda.h:119
LambdaArg * arg() const
Definition Lambda.h:118
Expr * defaultExpr() const
Definition Expr.h:25
Expr & expr() const
Definition Expr.h:20
AttrPath * path() const
Definition Expr.h:27
bool isLiteral() const
Definition Simple.h:130
const Identifier & id() const
Definition Simple.h:200
Identifier * id() const
Definition Lambda.h:37
const std::map< std::string, const Formal * > & dedup()
Deduplicated formals.
Definition Lambda.h:74
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 Range.h:116
LexerCursor rCur() const
Definition Range.h:117
A point in the source file.
Definition Range.h:57
std::size_t offset() const
Offset in the source file, starting from 0.
Definition Range.h:102
int64_t line() const
Line number, starting from 0.
Definition Range.h:93
LexerCursor lCur() const
Definition Basic.h:37
NodeKind kind() const
Definition Basic.h:34
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
bool operator<(const CompletionItem &, const CompletionItem &)
lspserver::Position toLSPPosition(llvm::StringRef Code, const nixf::LexerCursor &P)
Definition Convert.cpp:27
Body of textDocument/semanticTokens/full request.
TextDocumentIdentifier textDocument
The text document.