tsl-devkit/lsp-server/src/provider/text_document/references.cppm

195 lines
7.1 KiB
C++

module;
#include <cstring>
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<References, IRequestProvider>
{
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<protocol::Location> BuildReferencesResponse(const protocol::ReferenceParams& params, ExecutionContext& context);
std::vector<protocol::Location> 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<protocol::Location>& 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<protocol::Location>{});
std::optional<std::string> json = transform::Serialize(response);
if (!json.has_value())
return BuildErrorResponseMessage(request, protocol::ErrorCodes::InternalError, "Failed to serialize response");
return json.value();
}
std::vector<protocol::Location> 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<protocol::Location> References::FindReferences(const protocol::DocumentUri& uri, const std::string& identifier, bool include_declaration, ExecutionContext& context)
{
std::vector<protocol::Location> 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<protocol::Location>& /*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;
}
}