From b835c53f7b188b7b63d1d473161993d3152a01f5 Mon Sep 17 00:00:00 2001 From: Yingchi Long Date: Tue, 9 Apr 2024 21:15:07 +0800 Subject: [PATCH 1/2] libnixf/Sema: record AST node -> Definition map --- libnixf/include/nixf/Sema/VariableLookup.h | 26 ++++++++++ libnixf/src/Sema/VariableLookup.cpp | 27 +++++++---- libnixf/test/Sema/VariableLookup.cpp | 56 ++++++++++++++++++++++ 3 files changed, 101 insertions(+), 8 deletions(-) diff --git a/libnixf/include/nixf/Sema/VariableLookup.h b/libnixf/include/nixf/Sema/VariableLookup.h index 67e4f1c1a..6954eb2bf 100644 --- a/libnixf/include/nixf/Sema/VariableLookup.h +++ b/libnixf/include/nixf/Sema/VariableLookup.h @@ -86,12 +86,16 @@ class VariableLookupAnalysis { std::shared_ptr Def; }; + using ToDefMap = std::map>; + private: std::vector &Diags; std::map> WithDefs; // record with ... ; users. + ToDefMap ToDef; + void lookupVar(const ExprVar &Var, const std::shared_ptr &Env); std::shared_ptr dfsAttrs(const SemaAttrs &SA, @@ -118,11 +122,33 @@ class VariableLookupAnalysis { public: VariableLookupAnalysis(std::vector &Diags); + /// \brief Perform variable lookup analysis (def-use) on AST. + /// \note This method should be invoked after any other method called. + /// \note The result remains immutable thus it can be shared among threads. void runOnAST(const Node &Root); + /// \brief Query the which name/with binds to specific varaible. [[nodiscard]] LookupResult query(const ExprVar &Var) const { return Results.at(&Var); } + + /// \brief Get definition record for some name. + /// + /// For some cases, we need to get "definition" record to find all references + /// to this definition, on AST. + /// + /// Thus we need to store AST -> Definition + /// There are many pointers on AST, the convention is: + /// + /// 1. attrname "key" syntax is recorded. + // For static attrs, they are Node::NK_AttrName. + /// 2. "with" keyword is recorded. + /// 3. Lambda arguments, record its identifier. + [[nodiscard]] const Definition *toDef(const Node &N) const { + if (ToDef.contains(&N)) + return ToDef.at(&N).get(); + return nullptr; + } }; } // namespace nixf diff --git a/libnixf/src/Sema/VariableLookup.cpp b/libnixf/src/Sema/VariableLookup.cpp index 547a21750..2e4b33cf6 100644 --- a/libnixf/src/Sema/VariableLookup.cpp +++ b/libnixf/src/Sema/VariableLookup.cpp @@ -13,10 +13,17 @@ class DefBuilder { EnvNode::DefMap Def; public: - void addBuiltin(std::string Name) { add(std::move(Name), nullptr); } - void add(std::string Name, const Node *Entry) { + void addBuiltin(std::string Name) { + // Don't need to record def map for builtins. + auto _ = add(std::move(Name), nullptr); + } + + [[nodiscard("Record ToDef Map!")]] std::shared_ptr + add(std::string Name, const Node *Entry) { assert(!Def.contains(Name)); - Def.insert({std::move(Name), std::make_shared(Entry)}); + auto NewDef = std::make_shared(Entry); + Def.insert({std::move(Name), NewDef}); + return NewDef; } EnvNode::DefMap finish() { return std::move(Def); } @@ -100,14 +107,15 @@ void VariableLookupAnalysis::dfs(const ExprLambda &Lambda, // Function arg cannot duplicate to it's formal. // If it this unluckily happens, we would like to skip this definition. if (!Arg.formals() || !Arg.formals()->dedup().contains(Arg.id()->name())) - DBuilder.add(Arg.id()->name(), Arg.id()); + ToDef.insert_or_assign(Arg.id(), + DBuilder.add(Arg.id()->name(), Arg.id())); } // { foo, bar, ... } : body /// ^~~~~~~~~<-------------- add function formals. if (Arg.formals()) for (const auto &[Name, Formal] : Arg.formals()->dedup()) - DBuilder.add(Name, Formal->id()); + ToDef.insert_or_assign(Formal->id(), DBuilder.add(Name, Formal->id())); auto NewEnv = std::make_shared(Env, DBuilder.finish(), &Lambda); @@ -136,7 +144,7 @@ VariableLookupAnalysis::dfsAttrs(const SemaAttrs &SA, DefBuilder DB; // For each static names, create a name binding. for (const auto &[Name, Attr] : SA.staticAttrs()) - DB.add(Name, &Attr.key()); + ToDef.insert_or_assign(&Attr.key(), DB.add(Name, &Attr.key())); auto NewEnv = std::make_shared(Env, DB.finish(), Syntax); @@ -205,8 +213,11 @@ void VariableLookupAnalysis::trivialDispatch( void VariableLookupAnalysis::dfs(const ExprWith &With, const std::shared_ptr &Env) { auto NewEnv = std::make_shared(Env, EnvNode::DefMap{}, &With); - if (!WithDefs.contains(&With)) - WithDefs.insert({&With, std::make_shared(&With.kwWith())}); + if (!WithDefs.contains(&With)) { + auto NewDef = std::make_shared(&With.kwWith()); + ToDef.insert_or_assign(&With.kwWith(), NewDef); + WithDefs.insert_or_assign(&With, NewDef); + } if (With.with()) dfs(*With.with(), Env); diff --git a/libnixf/test/Sema/VariableLookup.cpp b/libnixf/test/Sema/VariableLookup.cpp index 3ea21d03b..c6df8f290 100644 --- a/libnixf/test/Sema/VariableLookup.cpp +++ b/libnixf/test/Sema/VariableLookup.cpp @@ -3,6 +3,7 @@ #include "nixf/Basic/Diagnostic.h" #include "nixf/Basic/Nodes/Expr.h" #include "nixf/Parse/Parser.h" +#include "nixf/Sema/ParentMap.h" #include "nixf/Sema/VariableLookup.h" using namespace nixf; @@ -171,4 +172,59 @@ TEST_F(VLATest, LivenessDupSymbol) { ASSERT_EQ(Diags[0].tags().size(), 0); } +TEST_F(VLATest, ToDefAttrs) { + std::shared_ptr AST = parse("rec { x = 1; y = x; z = x; }", Diags); + VariableLookupAnalysis VLA(Diags); + VLA.runOnAST(*AST); + + ParentMapAnalysis PMA; + PMA.runOnAST(*AST); + + ASSERT_EQ(Diags.size(), 0); + + ASSERT_TRUE(AST); + const Node *ID = AST->descend({{0, 6}, {0, 6}}); + ID = PMA.upTo(*ID, Node::NK_AttrName); + ASSERT_EQ(ID->kind(), Node::NK_AttrName); + + const auto *Def = VLA.toDef(*ID); + ASSERT_TRUE(Def); + + ASSERT_EQ(Def->uses().size(), 2); +} + +TEST_F(VLATest, ToDefLambda) { + std::shared_ptr AST = + parse("arg: { foo, bar} : arg + foo + bar", Diags); + VariableLookupAnalysis VLA(Diags); + VLA.runOnAST(*AST); + + ASSERT_EQ(Diags.size(), 0); + + ASSERT_TRUE(AST); + const Node *ID = AST->descend({{0, 1}, {0, 1}}); + ASSERT_EQ(ID->kind(), Node::NK_Identifer); + + const auto *Def = VLA.toDef(*ID); + ASSERT_TRUE(Def); + + ASSERT_EQ(Def->uses().size(), 1); +} + +TEST_F(VLATest, ToDefWith) { + std::shared_ptr AST = parse("with builtins; [ x y z ]", Diags); + VariableLookupAnalysis VLA(Diags); + VLA.runOnAST(*AST); + + ASSERT_EQ(Diags.size(), 0); + + ASSERT_TRUE(AST); + const Node *KwWith = AST->descend({{0, 1}, {0, 1}}); + + const auto *Def = VLA.toDef(*KwWith); + ASSERT_TRUE(Def); + + ASSERT_EQ(Def->uses().size(), 3); +} + } // namespace From bff9074a6a30db07287fd9013ac669a0d8239042 Mon Sep 17 00:00:00 2001 From: Yingchi Long Date: Tue, 9 Apr 2024 22:09:15 +0800 Subject: [PATCH 2/2] nixd: support find references --- nixd/include/nixd/Controller/Controller.h | 4 + nixd/lib/Controller/Definition.cpp | 83 +++++++++++++------- nixd/lib/Controller/Definition.h | 15 ++++ nixd/lib/Controller/FindReferences.cpp | 71 +++++++++++++++++ nixd/lib/Controller/LifeTime.cpp | 1 + nixd/lib/Controller/Support.cpp | 2 + nixd/meson.build | 1 + nixd/tools/nixd/test/initialize.md | 1 + nixd/tools/nixd/test/references-lambda.md | 93 ++++++++++++++++++++++ nixd/tools/nixd/test/references-let.md | 80 +++++++++++++++++++ nixd/tools/nixd/test/references-rec.md | 80 +++++++++++++++++++ nixd/tools/nixd/test/references-var.md | 93 ++++++++++++++++++++++ nixd/tools/nixd/test/references-with.md | 94 +++++++++++++++++++++++ 13 files changed, 590 insertions(+), 28 deletions(-) create mode 100644 nixd/lib/Controller/Definition.h create mode 100644 nixd/lib/Controller/FindReferences.cpp create mode 100644 nixd/tools/nixd/test/references-lambda.md create mode 100644 nixd/tools/nixd/test/references-let.md create mode 100644 nixd/tools/nixd/test/references-rec.md create mode 100644 nixd/tools/nixd/test/references-var.md create mode 100644 nixd/tools/nixd/test/references-with.md diff --git a/nixd/include/nixd/Controller/Controller.h b/nixd/include/nixd/Controller/Controller.h index b2af1892a..1f6a7b24c 100644 --- a/nixd/include/nixd/Controller/Controller.h +++ b/nixd/include/nixd/Controller/Controller.h @@ -79,6 +79,10 @@ class Controller : public lspserver::LSPServer { void onDefinition(const lspserver::TextDocumentPositionParams &Params, lspserver::Callback Reply); + void + onReferences(const lspserver::TextDocumentPositionParams &Params, + lspserver::Callback> Reply); + void publishDiagnostics(lspserver::PathRef File, std::optional Version, const std::vector &Diagnostics); diff --git a/nixd/lib/Controller/Definition.cpp b/nixd/lib/Controller/Definition.cpp index f1e4d62ac..621b394f3 100644 --- a/nixd/lib/Controller/Definition.cpp +++ b/nixd/lib/Controller/Definition.cpp @@ -3,75 +3,102 @@ /// [Go to Definition]: /// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_definition +#include "Definition.h" #include "Convert.h" #include "nixd/Controller/Controller.h" +#include "nixf/Sema/VariableLookup.h" #include #include using namespace nixd; +using namespace nixf; using namespace lspserver; +using namespace llvm; + +using LookupResult = VariableLookupAnalysis::LookupResult; +using ResultKind = VariableLookupAnalysis::LookupResultKind; namespace { -void gotoDefinition(const NixTU &TU, const nixf::Node &AST, nixf::Position Pos, +void gotoDefinition(const NixTU &TU, const Node &AST, nixf::Position Pos, URIForFile URI, Callback &Reply) { - using LookupResult = nixf::VariableLookupAnalysis::LookupResult; - using ResultKind = nixf::VariableLookupAnalysis::LookupResultKind; - - const nixf::Node *N = AST.descend({Pos, Pos}); + const Node *N = AST.descend({Pos, Pos}); if (!N) [[unlikely]] { Reply(error("cannot find AST node on given position")); return; } - const nixf::ParentMapAnalysis *PMA = TU.parentMap(); + const ParentMapAnalysis *PMA = TU.parentMap(); assert(PMA && "ParentMap should not be null as AST is not null"); - const nixf::Node *Var = PMA->upTo(*N, nixf::Node::NK_ExprVar); - if (!Var) [[unlikely]] { - Reply(error("cannot find variable on given position")); - return; - } - assert(Var->kind() == nixf::Node::NK_ExprVar); - // OK, this is an variable. Lookup it in VLA entries. - const nixf::VariableLookupAnalysis *VLA = TU.variableLookup(); + const VariableLookupAnalysis *VLA = TU.variableLookup(); if (!VLA) [[unlikely]] { Reply(error("cannot get variable analysis for nix unit")); return; } - LookupResult Result = VLA->query(static_cast(*Var)); - if (Result.Kind == ResultKind::Undefined) { - Reply(error("this varaible is undefined")); - return; + if (Expected ExpDef = findDefinition(*N, *PMA, *VLA)) { + assert(ExpDef->syntax()); + Reply(Location{ + .uri = std::move(URI), + .range = toLSPRange(ExpDef->syntax()->range()), + }); + } else { + Reply(ExpDef.takeError()); } +} - if (Result.Def->isBuiltin()) { - Reply(error("this is a builtin variable")); - return; - } +const Definition *findSelfDefinition(const Node &N, + const ParentMapAnalysis &PMA, + const VariableLookupAnalysis &VLA) { + // If "N" is a definition itself, just return it. + if (const Definition *Def = VLA.toDef(N)) + return Def; - assert(Result.Def->syntax()); + // If N is inside an attrset, it maybe an "AttrName", let's look for it. + const Node *Parent = PMA.query(N); + if (Parent && Parent->kind() == Node::NK_AttrName) + return VLA.toDef(*Parent); - Reply(Location{ - .uri = std::move(URI), - .range = toLSPRange(Result.Def->syntax()->range()), - }); + return nullptr; } } // namespace +Expected +nixd::findDefinition(const Node &N, const ParentMapAnalysis &PMA, + const VariableLookupAnalysis &VLA) { + const Node *Var = PMA.upTo(N, Node::NK_ExprVar); + if (!Var) [[unlikely]] { + if (const Definition *Def = findSelfDefinition(N, PMA, VLA)) + return *Def; + return error("cannot find variable on given position"); + } + assert(Var->kind() == Node::NK_ExprVar); + LookupResult Result = VLA.query(static_cast(*Var)); + + assert(Result.Def); + + if (Result.Kind == ResultKind::Undefined) + return error("this varaible is undefined"); + + if (Result.Def->isBuiltin()) + return error("this is a builtin variable"); + + return *Result.Def; +} + void Controller::onDefinition(const TextDocumentPositionParams &Params, Callback Reply) { auto Action = [Reply = std::move(Reply), URI = Params.textDocument.uri, Pos = toNixfPosition(Params.position), this]() mutable { std::string File(URI.file()); if (std::shared_ptr TU = getTU(File, Reply)) [[likely]] { - if (std::shared_ptr AST = getAST(*TU, Reply)) [[likely]] { + if (std::shared_ptr AST = getAST(*TU, Reply)) [[likely]] { gotoDefinition(*TU, *AST, Pos, std::move(URI), Reply); } } diff --git a/nixd/lib/Controller/Definition.h b/nixd/lib/Controller/Definition.h new file mode 100644 index 000000000..0b8e09c61 --- /dev/null +++ b/nixd/lib/Controller/Definition.h @@ -0,0 +1,15 @@ +#pragma once + +#include "nixf/Sema/ParentMap.h" +#include "nixf/Sema/VariableLookup.h" + +#include + +namespace nixd { + +/// \brief Heuristically find definition on some node +llvm::Expected +findDefinition(const nixf::Node &N, const nixf::ParentMapAnalysis &PMA, + const nixf::VariableLookupAnalysis &VLA); + +} // namespace nixd diff --git a/nixd/lib/Controller/FindReferences.cpp b/nixd/lib/Controller/FindReferences.cpp new file mode 100644 index 000000000..391e4ebd8 --- /dev/null +++ b/nixd/lib/Controller/FindReferences.cpp @@ -0,0 +1,71 @@ +/// \file +/// \brief This implements [Find References]. +/// [Find References]: +/// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_references + +#include "Convert.h" +#include "Definition.h" + +#include "nixd/Controller/Controller.h" + +#include +#include +#include +#include + +using namespace lspserver; +using namespace nixd; +using namespace llvm; +using namespace nixf; + +namespace { + +Error findReferences(const nixf::Node &Desc, const ParentMapAnalysis &PMA, + const VariableLookupAnalysis &VLA, const URIForFile &URI, + std::vector &Locations) { + + // 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(); + } + return error("Cannot find definition of this node"); +} + +} // namespace + +void Controller::onReferences(const TextDocumentPositionParams &Params, + Callback> Reply) { + auto Action = [Reply = std::move(Reply), URI = Params.textDocument.uri, + Pos = toNixfPosition(Params.position), this]() mutable { + std::string File(URI.file()); + if (std::shared_ptr TU = getTU(File, Reply)) [[likely]] { + 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; + } + std::vector Locations; + if (auto Err = findReferences(*Desc, *TU->parentMap(), + *TU->variableLookup(), URI, Locations)) { + Reply(std::move(Err)); + return; + } + Reply(std::move(Locations)); + } + } + }; + boost::asio::post(Pool, std::move(Action)); +} diff --git a/nixd/lib/Controller/LifeTime.cpp b/nixd/lib/Controller/LifeTime.cpp index 933da78f6..2a4750ecb 100644 --- a/nixd/lib/Controller/LifeTime.cpp +++ b/nixd/lib/Controller/LifeTime.cpp @@ -37,6 +37,7 @@ void Controller:: }, }, {"definitionProvider", true}, + {"referencesProvider", true}, {"hoverProvider", true}}, }; diff --git a/nixd/lib/Controller/Support.cpp b/nixd/lib/Controller/Support.cpp index ada4bafe5..fbaa3574a 100644 --- a/nixd/lib/Controller/Support.cpp +++ b/nixd/lib/Controller/Support.cpp @@ -125,6 +125,8 @@ Controller::Controller(std::unique_ptr In, // Language Features Registry.addMethod("textDocument/definition", this, &Controller::onDefinition); + Registry.addMethod("textDocument/references", this, + &Controller::onReferences); Registry.addMethod("textDocument/codeAction", this, &Controller::onCodeAction); Registry.addMethod("textDocument/hover", this, &Controller::onHover); diff --git a/nixd/meson.build b/nixd/meson.build index a57320bb6..448889250 100644 --- a/nixd/meson.build +++ b/nixd/meson.build @@ -9,6 +9,7 @@ libnixd_lib = library( 'lib/Controller/Definition.cpp', 'lib/Controller/Diagnostics.cpp', 'lib/Controller/EvalClient.cpp', + 'lib/Controller/FindReferences.cpp', 'lib/Controller/Hover.cpp', 'lib/Controller/LifeTime.cpp', 'lib/Controller/NixTU.cpp', diff --git a/nixd/tools/nixd/test/initialize.md b/nixd/tools/nixd/test/initialize.md index b3f551d26..1b4cc230e 100644 --- a/nixd/tools/nixd/test/initialize.md +++ b/nixd/tools/nixd/test/initialize.md @@ -34,6 +34,7 @@ CHECK-NEXT: "resolveProvider": false CHECK-NEXT: }, CHECK-NEXT: "definitionProvider": true, CHECK-NEXT: "hoverProvider": true, +CHECK-NEXT: "referencesProvider": true, CHECK-NEXT: "textDocumentSync": { CHECK-NEXT: "change": 2, CHECK-NEXT: "openClose": true, diff --git a/nixd/tools/nixd/test/references-lambda.md b/nixd/tools/nixd/test/references-lambda.md new file mode 100644 index 000000000..bcda6756e --- /dev/null +++ b/nixd/tools/nixd/test/references-lambda.md @@ -0,0 +1,93 @@ +# RUN: nixd --lit-test < %s | FileCheck %s + +<-- 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":"arg @ { foo, bar }: arg + foo + bar + arg" + } + } +} +``` + +<-- textDocument/references(2) + + +```json +{ + "jsonrpc":"2.0", + "id":2, + "method":"textDocument/references", + "params":{ + "textDocument":{ + "uri":"file:///basic.nix" + }, + "position":{ + "line": 0, + "character":0 + } + } +} +``` + +``` + CHECK: "id": 2, +CHECK-NEXT: "jsonrpc": "2.0", +CHECK-NEXT: "result": [ +CHECK-NEXT: { +CHECK-NEXT: "range": { +CHECK-NEXT: "end": { +CHECK-NEXT: "character": 23, +CHECK-NEXT: "line": 0 +CHECK-NEXT: }, +CHECK-NEXT: "start": { +CHECK-NEXT: "character": 20, +CHECK-NEXT: "line": 0 +CHECK-NEXT: } +CHECK-NEXT: }, +CHECK-NEXT: "uri": "file:///basic.nix" +CHECK-NEXT: }, +CHECK-NEXT: { +CHECK-NEXT: "range": { +CHECK-NEXT: "end": { +CHECK-NEXT: "character": 41, +CHECK-NEXT: "line": 0 +CHECK-NEXT: }, +CHECK-NEXT: "start": { +CHECK-NEXT: "character": 38, +CHECK-NEXT: "line": 0 +CHECK-NEXT: } +CHECK-NEXT: }, +CHECK-NEXT: "uri": "file:///basic.nix" +CHECK-NEXT: } +CHECK-NEXT: ] +``` + +```json +{"jsonrpc":"2.0","method":"exit"} +``` diff --git a/nixd/tools/nixd/test/references-let.md b/nixd/tools/nixd/test/references-let.md new file mode 100644 index 000000000..ca6904965 --- /dev/null +++ b/nixd/tools/nixd/test/references-let.md @@ -0,0 +1,80 @@ +# RUN: nixd --lit-test < %s | FileCheck %s + +<-- 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":"let x = 1; y = 2; in x + y" + } + } +} +``` + +<-- textDocument/references(2) + + +```json +{ + "jsonrpc":"2.0", + "id":2, + "method":"textDocument/references", + "params":{ + "textDocument":{ + "uri":"file:///basic.nix" + }, + "position":{ + "line": 0, + "character":4 + } + } +} +``` + +``` + CHECK: "id": 2, +CHECK-NEXT: "jsonrpc": "2.0", +CHECK-NEXT: "result": [ +CHECK-NEXT: { +CHECK-NEXT: "range": { +CHECK-NEXT: "end": { +CHECK-NEXT: "character": 22, +CHECK-NEXT: "line": 0 +CHECK-NEXT: }, +CHECK-NEXT: "start": { +CHECK-NEXT: "character": 21, +CHECK-NEXT: "line": 0 +CHECK-NEXT: } +CHECK-NEXT: }, +CHECK-NEXT: "uri": "file:///basic.nix" +CHECK-NEXT: } +CHECK-NEXT: ] +``` + +```json +{"jsonrpc":"2.0","method":"exit"} +``` diff --git a/nixd/tools/nixd/test/references-rec.md b/nixd/tools/nixd/test/references-rec.md new file mode 100644 index 000000000..c79ca9d08 --- /dev/null +++ b/nixd/tools/nixd/test/references-rec.md @@ -0,0 +1,80 @@ +# RUN: nixd --lit-test < %s | FileCheck %s + +<-- 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":"rec { x = 1; y = x; }" + } + } +} +``` + +<-- textDocument/references(2) + + +```json +{ + "jsonrpc":"2.0", + "id":2, + "method":"textDocument/references", + "params":{ + "textDocument":{ + "uri":"file:///basic.nix" + }, + "position":{ + "line": 0, + "character":6 + } + } +} +``` + +``` + CHECK: "id": 2, +CHECK-NEXT: "jsonrpc": "2.0", +CHECK-NEXT: "result": [ +CHECK-NEXT: { +CHECK-NEXT: "range": { +CHECK-NEXT: "end": { +CHECK-NEXT: "character": 18, +CHECK-NEXT: "line": 0 +CHECK-NEXT: }, +CHECK-NEXT: "start": { +CHECK-NEXT: "character": 17, +CHECK-NEXT: "line": 0 +CHECK-NEXT: } +CHECK-NEXT: }, +CHECK-NEXT: "uri": "file:///basic.nix" +CHECK-NEXT: } +CHECK-NEXT: ] +``` + +```json +{"jsonrpc":"2.0","method":"exit"} +``` diff --git a/nixd/tools/nixd/test/references-var.md b/nixd/tools/nixd/test/references-var.md new file mode 100644 index 000000000..4603ea4f8 --- /dev/null +++ b/nixd/tools/nixd/test/references-var.md @@ -0,0 +1,93 @@ +# RUN: nixd --lit-test < %s | FileCheck %s + +<-- 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":"arg @ { foo, bar }: arg + foo + bar + arg" + } + } +} +``` + +<-- textDocument/references(2) + + +```json +{ + "jsonrpc":"2.0", + "id":2, + "method":"textDocument/references", + "params":{ + "textDocument":{ + "uri":"file:///basic.nix" + }, + "position":{ + "line": 0, + "character":21 + } + } +} +``` + +``` + CHECK: "id": 2, +CHECK-NEXT: "jsonrpc": "2.0", +CHECK-NEXT: "result": [ +CHECK-NEXT: { +CHECK-NEXT: "range": { +CHECK-NEXT: "end": { +CHECK-NEXT: "character": 23, +CHECK-NEXT: "line": 0 +CHECK-NEXT: }, +CHECK-NEXT: "start": { +CHECK-NEXT: "character": 20, +CHECK-NEXT: "line": 0 +CHECK-NEXT: } +CHECK-NEXT: }, +CHECK-NEXT: "uri": "file:///basic.nix" +CHECK-NEXT: }, +CHECK-NEXT: { +CHECK-NEXT: "range": { +CHECK-NEXT: "end": { +CHECK-NEXT: "character": 41, +CHECK-NEXT: "line": 0 +CHECK-NEXT: }, +CHECK-NEXT: "start": { +CHECK-NEXT: "character": 38, +CHECK-NEXT: "line": 0 +CHECK-NEXT: } +CHECK-NEXT: }, +CHECK-NEXT: "uri": "file:///basic.nix" +CHECK-NEXT: } +CHECK-NEXT: ] +``` + +```json +{"jsonrpc":"2.0","method":"exit"} +``` diff --git a/nixd/tools/nixd/test/references-with.md b/nixd/tools/nixd/test/references-with.md new file mode 100644 index 000000000..d8799c835 --- /dev/null +++ b/nixd/tools/nixd/test/references-with.md @@ -0,0 +1,94 @@ +# RUN: nixd --lit-test < %s | FileCheck %s + +<-- 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 builtins; foo + bar" + } + } +} +``` + +<-- textDocument/references(2) + + +```json +{ + "jsonrpc":"2.0", + "id":2, + "method":"textDocument/references", + "params":{ + "textDocument":{ + "uri":"file:///basic.nix" + }, + "position":{ + "line": 0, + "character":3 + } + } +} +``` + +``` + CHECK: "id": 2, +CHECK-NEXT: "jsonrpc": "2.0", +CHECK-NEXT: "result": [ +CHECK-NEXT: { +CHECK-NEXT: "range": { +CHECK-NEXT: "end": { +CHECK-NEXT: "character": 18, +CHECK-NEXT: "line": 0 +CHECK-NEXT: }, +CHECK-NEXT: "start": { +CHECK-NEXT: "character": 15, +CHECK-NEXT: "line": 0 +CHECK-NEXT: } +CHECK-NEXT: }, +CHECK-NEXT: "uri": "file:///basic.nix" +CHECK-NEXT: }, +CHECK-NEXT: { +CHECK-NEXT: "range": { +CHECK-NEXT: "end": { +CHECK-NEXT: "character": 24, +CHECK-NEXT: "line": 0 +CHECK-NEXT: }, +CHECK-NEXT: "start": { +CHECK-NEXT: "character": 21, +CHECK-NEXT: "line": 0 +CHECK-NEXT: } +CHECK-NEXT: }, +CHECK-NEXT: "uri": "file:///basic.nix" +CHECK-NEXT: } +CHECK-NEXT: ] +``` + + +```json +{"jsonrpc":"2.0","method":"exit"} +```