nixd
Loading...
Searching...
No Matches
CodeAction.cpp
Go to the documentation of this file.
1/// \file
2/// \brief Implementation of [Code Action].
3/// [Code Action]:
4/// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_codeAction
5
6#include "CheckReturn.h"
7#include "Convert.h"
8
20
22
23#include <boost/asio/post.hpp>
24#include <llvm/Support/JSON.h>
25
26namespace nixd {
27
28using namespace llvm::json;
29using namespace lspserver;
30
31void Controller::onCodeAction(const lspserver::CodeActionParams &Params,
32 Callback<std::vector<CodeAction>> Reply) {
33 using CheckTy = std::vector<CodeAction>;
34 std::string File(Params.textDocument.uri.file());
35 Range Range = Params.range;
36 auto Action = [Reply = std::move(Reply), File, Range, this]() mutable {
37 return Reply([&]() -> llvm::Expected<CheckTy> {
38 const auto TU = CheckDefault(getTU(File));
39
40 const auto &Diagnostics = TU->diagnostics();
41 auto Actions = std::vector<CodeAction>();
42 Actions.reserve(Diagnostics.size());
43 std::string FileURI = URIForFile::canonicalize(File, File).uri();
44
45 for (const nixf::Diagnostic &D : Diagnostics) {
46 auto DRange = toLSPRange(TU->src(), D.range());
47 if (!Range.overlap(DRange))
48 continue;
49
50 // Determine if this diagnostic's fixes should be preferred
51 bool IsPreferred = false;
52 switch (D.kind()) {
53 case nixf::Diagnostic::DK_UnusedDefLet:
54 IsPreferred = true;
55 break;
56 default:
57 break;
58 }
59
60 // Add fixes.
61 for (const nixf::Fix &F : D.fixes()) {
62 std::vector<TextEdit> Edits;
63 Edits.reserve(F.edits().size());
64 for (const nixf::TextEdit &TE : F.edits()) {
65 Edits.emplace_back(TextEdit{
66 .range = toLSPRange(TU->src(), TE.oldRange()),
67 .newText = std::string(TE.newText()),
68 });
69 }
70 using Changes = std::map<std::string, std::vector<TextEdit>>;
71 WorkspaceEdit WE{.changes = Changes{
72 {FileURI, std::move(Edits)},
73 }};
74 Actions.emplace_back(CodeAction{
75 .title = F.message(),
76 .kind = std::string(CodeAction::QUICKFIX_KIND),
77 .isPreferred = IsPreferred,
78 .edit = std::move(WE),
79 });
80 }
81 }
82
83 // Add refactoring code actions based on cursor position
84 if (TU->ast() && TU->parentMap()) {
85 nixf::PositionRange NixfRange = toNixfRange(Range);
86 if (const nixf::Node *N = TU->ast()->descend(NixfRange)) {
87 addAttrNameActions(*N, *TU->parentMap(), FileURI, TU->src(), Actions);
88 addConvertToInheritAction(*N, *TU->parentMap(), FileURI, TU->src(),
89 Actions);
90 addFlattenAttrsAction(*N, *TU->parentMap(), FileURI, TU->src(),
91 Actions);
92 addPackAttrsAction(*N, *TU->parentMap(), FileURI, TU->src(), Actions);
93 addInheritToBindingAction(*N, *TU->parentMap(), FileURI, TU->src(),
94 Actions);
95 addNoogleDocAction(*N, *TU->parentMap(), Actions);
96 addRewriteStringAction(*N, *TU->parentMap(), FileURI, TU->src(),
97 Actions);
98
99 // Extract to file requires variable lookup analysis
100 if (TU->variableLookup()) {
101 addExtractToFileAction(*N, *TU->parentMap(), *TU->variableLookup(),
102 FileURI, TU->src(), Actions);
103 }
104 // Add with-to-let action (requires VLA for variable tracking)
105 if (TU->variableLookup()) {
106 addWithToLetAction(*N, *TU->parentMap(), *TU->variableLookup(),
107 FileURI, TU->src(), Actions);
108 }
109 // Add undefined variable to formals action (requires VLA)
110 if (TU->variableLookup()) {
111 addToFormalsAction(*N, *TU->parentMap(), *TU->variableLookup(),
112 FileURI, TU->src(), Actions);
113 }
114 }
115 }
116
117 // Selection-based actions (work on arbitrary text, not AST nodes)
118 addJsonToNixAction(TU->src(), Range, FileURI, Actions);
119
120 return Actions;
121 }());
122 };
123 boost::asio::post(Pool, std::move(Action));
124}
125
126void Controller::onCodeActionResolve(const lspserver::CodeAction &Params,
127 Callback<CodeAction> Reply) {
128 auto Action = [Reply = std::move(Reply), Params, this]() mutable {
129 // Check if this is a Noogle documentation action
130 if (Params.data) {
131 const auto *DataObj = Params.data->getAsObject();
132 if (DataObj) {
133 auto NoogleUrl = DataObj->getString("noogleUrl");
134 if (NoogleUrl) {
135 // Call window/showDocument to open the URL in external browser
136 ShowDocumentParams ShowParams;
137 ShowParams.externalUri = NoogleUrl->str();
138 ShowParams.external = true;
139
140 ShowDocument(
141 ShowParams, [](llvm::Expected<ShowDocumentResult> Result) {
142 if (!Result) {
143 lspserver::elog("Failed to open Noogle documentation: {0}",
144 Result.takeError());
145 }
146 });
147 }
148 }
149 }
150
151 // Return the resolved code action (unchanged for Noogle actions since
152 // the work is done via showDocument)
153 Reply(Params);
154 };
155 boost::asio::post(Pool, std::move(Action));
156}
157
158} // namespace nixd
Code action for adding undefined variables to lambda formals.
Code action for quoting/unquoting attribute names.
#define CheckDefault(x)
Variant of CheckReturn, but returns default constructed CheckTy.
Definition CheckReturn.h:16
Code action for converting bindings to inherit syntax.
Convert between LSP and nixf types.
Code action for extracting expressions to separate files.
Code action for flattening nested attribute sets.
Code action for converting inherit to explicit binding.
Code action for converting JSON to Nix expressions.
Code action for opening noogle.dev documentation.
Code action for packing dotted attribute paths into nested sets.
Code action for converting between string literal syntaxes.
Code action for converting with expressions to let/inherit.
llvm::unique_function< void(llvm::Expected< T >)> Callback
Definition Function.h:14
void elog(const char *Fmt, Ts &&...Vals)
Definition Logger.h:52
void addJsonToNixAction(llvm::StringRef Src, const lspserver::Range &Range, const std::string &FileURI, std::vector< lspserver::CodeAction > &Actions)
Add JSON to Nix conversion action for selected JSON text.
Definition JsonToNix.cpp:97
void addRewriteStringAction(const nixf::Node &N, const nixf::ParentMapAnalysis &PM, const std::string &FileURI, llvm::StringRef Src, std::vector< lspserver::CodeAction > &Actions)
Add rewrite action for string literal syntax conversion.
nixf::PositionRange toNixfRange(const lspserver::Range &P)
Definition Convert.cpp:36
void addPackAttrsAction(const nixf::Node &N, const nixf::ParentMapAnalysis &PM, const std::string &FileURI, llvm::StringRef Src, std::vector< lspserver::CodeAction > &Actions)
Add pack action for dotted attribute paths.
void addConvertToInheritAction(const nixf::Node &N, const nixf::ParentMapAnalysis &PM, const std::string &FileURI, llvm::StringRef Src, std::vector< lspserver::CodeAction > &Actions)
Add code action to convert binding to inherit syntax.
void addWithToLetAction(const nixf::Node &N, const nixf::ParentMapAnalysis &PM, const nixf::VariableLookupAnalysis &VLA, const std::string &FileURI, llvm::StringRef Src, std::vector< lspserver::CodeAction > &Actions)
Add code action to convert with expression to let/inherit.
void addFlattenAttrsAction(const nixf::Node &N, const nixf::ParentMapAnalysis &PM, const std::string &FileURI, llvm::StringRef Src, std::vector< lspserver::CodeAction > &Actions)
Add flatten action for nested attribute sets.
void addInheritToBindingAction(const nixf::Node &N, const nixf::ParentMapAnalysis &PM, const std::string &FileURI, llvm::StringRef Src, std::vector< lspserver::CodeAction > &Actions)
Add code action to convert inherit to explicit binding.
void addExtractToFileAction(const nixf::Node &N, const nixf::ParentMapAnalysis &PM, const nixf::VariableLookupAnalysis &VLA, const std::string &FileURI, llvm::StringRef Src, std::vector< lspserver::CodeAction > &Actions)
Add extract-to-file action for selected expressions.
void addAttrNameActions(const nixf::Node &N, const nixf::ParentMapAnalysis &PM, const std::string &FileURI, llvm::StringRef Src, std::vector< lspserver::CodeAction > &Actions)
Add refactoring code actions for attribute names (quote/unquote).
Definition AttrName.cpp:13
void addToFormalsAction(const nixf::Node &N, const nixf::ParentMapAnalysis &PM, const nixf::VariableLookupAnalysis &VLA, const std::string &FileURI, llvm::StringRef Src, std::vector< lspserver::CodeAction > &Actions)
Add code action to add undefined variable to lambda formals.
lspserver::Range toLSPRange(llvm::StringRef Code, const nixf::LexerCursorRange &R)
Definition Convert.cpp:40
void addNoogleDocAction(const nixf::Node &N, const nixf::ParentMapAnalysis &PM, std::vector< lspserver::CodeAction > &Actions)
Add a code action to open noogle.dev documentation for lib.* functions.
Definition NoogleDoc.cpp:30
Range range
The range for which the command was invoked.
TextDocumentIdentifier textDocument
The document in which the command was invoked.
std::optional< llvm::json::Value > data
static const llvm::StringLiteral QUICKFIX_KIND
bool overlap(const Range &RHS) const
static URIForFile canonicalize(llvm::StringRef AbsPath, llvm::StringRef TUPath)
llvm::StringRef file() const
Retrieves absolute path to the file.