diff --git a/nixd/lib/Controller/AST.cpp b/nixd/lib/Controller/AST.cpp index a22b64813..ba45c5c89 100644 --- a/nixd/lib/Controller/AST.cpp +++ b/nixd/lib/Controller/AST.cpp @@ -247,6 +247,19 @@ nixd::Selector nixd::mkSelector(const nixf::ExprSelect &Select, return BaseSelector; } +nixd::Selector nixd::mkSelector(const nixf::ExprSelect &Sel, + const nixf::VariableLookupAnalysis &VLA, + const nixf::ParentMapAnalysis &PM) { + if (Sel.expr().kind() != Node::NK_ExprVar) + throw NotVariableSelect(); + + const auto &Var = static_cast(Sel.expr()); + + auto BaseSelector = mkIdiomSelector(Var, VLA, PM); + + return mkSelector(Sel, std::move(BaseSelector)); +} + std::pair, std::string> nixd::getScopeAndPrefix(const Node &N, const ParentMapAnalysis &PM) { if (N.kind() != Node::NK_Identifer) diff --git a/nixd/lib/Controller/AST.h b/nixd/lib/Controller/AST.h index 343584820..fd04e0366 100644 --- a/nixd/lib/Controller/AST.h +++ b/nixd/lib/Controller/AST.h @@ -3,6 +3,7 @@ #include "nixd/Protocol/AttrSet.h" +#include #include #include @@ -49,6 +50,8 @@ upEnv(const nixf::Node &Desc, const nixf::VariableLookupAnalysis &VLA, std::pair, std::string> getScopeAndPrefix(const nixf::Node &N, const nixf::ParentMapAnalysis &PM); +struct IdiomException : std::exception {}; + /// \brief Exceptions scoped in nixd::mkIdiomSelector struct IdiomSelectorException : std::exception {}; @@ -59,13 +62,21 @@ struct NotAnIdiomException : IdiomSelectorException { } }; +struct VLAException : std::exception {}; + /// \brief No such variable. -struct NoSuchVarException : IdiomSelectorException { +struct NoSuchVarException : VLAException { [[nodiscard]] const char *what() const noexcept override { return "no such variable"; } }; +struct UndefinedVarException : VLAException { + [[nodiscard]] const char *what() const noexcept override { + return "undefined variable"; + } +}; + /// \brief Construct a nixd::Selector from \p Var. /// /// Try to heuristically find a selector of a variable, based on some known @@ -74,22 +85,31 @@ Selector mkIdiomSelector(const nixf::ExprVar &Var, const nixf::VariableLookupAnalysis &VLA, const nixf::ParentMapAnalysis &PM); -struct SelectorException : std::exception {}; - /// \brief The attrpath has a dynamic name, thus it cannot be trivially /// transformed to "static" selector. -struct DynamicNameException : SelectorException { +struct DynamicNameException : IdiomSelectorException { [[nodiscard]] const char *what() const noexcept override { return "dynamic attribute path encountered"; } }; +struct NotVariableSelect : IdiomSelectorException { + [[nodiscard]] const char *what() const noexcept override { + return "the base expression of the select is not a variable"; + } +}; + /// \brief Construct a nixd::Selector from \p AP. Selector mkSelector(const nixf::AttrPath &AP, Selector BaseSelector); /// \brief Construct a nixd::Selector from \p Select. Selector mkSelector(const nixf::ExprSelect &Select, Selector BaseSelector); +/// \brief Construct a nixd::Selector from \p Select. +Selector mkSelector(const nixf::ExprSelect &Select, + const nixf::VariableLookupAnalysis &VLA, + const nixf::ParentMapAnalysis &PM); + enum class FindAttrPathResult { OK, Inherit, diff --git a/nixd/lib/Controller/Completion.cpp b/nixd/lib/Controller/Completion.cpp index 6ec35bcbd..9abe43f7a 100644 --- a/nixd/lib/Controller/Completion.cpp +++ b/nixd/lib/Controller/Completion.cpp @@ -15,6 +15,7 @@ #include +#include #include #include #include @@ -318,9 +319,11 @@ void completeVarName(const VariableLookupAnalysis &VLA, // Invoke nixpkgs provider to get the completion list. // Variable names are always incomplete. NCP.completePackages(mkParams(Sel, /*IsComplete=*/false), List); - } catch (IdiomSelectorException &E) { - log(DBG "skipped, reason: {0}", E.what()); - return; + } catch (ExceedSizeError &) { + // Let "onCompletion" catch this exception to set "inComplete" field. + throw; + } catch (std::exception &E) { + return log(DBG "skipped, reason: {0}", E.what()); } #undef DBGPREFIX @@ -351,18 +354,14 @@ void completeSelect(const nixf::ExprSelect &Select, AttrSetClient &Client, // Ask nixpkgs provider to get idioms completion. NixpkgsCompletionProvider NCP(Client); - const auto Handler = [](std::exception &E) noexcept { - log(DBG "skipped, reason: {0}", E.what()); - }; try { Selector Sel = mkSelector(Select, mkIdiomSelector(Var, VLA, PM)); NCP.completePackages(mkParams(Sel, IsComplete), List); - } catch (IdiomSelectorException &E) { - Handler(E); - return; - } catch (SelectorException &E) { - Handler(E); - return; + } catch (ExceedSizeError &) { + // Let "onCompletion" catch this exception to set "inComplete" field. + throw; + } catch (std::exception &E) { + return log(DBG "skipped, reason: {0}", E.what()); } #undef DBGPREFIX diff --git a/nixd/lib/Controller/Definition.cpp b/nixd/lib/Controller/Definition.cpp index 5d20f5444..5a35503d6 100644 --- a/nixd/lib/Controller/Definition.cpp +++ b/nixd/lib/Controller/Definition.cpp @@ -10,14 +10,20 @@ #include "nixd/Controller/Controller.h" #include "nixd/Protocol/AttrSet.h" +#include "lspserver/Protocol.h" + #include #include +#include #include #include +#include +#include #include +#include #include using namespace nixd; @@ -90,23 +96,47 @@ const ExprVar *findVar(const Node &N, const ParentMapAnalysis &PMA, return static_cast(PMA.upTo(N, Node::NK_ExprVar)); } -Expected staticDef(URIForFile URI, const Node &N, - const ParentMapAnalysis &PMA, - const VariableLookupAnalysis &VLA) { - Expected ExpDef = findDefinition(N, PMA, VLA); - if (!ExpDef) - return ExpDef.takeError(); +const Definition &findVarDefinition(const ExprVar &Var, + const VariableLookupAnalysis &VLA) { + LookupResult Result = VLA.query(static_cast(Var)); + + if (Result.Kind == ResultKind::Undefined) + throw UndefinedVarException(); + + if (Result.Kind == ResultKind::NoSuchVar) + throw NoSuchVarException(); - if (ExpDef->isBuiltin()) - return error("this is a builtin variable defined by nix interpreter"); - assert(ExpDef->syntax()); + assert(Result.Def); + + return *Result.Def; +} +/// \brief Convert nixf::Definition to lspserver::Location +Location convertToLocation(const Definition &Def, URIForFile URI) { return Location{ .uri = std::move(URI), - .range = toLSPRange(ExpDef->syntax()->range()), + .range = toLSPRange(Def.syntax()->range()), }; } +struct NoLocationsFoundInNixpkgsException : std::exception { + [[nodiscard]] const char *what() const noexcept override { + return "no locations found in nixpkgs"; + } +}; + +class WorkerReportedException : std::exception { + llvm::Error E; + +public: + WorkerReportedException(llvm::Error E) : E(std::move(E)){}; + + llvm::Error takeError() { return std::move(E); } + [[nodiscard]] const char *what() const noexcept override { + return "worker reported some error"; + } +}; + /// \brief Resolve definition by invoking nixpkgs provider. /// /// Useful for users inspecting nixpkgs packages. For example, someone clicks @@ -138,8 +168,7 @@ class NixpkgsDefinitionProvider { NixpkgsDefinitionProvider(AttrSetClient &NixpkgsClient) : NixpkgsClient(NixpkgsClient) {} - Expected resolvePackage(std::vector Scope, - std::string Name) { + Locations resolveSelector(const nixd::Selector &Sel) { std::binary_semaphore Ready(0); Expected Desc = error("not replied"); auto OnReply = [&Ready, &Desc](llvm::Expected Resp) { @@ -149,22 +178,21 @@ class NixpkgsDefinitionProvider { Desc = Resp.takeError(); Ready.release(); }; - Scope.emplace_back(std::move(Name)); - NixpkgsClient.attrpathInfo(Scope, std::move(OnReply)); + NixpkgsClient.attrpathInfo(Sel, std::move(OnReply)); Ready.acquire(); if (!Desc) - return Desc.takeError(); + throw WorkerReportedException(Desc.takeError()); - const std::optional &Position = Desc->PackageDesc.Position; - if (!Position) - return error("meta.position is not available for this package"); + // Prioritize package location if it exists. + if (const std::optional &Position = Desc->PackageDesc.Position) + return Locations{parseLocation(*Position)}; - try { - return parseLocation(*Position); - } catch (std::exception &E) { - return error(E.what()); - } + // Use the location in "ValueMeta". + if (const auto &Loc = Desc->Meta.Location) + return Locations{*Loc}; + + throw NoLocationsFoundInNixpkgsException(); } }; @@ -198,29 +226,125 @@ class OptionsDefinitionProvider { } }; +/// \brief Get the locations of some attribute path. +/// +/// Usually this function will return a list of option declarations via RPC +Locations defineAttrPath(const Node &N, const ParentMapAnalysis &PM, + std::mutex &OptionsLock, + Controller::OptionMapTy &Options) { + using PathResult = FindAttrPathResult; + std::vector Scope; + auto R = findAttrPath(N, PM, Scope); + Locations Locs; + if (R == PathResult::OK) { + std::lock_guard _(OptionsLock); + // For each option worker, try to get it's decl position. + for (const auto &[_, Client] : Options) { + if (AttrSetClient *C = Client->client()) { + OptionsDefinitionProvider ODP(*C); + ODP.resolveLocations(Scope, Locs); + } + } + } + return Locs; +} + +/// \brief Get nixpkgs definition from a selector. +Locations defineNixpkgsSelector(const Selector &Sel, + AttrSetClient &NixpkgsClient) { + try { + // Ask nixpkgs provider information about this selector. + NixpkgsDefinitionProvider NDP(NixpkgsClient); + return NDP.resolveSelector(Sel); + } catch (NoLocationsFoundInNixpkgsException &E) { + elog("definition/idiom: {0}", E.what()); + } catch (WorkerReportedException &E) { + elog("definition/idiom/worker: {0}", E.takeError()); + } + return {}; +} + +/// \brief Get definiton of select expressions. +Locations defineSelect(const ExprSelect &Sel, const VariableLookupAnalysis &VLA, + const ParentMapAnalysis &PM, + AttrSetClient &NixpkgsClient) { + // Currently we can only deal with idioms. + // Maybe more data-flow analysis will be added though. + try { + return defineNixpkgsSelector(mkSelector(Sel, VLA, PM), NixpkgsClient); + } catch (IdiomSelectorException &E) { + elog("defintion/idiom/selector: {0}", E.what()); + } + return {}; +} + +Locations defineVarStatic(const ExprVar &Var, const VariableLookupAnalysis &VLA, + const URIForFile &URI) { + const Definition &Def = findVarDefinition(Var, VLA); + return {convertToLocation(Def, URI)}; +} + +template +std::vector mergeVec(std::vector A, const std::vector &B) { + A.insert(A.end(), B.begin(), B.end()); + return A; +} + +Locations defineVar(const ExprVar &Var, const VariableLookupAnalysis &VLA, + const ParentMapAnalysis &PM, AttrSetClient &NixpkgsClient, + const URIForFile &URI) { + try { + Locations StaticLocs = defineVarStatic(Var, VLA, URI); + + // Nixpkgs locations. + try { + Selector Sel = mkIdiomSelector(Var, VLA, PM); + Locations NixpkgsLocs = defineNixpkgsSelector(Sel, NixpkgsClient); + return mergeVec(std::move(StaticLocs), NixpkgsLocs); + } catch (std::exception &E) { + elog("definition/idiom/selector: {0}", E.what()); + return StaticLocs; + } + } catch (std::exception &E) { + elog("definition/static: {0}", E.what()); + } + return {}; +} + +/// \brief Squash a vector into smaller json variant. +template llvm::json::Value squash(std::vector List) { + std::size_t Size = List.size(); + switch (Size) { + case 0: + return nullptr; + case 1: + return std::move(List.back()); + default: + break; + } + return std::move(List); +} + +template +llvm::Expected squash(llvm::Expected> List) { + if (!List) + return List.takeError(); + return squash(std::move(*List)); +} + } // namespace -Expected -nixd::findDefinition(const Node &N, const ParentMapAnalysis &PMA, - const VariableLookupAnalysis &VLA) { +const Definition &nixd::findDefinition(const Node &N, + const ParentMapAnalysis &PMA, + const VariableLookupAnalysis &VLA) { const ExprVar *Var = findVar(N, PMA, VLA); if (!Var) [[unlikely]] { if (const Definition *Def = findSelfDefinition(N, PMA, VLA)) return *Def; - return error("cannot find variable on given position"); + throw CannotFindVarException(); } assert(Var->kind() == Node::NK_ExprVar); - LookupResult Result = VLA.query(static_cast(*Var)); - - if (Result.Kind == ResultKind::Undefined) - return error("this varaible is undefined"); - - if (Result.Kind == ResultKind::NoSuchVar) - return error("this varaible is not used in var lookup (duplicated attr?)"); - - assert(Result.Def); - - return *Result.Def; + return findVarDefinition(*Var, VLA); } void Controller::onDefinition(const TextDocumentPositionParams &Params, @@ -232,54 +356,41 @@ void Controller::onDefinition(const TextDocumentPositionParams &Params, if (std::shared_ptr AST = getAST(*TU, Reply)) [[likely]] { const VariableLookupAnalysis &VLA = *TU->variableLookup(); const ParentMapAnalysis &PM = *TU->parentMap(); - const Node *N = AST->descend({Pos, Pos}); - Locations Locs; - if (!N) [[unlikely]] { + const Node *MaybeN = AST->descend({Pos, Pos}); + if (!MaybeN) [[unlikely]] { Reply(error("cannot find AST node on given position")); return; } - using PathResult = FindAttrPathResult; - std::vector Scope; - auto R = findAttrPath(*N, PM, Scope); - if (R == PathResult::OK) { - std::lock_guard _(OptionsLock); - // For each option worker, try to get it's decl position. - for (const auto &[_, Client] : Options) { - if (AttrSetClient *C = Client->client()) { - OptionsDefinitionProvider ODP(*C); - ODP.resolveLocations(Scope, Locs); - } - } - } - if (havePackageScope(*N, VLA, PM) && nixpkgsClient()) { - // Ask nixpkgs client what's current package documentation. - NixpkgsDefinitionProvider NDP(*nixpkgsClient()); - auto [Scope, Name] = getScopeAndPrefix(*N, PM); - if (Expected Loc = - NDP.resolvePackage(std::move(Scope), std::move(Name))) - Locs.emplace_back(*Loc); - else - elog("cannot get nixpkgs definition for package: {0}", - Loc.takeError()); - } - - if (Expected StaticLoc = staticDef(URI, *N, PM, VLA)) - Locs.emplace_back(*StaticLoc); - else - elog("cannot get static def location: {0}", StaticLoc.takeError()); - - if (Locs.empty()) { + const Node &N = *MaybeN; + const Node *MaybeUpExpr = PM.upExpr(N); + if (!MaybeUpExpr) { Reply(nullptr); return; } - if (Locs.size() == 1) { - Reply(Locs.back()); - return; - } + const Node &UpExpr = *MaybeUpExpr; - Reply(std::move(Locs)); - return; + return Reply(squash([&]() -> llvm::Expected { + // Special case for inherited names. + if (const ExprVar *Var = findInheritVar(N, PM, VLA)) + return defineVar(*Var, VLA, PM, *nixpkgsClient(), URI); + + switch (UpExpr.kind()) { + case Node::NK_ExprVar: { + const auto &Var = static_cast(UpExpr); + return defineVar(Var, VLA, PM, *nixpkgsClient(), URI); + } + case Node::NK_ExprSelect: { + const auto &Sel = static_cast(UpExpr); + return defineSelect(Sel, VLA, PM, *nixpkgsClient()); + } + case Node::NK_ExprAttrs: + return defineAttrPath(N, PM, OptionsLock, Options); + default: + break; + } + return error("unknown node type for definition"); + }())); } } }; diff --git a/nixd/lib/Controller/Definition.h b/nixd/lib/Controller/Definition.h index 0b8e09c61..270660227 100644 --- a/nixd/lib/Controller/Definition.h +++ b/nixd/lib/Controller/Definition.h @@ -7,9 +7,15 @@ namespace nixd { +struct CannotFindVarException : std::exception { + [[nodiscard]] const char *what() const noexcept override { + return "cannot find variable on given node"; + } +}; + /// \brief Heuristically find definition on some node -llvm::Expected -findDefinition(const nixf::Node &N, const nixf::ParentMapAnalysis &PMA, - const nixf::VariableLookupAnalysis &VLA); +const nixf::Definition &findDefinition(const nixf::Node &N, + const nixf::ParentMapAnalysis &PMA, + const nixf::VariableLookupAnalysis &VLA); } // namespace nixd diff --git a/nixd/lib/Controller/DocumentHighlight.cpp b/nixd/lib/Controller/DocumentHighlight.cpp index 03371641c..5f437350f 100644 --- a/nixd/lib/Controller/DocumentHighlight.cpp +++ b/nixd/lib/Controller/DocumentHighlight.cpp @@ -13,36 +13,42 @@ #include #include +#include + using namespace lspserver; using namespace nixd; using namespace llvm; using namespace nixf; namespace { -Error highlight(const nixf::Node &Desc, const ParentMapAnalysis &PMA, - const VariableLookupAnalysis &VLA, const URIForFile &URI, - std::vector &Highlights) { + +std::vector highlight(const nixf::Node &Desc, + const ParentMapAnalysis &PMA, + const VariableLookupAnalysis &VLA, + const URIForFile &URI) { // Find "definition" - if (auto Def = findDefinition(Desc, PMA, VLA)) { - // OK, iterate all uses. - for (const auto *Use : Def->uses()) { - assert(Use); - Highlights.emplace_back(DocumentHighlight{ - .range = toLSPRange(Use->range()), - .kind = DocumentHighlightKind::Read, - }); - } - if (Def->syntax()) { - const Node &Syntax = *Def->syntax(); - Highlights.emplace_back(DocumentHighlight{ - .range = toLSPRange(Syntax.range()), - .kind = DocumentHighlightKind::Write, - }); - } - return Error::success(); + auto Def = findDefinition(Desc, PMA, VLA); + + std::vector Highlights; + // OK, iterate all uses. + for (const auto *Use : Def.uses()) { + assert(Use); + Highlights.emplace_back(DocumentHighlight{ + .range = toLSPRange(Use->range()), + .kind = DocumentHighlightKind::Read, + }); } - return error("Cannot find definition of this node"); + if (Def.syntax()) { + const Node &Syntax = *Def.syntax(); + Highlights.emplace_back(DocumentHighlight{ + .range = toLSPRange(Syntax.range()), + .kind = DocumentHighlightKind::Write, + }); + } + + return Highlights; } + } // namespace void Controller::onDocumentHighlight( @@ -58,20 +64,14 @@ void Controller::onDocumentHighlight( Reply(error("cannot find corresponding node on given position")); return; } - std::vector Highlights; - if (auto Err = highlight(*Desc, *TU->parentMap(), *TU->variableLookup(), - URI, Highlights)) { - // FIXME: Empty response if there are no def-use chain found. - // For document highlights, the specification explicitly specified LSP - // should do "fuzzy" things. - - // Empty response on error, don't reply all errors because this method - // is very frequently called. - Reply(std::vector{}); - lspserver::elog("textDocument/documentHighlight failed: {0}", Err); - return; + try { + const auto &PM = *TU->parentMap(); + const auto &VLA = *TU->variableLookup(); + return Reply(highlight(*Desc, PM, VLA, URI)); + } catch (std::exception &E) { + elog("textDocument/documentHighlight failed: {0}", E.what()); + return Reply(std::vector{}); } - Reply(std::move(Highlights)); } } }; diff --git a/nixd/lib/Controller/FindReferences.cpp b/nixd/lib/Controller/FindReferences.cpp index 391e4ebd8..e3369c5eb 100644 --- a/nixd/lib/Controller/FindReferences.cpp +++ b/nixd/lib/Controller/FindReferences.cpp @@ -20,27 +20,27 @@ using namespace nixf; namespace { -Error findReferences(const nixf::Node &Desc, const ParentMapAnalysis &PMA, - const VariableLookupAnalysis &VLA, const URIForFile &URI, - std::vector &Locations) { +std::vector findReferences(const nixf::Node &Desc, + const ParentMapAnalysis &PMA, + const VariableLookupAnalysis &VLA, + const URIForFile &URI) { // Two steps. // 1. Find some "definition" for this node. // 2. Find all "uses", and construct the vector. // Find "definition" - if (auto Def = findDefinition(Desc, PMA, VLA)) { - // OK, iterate all uses. - for (const auto *Use : Def->uses()) { - assert(Use); - Locations.emplace_back(Location{ - .uri = URI, - .range = toLSPRange(Use->range()), - }); - } - return Error::success(); + auto Def = findDefinition(Desc, PMA, VLA); + std::vector Locations; + // OK, iterate all uses. + for (const auto *Use : Def.uses()) { + assert(Use); + Locations.emplace_back(Location{ + .uri = URI, + .range = toLSPRange(Use->range()), + }); } - return error("Cannot find definition of this node"); + return Locations; } } // namespace @@ -54,16 +54,16 @@ void Controller::onReferences(const TextDocumentPositionParams &Params, if (std::shared_ptr AST = getAST(*TU, Reply)) [[likely]] { const nixf::Node *Desc = AST->descend({Pos, Pos}); if (!Desc) { - Reply(error("cannot find corresponding node on given position")); - return; + return Reply( + error("cannot find corresponding node on given position")); } - std::vector Locations; - if (auto Err = findReferences(*Desc, *TU->parentMap(), - *TU->variableLookup(), URI, Locations)) { - Reply(std::move(Err)); - return; + const auto &PM = *TU->parentMap(); + const auto &VLA = *TU->variableLookup(); + try { + return Reply(findReferences(*Desc, PM, VLA, URI)); + } catch (std::exception &E) { + return Reply(error("references: {0}", E.what())); } - Reply(std::move(Locations)); } } }; diff --git a/nixd/lib/Controller/Rename.cpp b/nixd/lib/Controller/Rename.cpp index 1064c7282..5e675c1fe 100644 --- a/nixd/lib/Controller/Rename.cpp +++ b/nixd/lib/Controller/Rename.cpp @@ -11,32 +11,45 @@ #include +#include + using namespace lspserver; using namespace nixd; using namespace llvm; using namespace nixf; namespace { -llvm::Expected rename(const nixf::Node &Desc, - const std::string &NewText, - const ParentMapAnalysis &PMA, - const VariableLookupAnalysis &VLA, - const URIForFile &URI) { + +struct RenameException : std::exception {}; + +struct RenameWithException : RenameException { + [[nodiscard]] const char *what() const noexcept override { + return "cannot rename `with` defined variable"; + } +}; + +struct RenameBuiltinException : RenameException { + [[nodiscard]] const char *what() const noexcept override { + return "cannot rename builtin variable"; + } +}; + +WorkspaceEdit rename(const nixf::Node &Desc, const std::string &NewText, + const ParentMapAnalysis &PMA, + const VariableLookupAnalysis &VLA, const URIForFile &URI) { using lspserver::TextEdit; // Find "definition" auto Def = findDefinition(Desc, PMA, VLA); - if (!Def) - return Def.takeError(); - if (Def->source() == Definition::DS_With) - return error("cannot rename `with` defined variables"); + if (Def.source() == Definition::DS_With) + throw RenameWithException(); - if (Def->isBuiltin()) - return error("cannot rename builtin variable"); + if (Def.isBuiltin()) + throw RenameBuiltinException(); std::vector Edits; - for (const auto *Use : Def->uses()) { + for (const auto *Use : Def.uses()) { Edits.emplace_back(TextEdit{ .range = toLSPRange(Use->range()), .newText = NewText, @@ -44,7 +57,7 @@ llvm::Expected rename(const nixf::Node &Desc, } Edits.emplace_back(TextEdit{ - .range = toLSPRange(Def->syntax()->range()), + .range = toLSPRange(Def.syntax()->range()), .newText = NewText, }); WorkspaceEdit WE; @@ -67,9 +80,13 @@ void Controller::onRename(const RenameParams &Params, Reply(error("cannot find corresponding node on given position")); return; } - Reply(rename(*Desc, NewText, *TU->parentMap(), *TU->variableLookup(), - URI)); - return; + const auto &PM = *TU->parentMap(); + const auto &VLA = *TU->variableLookup(); + try { + return Reply(rename(*Desc, NewText, PM, VLA, URI)); + } catch (std::exception &E) { + return Reply(error(E.what())); + } } } }; @@ -86,17 +103,17 @@ void Controller::onPrepareRename( if (std::shared_ptr AST = getAST(*TU, Reply)) [[likely]] { const nixf::Node *Desc = AST->descend({Pos, Pos}); if (!Desc) { - Reply(error("cannot find corresponding node on given position")); - return; + return Reply( + error("cannot find corresponding node on given position")); } - llvm::Expected Exp = - rename(*Desc, "", *TU->parentMap(), *TU->variableLookup(), URI); - if (Exp) { - Reply(toLSPRange(Desc->range())); - return; + const auto &PM = *TU->parentMap(); + const auto &VLA = *TU->variableLookup(); + try { + WorkspaceEdit WE = rename(*Desc, "", PM, VLA, URI); + return Reply(toLSPRange(Desc->range())); + } catch (std::exception &E) { + return Reply(error(E.what())); } - Reply(Exp.takeError()); - return; } } }; diff --git a/nixd/tools/nixd/test/definition/package.md b/nixd/tools/nixd/test/definition/package.md index c7ec87d94..a4882a424 100644 --- a/nixd/tools/nixd/test/definition/package.md +++ b/nixd/tools/nixd/test/definition/package.md @@ -62,28 +62,28 @@ CHECK-NEXT: "result": [ CHECK-NEXT: { CHECK-NEXT: "range": { CHECK-NEXT: "end": { -CHECK-NEXT: "character": 0, -CHECK-NEXT: "line": 33 +CHECK-NEXT: "character": 4, +CHECK-NEXT: "line": 0 CHECK-NEXT: }, CHECK-NEXT: "start": { CHECK-NEXT: "character": 0, -CHECK-NEXT: "line": 33 +CHECK-NEXT: "line": 0 CHECK-NEXT: } CHECK-NEXT: }, -CHECK-NEXT: "uri": "file:///foo" +CHECK-NEXT: "uri": "file:///basic.nix" CHECK-NEXT: }, CHECK-NEXT: { CHECK-NEXT: "range": { CHECK-NEXT: "end": { -CHECK-NEXT: "character": 4, -CHECK-NEXT: "line": 0 +CHECK-NEXT: "character": 0, +CHECK-NEXT: "line": 33 CHECK-NEXT: }, CHECK-NEXT: "start": { CHECK-NEXT: "character": 0, -CHECK-NEXT: "line": 0 +CHECK-NEXT: "line": 33 CHECK-NEXT: } CHECK-NEXT: }, -CHECK-NEXT: "uri": "file:///basic.nix" +CHECK-NEXT: "uri": "file:///foo" CHECK-NEXT: } ``` diff --git a/nixd/tools/nixd/test/definition/select-package-with.md b/nixd/tools/nixd/test/definition/select-package-with.md new file mode 100644 index 000000000..1ba092ecf --- /dev/null +++ b/nixd/tools/nixd/test/definition/select-package-with.md @@ -0,0 +1,82 @@ +# RUN: nixd --nixpkgs-expr="{ x.y.meta.position = \"/foo:33\"; }" --lit-test < %s | FileCheck %s + +Similar to [package.md](./package.md), but testing that we can do "selection" + "with". +i.e. testing if `with pkgs; [x.y]` works. + +<-- initialize(0) + +```json +{ + "jsonrpc":"2.0", + "id":0, + "method":"initialize", + "params":{ + "processId":123, + "rootPath":"", + "capabilities":{ + }, + "trace":"off" + } +} +``` + + +<-- textDocument/didOpen + +```json +{ + "jsonrpc":"2.0", + "method":"textDocument/didOpen", + "params":{ + "textDocument":{ + "uri":"file:///basic.nix", + "languageId":"nix", + "version":1, + "text":"with pkgs; [x.y]" + } + } +} +``` + +<-- textDocument/definition(2) + + +```json +{ + "jsonrpc":"2.0", + "id":2, + "method":"textDocument/definition", + "params":{ + "textDocument":{ + "uri":"file:///basic.nix" + }, + "position":{ + "line": 0, + "character":15 + } + } +} +``` + +``` + CHECK: "id": 2, +CHECK-NEXT: "jsonrpc": "2.0", +CHECK-NEXT: "result": { +CHECK-NEXT: "range": { +CHECK-NEXT: "end": { +CHECK-NEXT: "character": 0, +CHECK-NEXT: "line": 33 +CHECK-NEXT: }, +CHECK-NEXT: "start": { +CHECK-NEXT: "character": 0, +CHECK-NEXT: "line": 33 +CHECK-NEXT: } +CHECK-NEXT: }, +CHECK-NEXT: "uri": "file:///foo" +CHECK-NEXT: } +``` + + +```json +{"jsonrpc":"2.0","method":"exit"} +``` diff --git a/nixd/tools/nixd/test/definition/select-package.md b/nixd/tools/nixd/test/definition/select-package.md new file mode 100644 index 000000000..ff38fcc6b --- /dev/null +++ b/nixd/tools/nixd/test/definition/select-package.md @@ -0,0 +1,82 @@ +# RUN: nixd --nixpkgs-expr="{ x.meta.position = \"/foo:33\"; }" --lit-test < %s | FileCheck %s + +Similar to [package.md](./package.md), but testing that we can do "selection". +i.e. testing if `pkgs.x` works. + +<-- initialize(0) + +```json +{ + "jsonrpc":"2.0", + "id":0, + "method":"initialize", + "params":{ + "processId":123, + "rootPath":"", + "capabilities":{ + }, + "trace":"off" + } +} +``` + + +<-- textDocument/didOpen + +```json +{ + "jsonrpc":"2.0", + "method":"textDocument/didOpen", + "params":{ + "textDocument":{ + "uri":"file:///basic.nix", + "languageId":"nix", + "version":1, + "text":"pkgs.x" + } + } +} +``` + +<-- textDocument/definition(2) + + +```json +{ + "jsonrpc":"2.0", + "id":2, + "method":"textDocument/definition", + "params":{ + "textDocument":{ + "uri":"file:///basic.nix" + }, + "position":{ + "line": 0, + "character":5 + } + } +} +``` + +``` + CHECK: "id": 2, +CHECK-NEXT: "jsonrpc": "2.0", +CHECK-NEXT: "result": { +CHECK-NEXT: "range": { +CHECK-NEXT: "end": { +CHECK-NEXT: "character": 0, +CHECK-NEXT: "line": 33 +CHECK-NEXT: }, +CHECK-NEXT: "start": { +CHECK-NEXT: "character": 0, +CHECK-NEXT: "line": 33 +CHECK-NEXT: } +CHECK-NEXT: }, +CHECK-NEXT: "uri": "file:///foo" +CHECK-NEXT: } +``` + + +```json +{"jsonrpc":"2.0","method":"exit"} +``` diff --git a/nixd/tools/nixd/test/rename/duplicated.md b/nixd/tools/nixd/test/rename/duplicated.md index 116ff091a..9ff5c6922 100644 --- a/nixd/tools/nixd/test/rename/duplicated.md +++ b/nixd/tools/nixd/test/rename/duplicated.md @@ -61,7 +61,7 @@ ``` CHECK: "error": { CHECK-NEXT: "code": -32001, -CHECK-NEXT: "message": "this varaible is not used in var lookup (duplicated attr?)" +CHECK-NEXT: "message": "no such variable" CHECK-NEXT: }, CHECK-NEXT: "id": 2, CHECK-NEXT: "jsonrpc": "2.0" diff --git a/nixd/tools/nixd/test/rename/with.md b/nixd/tools/nixd/test/rename/with.md index 476fb24e8..56f6fea70 100644 --- a/nixd/tools/nixd/test/rename/with.md +++ b/nixd/tools/nixd/test/rename/with.md @@ -58,7 +58,7 @@ ``` ``` - CHECK: "message": "cannot rename `with` defined variables" + CHECK: "message": "cannot rename `with` defined variable" CHECK-NEXT: }, CHECK-NEXT: "id": 2, CHECK-NEXT: "jsonrpc": "2.0"