195 lines
7.1 KiB
C++
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;
|
|
}
|
|
}
|