module; #include export module lsp.provider.text_document.references; import tree_sitter; import spdlog; import std; import lsp.protocol; import lsp.codec.facade; import lsp.provider.base.interface; import lsp.manager.manager_hub; import lsp.language.symbol; import lsp.manager.document; extern "C" { } namespace transform = lsp::codec; export namespace lsp::provider::text_document { class References : public AutoRegisterProvider { public: static constexpr std::string_view kMethod = "textDocument/references"; static constexpr std::string_view kProviderName = "TextDocumentReferences"; References() = default; std::string ProvideResponse(const protocol::RequestMessage& request, ExecutionContext& context) override; private: std::vector BuildReferencesResponse(const protocol::ReferenceParams& params, ExecutionContext& context); std::vector FindReferences(const protocol::DocumentUri& uri, const std::string& identifier, bool include_declaration, ExecutionContext& context); std::string GetIdentifierAtPosition(const protocol::DocumentUri& uri, const protocol::Position& position, ExecutionContext& context); void FindReferencesInNode(TSNode node, const std::string& identifier, const std::string& content, const protocol::DocumentUri& uri, std::vector& locations, bool include_declaration); bool IsDefinitionNode(TSNode node, const std::string& identifier, const std::string& content); bool IsReferenceNode(TSNode node, const std::string& identifier, const std::string& content); }; namespace detail { constexpr const char* kIdentifier = "identifier"; constexpr const char* kCall = "call"; constexpr const char* kAttribute = "attribute"; constexpr const char* kFunctionDefinition = "function_definition_statement"; constexpr const char* kFunctionDeclaration = "function_declaration_statement"; constexpr const char* kClassDefinition = "class_definition_statement"; constexpr const char* kVarStatement = "var_statement"; constexpr const char* kConstStatement = "const_statement"; constexpr const char* kMethodWithImplementation = "method_with_implementation"; constexpr const char* kAssignmentExpression = "assignment_expression"; } using namespace detail; } namespace lsp::provider::text_document { std::string References::ProvideResponse(const protocol::RequestMessage& request, [[maybe_unused]] ExecutionContext& context) { spdlog::debug("TextDocumentReferencesProvider: Providing response for method {}", request.method); protocol::ResponseMessage response; response.id = request.id; response.result = transform::ToLSPAny(std::vector{}); std::optional json = transform::Serialize(response); if (!json.has_value()) return BuildErrorResponseMessage(request, protocol::ErrorCodes::InternalError, "Failed to serialize response"); return json.value(); } std::vector References::BuildReferencesResponse(const protocol::ReferenceParams& params, ExecutionContext& context) { spdlog::trace("{}: Processing references request for URI='{}', Position=({}, {})", GetProviderName(), params.textDocument.uri, params.position.line, params.position.character); std::string identifier = GetIdentifierAtPosition( params.textDocument.uri, params.position, context); if (identifier.empty()) { spdlog::info("{}: No identifier at position", GetProviderName()); return {}; } spdlog::debug("{}: Looking for references of '{}'", GetProviderName(), identifier); auto locations = FindReferences(params.textDocument.uri, identifier, params.context.includeDeclaration, context); spdlog::info("{}: Found {} references", GetProviderName(), locations.size()); return locations; } std::vector References::FindReferences(const protocol::DocumentUri& uri, const std::string& identifier, bool include_declaration, ExecutionContext& context) { std::vector locations; auto& hub = context.GetManagerHub(); auto content = hub.documents().GetContent(uri); auto tree = hub.parser().GetTree(uri); if (!content.has_value() || !tree) { spdlog::warn("{}: Document not found or no syntax tree: {}", GetProviderName(), uri); return locations; } TSNode root = ts_tree_root_node(tree); FindReferencesInNode(root, identifier, *content, uri, locations, include_declaration); return locations; } std::string References::GetIdentifierAtPosition(const protocol::DocumentUri& uri, const protocol::Position& position, ExecutionContext& context) { auto& hub = context.GetManagerHub(); auto content = hub.documents().GetContent(uri); auto tree = hub.parser().GetTree(uri); if (!content.has_value() || !tree) return ""; size_t byte_offset = 0; size_t current_line = 0; size_t current_col = 0; for (size_t i = 0; i < content->length(); i++) { if (current_line == position.line && current_col == position.character) { byte_offset = i; break; } if ((*content)[i] == '\n') { current_line++; current_col = 0; } else { current_col++; } } TSNode root = ts_tree_root_node(tree); TSNode node = ts_node_descendant_for_byte_range(root, byte_offset, byte_offset); while (!ts_node_is_null(node)) { const char* node_type = ts_node_type(node); if (strcmp(node_type, kIdentifier) == 0) { uint32_t start = ts_node_start_byte(node); uint32_t end = ts_node_end_byte(node); return content->substr(start, end - start); } node = ts_node_parent(node); } return ""; } void References::FindReferencesInNode(TSNode /*node*/, const std::string& /*identifier*/, const std::string& /*content*/, const protocol::DocumentUri& /*uri*/, std::vector& /*locations*/, bool /*include_declaration*/) { } bool References::IsDefinitionNode(TSNode /*node*/, const std::string& /*identifier*/, const std::string& /*content*/) { return false; } bool References::IsReferenceNode(TSNode /*node*/, const std::string& /*identifier*/, const std::string& /*content*/) { return false; } }