♻️ 重构符号表的代码
This commit is contained in:
parent
99f735f2df
commit
3274af67d5
File diff suppressed because it is too large
Load Diff
|
|
@ -2,11 +2,8 @@
|
|||
|
||||
#include "./table.hpp"
|
||||
|
||||
namespace lsp::language::symbol
|
||||
{
|
||||
// Symbol table builder - responsible for stable traversal logic only
|
||||
class Builder : public ast::ASTVisitor
|
||||
{
|
||||
namespace lsp::language::symbol {
|
||||
class Builder : public ast::ASTVisitor {
|
||||
public:
|
||||
explicit Builder(SymbolTable& table);
|
||||
|
||||
|
|
@ -20,7 +17,8 @@ namespace lsp::language::symbol
|
|||
void VisitFunctionDeclaration(ast::FunctionDeclaration& node) override;
|
||||
void VisitMethodDeclaration(ast::MethodDeclaration& node) override;
|
||||
void VisitPropertyDeclaration(ast::PropertyDeclaration& node) override;
|
||||
void VisitExternalMethodDefinition(ast::ExternalMethodDefinition& node) override;
|
||||
void VisitExternalMethodDefinition(
|
||||
ast::ExternalMethodDefinition& node) override;
|
||||
void VisitClassMember(ast::ClassMember& node) override;
|
||||
|
||||
// Declarations
|
||||
|
|
@ -54,41 +52,54 @@ namespace lsp::language::symbol
|
|||
void VisitTernaryExpression(ast::TernaryExpression& node) override;
|
||||
void VisitSubscriptExpression(ast::SubscriptExpression& node) override;
|
||||
void VisitArrayExpression(ast::ArrayExpression& node) override;
|
||||
void VisitAnonymousFunctionExpression(ast::AnonymousFunctionExpression& node) override;
|
||||
void VisitAnonymousFunctionExpression(
|
||||
ast::AnonymousFunctionExpression& node) override;
|
||||
|
||||
// Unary expressions (refined types)
|
||||
void VisitUnaryPlusExpression(ast::UnaryPlusExpression& node) override;
|
||||
void VisitUnaryMinusExpression(ast::UnaryMinusExpression& node) override;
|
||||
void VisitPrefixIncrementExpression(ast::PrefixIncrementExpression& node) override;
|
||||
void VisitPrefixDecrementExpression(ast::PrefixDecrementExpression& node) override;
|
||||
void VisitPostfixIncrementExpression(ast::PostfixIncrementExpression& node) override;
|
||||
void VisitPostfixDecrementExpression(ast::PostfixDecrementExpression& node) override;
|
||||
void VisitPrefixIncrementExpression(
|
||||
ast::PrefixIncrementExpression& node) override;
|
||||
void VisitPrefixDecrementExpression(
|
||||
ast::PrefixDecrementExpression& node) override;
|
||||
void VisitPostfixIncrementExpression(
|
||||
ast::PostfixIncrementExpression& node) override;
|
||||
void VisitPostfixDecrementExpression(
|
||||
ast::PostfixDecrementExpression& node) override;
|
||||
void VisitLogicalNotExpression(ast::LogicalNotExpression& node) override;
|
||||
void VisitBitwiseNotExpression(ast::BitwiseNotExpression& node) override;
|
||||
void VisitDerivativeExpression(ast::DerivativeExpression& node) override;
|
||||
void VisitMatrixTransposeExpression(ast::MatrixTransposeExpression& node) override;
|
||||
void VisitMatrixTransposeExpression(
|
||||
ast::MatrixTransposeExpression& node) override;
|
||||
void VisitExprOperatorExpression(ast::ExprOperatorExpression& node) override;
|
||||
|
||||
void VisitFunctionPointerExpression([[maybe_unused]] ast::FunctionPointerExpression& node) override;
|
||||
void VisitFunctionPointerExpression(
|
||||
[[maybe_unused]] ast::FunctionPointerExpression& node) override;
|
||||
|
||||
// Other expressions
|
||||
void VisitNewExpression(ast::NewExpression& node) override;
|
||||
void VisitEchoExpression(ast::EchoExpression& node) override;
|
||||
void VisitRaiseExpression(ast::RaiseExpression& node) override;
|
||||
void VisitInheritedExpression(ast::InheritedExpression& node) override;
|
||||
void VisitParenthesizedExpression(ast::ParenthesizedExpression& node) override;
|
||||
void VisitParenthesizedExpression(
|
||||
ast::ParenthesizedExpression& node) override;
|
||||
|
||||
void VisitExpressionStatement(ast::ExpressionStatement& node) override;
|
||||
void VisitBreakStatement([[maybe_unused]] ast::BreakStatement& node) override {}
|
||||
void VisitContinueStatement([[maybe_unused]] ast::ContinueStatement& node) override {}
|
||||
void VisitBreakStatement(
|
||||
[[maybe_unused]] ast::BreakStatement& node) override {}
|
||||
void VisitContinueStatement(
|
||||
[[maybe_unused]] ast::ContinueStatement& node) override {}
|
||||
void VisitReturnStatement(ast::ReturnStatement& node) override;
|
||||
void VisitTSSQLExpression([[maybe_unused]] ast::TSSQLExpression& node) override {}
|
||||
void VisitTSSQLExpression(
|
||||
[[maybe_unused]] ast::TSSQLExpression& node) override {}
|
||||
void VisitColumnReference(ast::ColumnReference& node) override;
|
||||
void VisitUnpackPattern([[maybe_unused]] ast::UnpackPattern& node) override;
|
||||
void VisitMatrixIterationStatement(ast::MatrixIterationStatement& node) override;
|
||||
void VisitMatrixIterationStatement(
|
||||
ast::MatrixIterationStatement& node) override;
|
||||
|
||||
// Compiler directives (correct names without Statement suffix)
|
||||
void VisitCompilerDirective([[maybe_unused]] ast::CompilerDirective& node) override;
|
||||
void VisitCompilerDirective(
|
||||
[[maybe_unused]] ast::CompilerDirective& node) override;
|
||||
void VisitConditionalDirective(ast::ConditionalDirective& node) override;
|
||||
void VisitConditionalBlock(ast::ConditionalBlock& node) override;
|
||||
void VisitTSLXBlock([[maybe_unused]] ast::TSLXBlock& node) override {}
|
||||
|
|
@ -96,33 +107,27 @@ namespace lsp::language::symbol
|
|||
void VisitParameter([[maybe_unused]] ast::Parameter& node) override {}
|
||||
|
||||
private:
|
||||
// Symbol creation helpers - now returns symbol with parameters pre-set
|
||||
SymbolId CreateFunctionSymbol(
|
||||
const std::string& name,
|
||||
const ast::Location& location,
|
||||
const std::string& name, const ast::Location& location,
|
||||
const std::vector<std::unique_ptr<ast::Parameter>>& parameters,
|
||||
const std::optional<ast::TypeAnnotation>& return_type);
|
||||
|
||||
SymbolId CreateMethodSymbol(
|
||||
const std::string& name,
|
||||
const ast::Location& location,
|
||||
const std::string& name, const ast::Location& location,
|
||||
const std::vector<std::unique_ptr<ast::Parameter>>& parameters,
|
||||
const std::optional<ast::TypeAnnotation>& return_type);
|
||||
|
||||
SymbolId CreateSymbol(
|
||||
const std::string& name,
|
||||
SymbolKind kind,
|
||||
const ast::Location& location,
|
||||
const std::string& name, SymbolKind kind, const ast::Location& location,
|
||||
const std::optional<std::string>& type_hint = std::nullopt);
|
||||
|
||||
SymbolId CreateSymbol(
|
||||
const std::string& name,
|
||||
SymbolKind kind,
|
||||
const ast::ASTNode& node,
|
||||
const std::string& name, SymbolKind kind, const ast::ASTNode& node,
|
||||
const std::optional<std::string>& type_hint = std::nullopt);
|
||||
|
||||
// Scope management
|
||||
ScopeId EnterScopeWithSymbol(ScopeKind kind, SymbolId symbol_id, const ast::Location& range);
|
||||
ScopeId EnterScopeWithSymbol(ScopeKind kind, SymbolId symbol_id,
|
||||
const ast::Location& range);
|
||||
ScopeId EnterScope(ScopeKind kind, const ast::Location& range);
|
||||
void ExitScope();
|
||||
|
||||
|
|
@ -141,16 +146,13 @@ namespace lsp::language::symbol
|
|||
const std::vector<std::unique_ptr<ast::Parameter>>& parameters) const;
|
||||
|
||||
private:
|
||||
// Symbol table reference
|
||||
SymbolTable& table_;
|
||||
|
||||
// Current traversal state
|
||||
ScopeId current_scope_id_;
|
||||
std::optional<SymbolId> current_parent_symbol_id_;
|
||||
std::optional<SymbolId> current_function_id_;
|
||||
|
||||
// Interface/implementation section tracking
|
||||
bool in_interface_section_;
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace lsp::language::symbol
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
#include "call.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace lsp::language::symbol::graph {
|
||||
|
||||
void Call::OnSymbolRemoved(SymbolId id) {
|
||||
callers_map_.erase(id);
|
||||
callees_map_.erase(id);
|
||||
|
||||
for (auto& [_, calls] : callers_map_) {
|
||||
calls.erase(std::remove_if(calls.begin(), calls.end(),
|
||||
[id](const symbol::Call& call) {
|
||||
return call.caller == id;
|
||||
}),
|
||||
calls.end());
|
||||
}
|
||||
|
||||
for (auto& [_, calls] : callees_map_) {
|
||||
calls.erase(std::remove_if(calls.begin(), calls.end(),
|
||||
[id](const symbol::Call& call) {
|
||||
return call.callee == id;
|
||||
}),
|
||||
calls.end());
|
||||
}
|
||||
}
|
||||
|
||||
void Call::Clear() {
|
||||
callers_map_.clear();
|
||||
callees_map_.clear();
|
||||
}
|
||||
|
||||
void Call::AddCall(SymbolId caller, SymbolId callee,
|
||||
const ast::Location& location) {
|
||||
symbol::Call call{caller, callee, location};
|
||||
callers_map_[callee].push_back(call);
|
||||
callees_map_[caller].push_back(call);
|
||||
}
|
||||
|
||||
const std::vector<symbol::Call>& Call::callers(SymbolId id) const {
|
||||
static const std::vector<symbol::Call> kEmpty;
|
||||
auto it = callers_map_.find(id);
|
||||
return it != callers_map_.end() ? it->second : kEmpty;
|
||||
}
|
||||
|
||||
const std::vector<symbol::Call>& Call::callees(SymbolId id) const {
|
||||
static const std::vector<symbol::Call> kEmpty;
|
||||
auto it = callees_map_.find(id);
|
||||
return it != callees_map_.end() ? it->second : kEmpty;
|
||||
}
|
||||
|
||||
} // namespace lsp::language::symbol::graph
|
||||
|
|
@ -1,77 +1,26 @@
|
|||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "../interface.hpp"
|
||||
#include "../types.hpp"
|
||||
|
||||
namespace lsp::language::symbol::graph
|
||||
{
|
||||
namespace lsp::language::symbol::graph {
|
||||
|
||||
class Call : public ISymbolGraph
|
||||
{
|
||||
class Call : public ISymbolGraph {
|
||||
public:
|
||||
void OnSymbolRemoved(SymbolId id) override
|
||||
{
|
||||
callers_map_.erase(id);
|
||||
callees_map_.erase(id);
|
||||
void OnSymbolRemoved(SymbolId id) override;
|
||||
void Clear() override;
|
||||
|
||||
// Remove all calls where this symbol is the caller
|
||||
for (auto& [symbol_id, calls] : callers_map_)
|
||||
{
|
||||
calls.erase(std::remove_if(calls.begin(), calls.end(), [id](const symbol::Call& call) {
|
||||
return call.caller == id;
|
||||
}),
|
||||
calls.end());
|
||||
}
|
||||
void AddCall(SymbolId caller, SymbolId callee, const ast::Location& location);
|
||||
|
||||
// Remove all calls where this symbol is the callee
|
||||
for (auto& [symbol_id, calls] : callees_map_)
|
||||
{
|
||||
calls.erase(std::remove_if(calls.begin(), calls.end(), [id](const symbol::Call& call) {
|
||||
return call.callee == id;
|
||||
}),
|
||||
calls.end());
|
||||
}
|
||||
}
|
||||
|
||||
void Clear() override
|
||||
{
|
||||
callers_map_.clear();
|
||||
callees_map_.clear();
|
||||
}
|
||||
|
||||
void AddCall(SymbolId caller, SymbolId callee, const ast::Location& location)
|
||||
{
|
||||
symbol::Call call{ caller, callee, location };
|
||||
callers_map_[callee].push_back(call); // Who calls this symbol
|
||||
callees_map_[caller].push_back(call); // What this symbol calls
|
||||
}
|
||||
|
||||
// Accessor (snake_case)
|
||||
const std::vector<symbol::Call>& callers(SymbolId id) const
|
||||
{
|
||||
static const std::vector<symbol::Call> kEmpty;
|
||||
auto it = callers_map_.find(id);
|
||||
return it != callers_map_.end() ? it->second : kEmpty;
|
||||
}
|
||||
|
||||
// Accessor (snake_case)
|
||||
const std::vector<symbol::Call>& callees(SymbolId id) const
|
||||
{
|
||||
static const std::vector<symbol::Call> kEmpty;
|
||||
auto it = callees_map_.find(id);
|
||||
return it != callees_map_.end() ? it->second : kEmpty;
|
||||
}
|
||||
const std::vector<symbol::Call>& callers(SymbolId id) const;
|
||||
const std::vector<symbol::Call>& callees(SymbolId id) const;
|
||||
|
||||
private:
|
||||
// Map: symbol -> who calls it (incoming calls)
|
||||
std::unordered_map<SymbolId, std::vector<symbol::Call>> callers_map_;
|
||||
|
||||
// Map: symbol -> what it calls (outgoing calls)
|
||||
std::unordered_map<SymbolId, std::vector<symbol::Call>> callees_map_;
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace lsp::language::symbol::graph
|
||||
|
|
|
|||
|
|
@ -0,0 +1,58 @@
|
|||
#include "inheritance.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace lsp::language::symbol::graph {
|
||||
|
||||
void Inheritance::OnSymbolRemoved(SymbolId id) {
|
||||
base_classes_.erase(id);
|
||||
derived_classes_.erase(id);
|
||||
|
||||
for (auto& [_, bases] : base_classes_) {
|
||||
bases.erase(std::remove(bases.begin(), bases.end(), id), bases.end());
|
||||
}
|
||||
|
||||
for (auto& [_, derived] : derived_classes_) {
|
||||
derived.erase(std::remove(derived.begin(), derived.end(), id),
|
||||
derived.end());
|
||||
}
|
||||
}
|
||||
|
||||
void Inheritance::Clear() {
|
||||
base_classes_.clear();
|
||||
derived_classes_.clear();
|
||||
}
|
||||
|
||||
void Inheritance::AddInheritance(SymbolId derived, SymbolId base) {
|
||||
base_classes_[derived].push_back(base);
|
||||
derived_classes_[base].push_back(derived);
|
||||
}
|
||||
|
||||
const std::vector<SymbolId>& Inheritance::base_classes(SymbolId id) const {
|
||||
static const std::vector<SymbolId> kEmpty;
|
||||
auto it = base_classes_.find(id);
|
||||
return it != base_classes_.end() ? it->second : kEmpty;
|
||||
}
|
||||
|
||||
const std::vector<SymbolId>& Inheritance::derived_classes(SymbolId id) const {
|
||||
static const std::vector<SymbolId> kEmpty;
|
||||
auto it = derived_classes_.find(id);
|
||||
return it != derived_classes_.end() ? it->second : kEmpty;
|
||||
}
|
||||
|
||||
bool Inheritance::IsSubclassOf(SymbolId derived, SymbolId base) const {
|
||||
auto it = base_classes_.find(derived);
|
||||
if (it == base_classes_.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (SymbolId parent : it->second) {
|
||||
if (parent == base || IsSubclassOf(parent, base)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace lsp::language::symbol::graph
|
||||
|
|
@ -1,90 +1,27 @@
|
|||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "../interface.hpp"
|
||||
#include "../types.hpp"
|
||||
|
||||
namespace lsp::language::symbol::graph
|
||||
{
|
||||
namespace lsp::language::symbol::graph {
|
||||
|
||||
class Inheritance : public ISymbolGraph
|
||||
{
|
||||
class Inheritance : public ISymbolGraph {
|
||||
public:
|
||||
void OnSymbolRemoved(SymbolId id) override
|
||||
{
|
||||
base_classes_.erase(id);
|
||||
derived_classes_.erase(id);
|
||||
void OnSymbolRemoved(SymbolId id) override;
|
||||
void Clear() override;
|
||||
|
||||
// Remove from all base class lists
|
||||
for (auto& [derived_id, bases] : base_classes_)
|
||||
{
|
||||
bases.erase(std::remove(bases.begin(), bases.end(), id), bases.end());
|
||||
}
|
||||
void AddInheritance(SymbolId derived, SymbolId base);
|
||||
|
||||
// Remove from all derived class lists
|
||||
for (auto& [base_id, derived] : derived_classes_)
|
||||
{
|
||||
derived.erase(std::remove(derived.begin(), derived.end(), id),
|
||||
derived.end());
|
||||
}
|
||||
}
|
||||
|
||||
void Clear() override
|
||||
{
|
||||
base_classes_.clear();
|
||||
derived_classes_.clear();
|
||||
}
|
||||
|
||||
void AddInheritance(SymbolId derived, SymbolId base)
|
||||
{
|
||||
base_classes_[derived].push_back(base);
|
||||
derived_classes_[base].push_back(derived);
|
||||
}
|
||||
|
||||
// Accessor (snake_case)
|
||||
const std::vector<SymbolId>& base_classes(SymbolId id) const
|
||||
{
|
||||
static const std::vector<SymbolId> kEmpty;
|
||||
auto it = base_classes_.find(id);
|
||||
return it != base_classes_.end() ? it->second : kEmpty;
|
||||
}
|
||||
|
||||
// Accessor (snake_case)
|
||||
const std::vector<SymbolId>& derived_classes(SymbolId id) const
|
||||
{
|
||||
static const std::vector<SymbolId> kEmpty;
|
||||
auto it = derived_classes_.find(id);
|
||||
return it != derived_classes_.end() ? it->second : kEmpty;
|
||||
}
|
||||
|
||||
bool IsSubclassOf(SymbolId derived, SymbolId base) const
|
||||
{
|
||||
auto it = base_classes_.find(derived);
|
||||
if (it == base_classes_.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (SymbolId parent : it->second)
|
||||
{
|
||||
if (parent == base || IsSubclassOf(parent, base))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
const std::vector<SymbolId>& base_classes(SymbolId id) const;
|
||||
const std::vector<SymbolId>& derived_classes(SymbolId id) const;
|
||||
bool IsSubclassOf(SymbolId derived, SymbolId base) const;
|
||||
|
||||
private:
|
||||
// Map from derived class to its base classes
|
||||
std::unordered_map<SymbolId, std::vector<SymbolId>> base_classes_;
|
||||
|
||||
// Map from base class to its derived classes
|
||||
std::unordered_map<SymbolId, std::vector<SymbolId>> derived_classes_;
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace lsp::language::symbol::graph
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
#include "reference.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace lsp::language::symbol::graph {
|
||||
|
||||
void Reference::OnSymbolRemoved(SymbolId id) {
|
||||
references_.erase(id);
|
||||
|
||||
for (auto& [_, refs] : references_) {
|
||||
refs.erase(std::remove_if(refs.begin(), refs.end(),
|
||||
[id](const symbol::Reference& ref) {
|
||||
return ref.symbol_id == id;
|
||||
}),
|
||||
refs.end());
|
||||
}
|
||||
}
|
||||
|
||||
void Reference::Clear() { references_.clear(); }
|
||||
|
||||
void Reference::AddReference(SymbolId symbol_id, const ast::Location& location,
|
||||
bool is_definition, bool is_write) {
|
||||
references_[symbol_id].push_back(
|
||||
{location, symbol_id, is_definition, is_write});
|
||||
}
|
||||
|
||||
const std::vector<symbol::Reference>& Reference::references(SymbolId id) const {
|
||||
static const std::vector<symbol::Reference> kEmpty;
|
||||
auto it = references_.find(id);
|
||||
return it != references_.end() ? it->second : kEmpty;
|
||||
}
|
||||
|
||||
std::optional<ast::Location> Reference::FindDefinitionLocation(
|
||||
SymbolId id) const {
|
||||
auto it = references_.find(id);
|
||||
if (it != references_.end()) {
|
||||
for (const auto& ref : it->second) {
|
||||
if (ref.is_definition) {
|
||||
return ref.location;
|
||||
}
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
} // namespace lsp::language::symbol::graph
|
||||
|
|
@ -1,67 +1,27 @@
|
|||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "../interface.hpp"
|
||||
#include "../types.hpp"
|
||||
|
||||
namespace lsp::language::symbol::graph
|
||||
{
|
||||
namespace lsp::language::symbol::graph {
|
||||
|
||||
class Reference : public ISymbolGraph
|
||||
{
|
||||
class Reference : public ISymbolGraph {
|
||||
public:
|
||||
void OnSymbolRemoved(SymbolId id) override
|
||||
{
|
||||
references_.erase(id);
|
||||
void OnSymbolRemoved(SymbolId id) override;
|
||||
void Clear() override;
|
||||
|
||||
// Remove all references to the removed symbol
|
||||
for (auto& [symbol_id, refs] : references_)
|
||||
{
|
||||
refs.erase(std::remove_if(refs.begin(), refs.end(), [id](const symbol::Reference& ref) {
|
||||
return ref.symbol_id == id;
|
||||
}),
|
||||
refs.end());
|
||||
}
|
||||
}
|
||||
void AddReference(SymbolId symbol_id, const ast::Location& location,
|
||||
bool is_definition = false, bool is_write = false);
|
||||
|
||||
void Clear() override { references_.clear(); }
|
||||
|
||||
void AddReference(SymbolId symbol_id, const ast::Location& location, bool is_definition = false, bool is_write = false)
|
||||
{
|
||||
references_[symbol_id].push_back(
|
||||
{ location, symbol_id, is_definition, is_write });
|
||||
}
|
||||
|
||||
// Accessor (snake_case)
|
||||
const std::vector<symbol::Reference>& references(SymbolId id) const
|
||||
{
|
||||
static const std::vector<symbol::Reference> kEmpty;
|
||||
auto it = references_.find(id);
|
||||
return it != references_.end() ? it->second : kEmpty;
|
||||
}
|
||||
|
||||
std::optional<ast::Location> FindDefinitionLocation(SymbolId id) const
|
||||
{
|
||||
auto it = references_.find(id);
|
||||
if (it != references_.end())
|
||||
{
|
||||
for (const auto& ref : it->second)
|
||||
{
|
||||
if (ref.is_definition)
|
||||
{
|
||||
return ref.location;
|
||||
}
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
const std::vector<symbol::Reference>& references(SymbolId id) const;
|
||||
std::optional<ast::Location> FindDefinitionLocation(SymbolId id) const;
|
||||
|
||||
private:
|
||||
// Map from symbol to all its references
|
||||
std::unordered_map<SymbolId, std::vector<symbol::Reference>> references_;
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace lsp::language::symbol::graph
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
#include "location.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace lsp::language::symbol::index {
|
||||
|
||||
void Location::OnSymbolAdded(const Symbol& symbol) {
|
||||
const auto& loc = symbol.selection_range();
|
||||
entries_.push_back({loc.start_offset, loc.end_offset, symbol.id()});
|
||||
needs_sort_ = true;
|
||||
}
|
||||
|
||||
void Location::OnSymbolRemoved(SymbolId id) {
|
||||
entries_.erase(
|
||||
std::remove_if(entries_.begin(), entries_.end(),
|
||||
[id](const Entry& e) { return e.symbol_id == id; }),
|
||||
entries_.end());
|
||||
}
|
||||
|
||||
void Location::Clear() {
|
||||
entries_.clear();
|
||||
needs_sort_ = false;
|
||||
}
|
||||
|
||||
std::optional<SymbolId> Location::FindSymbolAt(
|
||||
const ast::Location& location) const {
|
||||
EnsureSorted();
|
||||
uint32_t pos = location.start_offset;
|
||||
|
||||
auto it =
|
||||
std::lower_bound(entries_.begin(), entries_.end(), pos,
|
||||
[](const Entry& e, uint32_t p) { return e.start < p; });
|
||||
|
||||
if (it != entries_.begin()) {
|
||||
--it;
|
||||
}
|
||||
|
||||
std::optional<SymbolId> result;
|
||||
uint32_t min_span = UINT32_MAX;
|
||||
|
||||
for (; it != entries_.end() && it->start <= pos; ++it) {
|
||||
if (pos >= it->start && pos < it->end) {
|
||||
uint32_t span = it->end - it->start;
|
||||
if (span < min_span) {
|
||||
min_span = span;
|
||||
result = it->symbol_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Location::Entry::operator<(const Entry& other) const {
|
||||
if (start != other.start) {
|
||||
return start < other.start;
|
||||
}
|
||||
return end > other.end;
|
||||
}
|
||||
|
||||
void Location::EnsureSorted() const {
|
||||
if (needs_sort_) {
|
||||
std::sort(entries_.begin(), entries_.end());
|
||||
needs_sort_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace lsp::language::symbol::index
|
||||
|
|
@ -1,100 +1,34 @@
|
|||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include "../interface.hpp"
|
||||
#include "../types.hpp"
|
||||
|
||||
namespace lsp::language::symbol::index
|
||||
{
|
||||
namespace lsp::language::symbol::index {
|
||||
|
||||
class Location : public ISymbolIndex
|
||||
{
|
||||
class Location : public ISymbolIndex {
|
||||
public:
|
||||
void OnSymbolAdded(const Symbol& symbol) override
|
||||
{
|
||||
const auto& loc = symbol.selection_range();
|
||||
entries_.push_back({ loc.start_offset, loc.end_offset, symbol.id() });
|
||||
needs_sort_ = true;
|
||||
}
|
||||
void OnSymbolAdded(const Symbol& symbol) override;
|
||||
void OnSymbolRemoved(SymbolId id) override;
|
||||
void Clear() override;
|
||||
|
||||
void OnSymbolRemoved(SymbolId id) override
|
||||
{
|
||||
entries_.erase(std::remove_if(entries_.begin(), entries_.end(), [id](const Entry& e) {
|
||||
return e.symbol_id == id;
|
||||
}),
|
||||
entries_.end());
|
||||
}
|
||||
|
||||
void Clear() override
|
||||
{
|
||||
entries_.clear();
|
||||
needs_sort_ = false;
|
||||
}
|
||||
|
||||
std::optional<SymbolId> FindSymbolAt(const ast::Location& location) const
|
||||
{
|
||||
EnsureSorted();
|
||||
uint32_t pos = location.start_offset;
|
||||
|
||||
auto it = std::lower_bound(entries_.begin(), entries_.end(), pos, [](const Entry& e, uint32_t p) {
|
||||
return e.start < p;
|
||||
});
|
||||
|
||||
if (it != entries_.begin())
|
||||
{
|
||||
--it;
|
||||
}
|
||||
|
||||
std::optional<SymbolId> result;
|
||||
uint32_t min_span = UINT32_MAX;
|
||||
|
||||
for (; it != entries_.end() && it->start <= pos; ++it)
|
||||
{
|
||||
if (pos >= it->start && pos < it->end)
|
||||
{
|
||||
uint32_t span = it->end - it->start;
|
||||
if (span < min_span)
|
||||
{
|
||||
min_span = span;
|
||||
result = it->symbol_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
std::optional<SymbolId> FindSymbolAt(const ast::Location& location) const;
|
||||
|
||||
private:
|
||||
struct Entry
|
||||
{
|
||||
struct Entry {
|
||||
uint32_t start;
|
||||
uint32_t end;
|
||||
SymbolId symbol_id;
|
||||
|
||||
bool operator<(const Entry& other) const
|
||||
{
|
||||
if (start != other.start)
|
||||
{
|
||||
return start < other.start;
|
||||
}
|
||||
return end > other.end;
|
||||
}
|
||||
bool operator<(const Entry& other) const;
|
||||
};
|
||||
|
||||
void EnsureSorted() const
|
||||
{
|
||||
if (needs_sort_)
|
||||
{
|
||||
std::sort(entries_.begin(), entries_.end());
|
||||
needs_sort_ = false;
|
||||
}
|
||||
}
|
||||
void EnsureSorted() const;
|
||||
|
||||
mutable std::vector<Entry> entries_;
|
||||
mutable bool needs_sort_ = false;
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace lsp::language::symbol::index
|
||||
|
|
|
|||
|
|
@ -0,0 +1,93 @@
|
|||
#include "scope.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace lsp::language::symbol::index {
|
||||
|
||||
void Scope::OnSymbolAdded(const Symbol&) {}
|
||||
|
||||
void Scope::OnSymbolRemoved(SymbolId id) {
|
||||
for (auto& [_, scope] : scopes_) {
|
||||
for (auto it = scope.symbols.begin(); it != scope.symbols.end();) {
|
||||
if (it->second == id) {
|
||||
it = scope.symbols.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Scope::Clear() {
|
||||
scopes_.clear();
|
||||
next_scope_id_ = 1;
|
||||
global_scope_ = kInvalidScopeId;
|
||||
}
|
||||
|
||||
ScopeId Scope::CreateScope(ScopeKind kind, const ast::Location& range,
|
||||
std::optional<ScopeId> parent,
|
||||
std::optional<SymbolId> owner) {
|
||||
ScopeId id = next_scope_id_++;
|
||||
scopes_[id] = {id, kind, range, parent, owner, {}};
|
||||
|
||||
if (kind == ScopeKind::kGlobal) {
|
||||
global_scope_ = id;
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void Scope::AddSymbol(ScopeId scope_id, const std::string& name,
|
||||
SymbolId symbol_id) {
|
||||
auto it = scopes_.find(scope_id);
|
||||
if (it != scopes_.end()) {
|
||||
it->second.symbols[ToLower(name)] = symbol_id;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<SymbolId> Scope::FindSymbolInScope(
|
||||
ScopeId scope_id, const std::string& name) const {
|
||||
auto it = scopes_.find(scope_id);
|
||||
if (it == scopes_.end()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto sym_it = it->second.symbols.find(ToLower(name));
|
||||
return sym_it != it->second.symbols.end() ? std::optional(sym_it->second)
|
||||
: std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<SymbolId> Scope::FindSymbolInScopeChain(
|
||||
ScopeId scope_id, const std::string& name) const {
|
||||
std::optional<ScopeId> current = scope_id;
|
||||
|
||||
while (current) {
|
||||
if (auto result = FindSymbolInScope(*current, name)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
auto it = scopes_.find(*current);
|
||||
current = it != scopes_.end() ? it->second.parent : std::nullopt;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const symbol::Scope* Scope::scope(ScopeId id) const {
|
||||
auto it = scopes_.find(id);
|
||||
return it != scopes_.end() ? &it->second : nullptr;
|
||||
}
|
||||
|
||||
ScopeId Scope::global_scope() const { return global_scope_; }
|
||||
|
||||
const std::unordered_map<ScopeId, symbol::Scope>& Scope::all_scopes() const {
|
||||
return scopes_;
|
||||
}
|
||||
|
||||
std::string Scope::ToLower(const std::string& s) {
|
||||
std::string result = s;
|
||||
std::transform(result.begin(), result.end(), result.begin(), ::tolower);
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace lsp::language::symbol::index
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
|
@ -8,137 +7,46 @@
|
|||
#include "../interface.hpp"
|
||||
#include "../types.hpp"
|
||||
|
||||
namespace lsp::language::symbol
|
||||
{
|
||||
namespace lsp::language::symbol {
|
||||
|
||||
struct Scope
|
||||
{
|
||||
struct Scope {
|
||||
ScopeId id;
|
||||
ScopeKind kind;
|
||||
ast::Location range;
|
||||
std::optional<ScopeId> parent;
|
||||
std::optional<SymbolId> owner;
|
||||
std::unordered_map<std::string, SymbolId> symbols;
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace lsp::language::symbol
|
||||
|
||||
namespace lsp::language::symbol::index
|
||||
{
|
||||
namespace lsp::language::symbol::index {
|
||||
|
||||
class Scope : public ISymbolIndex
|
||||
{
|
||||
class Scope : public ISymbolIndex {
|
||||
public:
|
||||
void OnSymbolAdded(const Symbol&) override {}
|
||||
void OnSymbolAdded(const Symbol&) override;
|
||||
void OnSymbolRemoved(SymbolId id) override;
|
||||
void Clear() override;
|
||||
|
||||
void OnSymbolRemoved(SymbolId id) override
|
||||
{
|
||||
for (auto& [_, scope] : scopes_)
|
||||
{
|
||||
for (auto it = scope.symbols.begin(); it != scope.symbols.end();)
|
||||
{
|
||||
if (it->second == id)
|
||||
{
|
||||
it = scope.symbols.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ScopeId CreateScope(ScopeKind kind, const ast::Location& range,
|
||||
std::optional<ScopeId> parent = std::nullopt,
|
||||
std::optional<SymbolId> owner = std::nullopt);
|
||||
void AddSymbol(ScopeId scope_id, const std::string& name, SymbolId symbol_id);
|
||||
std::optional<SymbolId> FindSymbolInScope(ScopeId scope_id,
|
||||
const std::string& name) const;
|
||||
std::optional<SymbolId> FindSymbolInScopeChain(ScopeId scope_id,
|
||||
const std::string& name) const;
|
||||
|
||||
void Clear() override
|
||||
{
|
||||
scopes_.clear();
|
||||
next_scope_id_ = 1;
|
||||
global_scope_ = kInvalidScopeId;
|
||||
}
|
||||
|
||||
ScopeId CreateScope(ScopeKind kind, const ast::Location& range, std::optional<ScopeId> parent = std::nullopt, std::optional<SymbolId> owner = std::nullopt)
|
||||
{
|
||||
ScopeId id = next_scope_id_++;
|
||||
scopes_[id] = { id, kind, range, parent, owner, {} };
|
||||
|
||||
if (kind == ScopeKind::kGlobal)
|
||||
{
|
||||
global_scope_ = id;
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void AddSymbol(ScopeId scope_id, const std::string& name, SymbolId symbol_id)
|
||||
{
|
||||
auto it = scopes_.find(scope_id);
|
||||
if (it != scopes_.end())
|
||||
{
|
||||
it->second.symbols[ToLower(name)] = symbol_id;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<SymbolId> FindSymbolInScope(
|
||||
ScopeId scope_id,
|
||||
const std::string& name) const
|
||||
{
|
||||
auto it = scopes_.find(scope_id);
|
||||
if (it == scopes_.end())
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto sym_it = it->second.symbols.find(ToLower(name));
|
||||
return sym_it != it->second.symbols.end() ? std::optional(sym_it->second) : std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<SymbolId> FindSymbolInScopeChain(
|
||||
ScopeId scope_id,
|
||||
const std::string& name) const
|
||||
{
|
||||
std::optional<ScopeId> current = scope_id;
|
||||
|
||||
while (current)
|
||||
{
|
||||
if (auto result = FindSymbolInScope(*current, name))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
auto it = scopes_.find(*current);
|
||||
current = it != scopes_.end() ? it->second.parent : std::nullopt;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Accessor (snake_case)
|
||||
const symbol::Scope* scope(ScopeId id) const
|
||||
{
|
||||
auto it = scopes_.find(id);
|
||||
return it != scopes_.end() ? &it->second : nullptr;
|
||||
}
|
||||
|
||||
// Accessor (snake_case)
|
||||
ScopeId global_scope() const { return global_scope_; }
|
||||
|
||||
// Accessor (snake_case)
|
||||
const std::unordered_map<ScopeId, symbol::Scope>& all_scopes() const
|
||||
{
|
||||
return scopes_;
|
||||
}
|
||||
const symbol::Scope* scope(ScopeId id) const;
|
||||
ScopeId global_scope() const;
|
||||
const std::unordered_map<ScopeId, symbol::Scope>& all_scopes() const;
|
||||
|
||||
private:
|
||||
static std::string ToLower(const std::string& s)
|
||||
{
|
||||
std::string result = s;
|
||||
std::transform(result.begin(), result.end(), result.begin(), ::tolower);
|
||||
return result;
|
||||
}
|
||||
static std::string ToLower(const std::string& s);
|
||||
|
||||
ScopeId next_scope_id_ = 1;
|
||||
ScopeId global_scope_ = kInvalidScopeId;
|
||||
std::unordered_map<ScopeId, symbol::Scope> scopes_;
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace lsp::language::symbol::index
|
||||
|
|
|
|||
|
|
@ -2,26 +2,21 @@
|
|||
|
||||
#include "./types.hpp"
|
||||
|
||||
namespace lsp::language::symbol
|
||||
{
|
||||
namespace lsp::language::symbol {
|
||||
|
||||
// 索引接口:用于通过各种方式查找符号
|
||||
class ISymbolIndex
|
||||
{
|
||||
class ISymbolIndex {
|
||||
public:
|
||||
virtual ~ISymbolIndex() = default;
|
||||
virtual void OnSymbolAdded(const Symbol& symbol) = 0;
|
||||
virtual void OnSymbolRemoved(SymbolId id) = 0;
|
||||
virtual void Clear() = 0;
|
||||
};
|
||||
};
|
||||
|
||||
// 关系接口:用于管理符号之间的关系
|
||||
class ISymbolGraph
|
||||
{
|
||||
class ISymbolGraph {
|
||||
public:
|
||||
virtual ~ISymbolGraph() = default;
|
||||
virtual void OnSymbolRemoved(SymbolId id) = 0;
|
||||
virtual void Clear() = 0;
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace lsp::language::symbol
|
||||
|
|
|
|||
|
|
@ -1,95 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
#include <algorithm>
|
||||
#include "./interface.hpp"
|
||||
#include "./types.hpp"
|
||||
|
||||
namespace lsp::language::symbol
|
||||
{
|
||||
|
||||
class LocationIndex : public ISymbolIndex
|
||||
{
|
||||
public:
|
||||
void OnSymbolAdded(const Symbol& symbol) override
|
||||
{
|
||||
const auto& range = symbol.range();
|
||||
entries_.push_back({ range.start_offset,
|
||||
range.end_offset,
|
||||
symbol.id() });
|
||||
needs_sort_ = true;
|
||||
}
|
||||
|
||||
void OnSymbolRemoved(SymbolId id) override
|
||||
{
|
||||
entries_.erase(
|
||||
std::remove_if(entries_.begin(), entries_.end(), [id](const Entry& e) { return e.symbol_id == id; }),
|
||||
entries_.end());
|
||||
}
|
||||
|
||||
void Clear() override
|
||||
{
|
||||
entries_.clear();
|
||||
needs_sort_ = false;
|
||||
}
|
||||
|
||||
std::optional<SymbolId> FindAt(const ast::Location& location) const
|
||||
{
|
||||
EnsureSorted();
|
||||
uint32_t pos = location.start_offset;
|
||||
|
||||
auto it = std::lower_bound(
|
||||
entries_.begin(), entries_.end(), pos, [](const Entry& e, uint32_t p) { return e.start < p; });
|
||||
|
||||
if (it != entries_.begin())
|
||||
--it;
|
||||
|
||||
std::optional<SymbolId> result;
|
||||
uint32_t min_span = UINT32_MAX;
|
||||
|
||||
for (; it != entries_.end() && it->start <= pos; ++it)
|
||||
{
|
||||
if (pos >= it->start && pos < it->end)
|
||||
{
|
||||
uint32_t span = it->end - it->start;
|
||||
if (span < min_span)
|
||||
{
|
||||
min_span = span;
|
||||
result = it->symbol_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
struct Entry
|
||||
{
|
||||
uint32_t start;
|
||||
uint32_t end;
|
||||
SymbolId symbol_id;
|
||||
|
||||
bool operator<(const Entry& other) const
|
||||
{
|
||||
if (start != other.start)
|
||||
return start < other.start;
|
||||
return end > other.end;
|
||||
}
|
||||
};
|
||||
|
||||
void EnsureSorted() const
|
||||
{
|
||||
if (needs_sort_)
|
||||
{
|
||||
std::sort(entries_.begin(), entries_.end());
|
||||
needs_sort_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
mutable std::vector<Entry> entries_;
|
||||
mutable bool needs_sort_ = false;
|
||||
};
|
||||
|
||||
} // namespace lsp::language::symbol
|
||||
|
|
@ -1,130 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include "./interface.hpp"
|
||||
#include "./types.hpp"
|
||||
|
||||
namespace lsp::language::symbol
|
||||
{
|
||||
|
||||
struct Scope
|
||||
{
|
||||
ScopeId id;
|
||||
ScopeKind kind;
|
||||
ast::Location range;
|
||||
std::optional<ScopeId> parent;
|
||||
std::optional<SymbolId> owner;
|
||||
std::unordered_map<std::string, SymbolId> symbols;
|
||||
};
|
||||
|
||||
class ScopeIndex : public ISymbolIndex
|
||||
{
|
||||
public:
|
||||
void OnSymbolAdded(const Symbol&) override {}
|
||||
|
||||
void OnSymbolRemoved(SymbolId id) override
|
||||
{
|
||||
for (auto& [_, scope] : scopes_)
|
||||
{
|
||||
for (auto it = scope.symbols.begin(); it != scope.symbols.end();)
|
||||
{
|
||||
if (it->second == id)
|
||||
{
|
||||
it = scope.symbols.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Clear() override
|
||||
{
|
||||
scopes_.clear();
|
||||
next_scope_id_ = 1;
|
||||
global_scope_ = kInvalidScopeId;
|
||||
}
|
||||
|
||||
ScopeId CreateScope(ScopeKind kind, const ast::Location& range, std::optional<ScopeId> parent = std::nullopt, std::optional<SymbolId> owner = std::nullopt)
|
||||
{
|
||||
ScopeId id = next_scope_id_++;
|
||||
scopes_[id] = { id, kind, range, parent, owner, {} };
|
||||
|
||||
if (kind == ScopeKind::kGlobal)
|
||||
{
|
||||
global_scope_ = id;
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void AddSymbol(ScopeId scope_id, const std::string& name, SymbolId symbol_id)
|
||||
{
|
||||
auto it = scopes_.find(scope_id);
|
||||
if (it != scopes_.end())
|
||||
{
|
||||
it->second.symbols[ToLower(name)] = symbol_id;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<SymbolId> FindInScope(ScopeId scope_id, const std::string& name) const
|
||||
{
|
||||
auto it = scopes_.find(scope_id);
|
||||
if (it == scopes_.end())
|
||||
return std::nullopt;
|
||||
|
||||
auto sym_it = it->second.symbols.find(ToLower(name));
|
||||
return sym_it != it->second.symbols.end() ?
|
||||
std::optional(sym_it->second) :
|
||||
std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<SymbolId> FindInScopeChain(ScopeId scope_id, const std::string& name) const
|
||||
{
|
||||
std::optional<ScopeId> current = scope_id;
|
||||
|
||||
while (current)
|
||||
{
|
||||
if (auto result = FindInScope(*current, name))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
auto it = scopes_.find(*current);
|
||||
current = it != scopes_.end() ? it->second.parent : std::nullopt;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const Scope* GetScope(ScopeId id) const
|
||||
{
|
||||
auto it = scopes_.find(id);
|
||||
return it != scopes_.end() ? &it->second : nullptr;
|
||||
}
|
||||
|
||||
ScopeId GetGlobalScope() const { return global_scope_; }
|
||||
|
||||
const std::unordered_map<ScopeId, Scope>& GetAllScopes() const
|
||||
{
|
||||
return scopes_;
|
||||
}
|
||||
|
||||
private:
|
||||
static std::string ToLower(const std::string& s)
|
||||
{
|
||||
std::string result = s;
|
||||
std::transform(result.begin(), result.end(), result.begin(), ::tolower);
|
||||
return result;
|
||||
}
|
||||
|
||||
ScopeId next_scope_id_ = 1;
|
||||
ScopeId global_scope_ = kInvalidScopeId;
|
||||
std::unordered_map<ScopeId, Scope> scopes_;
|
||||
};
|
||||
|
||||
} // namespace lsp::language::symbol
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
#include "store.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace lsp::language::symbol {
|
||||
|
||||
SymbolId SymbolStore::Add(Symbol def) {
|
||||
SymbolId id = next_id_++;
|
||||
std::visit([id](auto& s) { s.id = id; }, def.mutable_data());
|
||||
|
||||
auto [it, _] = definitions_.emplace(id, std::move(def));
|
||||
const auto& stored = it->second;
|
||||
by_name_[stored.name()].push_back(id);
|
||||
return id;
|
||||
}
|
||||
|
||||
bool SymbolStore::Remove(SymbolId id) {
|
||||
auto it = definitions_.find(id);
|
||||
if (it == definitions_.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string& name = it->second.name();
|
||||
auto& ids = by_name_[name];
|
||||
ids.erase(std::remove(ids.begin(), ids.end(), id), ids.end());
|
||||
if (ids.empty()) {
|
||||
by_name_.erase(name);
|
||||
}
|
||||
|
||||
definitions_.erase(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
void SymbolStore::Clear() {
|
||||
definitions_.clear();
|
||||
by_name_.clear();
|
||||
next_id_ = 1;
|
||||
}
|
||||
|
||||
const Symbol* SymbolStore::Get(SymbolId id) const {
|
||||
auto it = definitions_.find(id);
|
||||
return it != definitions_.end() ? &it->second : nullptr;
|
||||
}
|
||||
|
||||
std::vector<std::reference_wrapper<const Symbol>> SymbolStore::GetAll() const {
|
||||
std::vector<std::reference_wrapper<const Symbol>> result;
|
||||
result.reserve(definitions_.size());
|
||||
for (const auto& [_, def] : definitions_) {
|
||||
result.push_back(std::cref(def));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<SymbolId> SymbolStore::FindByName(const std::string& name) const {
|
||||
auto it = by_name_.find(name);
|
||||
return it != by_name_.end() ? it->second : std::vector<SymbolId>();
|
||||
}
|
||||
|
||||
} // namespace lsp::language::symbol
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
|
@ -8,80 +7,22 @@
|
|||
|
||||
#include "./types.hpp"
|
||||
|
||||
namespace lsp::language::symbol
|
||||
{
|
||||
namespace lsp::language::symbol {
|
||||
|
||||
class SymbolStore
|
||||
{
|
||||
class SymbolStore {
|
||||
public:
|
||||
SymbolId Add(Symbol def)
|
||||
{
|
||||
SymbolId id = next_id_++;
|
||||
// Update the symbol's ID
|
||||
std::visit([id](auto& s) { s.id = id; }, def.mutable_data());
|
||||
SymbolId Add(Symbol def);
|
||||
bool Remove(SymbolId id);
|
||||
void Clear();
|
||||
|
||||
auto [it, inserted] = definitions_.emplace(id, std::move(def));
|
||||
const auto& stored = it->second;
|
||||
by_name_[stored.name()].push_back(id);
|
||||
return id;
|
||||
}
|
||||
|
||||
bool Remove(SymbolId id)
|
||||
{
|
||||
auto it = definitions_.find(id);
|
||||
if (it == definitions_.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string& name = it->second.name();
|
||||
auto& ids = by_name_[name];
|
||||
ids.erase(std::remove(ids.begin(), ids.end(), id), ids.end());
|
||||
if (ids.empty())
|
||||
{
|
||||
by_name_.erase(name);
|
||||
}
|
||||
|
||||
definitions_.erase(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
definitions_.clear();
|
||||
by_name_.clear();
|
||||
next_id_ = 1;
|
||||
}
|
||||
|
||||
// Accessor (snake_case) - 返回指针是合理的,因为可能不存在
|
||||
const Symbol* Get(SymbolId id) const
|
||||
{
|
||||
auto it = definitions_.find(id);
|
||||
return it != definitions_.end() ? &it->second : nullptr;
|
||||
}
|
||||
|
||||
// Accessor (snake_case) - 使用reference_wrapper替代指针
|
||||
std::vector<std::reference_wrapper<const Symbol>> GetAll() const
|
||||
{
|
||||
std::vector<std::reference_wrapper<const Symbol>> result;
|
||||
result.reserve(definitions_.size());
|
||||
for (const auto& [id, def] : definitions_)
|
||||
{
|
||||
result.push_back(std::cref(def));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<SymbolId> FindByName(const std::string& name) const
|
||||
{
|
||||
auto it = by_name_.find(name);
|
||||
return it != by_name_.end() ? it->second : std::vector<SymbolId>();
|
||||
}
|
||||
const Symbol* Get(SymbolId id) const;
|
||||
std::vector<std::reference_wrapper<const Symbol>> GetAll() const;
|
||||
std::vector<SymbolId> FindByName(const std::string& name) const;
|
||||
|
||||
private:
|
||||
SymbolId next_id_ = 1;
|
||||
std::unordered_map<SymbolId, Symbol> definitions_;
|
||||
std::unordered_map<std::string, std::vector<SymbolId>> by_name_;
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace lsp::language::symbol
|
||||
|
|
|
|||
|
|
@ -0,0 +1,104 @@
|
|||
#include "table.hpp"
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace lsp::language::symbol {
|
||||
|
||||
SymbolId SymbolTable::CreateSymbol(Symbol symbol) {
|
||||
auto def = Symbol(std::move(symbol));
|
||||
auto id = store_.Add(def);
|
||||
|
||||
location_index_.OnSymbolAdded(def);
|
||||
scope_index_.OnSymbolAdded(def);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
bool SymbolTable::RemoveSymbol(SymbolId id) {
|
||||
location_index_.OnSymbolRemoved(id);
|
||||
scope_index_.OnSymbolRemoved(id);
|
||||
reference_graph_.OnSymbolRemoved(id);
|
||||
inheritance_graph_.OnSymbolRemoved(id);
|
||||
call_graph_.OnSymbolRemoved(id);
|
||||
|
||||
return store_.Remove(id);
|
||||
}
|
||||
|
||||
void SymbolTable::Clear() {
|
||||
store_.Clear();
|
||||
location_index_.Clear();
|
||||
scope_index_.Clear();
|
||||
reference_graph_.Clear();
|
||||
inheritance_graph_.Clear();
|
||||
call_graph_.Clear();
|
||||
}
|
||||
|
||||
std::vector<SymbolId> SymbolTable::FindSymbolsByName(
|
||||
const std::string& name) const {
|
||||
return store_.FindByName(name);
|
||||
}
|
||||
|
||||
std::optional<SymbolId> SymbolTable::FindSymbolAt(
|
||||
const ast::Location& location) const {
|
||||
return location_index_.FindSymbolAt(location);
|
||||
}
|
||||
|
||||
const Symbol* SymbolTable::definition(SymbolId id) const {
|
||||
return store_.Get(id);
|
||||
}
|
||||
|
||||
std::vector<std::reference_wrapper<const Symbol>> SymbolTable::all_definitions()
|
||||
const {
|
||||
return store_.GetAll();
|
||||
}
|
||||
|
||||
index::Location& SymbolTable::locations() { return location_index_; }
|
||||
index::Scope& SymbolTable::scopes() { return scope_index_; }
|
||||
|
||||
const index::Location& SymbolTable::locations() const {
|
||||
return location_index_;
|
||||
}
|
||||
|
||||
const index::Scope& SymbolTable::scopes() const { return scope_index_; }
|
||||
|
||||
graph::Reference& SymbolTable::references() { return reference_graph_; }
|
||||
graph::Inheritance& SymbolTable::inheritance() { return inheritance_graph_; }
|
||||
graph::Call& SymbolTable::calls() { return call_graph_; }
|
||||
|
||||
const graph::Reference& SymbolTable::references() const {
|
||||
return reference_graph_;
|
||||
}
|
||||
|
||||
const graph::Inheritance& SymbolTable::inheritance() const {
|
||||
return inheritance_graph_;
|
||||
}
|
||||
|
||||
const graph::Call& SymbolTable::calls() const { return call_graph_; }
|
||||
|
||||
ScopeId SymbolTable::CreateScope(ScopeKind kind, const ast::Location& range,
|
||||
std::optional<ScopeId> parent,
|
||||
std::optional<SymbolId> owner) {
|
||||
return scope_index_.CreateScope(kind, range, parent, owner);
|
||||
}
|
||||
|
||||
void SymbolTable::AddSymbolToScope(ScopeId scope_id, const std::string& name,
|
||||
SymbolId symbol_id) {
|
||||
scope_index_.AddSymbol(scope_id, name, symbol_id);
|
||||
}
|
||||
|
||||
void SymbolTable::AddReference(SymbolId symbol_id,
|
||||
const ast::Location& location,
|
||||
bool is_definition, bool is_write) {
|
||||
reference_graph_.AddReference(symbol_id, location, is_definition, is_write);
|
||||
}
|
||||
|
||||
void SymbolTable::AddInheritance(SymbolId derived, SymbolId base) {
|
||||
inheritance_graph_.AddInheritance(derived, base);
|
||||
}
|
||||
|
||||
void SymbolTable::AddCall(SymbolId caller, SymbolId callee,
|
||||
const ast::Location& location) {
|
||||
call_graph_.AddCall(caller, callee, location);
|
||||
}
|
||||
|
||||
} // namespace lsp::language::symbol
|
||||
|
|
@ -11,138 +11,53 @@
|
|||
#include "./index/scope.hpp"
|
||||
#include "./store.hpp"
|
||||
|
||||
namespace lsp::language::symbol
|
||||
{
|
||||
namespace lsp::language::symbol {
|
||||
|
||||
class SymbolTable
|
||||
{
|
||||
class SymbolTable {
|
||||
public:
|
||||
SymbolTable() = default;
|
||||
|
||||
// ===== Symbol Operations =====
|
||||
SymbolId CreateSymbol(Symbol symbol);
|
||||
bool RemoveSymbol(SymbolId id);
|
||||
void Clear();
|
||||
|
||||
SymbolId CreateSymbol(Symbol symbol)
|
||||
{
|
||||
auto def = Symbol(std::move(symbol));
|
||||
auto id = store_.Add(def);
|
||||
std::vector<SymbolId> FindSymbolsByName(const std::string& name) const;
|
||||
std::optional<SymbolId> FindSymbolAt(const ast::Location& location) const;
|
||||
|
||||
// Notify all indexes
|
||||
location_index_.OnSymbolAdded(def);
|
||||
scope_index_.OnSymbolAdded(def);
|
||||
const Symbol* definition(SymbolId id) const;
|
||||
std::vector<std::reference_wrapper<const Symbol>> all_definitions() const;
|
||||
|
||||
return id;
|
||||
}
|
||||
index::Location& locations();
|
||||
index::Scope& scopes();
|
||||
|
||||
bool RemoveSymbol(SymbolId id)
|
||||
{
|
||||
// Notify all components
|
||||
location_index_.OnSymbolRemoved(id);
|
||||
scope_index_.OnSymbolRemoved(id);
|
||||
reference_graph_.OnSymbolRemoved(id);
|
||||
inheritance_graph_.OnSymbolRemoved(id);
|
||||
call_graph_.OnSymbolRemoved(id);
|
||||
const index::Location& locations() const;
|
||||
const index::Scope& scopes() const;
|
||||
|
||||
return store_.Remove(id);
|
||||
}
|
||||
graph::Reference& references();
|
||||
graph::Inheritance& inheritance();
|
||||
graph::Call& calls();
|
||||
|
||||
void Clear()
|
||||
{
|
||||
store_.Clear();
|
||||
location_index_.Clear();
|
||||
scope_index_.Clear();
|
||||
reference_graph_.Clear();
|
||||
inheritance_graph_.Clear();
|
||||
call_graph_.Clear();
|
||||
}
|
||||
const graph::Reference& references() const;
|
||||
const graph::Inheritance& inheritance() const;
|
||||
const graph::Call& calls() const;
|
||||
|
||||
// ===== Basic Queries =====
|
||||
|
||||
std::vector<SymbolId> FindSymbolsByName(const std::string& name) const
|
||||
{
|
||||
return store_.FindByName(name);
|
||||
}
|
||||
|
||||
std::optional<SymbolId> FindSymbolAt(const ast::Location& location) const
|
||||
{
|
||||
return location_index_.FindSymbolAt(location);
|
||||
}
|
||||
|
||||
// ===== Accessors (snake_case) =====
|
||||
|
||||
// Get single definition - 返回指针是合理的,因为可能不存在
|
||||
const Symbol* definition(SymbolId id) const
|
||||
{
|
||||
return store_.Get(id);
|
||||
}
|
||||
|
||||
// Get all definitions - 使用reference_wrapper替代指针
|
||||
std::vector<std::reference_wrapper<const Symbol>> all_definitions() const
|
||||
{
|
||||
return store_.GetAll();
|
||||
}
|
||||
|
||||
// Access indexes (non-const)
|
||||
index::Location& locations() { return location_index_; }
|
||||
index::Scope& scopes() { return scope_index_; }
|
||||
|
||||
// Access indexes (const)
|
||||
const index::Location& locations() const { return location_index_; }
|
||||
const index::Scope& scopes() const { return scope_index_; }
|
||||
|
||||
// Access graphs (non-const)
|
||||
graph::Reference& references() { return reference_graph_; }
|
||||
graph::Inheritance& inheritance() { return inheritance_graph_; }
|
||||
graph::Call& calls() { return call_graph_; }
|
||||
|
||||
// Access graphs (const)
|
||||
const graph::Reference& references() const { return reference_graph_; }
|
||||
const graph::Inheritance& inheritance() const { return inheritance_graph_; }
|
||||
const graph::Call& calls() const { return call_graph_; }
|
||||
|
||||
// ===== Convenience Methods (shortcuts for common operations) =====
|
||||
|
||||
// Create scope
|
||||
ScopeId CreateScope(ScopeKind kind, const ast::Location& range, std::optional<ScopeId> parent = std::nullopt, std::optional<SymbolId> owner = std::nullopt)
|
||||
{
|
||||
return scope_index_.CreateScope(kind, range, parent, owner);
|
||||
}
|
||||
|
||||
// Add symbol to scope
|
||||
void AddSymbolToScope(ScopeId scope_id, const std::string& name, SymbolId symbol_id)
|
||||
{
|
||||
scope_index_.AddSymbol(scope_id, name, symbol_id);
|
||||
}
|
||||
|
||||
// Add reference
|
||||
void AddReference(SymbolId symbol_id, const ast::Location& location, bool is_definition = false, bool is_write = false)
|
||||
{
|
||||
reference_graph_.AddReference(symbol_id, location, is_definition, is_write);
|
||||
}
|
||||
|
||||
// Add inheritance relationship
|
||||
void AddInheritance(SymbolId derived, SymbolId base)
|
||||
{
|
||||
inheritance_graph_.AddInheritance(derived, base);
|
||||
}
|
||||
|
||||
// Add call relationship
|
||||
void AddCall(SymbolId caller, SymbolId callee, const ast::Location& location)
|
||||
{
|
||||
call_graph_.AddCall(caller, callee, location);
|
||||
}
|
||||
ScopeId CreateScope(ScopeKind kind, const ast::Location& range,
|
||||
std::optional<ScopeId> parent = std::nullopt,
|
||||
std::optional<SymbolId> owner = std::nullopt);
|
||||
void AddSymbolToScope(ScopeId scope_id, const std::string& name,
|
||||
SymbolId symbol_id);
|
||||
void AddReference(SymbolId symbol_id, const ast::Location& location,
|
||||
bool is_definition = false, bool is_write = false);
|
||||
void AddInheritance(SymbolId derived, SymbolId base);
|
||||
void AddCall(SymbolId caller, SymbolId callee, const ast::Location& location);
|
||||
|
||||
private:
|
||||
// Storage layer
|
||||
SymbolStore store_;
|
||||
|
||||
// Index layer
|
||||
index::Location location_index_;
|
||||
index::Scope scope_index_;
|
||||
|
||||
// Graph layer
|
||||
graph::Reference reference_graph_;
|
||||
graph::Inheritance inheritance_graph_;
|
||||
graph::Call call_graph_;
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace lsp::language::symbol
|
||||
|
|
|
|||
|
|
@ -5,66 +5,50 @@
|
|||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include "../ast/types.hpp"
|
||||
#include "../../protocol/protocol.hpp"
|
||||
#include "../ast/types.hpp"
|
||||
|
||||
namespace lsp::language::symbol
|
||||
{
|
||||
namespace lsp::language::symbol {
|
||||
|
||||
// ===== Basic Types =====
|
||||
// ===== Basic Types =====
|
||||
|
||||
using SymbolId = uint64_t;
|
||||
constexpr SymbolId kInvalidSymbolId = 0;
|
||||
using SymbolId = uint64_t;
|
||||
constexpr SymbolId kInvalidSymbolId = 0;
|
||||
|
||||
using ScopeId = uint64_t;
|
||||
constexpr ScopeId kInvalidScopeId = 0;
|
||||
using ScopeId = uint64_t;
|
||||
constexpr ScopeId kInvalidScopeId = 0;
|
||||
|
||||
using SymbolKind = protocol::SymbolKind;
|
||||
using SymbolKind = protocol::SymbolKind;
|
||||
|
||||
enum class ScopeKind
|
||||
{
|
||||
enum class ScopeKind {
|
||||
kGlobal,
|
||||
kUnit,
|
||||
kClass,
|
||||
kFunction,
|
||||
kAnonymousFunction,
|
||||
kBlock
|
||||
};
|
||||
};
|
||||
|
||||
enum class VariableScope
|
||||
{
|
||||
kAutomatic,
|
||||
kStatic,
|
||||
kGlobal,
|
||||
kParameter,
|
||||
kField
|
||||
};
|
||||
enum class VariableScope { kAutomatic, kStatic, kGlobal, kParameter, kField };
|
||||
|
||||
enum class UnitVisibility
|
||||
{
|
||||
kInterface,
|
||||
kImplementation
|
||||
};
|
||||
enum class UnitVisibility { kInterface, kImplementation };
|
||||
|
||||
// ===== Parameters =====
|
||||
// ===== Parameters =====
|
||||
|
||||
struct Parameter
|
||||
{
|
||||
struct Parameter {
|
||||
std::string name;
|
||||
std::optional<std::string> type;
|
||||
std::optional<std::string> default_value;
|
||||
};
|
||||
};
|
||||
|
||||
struct UnitImport
|
||||
{
|
||||
struct UnitImport {
|
||||
std::string unit_name;
|
||||
ast::Location location;
|
||||
};
|
||||
};
|
||||
|
||||
// ===== Symbol Types (all fields directly expanded) =====
|
||||
// ===== Symbol Types (all fields directly expanded) =====
|
||||
|
||||
struct Function
|
||||
{
|
||||
struct Function {
|
||||
static constexpr SymbolKind kind = SymbolKind::Function;
|
||||
|
||||
SymbolId id = kInvalidSymbolId;
|
||||
|
|
@ -80,14 +64,10 @@ namespace lsp::language::symbol
|
|||
std::vector<UnitImport> imports;
|
||||
std::optional<UnitVisibility> unit_visibility;
|
||||
|
||||
bool HasImplementation() const
|
||||
{
|
||||
return implementation_range.has_value();
|
||||
}
|
||||
};
|
||||
bool HasImplementation() const { return implementation_range.has_value(); }
|
||||
};
|
||||
|
||||
struct Class
|
||||
{
|
||||
struct Class {
|
||||
static constexpr SymbolKind kind = SymbolKind::Class;
|
||||
|
||||
SymbolId id = kInvalidSymbolId;
|
||||
|
|
@ -101,10 +81,9 @@ namespace lsp::language::symbol
|
|||
std::vector<SymbolId> members;
|
||||
|
||||
std::vector<UnitImport> imports;
|
||||
};
|
||||
};
|
||||
|
||||
struct Method
|
||||
{
|
||||
struct Method {
|
||||
static constexpr SymbolKind kind = SymbolKind::Method;
|
||||
|
||||
SymbolId id = kInvalidSymbolId;
|
||||
|
|
@ -127,14 +106,10 @@ namespace lsp::language::symbol
|
|||
|
||||
std::vector<UnitImport> imports;
|
||||
|
||||
bool HasImplementation() const
|
||||
{
|
||||
return implementation_range.has_value();
|
||||
}
|
||||
};
|
||||
bool HasImplementation() const { return implementation_range.has_value(); }
|
||||
};
|
||||
|
||||
struct Property
|
||||
{
|
||||
struct Property {
|
||||
static constexpr SymbolKind kind = SymbolKind::Property;
|
||||
|
||||
SymbolId id = kInvalidSymbolId;
|
||||
|
|
@ -147,10 +122,9 @@ namespace lsp::language::symbol
|
|||
std::optional<std::string> type;
|
||||
std::optional<SymbolId> getter;
|
||||
std::optional<SymbolId> setter;
|
||||
};
|
||||
};
|
||||
|
||||
struct Field
|
||||
{
|
||||
struct Field {
|
||||
static constexpr SymbolKind kind = SymbolKind::Field;
|
||||
|
||||
SymbolId id = kInvalidSymbolId;
|
||||
|
|
@ -162,10 +136,9 @@ namespace lsp::language::symbol
|
|||
std::optional<ast::ReferenceModifier> reference_modifier;
|
||||
std::optional<std::string> type;
|
||||
bool is_static = false;
|
||||
};
|
||||
};
|
||||
|
||||
struct Variable
|
||||
{
|
||||
struct Variable {
|
||||
static constexpr SymbolKind kind = SymbolKind::Variable;
|
||||
|
||||
SymbolId id = kInvalidSymbolId;
|
||||
|
|
@ -178,10 +151,9 @@ namespace lsp::language::symbol
|
|||
VariableScope storage = VariableScope::kAutomatic;
|
||||
std::optional<UnitVisibility> unit_visibility;
|
||||
bool has_initializer = false;
|
||||
};
|
||||
};
|
||||
|
||||
struct Constant
|
||||
{
|
||||
struct Constant {
|
||||
static constexpr SymbolKind kind = SymbolKind::Constant;
|
||||
|
||||
SymbolId id = kInvalidSymbolId;
|
||||
|
|
@ -191,10 +163,9 @@ namespace lsp::language::symbol
|
|||
|
||||
std::optional<std::string> type;
|
||||
std::string value;
|
||||
};
|
||||
};
|
||||
|
||||
struct Unit
|
||||
{
|
||||
struct Unit {
|
||||
static constexpr SymbolKind kind = SymbolKind::Namespace;
|
||||
|
||||
SymbolId id = kInvalidSymbolId;
|
||||
|
|
@ -204,50 +175,45 @@ namespace lsp::language::symbol
|
|||
|
||||
std::vector<UnitImport> interface_imports;
|
||||
std::vector<UnitImport> implementation_imports;
|
||||
};
|
||||
};
|
||||
|
||||
struct Reference
|
||||
{
|
||||
struct Reference {
|
||||
ast::Location location;
|
||||
SymbolId symbol_id;
|
||||
bool is_definition;
|
||||
bool is_write;
|
||||
};
|
||||
};
|
||||
|
||||
struct Call
|
||||
{
|
||||
struct Call {
|
||||
SymbolId caller;
|
||||
SymbolId callee;
|
||||
ast::Location call_site;
|
||||
};
|
||||
};
|
||||
|
||||
// ===== Symbol Data Variant =====
|
||||
// ===== Symbol Data Variant =====
|
||||
|
||||
using SymbolData = std::variant<Function, Class, Method, Property, Field, Variable, Constant, Unit>;
|
||||
using SymbolData = std::variant<Function, Class, Method, Property, Field,
|
||||
Variable, Constant, Unit>;
|
||||
|
||||
// ===== Symbol =====
|
||||
// ===== Symbol =====
|
||||
|
||||
class Symbol
|
||||
{
|
||||
class Symbol {
|
||||
public:
|
||||
explicit Symbol(SymbolData data) : data_(std::move(data)) {}
|
||||
|
||||
// Type checking and conversion
|
||||
template<typename T>
|
||||
bool Is() const
|
||||
{
|
||||
template <typename T>
|
||||
bool Is() const {
|
||||
return std::holds_alternative<T>(data_);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const T* As() const
|
||||
{
|
||||
template <typename T>
|
||||
const T* As() const {
|
||||
return std::get_if<T>(&data_);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T* As()
|
||||
{
|
||||
template <typename T>
|
||||
T* As() {
|
||||
return std::get_if<T>(&data_);
|
||||
}
|
||||
|
||||
|
|
@ -256,34 +222,29 @@ namespace lsp::language::symbol
|
|||
SymbolData& mutable_data() { return data_; }
|
||||
|
||||
// Common accessors (all symbol types have these)
|
||||
SymbolId id() const
|
||||
{
|
||||
SymbolId id() const {
|
||||
return std::visit([](const auto& s) { return s.id; }, data_);
|
||||
}
|
||||
|
||||
const std::string& name() const
|
||||
{
|
||||
const std::string& name() const {
|
||||
return std::visit([](const auto& s) -> const auto& { return s.name; },
|
||||
data_);
|
||||
}
|
||||
|
||||
ast::Location selection_range() const
|
||||
{
|
||||
ast::Location selection_range() const {
|
||||
return std::visit([](const auto& s) { return s.selection_range; }, data_);
|
||||
}
|
||||
|
||||
ast::Location range() const
|
||||
{
|
||||
ast::Location range() const {
|
||||
return std::visit([](const auto& s) { return s.range; }, data_);
|
||||
}
|
||||
|
||||
SymbolKind kind() const
|
||||
{
|
||||
SymbolKind kind() const {
|
||||
return std::visit([](const auto& s) { return s.kind; }, data_);
|
||||
}
|
||||
|
||||
private:
|
||||
SymbolData data_;
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace lsp::language::symbol
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,34 +1,33 @@
|
|||
#include "./symbol.hpp"
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include "./symbol.hpp"
|
||||
|
||||
#include "../../protocol/transform/facade.hpp"
|
||||
#include "../../service/document.hpp"
|
||||
#include "../../service/symbol.hpp"
|
||||
|
||||
namespace lsp::provider::workspace
|
||||
{
|
||||
std::string Symbol::GetMethod() const
|
||||
{
|
||||
return "workspace/symbol";
|
||||
}
|
||||
namespace lsp::provider::workspace {
|
||||
std::string Symbol::GetMethod() const { return "workspace/symbol"; }
|
||||
|
||||
std::string Symbol::GetProviderName() const
|
||||
{
|
||||
return "WorkSpaceSymbol";
|
||||
}
|
||||
std::string Symbol::GetProviderName() const { return "WorkSpaceSymbol"; }
|
||||
|
||||
std::string Symbol::ProvideResponse(const protocol::RequestMessage& request, ExecutionContext& context)
|
||||
{
|
||||
spdlog::debug("WorkspaceSymbolProvider: Providing response for method {}", request.method);
|
||||
std::string Symbol::ProvideResponse(const protocol::RequestMessage& request,
|
||||
ExecutionContext& context) {
|
||||
spdlog::debug("WorkspaceSymbolProvider: Providing response for method {}",
|
||||
request.method);
|
||||
|
||||
if (!request.params.has_value())
|
||||
{
|
||||
if (!request.params.has_value()) {
|
||||
spdlog::warn("{}: Missing params in request", GetProviderName());
|
||||
return BuildErrorResponseMessage(request, protocol::ErrorCodes::InvalidParams, "Missing params");
|
||||
return BuildErrorResponseMessage(
|
||||
request, protocol::ErrorCodes::InvalidParams, "Missing params");
|
||||
}
|
||||
|
||||
protocol::WorkspaceSymbolParams params = transform::FromLSPAny.template operator()<protocol::WorkspaceSymbolParams>(request.params.value());
|
||||
protocol::WorkspaceSymbolParams params =
|
||||
transform::FromLSPAny.template
|
||||
operator()<protocol::WorkspaceSymbolParams>(request.params.value());
|
||||
|
||||
auto symbols = BuildSymbolResponse(params, context);
|
||||
|
||||
|
|
@ -38,71 +37,72 @@ namespace lsp::provider::workspace
|
|||
|
||||
std::optional<std::string> json = transform::Serialize(response);
|
||||
if (!json.has_value())
|
||||
return BuildErrorResponseMessage(request, protocol::ErrorCodes::InternalError, "Failed to serialize response");
|
||||
return BuildErrorResponseMessage(request,
|
||||
protocol::ErrorCodes::InternalError,
|
||||
"Failed to serialize response");
|
||||
return json.value();
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<protocol::SymbolInformation> Symbol::BuildSymbolResponse(const protocol::WorkspaceSymbolParams& params, ExecutionContext& context)
|
||||
{
|
||||
spdlog::trace("{}: Searching for symbols matching '{}'", GetProviderName(), params.query);
|
||||
std::vector<protocol::SymbolInformation> Symbol::BuildSymbolResponse(
|
||||
const protocol::WorkspaceSymbolParams& params, ExecutionContext& context) {
|
||||
spdlog::trace("{}: Searching for symbols matching '{}'", GetProviderName(),
|
||||
params.query);
|
||||
|
||||
auto symbols = SearchSymbols(params.query, context);
|
||||
|
||||
// 按匹配分数排序
|
||||
std::sort(symbols.begin(), symbols.end(), [¶ms](const protocol::SymbolInformation& a, const protocol::SymbolInformation& b) {
|
||||
std::sort(symbols.begin(), symbols.end(),
|
||||
[¶ms](const protocol::SymbolInformation& a,
|
||||
const protocol::SymbolInformation& b) {
|
||||
// 可以实现更复杂的排序逻辑
|
||||
return a.name < b.name;
|
||||
});
|
||||
|
||||
// 限制返回数量(避免返回太多结果)
|
||||
const size_t max_results = 100;
|
||||
if (symbols.size() > max_results)
|
||||
{
|
||||
if (symbols.size() > max_results) {
|
||||
symbols.resize(max_results);
|
||||
spdlog::debug("{}: Limited results to {} symbols", GetProviderName(), max_results);
|
||||
spdlog::debug("{}: Limited results to {} symbols", GetProviderName(),
|
||||
max_results);
|
||||
}
|
||||
|
||||
spdlog::info("{}: Found {} symbols matching '{}'", GetProviderName(), symbols.size(), params.query);
|
||||
spdlog::info("{}: Found {} symbols matching '{}'", GetProviderName(),
|
||||
symbols.size(), params.query);
|
||||
|
||||
return symbols;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<protocol::SymbolInformation> Symbol::SearchSymbols(const std::string& query, ExecutionContext& context)
|
||||
{
|
||||
std::vector<protocol::SymbolInformation> Symbol::SearchSymbols(
|
||||
const std::string& query, ExecutionContext& context) {
|
||||
std::vector<protocol::SymbolInformation> results;
|
||||
|
||||
// 从容器获取服务
|
||||
const auto& document_service = context.GetService<service::Document>();
|
||||
const auto& symbol_service = context.GetService<service::Symbol>();
|
||||
auto document_service = context.GetService<service::Document>();
|
||||
auto symbol_service = context.GetService<service::Symbol>();
|
||||
|
||||
// 获取所有打开的文档
|
||||
auto document_uris = document_service.GetAllDocumentUris();
|
||||
auto document_uris = document_service->GetAllDocumentUris();
|
||||
|
||||
for (const auto& uri : document_uris)
|
||||
{
|
||||
for (const auto& uri : document_uris) {
|
||||
// 获取文档符号
|
||||
auto doc_symbols = symbol_service.GetDocumentSymbols(uri);
|
||||
auto doc_symbols = symbol_service->GetDocumentSymbols(uri);
|
||||
|
||||
// 递归转换并过滤符号
|
||||
for (const auto& symbol : doc_symbols)
|
||||
{
|
||||
for (const auto& symbol : doc_symbols) {
|
||||
ConvertToSymbolInformation(symbol, uri, query, results);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
||||
void Symbol::ConvertToSymbolInformation(
|
||||
void Symbol::ConvertToSymbolInformation(
|
||||
const protocol::DocumentSymbol& doc_symbol,
|
||||
const protocol::DocumentUri& uri,
|
||||
const std::string& query,
|
||||
const protocol::DocumentUri& uri, const std::string& query,
|
||||
std::vector<protocol::SymbolInformation>& results,
|
||||
const std::string& container_name)
|
||||
{
|
||||
const std::string& container_name) {
|
||||
// 检查是否匹配查询
|
||||
if (MatchesQuery(doc_symbol.name, query))
|
||||
{
|
||||
if (MatchesQuery(doc_symbol.name, query)) {
|
||||
protocol::SymbolInformation info;
|
||||
info.name = doc_symbol.name;
|
||||
info.kind = doc_symbol.kind;
|
||||
|
|
@ -110,8 +110,7 @@ namespace lsp::provider::workspace
|
|||
info.location.range = doc_symbol.range;
|
||||
|
||||
// 设置容器名称
|
||||
if (!container_name.empty())
|
||||
{
|
||||
if (!container_name.empty()) {
|
||||
info.containerName = container_name;
|
||||
}
|
||||
|
||||
|
|
@ -119,73 +118,66 @@ namespace lsp::provider::workspace
|
|||
}
|
||||
|
||||
// 递归处理子符号
|
||||
std::string new_container = container_name.empty() ? doc_symbol.name : container_name + "." + doc_symbol.name;
|
||||
std::string new_container = container_name.empty()
|
||||
? doc_symbol.name
|
||||
: container_name + "." + doc_symbol.name;
|
||||
|
||||
for (const auto& child : doc_symbol.children.value())
|
||||
{
|
||||
for (const auto& child : doc_symbol.children.value()) {
|
||||
ConvertToSymbolInformation(child, uri, query, results, new_container);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Symbol::MatchesQuery(const std::string& symbol_name, const std::string& query)
|
||||
{
|
||||
bool Symbol::MatchesQuery(const std::string& symbol_name,
|
||||
const std::string& query) {
|
||||
// 空查询匹配所有
|
||||
if (query.empty())
|
||||
return true;
|
||||
if (query.empty()) return true;
|
||||
|
||||
// 转换为小写进行不区分大小写的匹配
|
||||
std::string lower_symbol = symbol_name;
|
||||
std::string lower_query = query;
|
||||
std::transform(lower_symbol.begin(), lower_symbol.end(), lower_symbol.begin(), ::tolower);
|
||||
std::transform(lower_query.begin(), lower_query.end(), lower_query.begin(), ::tolower);
|
||||
std::transform(lower_symbol.begin(), lower_symbol.end(), lower_symbol.begin(),
|
||||
::tolower);
|
||||
std::transform(lower_query.begin(), lower_query.end(), lower_query.begin(),
|
||||
::tolower);
|
||||
|
||||
// 1. 精确匹配
|
||||
if (lower_symbol == lower_query)
|
||||
return true;
|
||||
if (lower_symbol == lower_query) return true;
|
||||
|
||||
// 2. 前缀匹配
|
||||
if (lower_symbol.find(lower_query) == 0)
|
||||
return true;
|
||||
if (lower_symbol.find(lower_query) == 0) return true;
|
||||
|
||||
// 3. 包含匹配
|
||||
if (lower_symbol.find(lower_query) != std::string::npos)
|
||||
return true;
|
||||
if (lower_symbol.find(lower_query) != std::string::npos) return true;
|
||||
|
||||
// 4. 模糊匹配(驼峰匹配)
|
||||
if (FuzzyMatch(query, symbol_name))
|
||||
return true;
|
||||
if (FuzzyMatch(query, symbol_name)) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Symbol::FuzzyMatch(const std::string& pattern, const std::string& text)
|
||||
{
|
||||
bool Symbol::FuzzyMatch(const std::string& pattern, const std::string& text) {
|
||||
// 实现驼峰匹配
|
||||
// 例如 "gS" 匹配 "getString"
|
||||
size_t pattern_idx = 0;
|
||||
size_t text_idx = 0;
|
||||
|
||||
while (pattern_idx < pattern.length() && text_idx < text.length())
|
||||
{
|
||||
while (pattern_idx < pattern.length() && text_idx < text.length()) {
|
||||
char p = pattern[pattern_idx];
|
||||
|
||||
// 查找下一个匹配字符
|
||||
bool found = false;
|
||||
while (text_idx < text.length())
|
||||
{
|
||||
while (text_idx < text.length()) {
|
||||
char t = text[text_idx];
|
||||
|
||||
// 不区分大小写匹配
|
||||
if (std::tolower(p) == std::tolower(t))
|
||||
{
|
||||
if (std::tolower(p) == std::tolower(t)) {
|
||||
found = true;
|
||||
text_idx++;
|
||||
break;
|
||||
}
|
||||
|
||||
// 如果模式字符是大写,只在大写字母位置匹配
|
||||
if (std::isupper(p) && !std::isupper(t))
|
||||
{
|
||||
if (std::isupper(p) && !std::isupper(t)) {
|
||||
text_idx++;
|
||||
continue;
|
||||
}
|
||||
|
|
@ -193,50 +185,46 @@ namespace lsp::provider::workspace
|
|||
text_idx++;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
return false;
|
||||
if (!found) return false;
|
||||
|
||||
pattern_idx++;
|
||||
}
|
||||
|
||||
return pattern_idx == pattern.length();
|
||||
}
|
||||
}
|
||||
|
||||
int Symbol::CalculateScore(const std::string& symbol_name, const std::string& query)
|
||||
{
|
||||
int Symbol::CalculateScore(const std::string& symbol_name,
|
||||
const std::string& query) {
|
||||
int score = 0;
|
||||
|
||||
// 精确匹配得分最高
|
||||
if (symbol_name == query)
|
||||
return 1000;
|
||||
if (symbol_name == query) return 1000;
|
||||
|
||||
// 前缀匹配得分次高
|
||||
if (symbol_name.find(query) == 0)
|
||||
return 900;
|
||||
if (symbol_name.find(query) == 0) return 900;
|
||||
|
||||
// 不区分大小写的前缀匹配
|
||||
std::string lower_symbol = symbol_name;
|
||||
std::string lower_query = query;
|
||||
std::transform(lower_symbol.begin(), lower_symbol.end(), lower_symbol.begin(), ::tolower);
|
||||
std::transform(lower_query.begin(), lower_query.end(), lower_query.begin(), ::tolower);
|
||||
std::transform(lower_symbol.begin(), lower_symbol.end(), lower_symbol.begin(),
|
||||
::tolower);
|
||||
std::transform(lower_query.begin(), lower_query.end(), lower_query.begin(),
|
||||
::tolower);
|
||||
|
||||
if (lower_symbol.find(lower_query) == 0)
|
||||
return 800;
|
||||
if (lower_symbol.find(lower_query) == 0) return 800;
|
||||
|
||||
// 包含匹配
|
||||
size_t pos = lower_symbol.find(lower_query);
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
if (pos != std::string::npos) {
|
||||
// 越靠前得分越高
|
||||
score = 700 - static_cast<int>(pos * 10);
|
||||
}
|
||||
|
||||
// 模糊匹配得分最低
|
||||
if (FuzzyMatch(query, symbol_name))
|
||||
{
|
||||
if (FuzzyMatch(query, symbol_name)) {
|
||||
score = std::max(score, 500);
|
||||
}
|
||||
|
||||
return score;
|
||||
}
|
||||
}
|
||||
} // namespace lsp::provider::workspace
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
#include <spdlog/spdlog.h>
|
||||
#include "./conversion.hpp"
|
||||
|
||||
namespace lsp::service::symbol
|
||||
{
|
||||
FileSymbolIndex BuildFromEditingTable(const EditingSymbolTable& editing_table)
|
||||
{
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include "./utils.hpp"
|
||||
|
||||
namespace lsp::service::symbol {
|
||||
FileSymbolIndex BuildFromEditingTable(const EditingSymbolTable& editing_table) {
|
||||
FileSymbolIndex index;
|
||||
|
||||
index.uri = editing_table.uri;
|
||||
|
|
@ -15,174 +16,96 @@ namespace lsp::service::symbol
|
|||
ExtractSymbols(index, *editing_table.symbol_table);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
void ExtractSymbols(FileSymbolIndex& index, const language::symbol::SymbolTable& table)
|
||||
{
|
||||
auto all_defs = table.GetAllDefinitions();
|
||||
if (all_defs.empty())
|
||||
return;
|
||||
|
||||
// 根据文件类型提取不同的符号
|
||||
switch (index.file_type)
|
||||
{
|
||||
case TsfFileType::kUnit:
|
||||
ExtractUnitSymbols(index, table, all_defs);
|
||||
break;
|
||||
|
||||
case TsfFileType::kClass:
|
||||
ExtractClassSymbols(index, table, all_defs);
|
||||
break;
|
||||
|
||||
case TsfFileType::kFunction:
|
||||
ExtractFunctionSymbols(index, table, all_defs);
|
||||
break;
|
||||
|
||||
case TsfFileType::kScript:
|
||||
ExtractScriptSymbols(index, table, all_defs);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ExtractUnitSymbols(FileSymbolIndex& index, const language::symbol::SymbolTable& table, const std::vector<const language::symbol::SymbolDefinition*>& all_defs)
|
||||
{
|
||||
for (const auto* def : all_defs)
|
||||
{
|
||||
if (def->kind == language::symbol::SymbolKind::Module)
|
||||
{
|
||||
index.primary_symbol = def->name;
|
||||
index.primary_kind = def->kind;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 提取顶层函数(没有父符号的 Function)
|
||||
for (const auto* def : all_defs)
|
||||
{
|
||||
if (def->kind == language::symbol::SymbolKind::Function && !def->parent_id)
|
||||
{
|
||||
MemberSymbol member;
|
||||
member.name = def->name;
|
||||
member.kind = def->kind;
|
||||
member.return_type = def->type_hint;
|
||||
|
||||
index.exported_symbols.push_back(std::move(member));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ExtractClassSymbols(FileSymbolIndex& index, const language::symbol::SymbolTable& table, const std::vector<const language::symbol::SymbolDefinition*>& all_defs)
|
||||
{
|
||||
// 查找 Class 定义
|
||||
const language::symbol::SymbolDefinition* class_def = nullptr;
|
||||
for (const auto* def : all_defs)
|
||||
{
|
||||
if (def->kind == language::symbol::SymbolKind::Class)
|
||||
{
|
||||
index.primary_symbol = def->name;
|
||||
index.primary_kind = def->kind;
|
||||
class_def = def;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!class_def)
|
||||
return;
|
||||
|
||||
// 提取类的所有成员
|
||||
auto children = table.GetChildren(class_def->id);
|
||||
for (auto child_id : children)
|
||||
{
|
||||
const auto* member_def = table.GetDefinition(child_id);
|
||||
if (!member_def)
|
||||
continue;
|
||||
|
||||
// 只提取 public 成员
|
||||
if (member_def->access_modifier && *member_def->access_modifier != language::ast::AccessModifier::kPublic)
|
||||
continue;
|
||||
|
||||
// 过滤掉私有成员
|
||||
if (member_def->kind == language::symbol::SymbolKind::Method ||
|
||||
member_def->kind == language::symbol::SymbolKind::Property ||
|
||||
member_def->kind == language::symbol::SymbolKind::Field ||
|
||||
member_def->kind == language::symbol::SymbolKind::Constructor)
|
||||
{
|
||||
MemberSymbol member;
|
||||
member.name = member_def->name;
|
||||
member.kind = member_def->kind;
|
||||
member.return_type = member_def->type_hint;
|
||||
member.access = member_def->access_modifier;
|
||||
member.is_class_method = member_def->is_class_method;
|
||||
member.parameters = ExtractFunctionParameters(table, member_def->id);
|
||||
|
||||
index.exported_symbols.push_back(std::move(member));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ExtractFunctionSymbols(FileSymbolIndex& index, const language::symbol::SymbolTable& table, const std::vector<const language::symbol::SymbolDefinition*>& all_defs)
|
||||
{
|
||||
// Function 文件的主符号就是函数本身
|
||||
for (const auto* def : all_defs)
|
||||
{
|
||||
if (def->kind == language::symbol::SymbolKind::Function && !def->parent_id)
|
||||
{
|
||||
index.primary_symbol = def->name;
|
||||
index.primary_kind = def->kind;
|
||||
|
||||
MemberSymbol member;
|
||||
member.name = def->name;
|
||||
member.kind = def->kind;
|
||||
member.return_type = def->type_hint;
|
||||
member.parameters = ExtractFunctionParameters(table, def->id);
|
||||
index.exported_symbols.push_back(std::move(member));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ExtractScriptSymbols(FileSymbolIndex& index, const language::symbol::SymbolTable& table, const std::vector<const language::symbol::SymbolDefinition*>& all_defs)
|
||||
{
|
||||
index.primary_symbol = "Script";
|
||||
index.primary_kind = language::symbol::SymbolKind::Module;
|
||||
|
||||
for (const auto* def : all_defs)
|
||||
{
|
||||
if (def->kind == language::symbol::SymbolKind::Function && !def->parent_id)
|
||||
{
|
||||
MemberSymbol member;
|
||||
member.name = def->name;
|
||||
member.kind = def->kind;
|
||||
member.return_type = def->type_hint;
|
||||
member.parameters = ExtractFunctionParameters(table, def->id);
|
||||
|
||||
index.exported_symbols.push_back(std::move(member));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<symbol::Parameter> ExtractFunctionParameters(const language::symbol::SymbolTable& table, language::symbol::SymbolId function_id)
|
||||
{
|
||||
std::vector<symbol::Parameter> params;
|
||||
|
||||
auto children = table.GetChildren(function_id);
|
||||
for (auto child_id : children)
|
||||
{
|
||||
const auto* child_def = table.GetDefinition(child_id);
|
||||
if (!child_def)
|
||||
continue;
|
||||
|
||||
if (child_def->kind == language::symbol::SymbolKind::Variable)
|
||||
{
|
||||
symbol::Parameter param;
|
||||
param.name = child_def->name;
|
||||
param.type = child_def->type_hint.value_or("");
|
||||
param.has_default = false;
|
||||
params.push_back(std::move(param));
|
||||
}
|
||||
}
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ExtractSymbols(FileSymbolIndex& index,
|
||||
const language::symbol::SymbolTable& table) {
|
||||
switch (index.file_type) {
|
||||
case TsfFileType::kUnit:
|
||||
ExtractUnitSymbols(index, table);
|
||||
break;
|
||||
case TsfFileType::kClass:
|
||||
ExtractClassSymbols(index, table);
|
||||
break;
|
||||
case TsfFileType::kFunction:
|
||||
ExtractFunctionSymbols(index, table);
|
||||
break;
|
||||
case TsfFileType::kScript:
|
||||
ExtractScriptSymbols(index, table);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ExtractUnitSymbols(FileSymbolIndex& index,
|
||||
const language::symbol::SymbolTable& table) {
|
||||
auto top_level = utils::GetTopLevelSymbols(table);
|
||||
for (const auto* symbol : top_level) {
|
||||
if (symbol->kind() == language::symbol::SymbolKind::Namespace) {
|
||||
index.primary_symbol = symbol->name();
|
||||
index.primary_kind = symbol->kind();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto* symbol : top_level) {
|
||||
if (symbol->kind() == language::symbol::SymbolKind::Function) {
|
||||
index.exported_symbols.push_back(utils::BuildMemberSymbol(*symbol));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ExtractClassSymbols(FileSymbolIndex& index,
|
||||
const language::symbol::SymbolTable& table) {
|
||||
auto top_level = utils::GetTopLevelSymbols(table);
|
||||
const language::symbol::Symbol* class_symbol = nullptr;
|
||||
|
||||
for (const auto* symbol : top_level) {
|
||||
if (symbol->kind() == language::symbol::SymbolKind::Class) {
|
||||
class_symbol = symbol;
|
||||
index.primary_symbol = symbol->name();
|
||||
index.primary_kind = symbol->kind();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!class_symbol) return;
|
||||
|
||||
auto members = utils::GetChildSymbols(table, class_symbol->id());
|
||||
for (const auto* member : members) {
|
||||
if (!utils::IsPublicMember(*member)) continue;
|
||||
|
||||
if (member->kind() == language::symbol::SymbolKind::Method ||
|
||||
member->kind() == language::symbol::SymbolKind::Property ||
|
||||
member->kind() == language::symbol::SymbolKind::Field) {
|
||||
index.exported_symbols.push_back(utils::BuildMemberSymbol(*member));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ExtractFunctionSymbols(FileSymbolIndex& index,
|
||||
const language::symbol::SymbolTable& table) {
|
||||
auto top_level = utils::GetTopLevelSymbols(table);
|
||||
for (const auto* symbol : top_level) {
|
||||
if (symbol->kind() == language::symbol::SymbolKind::Function) {
|
||||
index.primary_symbol = symbol->name();
|
||||
index.primary_kind = symbol->kind();
|
||||
index.exported_symbols.push_back(utils::BuildMemberSymbol(*symbol));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ExtractScriptSymbols(FileSymbolIndex& index,
|
||||
const language::symbol::SymbolTable& table) {
|
||||
index.primary_symbol = "Script";
|
||||
index.primary_kind = language::symbol::SymbolKind::Namespace;
|
||||
|
||||
auto top_level = utils::GetTopLevelSymbols(table);
|
||||
for (const auto* symbol : top_level) {
|
||||
if (symbol->kind() == language::symbol::SymbolKind::Function) {
|
||||
index.exported_symbols.push_back(utils::BuildMemberSymbol(*symbol));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace lsp::service::symbol
|
||||
|
|
|
|||
|
|
@ -1,14 +1,17 @@
|
|||
#pragma once
|
||||
#include "./types.hpp"
|
||||
#include "../../../language/symbol/table.hpp"
|
||||
#include "./types.hpp"
|
||||
|
||||
namespace lsp::service::symbol
|
||||
{
|
||||
FileSymbolIndex BuildFromEditingTable(const EditingSymbolTable& editing_table);
|
||||
void ExtractSymbols(FileSymbolIndex& index, const language::symbol::SymbolTable& table);
|
||||
void ExtractUnitSymbols(FileSymbolIndex& index, const language::symbol::SymbolTable& table, const std::vector<const language::symbol::SymbolDefinition*>& all_defs);
|
||||
void ExtractClassSymbols(FileSymbolIndex& index, const language::symbol::SymbolTable& table, const std::vector<const language::symbol::SymbolDefinition*>& all_defs);
|
||||
void ExtractFunctionSymbols(FileSymbolIndex& index, const language::symbol::SymbolTable& table, const std::vector<const language::symbol::SymbolDefinition*>& all_defs);
|
||||
void ExtractScriptSymbols(FileSymbolIndex& index, const language::symbol::SymbolTable& table, const std::vector<const language::symbol::SymbolDefinition*>& all_defs);
|
||||
std::vector<symbol::Parameter> ExtractFunctionParameters(const language::symbol::SymbolTable& table, language::symbol::SymbolId function_id);
|
||||
}
|
||||
namespace lsp::service::symbol {
|
||||
FileSymbolIndex BuildFromEditingTable(const EditingSymbolTable& editing_table);
|
||||
void ExtractSymbols(FileSymbolIndex& index,
|
||||
const language::symbol::SymbolTable& table);
|
||||
void ExtractUnitSymbols(FileSymbolIndex& index,
|
||||
const language::symbol::SymbolTable& table);
|
||||
void ExtractClassSymbols(FileSymbolIndex& index,
|
||||
const language::symbol::SymbolTable& table);
|
||||
void ExtractFunctionSymbols(FileSymbolIndex& index,
|
||||
const language::symbol::SymbolTable& table);
|
||||
void ExtractScriptSymbols(FileSymbolIndex& index,
|
||||
const language::symbol::SymbolTable& table);
|
||||
} // namespace lsp::service::symbol
|
||||
|
|
|
|||
|
|
@ -0,0 +1,207 @@
|
|||
#include "utils.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace lsp::service::symbol::utils {
|
||||
namespace {
|
||||
|
||||
std::vector<const language::symbol::Symbol*> CollectSymbolsInScope(
|
||||
const language::symbol::SymbolTable& table,
|
||||
language::symbol::ScopeId scope_id) {
|
||||
std::vector<const language::symbol::Symbol*> result;
|
||||
const auto* scope = table.scopes().scope(scope_id);
|
||||
if (!scope) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result.reserve(scope->symbols.size());
|
||||
for (const auto& [_, symbol_id] : scope->symbols) {
|
||||
if (const auto* symbol = table.definition(symbol_id)) {
|
||||
result.push_back(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(result.begin(), result.end(),
|
||||
[](const language::symbol::Symbol* lhs,
|
||||
const language::symbol::Symbol* rhs) {
|
||||
return lhs->range().start_offset < rhs->range().start_offset;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
language::symbol::ScopeId FindScopeOwnedBy(
|
||||
const language::symbol::SymbolTable& table,
|
||||
language::symbol::SymbolId owner) {
|
||||
const auto& scopes = table.scopes().all_scopes();
|
||||
for (const auto& [scope_id, info] : scopes) {
|
||||
if (info.owner && *info.owner == owner) {
|
||||
return scope_id;
|
||||
}
|
||||
}
|
||||
return language::symbol::kInvalidScopeId;
|
||||
}
|
||||
|
||||
std::string BuildParameterList(
|
||||
const std::vector<language::symbol::Parameter>& params) {
|
||||
std::string detail = "(";
|
||||
for (size_t i = 0; i < params.size(); ++i) {
|
||||
if (i > 0) {
|
||||
detail += ", ";
|
||||
}
|
||||
|
||||
detail += params[i].name;
|
||||
if (params[i].type && !params[i].type->empty()) {
|
||||
detail += ": " + *params[i].type;
|
||||
}
|
||||
}
|
||||
detail += ")";
|
||||
return detail;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::vector<const language::symbol::Symbol*> GetTopLevelSymbols(
|
||||
const language::symbol::SymbolTable& table) {
|
||||
auto global_scope = table.scopes().global_scope();
|
||||
if (global_scope == language::symbol::kInvalidScopeId) {
|
||||
return {};
|
||||
}
|
||||
return CollectSymbolsInScope(table, global_scope);
|
||||
}
|
||||
|
||||
std::vector<const language::symbol::Symbol*> GetChildSymbols(
|
||||
const language::symbol::SymbolTable& table,
|
||||
language::symbol::SymbolId parent_id) {
|
||||
auto scope_id = FindScopeOwnedBy(table, parent_id);
|
||||
if (scope_id == language::symbol::kInvalidScopeId) {
|
||||
return {};
|
||||
}
|
||||
return CollectSymbolsInScope(table, scope_id);
|
||||
}
|
||||
|
||||
std::vector<service::symbol::Parameter> ConvertParameters(
|
||||
const std::vector<language::symbol::Parameter>& params) {
|
||||
std::vector<service::symbol::Parameter> converted;
|
||||
converted.reserve(params.size());
|
||||
|
||||
for (const auto& param : params) {
|
||||
service::symbol::Parameter converted_param;
|
||||
converted_param.name = param.name;
|
||||
converted_param.type = param.type.value_or("");
|
||||
converted_param.has_default = param.default_value.has_value();
|
||||
converted.push_back(std::move(converted_param));
|
||||
}
|
||||
|
||||
return converted;
|
||||
}
|
||||
|
||||
std::optional<std::string> GetReturnType(
|
||||
const language::symbol::Symbol& symbol) {
|
||||
if (const auto* fn = symbol.As<language::symbol::Function>()) {
|
||||
return fn->return_type;
|
||||
}
|
||||
if (const auto* method = symbol.As<language::symbol::Method>()) {
|
||||
return method->return_type;
|
||||
}
|
||||
if (const auto* property = symbol.As<language::symbol::Property>()) {
|
||||
return property->type;
|
||||
}
|
||||
if (const auto* field = symbol.As<language::symbol::Field>()) {
|
||||
return field->type;
|
||||
}
|
||||
if (const auto* variable = symbol.As<language::symbol::Variable>()) {
|
||||
if (variable->type) {
|
||||
return *variable->type;
|
||||
}
|
||||
}
|
||||
if (const auto* constant = symbol.As<language::symbol::Constant>()) {
|
||||
if (constant->type && !constant->type->empty()) {
|
||||
return constant->type;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::string BuildSymbolDetail(const language::symbol::Symbol& symbol) {
|
||||
if (const auto* fn = symbol.As<language::symbol::Function>()) {
|
||||
std::string detail = BuildParameterList(fn->parameters);
|
||||
if (fn->return_type && !fn->return_type->empty()) {
|
||||
detail += ": " + *fn->return_type;
|
||||
}
|
||||
return detail;
|
||||
}
|
||||
|
||||
if (const auto* method = symbol.As<language::symbol::Method>()) {
|
||||
std::string detail = BuildParameterList(method->parameters);
|
||||
if (method->return_type && !method->return_type->empty()) {
|
||||
detail += ": " + *method->return_type;
|
||||
}
|
||||
return detail;
|
||||
}
|
||||
|
||||
if (const auto* property = symbol.As<language::symbol::Property>()) {
|
||||
return property->type ? ": " + *property->type : "";
|
||||
}
|
||||
|
||||
if (const auto* field = symbol.As<language::symbol::Field>()) {
|
||||
return field->type ? ": " + *field->type : "";
|
||||
}
|
||||
|
||||
if (const auto* variable = symbol.As<language::symbol::Variable>()) {
|
||||
return variable->type ? ": " + *variable->type : "";
|
||||
}
|
||||
|
||||
if (const auto* constant = symbol.As<language::symbol::Constant>()) {
|
||||
if (constant->type && !constant->type->empty()) {
|
||||
return ": " + *constant->type;
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
bool IsPublicMember(const language::symbol::Symbol& symbol) {
|
||||
if (const auto* method = symbol.As<language::symbol::Method>()) {
|
||||
return method->access == language::ast::AccessModifier::kPublic;
|
||||
}
|
||||
if (const auto* property = symbol.As<language::symbol::Property>()) {
|
||||
return property->access == language::ast::AccessModifier::kPublic;
|
||||
}
|
||||
if (const auto* field = symbol.As<language::symbol::Field>()) {
|
||||
return field->access == language::ast::AccessModifier::kPublic;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
service::symbol::MemberSymbol BuildMemberSymbol(
|
||||
const language::symbol::Symbol& symbol) {
|
||||
service::symbol::MemberSymbol member;
|
||||
member.name = symbol.name();
|
||||
member.kind = symbol.kind();
|
||||
member.is_class_method = false;
|
||||
|
||||
if (const auto* fn = symbol.As<language::symbol::Function>()) {
|
||||
member.parameters = ConvertParameters(fn->parameters);
|
||||
member.return_type = fn->return_type;
|
||||
} else if (const auto* method = symbol.As<language::symbol::Method>()) {
|
||||
member.parameters = ConvertParameters(method->parameters);
|
||||
member.return_type = method->return_type;
|
||||
member.access = method->access;
|
||||
member.is_class_method = method->is_static;
|
||||
} else if (const auto* property = symbol.As<language::symbol::Property>()) {
|
||||
member.return_type = property->type;
|
||||
member.access = property->access;
|
||||
} else if (const auto* field = symbol.As<language::symbol::Field>()) {
|
||||
member.return_type = field->type;
|
||||
member.access = field->access;
|
||||
member.is_class_method = field->is_static;
|
||||
} else if (const auto* variable = symbol.As<language::symbol::Variable>()) {
|
||||
member.return_type = variable->type;
|
||||
} else if (const auto* constant = symbol.As<language::symbol::Constant>()) {
|
||||
member.return_type = constant->type;
|
||||
}
|
||||
|
||||
return member;
|
||||
}
|
||||
|
||||
} // namespace lsp::service::symbol::utils
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "../../../language/symbol/table.hpp"
|
||||
#include "./types.hpp"
|
||||
|
||||
namespace lsp::service::symbol::utils {
|
||||
|
||||
std::vector<const language::symbol::Symbol*> GetTopLevelSymbols(
|
||||
const language::symbol::SymbolTable& table);
|
||||
|
||||
std::vector<const language::symbol::Symbol*> GetChildSymbols(
|
||||
const language::symbol::SymbolTable& table,
|
||||
language::symbol::SymbolId parent_id);
|
||||
|
||||
std::vector<service::symbol::Parameter> ConvertParameters(
|
||||
const std::vector<language::symbol::Parameter>& params);
|
||||
|
||||
std::string BuildSymbolDetail(const language::symbol::Symbol& symbol);
|
||||
|
||||
service::symbol::MemberSymbol BuildMemberSymbol(
|
||||
const language::symbol::Symbol& symbol);
|
||||
|
||||
bool IsPublicMember(const language::symbol::Symbol& symbol);
|
||||
|
||||
std::optional<std::string> GetReturnType(
|
||||
const language::symbol::Symbol& symbol);
|
||||
|
||||
} // namespace lsp::service::symbol::utils
|
||||
|
|
@ -1,72 +1,62 @@
|
|||
#include "./parser.hpp"
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <filesystem>
|
||||
#include "./utils/text_coordinates.hpp"
|
||||
|
||||
#include "../language/ast/deserializer.hpp"
|
||||
#include "./parser.hpp"
|
||||
#include "../language/symbol/builder.hpp"
|
||||
#include "./detail/symbol/utils.hpp"
|
||||
#include "./utils/text_coordinates.hpp"
|
||||
|
||||
namespace lsp::service
|
||||
{
|
||||
// ============= TreeSitter ================== //
|
||||
namespace lsp::service {
|
||||
// ============= TreeSitter ================== //
|
||||
|
||||
TreeSitter::TreeSitter()
|
||||
{
|
||||
TreeSitter::TreeSitter() {
|
||||
parser_ = ts_parser_new();
|
||||
if (!parser_)
|
||||
throw std::runtime_error("Failed to create tree-sitter parser");
|
||||
}
|
||||
if (!parser_) throw std::runtime_error("Failed to create tree-sitter parser");
|
||||
}
|
||||
|
||||
TreeSitter::~TreeSitter()
|
||||
{
|
||||
if (parser_)
|
||||
ts_parser_delete(parser_);
|
||||
}
|
||||
TreeSitter::~TreeSitter() {
|
||||
if (parser_) ts_parser_delete(parser_);
|
||||
}
|
||||
|
||||
TreeSitter::TreeSitter(TreeSitter&& other) noexcept :
|
||||
parser_(other.parser_)
|
||||
{
|
||||
TreeSitter::TreeSitter(TreeSitter&& other) noexcept : parser_(other.parser_) {
|
||||
other.parser_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool TreeSitter::SetLanguage(const TSLanguage* language)
|
||||
{
|
||||
if (!parser_)
|
||||
return false;
|
||||
bool TreeSitter::SetLanguage(const TSLanguage* language) {
|
||||
if (!parser_) return false;
|
||||
return ts_parser_set_language(parser_, language);
|
||||
}
|
||||
}
|
||||
|
||||
TSTree* TreeSitter::Parse(const char* content, size_t length, TSTree* old_tree)
|
||||
{
|
||||
if (!parser_)
|
||||
return nullptr;
|
||||
TSTree* TreeSitter::Parse(const char* content, size_t length,
|
||||
TSTree* old_tree) {
|
||||
if (!parser_) return nullptr;
|
||||
return ts_parser_parse_string(parser_, old_tree, content, length);
|
||||
}
|
||||
}
|
||||
|
||||
TSParser* TreeSitter::GetRawParser() const
|
||||
{
|
||||
return parser_;
|
||||
};
|
||||
TSParser* TreeSitter::GetRawParser() const { return parser_; };
|
||||
|
||||
// ============= SyntaxTree ================== //
|
||||
// ============= SyntaxTree ================== //
|
||||
|
||||
SyntaxTree::SyntaxTree(TSTree* tree) :
|
||||
tree_(tree, ts_tree_delete) {}
|
||||
SyntaxTree::SyntaxTree(TSTree* tree) : tree_(tree, ts_tree_delete) {}
|
||||
|
||||
SyntaxTree::~SyntaxTree() = default;
|
||||
SyntaxTree::~SyntaxTree() = default;
|
||||
|
||||
TSTree* SyntaxTree::Get() const
|
||||
{
|
||||
return tree_.get();
|
||||
}
|
||||
TSTree* SyntaxTree::Get() const { return tree_.get(); }
|
||||
|
||||
void SyntaxTree::ApplyEdit(const protocol::TextDocumentContentChangeEvent& change, const protocol::string& content)
|
||||
{
|
||||
if (!tree_)
|
||||
return;
|
||||
protocol::uinteger start_offset = utils::text_coordinates::ToOffset(change.range.start, content);
|
||||
protocol::uinteger end_offset = utils::text_coordinates::ToOffset(change.range.end, content);
|
||||
void SyntaxTree::ApplyEdit(
|
||||
const protocol::TextDocumentContentChangeEvent& change,
|
||||
const protocol::string& content) {
|
||||
if (!tree_) return;
|
||||
protocol::uinteger start_offset =
|
||||
utils::text_coordinates::ToOffset(change.range.start, content);
|
||||
protocol::uinteger end_offset =
|
||||
utils::text_coordinates::ToOffset(change.range.end, content);
|
||||
|
||||
TSInputEdit edit = {};
|
||||
edit.start_byte = start_offset;
|
||||
|
|
@ -75,61 +65,56 @@ namespace lsp::service
|
|||
|
||||
edit.start_point = utils::text_coordinates::ToPoint(change.range.start);
|
||||
edit.old_end_point = utils::text_coordinates::ToPoint(change.range.end);
|
||||
edit.new_end_point = utils::text_coordinates::CalculateEndPoint(change.text, edit.start_point);
|
||||
edit.new_end_point =
|
||||
utils::text_coordinates::CalculateEndPoint(change.text, edit.start_point);
|
||||
|
||||
ts_tree_edit(tree_.get(), &edit);
|
||||
}
|
||||
}
|
||||
|
||||
TSNode SyntaxTree::GetRootNode() const
|
||||
{
|
||||
TSNode SyntaxTree::GetRootNode() const {
|
||||
return tree_ ? ts_tree_root_node(tree_.get()) : TSNode{};
|
||||
}
|
||||
}
|
||||
|
||||
// ============= SyntaxTreeManager ================== //
|
||||
// ============= SyntaxTreeManager ================== //
|
||||
|
||||
void SyntaxTreeManager::StoreTree(const protocol::DocumentUri& uri, std::unique_ptr<SyntaxTree> tree)
|
||||
{
|
||||
void SyntaxTreeManager::StoreTree(const protocol::DocumentUri& uri,
|
||||
std::unique_ptr<SyntaxTree> tree) {
|
||||
std::unique_lock<std::shared_mutex> lock(mutex_);
|
||||
trees_[uri] = std::move(tree);
|
||||
}
|
||||
}
|
||||
|
||||
void SyntaxTreeManager::RemoveTree(const protocol::DocumentUri& uri)
|
||||
{
|
||||
void SyntaxTreeManager::RemoveTree(const protocol::DocumentUri& uri) {
|
||||
std::unique_lock<std::shared_mutex> lock(mutex_);
|
||||
trees_.erase(uri);
|
||||
}
|
||||
}
|
||||
|
||||
SyntaxTree* SyntaxTreeManager::GetTree(const protocol::DocumentUri& uri)
|
||||
{
|
||||
SyntaxTree* SyntaxTreeManager::GetTree(const protocol::DocumentUri& uri) {
|
||||
std::shared_lock<std::shared_mutex> lock(mutex_);
|
||||
auto it = trees_.find(uri);
|
||||
return (it != trees_.end()) ? it->second.get() : nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
const SyntaxTree* SyntaxTreeManager::GetTree(const protocol::DocumentUri& uri) const
|
||||
{
|
||||
const SyntaxTree* SyntaxTreeManager::GetTree(
|
||||
const protocol::DocumentUri& uri) const {
|
||||
std::shared_lock<std::shared_mutex> lock(mutex_);
|
||||
auto it = trees_.find(uri);
|
||||
return (it != trees_.end()) ? it->second.get() : nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
size_t SyntaxTreeManager::GetTreeCount() const
|
||||
{
|
||||
size_t SyntaxTreeManager::GetTreeCount() const {
|
||||
std::shared_lock<std::shared_mutex> lock(mutex_);
|
||||
return trees_.size();
|
||||
}
|
||||
}
|
||||
|
||||
void SyntaxTreeManager::Clear()
|
||||
{
|
||||
void SyntaxTreeManager::Clear() {
|
||||
std::unique_lock<std::shared_mutex> lock(mutex_);
|
||||
trees_.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// ============= Parser ================== //
|
||||
// ============= Parser ================== //
|
||||
|
||||
Parser::Parser(std::shared_ptr<EventBus> event_bus) :
|
||||
event_bus_(std::move(event_bus))
|
||||
{
|
||||
Parser::Parser(std::shared_ptr<EventBus> event_bus)
|
||||
: event_bus_(std::move(event_bus)) {
|
||||
if (parser_.SetLanguage(tree_sitter_tsf()))
|
||||
spdlog::info("Set tree-sitter-tsf successfully");
|
||||
else
|
||||
|
|
@ -141,58 +126,53 @@ namespace lsp::service
|
|||
[this](const auto& e) { OnDocumentChanged(e); });
|
||||
event_bus_->Subscribe<events::DocumentClosed>(
|
||||
[this](const auto& e) { OnDocumentClosed(e); });
|
||||
}
|
||||
}
|
||||
|
||||
Parser::~Parser() = default;
|
||||
Parser::~Parser() = default;
|
||||
|
||||
TSParser* Parser::GetRawParser() const
|
||||
{
|
||||
return parser_.GetRawParser();
|
||||
};
|
||||
TSParser* Parser::GetRawParser() const { return parser_.GetRawParser(); };
|
||||
|
||||
TSTree* Parser::GetTree(const protocol::DocumentUri& uri) const
|
||||
{
|
||||
TSTree* Parser::GetTree(const protocol::DocumentUri& uri) const {
|
||||
auto* syntax_tree = syntax_tree_manager_.GetTree(uri);
|
||||
return syntax_tree ? syntax_tree->Get() : nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<symbol::EditingSymbolTable> Parser::ParseTsfFile(const std::string& file_path)
|
||||
{
|
||||
std::optional<symbol::EditingSymbolTable> Parser::ParseTsfFile(
|
||||
const std::string& file_path) {
|
||||
std::string extension = std::filesystem::path(file_path).extension().string();
|
||||
std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
|
||||
if (extension != ".tsf" && extension != ".tsl")
|
||||
return std::nullopt;
|
||||
std::transform(extension.begin(), extension.end(), extension.begin(),
|
||||
::tolower);
|
||||
if (extension != ".tsf" && extension != ".tsl") return std::nullopt;
|
||||
spdlog::debug("Parse tsf file: {}", file_path);
|
||||
|
||||
// 读取文件
|
||||
std::ifstream file(file_path);
|
||||
if (!file.is_open())
|
||||
{
|
||||
if (!file.is_open()) {
|
||||
spdlog::trace("Cannot open file: {}", file_path);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::string content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
||||
std::string content((std::istreambuf_iterator<char>(file)),
|
||||
std::istreambuf_iterator<char>());
|
||||
file.close();
|
||||
|
||||
// 创建局部的 parser 和 extractor(线程安全)
|
||||
TreeSitter local_parser;
|
||||
if (!local_parser.SetLanguage(tree_sitter_tsf()))
|
||||
{
|
||||
if (!local_parser.SetLanguage(tree_sitter_tsf())) {
|
||||
spdlog::error("Failed to set tree-sitter language in ParseTsfFile");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// 解析
|
||||
TSTree* tree = local_parser.Parse(content.c_str(), content.length());
|
||||
if (!tree)
|
||||
{
|
||||
if (!tree) {
|
||||
spdlog::trace("Failed to parse file: {}", file_path);
|
||||
return std::nullopt;
|
||||
}
|
||||
// 解析
|
||||
auto tree_deleter = [](TSTree* t) { ts_tree_delete(t); };
|
||||
std::unique_ptr<TSTree, decltype(tree_deleter)> tree_guard(tree, tree_deleter);
|
||||
std::unique_ptr<TSTree, decltype(tree_deleter)> tree_guard(tree,
|
||||
tree_deleter);
|
||||
|
||||
symbol::EditingSymbolTable table;
|
||||
table.uri = "file://" + std::filesystem::absolute(file_path).string();
|
||||
|
|
@ -203,34 +183,29 @@ namespace lsp::service
|
|||
table.file_path = std::filesystem::absolute(file_path).string();
|
||||
|
||||
auto ast_result = table.deserializer->Parse(ts_tree_root_node(tree), content);
|
||||
if (ast_result.HasErrors())
|
||||
{
|
||||
if (ast_result.HasErrors()) {
|
||||
for (const auto& it : ast_result.errors)
|
||||
spdlog::warn("Parse Error = {}", it.message);
|
||||
}
|
||||
if (!ast_result.root)
|
||||
{
|
||||
if (!ast_result.root) {
|
||||
spdlog::error("AST root is null for: {}", file_path);
|
||||
return std::nullopt;
|
||||
}
|
||||
table.ast_root = std::move(ast_result.root);
|
||||
table.symbol_table = std::make_unique<language::symbol::SymbolTable>();
|
||||
|
||||
try
|
||||
{
|
||||
// 使用 Builder 构建符号表
|
||||
table.symbol_table->Build(*table.ast_root);
|
||||
try {
|
||||
language::symbol::Builder builder(*table.symbol_table);
|
||||
builder.Build(*table.ast_root);
|
||||
spdlog::debug("Successfully built symbol table for: {}", file_path);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
spdlog::error("Failed to build symbol table for {}: {}", file_path, e.what());
|
||||
} catch (const std::exception& e) {
|
||||
spdlog::error("Failed to build symbol table for {}: {}", file_path,
|
||||
e.what());
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto metadata = InferFileMetadata(*table.symbol_table);
|
||||
if (!metadata)
|
||||
{
|
||||
if (!metadata) {
|
||||
spdlog::warn("Cannot infer file metadata from symbol table: {}", file_path);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
|
@ -241,119 +216,113 @@ namespace lsp::service
|
|||
auto normalize_name = [](std::string s) {
|
||||
std::transform(s.begin(), s.end(), s.begin(), ::tolower);
|
||||
size_t at_pos = s.find("@");
|
||||
if (at_pos != std::string::npos)
|
||||
s = s.substr(0, at_pos);
|
||||
if (at_pos != std::string::npos) s = s.substr(0, at_pos);
|
||||
return s;
|
||||
};
|
||||
if (normalize_name(file_stem) != normalize_name(metadata->primary_symbol))
|
||||
spdlog::warn("File name '{}' doesn't match primary symbol '{}' in: {}", file_stem, metadata->primary_symbol, file_path);
|
||||
spdlog::warn("File name '{}' doesn't match primary symbol '{}' in: {}",
|
||||
file_stem, metadata->primary_symbol, file_path);
|
||||
|
||||
spdlog::debug("Successfully parsed file: {} (type: {}, symbol: {})",
|
||||
file_path,
|
||||
static_cast<int>(metadata->file_type),
|
||||
file_path, static_cast<int>(metadata->file_type),
|
||||
metadata->primary_symbol);
|
||||
return table;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<Parser::FileMetadata> Parser::InferFileMetadata(const language::symbol::SymbolTable& table)
|
||||
{
|
||||
auto top_level_symbols = table.GetDocumentSymbols();
|
||||
std::optional<Parser::FileMetadata> Parser::InferFileMetadata(
|
||||
const language::symbol::SymbolTable& table) {
|
||||
auto top_level_symbols = symbol::utils::GetTopLevelSymbols(table);
|
||||
|
||||
if (top_level_symbols.empty())
|
||||
{
|
||||
if (top_level_symbols.empty()) {
|
||||
spdlog::warn("No top-level symbol definitions found in symbol table");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// 过滤掉特殊的全局命名空间符号 "::"
|
||||
std::vector<const language::symbol::SymbolDefinition*> filtered_symbols;
|
||||
for (const auto* sym : top_level_symbols)
|
||||
{
|
||||
if (sym->name != "::")
|
||||
{
|
||||
std::vector<const language::symbol::Symbol*> filtered_symbols;
|
||||
filtered_symbols.reserve(top_level_symbols.size());
|
||||
for (const auto* sym : top_level_symbols) {
|
||||
if (sym->name() != "::") {
|
||||
filtered_symbols.push_back(sym);
|
||||
}
|
||||
}
|
||||
|
||||
if (filtered_symbols.empty())
|
||||
{
|
||||
if (filtered_symbols.empty()) {
|
||||
spdlog::warn("No valid top-level symbols found (only global namespace)");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// 按位置排序
|
||||
std::sort(filtered_symbols.begin(), filtered_symbols.end(), [](const language::symbol::SymbolDefinition* a, const language::symbol::SymbolDefinition* b) {
|
||||
if (a->location.start_byte != b->location.start_byte)
|
||||
{
|
||||
return a->location.start_byte < b->location.start_byte;
|
||||
}
|
||||
if (a->location.start_line != b->location.start_line)
|
||||
{
|
||||
return a->location.start_line < b->location.start_line;
|
||||
}
|
||||
return a->location.start_column < b->location.start_column;
|
||||
std::sort(
|
||||
filtered_symbols.begin(), filtered_symbols.end(),
|
||||
[](const language::symbol::Symbol* a, const language::symbol::Symbol* b) {
|
||||
const auto& loc_a = a->range();
|
||||
const auto& loc_b = b->range();
|
||||
|
||||
if (loc_a.start_offset != loc_b.start_offset)
|
||||
return loc_a.start_offset < loc_b.start_offset;
|
||||
if (loc_a.start_line != loc_b.start_line)
|
||||
return loc_a.start_line < loc_b.start_line;
|
||||
return loc_a.start_column < loc_b.start_column;
|
||||
});
|
||||
|
||||
// 取第一个真实的顶层符号
|
||||
const language::symbol::SymbolDefinition* first_top_level = filtered_symbols[0];
|
||||
const language::symbol::Symbol* first_top_level = filtered_symbols[0];
|
||||
|
||||
FileMetadata metadata;
|
||||
metadata.primary_symbol = first_top_level->name;
|
||||
metadata.primary_kind = first_top_level->kind;
|
||||
metadata.primary_symbol = first_top_level->name();
|
||||
metadata.primary_kind = first_top_level->kind();
|
||||
|
||||
// 根据第一个符号推断文件类型
|
||||
switch (first_top_level->kind)
|
||||
{
|
||||
switch (first_top_level->kind()) {
|
||||
case protocol::SymbolKind::Module:
|
||||
metadata.file_type = symbol::TsfFileType::kUnit;
|
||||
spdlog::trace("File type inferred as Unit from first symbol: '{}'", first_top_level->name);
|
||||
spdlog::trace("File type inferred as Unit from first symbol: '{}'",
|
||||
first_top_level->name());
|
||||
break;
|
||||
|
||||
case protocol::SymbolKind::Class:
|
||||
metadata.file_type = symbol::TsfFileType::kClass;
|
||||
spdlog::trace("File type inferred as Class from first symbol: '{}'", first_top_level->name);
|
||||
spdlog::trace("File type inferred as Class from first symbol: '{}'",
|
||||
first_top_level->name());
|
||||
break;
|
||||
|
||||
case protocol::SymbolKind::Function:
|
||||
metadata.file_type = symbol::TsfFileType::kFunction;
|
||||
spdlog::trace("File type inferred as Function from first symbol: '{}'", first_top_level->name);
|
||||
spdlog::trace("File type inferred as Function from first symbol: '{}'",
|
||||
first_top_level->name());
|
||||
break;
|
||||
|
||||
default:
|
||||
metadata.file_type = symbol::TsfFileType::kScript;
|
||||
spdlog::trace("File type inferred as Script (default) from first symbol: '{}'", first_top_level->name);
|
||||
spdlog::trace(
|
||||
"File type inferred as Script (default) from first symbol: '{}'",
|
||||
first_top_level->name());
|
||||
break;
|
||||
}
|
||||
|
||||
spdlog::debug("Inferred file metadata: type={}, symbol='{}', kind={}, location={}:{}",
|
||||
static_cast<int>(metadata.file_type),
|
||||
metadata.primary_symbol,
|
||||
spdlog::debug(
|
||||
"Inferred file metadata: type={}, symbol='{}', kind={}, location={}:{}",
|
||||
static_cast<int>(metadata.file_type), metadata.primary_symbol,
|
||||
static_cast<int>(metadata.primary_kind),
|
||||
first_top_level->location.start_line,
|
||||
first_top_level->location.start_column);
|
||||
first_top_level->range().start_line,
|
||||
first_top_level->range().start_column);
|
||||
|
||||
return metadata;
|
||||
}
|
||||
}
|
||||
|
||||
void Parser::OnDocumentOpened(const events::DocumentOpend& event)
|
||||
{
|
||||
TSTree* tree = parser_.Parse(event.textDocument.text.c_str(), event.textDocument.text.length());
|
||||
if (tree)
|
||||
{
|
||||
syntax_tree_manager_.StoreTree(event.textDocument.uri, std::make_unique<SyntaxTree>(tree));
|
||||
event_bus_->Publish(events::DocumentParsed{
|
||||
.item = event.textDocument,
|
||||
.tree = tree });
|
||||
void Parser::OnDocumentOpened(const events::DocumentOpend& event) {
|
||||
TSTree* tree = parser_.Parse(event.textDocument.text.c_str(),
|
||||
event.textDocument.text.length());
|
||||
if (tree) {
|
||||
syntax_tree_manager_.StoreTree(event.textDocument.uri,
|
||||
std::make_unique<SyntaxTree>(tree));
|
||||
event_bus_->Publish(
|
||||
events::DocumentParsed{.item = event.textDocument, .tree = tree});
|
||||
spdlog::debug("Successfully parsed document: {}", event.textDocument.uri);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
spdlog::error("Failed to parsed document: {}", event.textDocument.uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Parser::OnDocumentChanged(const events::DocumentChanged& event)
|
||||
{
|
||||
void Parser::OnDocumentChanged(const events::DocumentChanged& event) {
|
||||
SyntaxTree* syntax_tree = syntax_tree_manager_.GetTree(event.uri);
|
||||
TSTree* old_tree = syntax_tree ? syntax_tree->Get() : nullptr;
|
||||
|
||||
|
|
@ -363,28 +332,29 @@ namespace lsp::service
|
|||
syntax_tree->ApplyEdit(change, event.content);
|
||||
|
||||
// 增量解析
|
||||
TSTree* tree = parser_.Parse(event.content.c_str(), event.content.length(), old_tree);
|
||||
TSTree* tree =
|
||||
parser_.Parse(event.content.c_str(), event.content.length(), old_tree);
|
||||
|
||||
if (tree)
|
||||
{
|
||||
syntax_tree_manager_.StoreTree(event.uri, std::make_unique<SyntaxTree>(tree));
|
||||
if (tree) {
|
||||
syntax_tree_manager_.StoreTree(event.uri,
|
||||
std::make_unique<SyntaxTree>(tree));
|
||||
event_bus_->Publish(events::DocumentReparsed{
|
||||
.item{ .uri = event.uri, .languageId = "", .version = event.version, .text = std::move(event.content) },
|
||||
.item{.uri = event.uri,
|
||||
.languageId = "",
|
||||
.version = event.version,
|
||||
.text = std::move(event.content)},
|
||||
.tree = tree,
|
||||
});
|
||||
|
||||
spdlog::debug("Document reparsed successfully: {}", event.uri);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
spdlog::error("Failed to reparse document: {}", event.uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Parser::OnDocumentClosed(const events::DocumentClosed& event)
|
||||
{
|
||||
void Parser::OnDocumentClosed(const events::DocumentClosed& event) {
|
||||
syntax_tree_manager_.RemoveTree(event.textDocument.uri);
|
||||
spdlog::debug("Removed syntax tree for: {}", event.textDocument.uri);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace lsp::service
|
||||
|
|
|
|||
|
|
@ -1,16 +1,60 @@
|
|||
#include <spdlog/spdlog.h>
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include "../language/ast/deserializer.hpp"
|
||||
#include "./detail/symbol/conversion.hpp"
|
||||
#include "./parser.hpp"
|
||||
#include "./symbol.hpp"
|
||||
|
||||
namespace lsp::service
|
||||
{
|
||||
Symbol::Symbol(std::shared_ptr<EventBus> event_bus) :
|
||||
event_bus_(std::move(event_bus))
|
||||
{
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <vector>
|
||||
|
||||
#include "../language/ast/deserializer.hpp"
|
||||
#include "../language/symbol/builder.hpp"
|
||||
#include "./detail/symbol/conversion.hpp"
|
||||
#include "./detail/symbol/utils.hpp"
|
||||
#include "./parser.hpp"
|
||||
|
||||
namespace lsp::service {
|
||||
namespace {
|
||||
|
||||
lsp::protocol::Range ToRange(const language::ast::Location& location) {
|
||||
lsp::protocol::Range range;
|
||||
range.start.line = location.start_line;
|
||||
range.start.character = location.start_column;
|
||||
range.end.line = location.end_line;
|
||||
range.end.character = location.end_column;
|
||||
return range;
|
||||
}
|
||||
|
||||
lsp::protocol::DocumentSymbol ToDocumentSymbol(
|
||||
const language::symbol::SymbolTable& table,
|
||||
const language::symbol::Symbol& symbol) {
|
||||
lsp::protocol::DocumentSymbol doc_symbol;
|
||||
doc_symbol.name = symbol.name();
|
||||
doc_symbol.kind = symbol.kind();
|
||||
doc_symbol.range = ToRange(symbol.range());
|
||||
doc_symbol.selectionRange = ToRange(symbol.selection_range());
|
||||
|
||||
auto detail = lsp::service::symbol::utils::BuildSymbolDetail(symbol);
|
||||
if (!detail.empty()) {
|
||||
doc_symbol.detail = detail;
|
||||
}
|
||||
|
||||
auto children =
|
||||
lsp::service::symbol::utils::GetChildSymbols(table, symbol.id());
|
||||
if (!children.empty()) {
|
||||
std::vector<lsp::protocol::DocumentSymbol> child_symbols;
|
||||
child_symbols.reserve(children.size());
|
||||
for (const auto* child : children) {
|
||||
child_symbols.push_back(ToDocumentSymbol(table, *child));
|
||||
}
|
||||
doc_symbol.children = std::move(child_symbols);
|
||||
}
|
||||
|
||||
return doc_symbol;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
Symbol::Symbol(std::shared_ptr<EventBus> event_bus)
|
||||
: event_bus_(std::move(event_bus)) {
|
||||
// 订阅文档事件
|
||||
event_bus_->Subscribe<events::DocumentParsed>(
|
||||
[this](const auto& e) { OnDocumentParsed(e); });
|
||||
|
|
@ -20,19 +64,17 @@ namespace lsp::service
|
|||
|
||||
event_bus_->Subscribe<events::DocumentClosed>(
|
||||
[this](const auto& e) { OnDocumentClosed(e); });
|
||||
}
|
||||
}
|
||||
|
||||
Symbol::~Symbol() = default;
|
||||
Symbol::~Symbol() = default;
|
||||
|
||||
// ==================== 符号加载 ====================
|
||||
// ==================== 符号加载 ====================
|
||||
|
||||
void Symbol::LoadSystemSymbols(const std::string& folder)
|
||||
{
|
||||
void Symbol::LoadSystemSymbols(const std::string& folder) {
|
||||
spdlog::info("Loading system symbols from: {}", folder);
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
|
||||
if (!std::filesystem::exists(folder))
|
||||
{
|
||||
if (!std::filesystem::exists(folder)) {
|
||||
spdlog::warn("System folder does not exist: {}", folder);
|
||||
return;
|
||||
}
|
||||
|
|
@ -43,14 +85,12 @@ namespace lsp::service
|
|||
auto options = std::filesystem::directory_options::follow_directory_symlink |
|
||||
std::filesystem::directory_options::skip_permission_denied;
|
||||
|
||||
for (const auto& entry : std::filesystem::recursive_directory_iterator(folder, options))
|
||||
{
|
||||
try
|
||||
{
|
||||
for (const auto& entry :
|
||||
std::filesystem::recursive_directory_iterator(folder, options)) {
|
||||
try {
|
||||
// 使用 Parser 解析文件
|
||||
auto table_opt = Parser::ParseTsfFile(entry.path().string());
|
||||
if (!table_opt)
|
||||
{
|
||||
if (!table_opt) {
|
||||
failed++;
|
||||
continue;
|
||||
}
|
||||
|
|
@ -58,42 +98,41 @@ namespace lsp::service
|
|||
auto& table = *table_opt;
|
||||
|
||||
// 确保符号表存在
|
||||
if (!table.symbol_table)
|
||||
{
|
||||
if (!table.symbol_table) {
|
||||
spdlog::warn("Symbol table is null for: {}", entry.path().string());
|
||||
failed++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 提取系统索引
|
||||
symbol::SystemSymbolIndex system_index = symbol::BuildFromEditingTable(table);
|
||||
symbol::SystemSymbolIndex system_index =
|
||||
symbol::BuildFromEditingTable(table);
|
||||
|
||||
system_repo_.Add(std::move(system_index));
|
||||
loaded++;
|
||||
|
||||
if (loaded % 100 == 0)
|
||||
{
|
||||
if (loaded % 100 == 0) {
|
||||
spdlog::debug("Loaded {} system symbols", loaded);
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
spdlog::error("Exception loading system symbol {}: {}", entry.path().string(), e.what());
|
||||
} catch (const std::exception& e) {
|
||||
spdlog::error("Exception loading system symbol {}: {}",
|
||||
entry.path().string(), e.what());
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
|
||||
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start).count();
|
||||
spdlog::info("System symbols loaded: {} files, {} failed, {}ms", loaded, failed, duration);
|
||||
}
|
||||
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::steady_clock::now() - start)
|
||||
.count();
|
||||
spdlog::info("System symbols loaded: {} files, {} failed, {}ms", loaded,
|
||||
failed, duration);
|
||||
}
|
||||
|
||||
void Symbol::LoadWorkspaceSymbols(const std::string& folder)
|
||||
{
|
||||
void Symbol::LoadWorkspaceSymbols(const std::string& folder) {
|
||||
spdlog::info("Loading workspace symbols from: {}", folder);
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
|
||||
if (!std::filesystem::exists(folder))
|
||||
{
|
||||
if (!std::filesystem::exists(folder)) {
|
||||
spdlog::warn("Workspace folder does not exist: {}", folder);
|
||||
return;
|
||||
}
|
||||
|
|
@ -104,17 +143,15 @@ namespace lsp::service
|
|||
auto options = std::filesystem::directory_options::follow_directory_symlink |
|
||||
std::filesystem::directory_options::skip_permission_denied;
|
||||
|
||||
for (const auto& entry : std::filesystem::recursive_directory_iterator(folder, options))
|
||||
{
|
||||
for (const auto& entry :
|
||||
std::filesystem::recursive_directory_iterator(folder, options)) {
|
||||
if (!entry.is_regular_file() || entry.path().extension() != ".tsf")
|
||||
continue;
|
||||
|
||||
try
|
||||
{
|
||||
try {
|
||||
// 使用 Parser 解析文件
|
||||
auto table_opt = Parser::ParseTsfFile(entry.path().string());
|
||||
if (!table_opt)
|
||||
{
|
||||
if (!table_opt) {
|
||||
failed++;
|
||||
continue;
|
||||
}
|
||||
|
|
@ -122,8 +159,7 @@ namespace lsp::service
|
|||
auto& table = *table_opt;
|
||||
|
||||
// 确保符号表存在
|
||||
if (!table.symbol_table)
|
||||
{
|
||||
if (!table.symbol_table) {
|
||||
spdlog::warn("Symbol table is null for: {}", entry.path().string());
|
||||
failed++;
|
||||
continue;
|
||||
|
|
@ -137,10 +173,9 @@ namespace lsp::service
|
|||
|
||||
if (loaded % 100 == 0)
|
||||
spdlog::debug("Loaded {} workspace symbols", loaded);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
spdlog::error("Exception loading workspace symbol {}: {}", entry.path().string(), e.what());
|
||||
} catch (const std::exception& e) {
|
||||
spdlog::error("Exception loading workspace symbol {}: {}",
|
||||
entry.path().string(), e.what());
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
|
|
@ -149,11 +184,11 @@ namespace lsp::service
|
|||
std::chrono::steady_clock::now() - start)
|
||||
.count();
|
||||
|
||||
spdlog::info("Workspace symbols loaded: {} files, {} failed, {}ms", loaded, failed, duration);
|
||||
}
|
||||
spdlog::info("Workspace symbols loaded: {} files, {} failed, {}ms", loaded,
|
||||
failed, duration);
|
||||
}
|
||||
|
||||
void Symbol::ReloadWorkspaceSymbols(const std::string& folder)
|
||||
{
|
||||
void Symbol::ReloadWorkspaceSymbols(const std::string& folder) {
|
||||
spdlog::info("Reloading workspace symbols");
|
||||
|
||||
// 清空现有符号
|
||||
|
|
@ -161,35 +196,45 @@ namespace lsp::service
|
|||
|
||||
// 重新加载
|
||||
LoadWorkspaceSymbols(folder);
|
||||
}
|
||||
}
|
||||
|
||||
const symbol::repository::System& Symbol::SystemRepo() const
|
||||
{
|
||||
const symbol::repository::System& Symbol::SystemRepo() const {
|
||||
return system_repo_;
|
||||
}
|
||||
}
|
||||
|
||||
const symbol::repository::Workspace& Symbol::WorkspaceRepo() const
|
||||
{
|
||||
const symbol::repository::Workspace& Symbol::WorkspaceRepo() const {
|
||||
return workspace_repo_;
|
||||
}
|
||||
}
|
||||
|
||||
const symbol::repository::Editing& Symbol::EditingRepo() const
|
||||
{
|
||||
const symbol::repository::Editing& Symbol::EditingRepo() const {
|
||||
return editing_repo_;
|
||||
}
|
||||
|
||||
std::vector<protocol::DocumentSymbol> Symbol::GetDocumentSymbols(
|
||||
const protocol::DocumentUri& uri) const {
|
||||
std::vector<protocol::DocumentSymbol> result;
|
||||
const auto* table = editing_repo_.GetSymbolTable(uri);
|
||||
if (!table) return result;
|
||||
|
||||
auto top_level = symbol::utils::GetTopLevelSymbols(*table);
|
||||
result.reserve(top_level.size());
|
||||
for (const auto* symbol : top_level) {
|
||||
if (symbol->name() == "::") continue;
|
||||
result.push_back(ToDocumentSymbol(*table, *symbol));
|
||||
}
|
||||
|
||||
// ==================== 事件处理 ====================
|
||||
return result;
|
||||
}
|
||||
|
||||
void Symbol::OnDocumentParsed(const events::DocumentParsed& event)
|
||||
{
|
||||
if (!event.tree)
|
||||
{
|
||||
// ==================== 事件处理 ====================
|
||||
|
||||
void Symbol::OnDocumentParsed(const events::DocumentParsed& event) {
|
||||
if (!event.tree) {
|
||||
spdlog::warn("Received null tree for document: {}", event.item.uri);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
try {
|
||||
// 创建 EditingSymbolTable
|
||||
symbol::EditingSymbolTable table;
|
||||
table.uri = event.item.uri;
|
||||
|
|
@ -199,9 +244,9 @@ namespace lsp::service
|
|||
table.deserializer = std::make_unique<language::ast::Deserializer>();
|
||||
|
||||
// 使用 AST 反序列化器解析文档
|
||||
auto ast_result = table.deserializer->Parse(ts_tree_root_node(event.tree), event.item.text);
|
||||
if (!ast_result.IsSuccess())
|
||||
{
|
||||
auto ast_result = table.deserializer->Parse(ts_tree_root_node(event.tree),
|
||||
event.item.text);
|
||||
if (!ast_result.IsSuccess()) {
|
||||
spdlog::error("Failed to deserialize AST for: {}", event.item.uri);
|
||||
return;
|
||||
}
|
||||
|
|
@ -210,22 +255,20 @@ namespace lsp::service
|
|||
|
||||
// 确定文件类型 (根据 AST root 的 Program 内容判断)
|
||||
symbol::TsfFileType file_type = symbol::TsfFileType::kScript;
|
||||
if (table.ast_root)
|
||||
{
|
||||
if (table.ast_root) {
|
||||
auto& program = *table.ast_root;
|
||||
if (!program.statements.empty())
|
||||
{
|
||||
if (!program.statements.empty()) {
|
||||
auto& first_stmt = program.statements[0];
|
||||
if (auto* unit_def = dynamic_cast<language::ast::UnitDefinition*>(first_stmt.get()))
|
||||
{
|
||||
if (auto* unit_def = dynamic_cast<language::ast::UnitDefinition*>(
|
||||
first_stmt.get())) {
|
||||
file_type = symbol::TsfFileType::kUnit;
|
||||
}
|
||||
else if (auto* class_def = dynamic_cast<language::ast::ClassDefinition*>(first_stmt.get()))
|
||||
{
|
||||
} else if (auto* class_def =
|
||||
dynamic_cast<language::ast::ClassDefinition*>(
|
||||
first_stmt.get())) {
|
||||
file_type = symbol::TsfFileType::kClass;
|
||||
}
|
||||
else if (auto* func_def = dynamic_cast<language::ast::FunctionDefinition*>(first_stmt.get()))
|
||||
{
|
||||
} else if (auto* func_def =
|
||||
dynamic_cast<language::ast::FunctionDefinition*>(
|
||||
first_stmt.get())) {
|
||||
file_type = symbol::TsfFileType::kFunction;
|
||||
}
|
||||
}
|
||||
|
|
@ -234,26 +277,26 @@ namespace lsp::service
|
|||
|
||||
// 创建符号表并构建
|
||||
table.symbol_table = std::make_unique<language::symbol::SymbolTable>();
|
||||
table.symbol_table->Build(*table.ast_root);
|
||||
language::symbol::Builder builder(*table.symbol_table);
|
||||
builder.Build(*table.ast_root);
|
||||
// 添加到编辑仓库
|
||||
editing_repo_.AddOrUpdate(std::move(table));
|
||||
|
||||
spdlog::debug("Document parsed and added to editing repo: {}", event.item.uri);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
spdlog::error("Exception loading editing symbol {}: {}", event.item.uri, e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void Symbol::OnDocumentReparsed(const events::DocumentReparsed& event)
|
||||
{
|
||||
spdlog::debug("OnDocumentReparsed");
|
||||
}
|
||||
|
||||
void Symbol::OnDocumentClosed(const events::DocumentClosed& event)
|
||||
{
|
||||
editing_repo_.Remove(event.textDocument.uri);
|
||||
spdlog::debug("Document closed and removed from editing repo: {}", event.textDocument.uri);
|
||||
spdlog::debug("Document parsed and added to editing repo: {}",
|
||||
event.item.uri);
|
||||
} catch (const std::exception& e) {
|
||||
spdlog::error("Exception loading editing symbol {}: {}", event.item.uri,
|
||||
e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void Symbol::OnDocumentReparsed(const events::DocumentReparsed& event) {
|
||||
spdlog::debug("OnDocumentReparsed");
|
||||
}
|
||||
|
||||
void Symbol::OnDocumentClosed(const events::DocumentClosed& event) {
|
||||
editing_repo_.Remove(event.textDocument.uri);
|
||||
spdlog::debug("Document closed and removed from editing repo: {}",
|
||||
event.textDocument.uri);
|
||||
}
|
||||
} // namespace lsp::service
|
||||
|
|
|
|||
|
|
@ -1,15 +1,16 @@
|
|||
#pragma once
|
||||
#include <memory>
|
||||
#include "./base/events.hpp"
|
||||
#include <vector>
|
||||
|
||||
#include "../protocol/protocol.hpp"
|
||||
#include "./base/event_bus.hpp"
|
||||
#include "./base/events.hpp"
|
||||
#include "./detail/symbol/repository/editing.hpp"
|
||||
#include "./detail/symbol/repository/system.hpp"
|
||||
#include "./detail/symbol/repository/workspace.hpp"
|
||||
#include "./detail/symbol/repository/editing.hpp"
|
||||
|
||||
namespace lsp::service
|
||||
{
|
||||
class Symbol
|
||||
{
|
||||
namespace lsp::service {
|
||||
class Symbol {
|
||||
public:
|
||||
explicit Symbol(std::shared_ptr<EventBus> event_bus);
|
||||
~Symbol();
|
||||
|
|
@ -22,6 +23,8 @@ namespace lsp::service
|
|||
const symbol::repository::System& SystemRepo() const;
|
||||
const symbol::repository::Workspace& WorkspaceRepo() const;
|
||||
const symbol::repository::Editing& EditingRepo() const;
|
||||
std::vector<protocol::DocumentSymbol> GetDocumentSymbols(
|
||||
const protocol::DocumentUri& uri) const;
|
||||
|
||||
private:
|
||||
void OnDocumentParsed(const events::DocumentParsed& event);
|
||||
|
|
@ -34,5 +37,5 @@ namespace lsp::service
|
|||
symbol::repository::Editing editing_repo_;
|
||||
|
||||
std::shared_ptr<EventBus> event_bus_;
|
||||
};
|
||||
}
|
||||
};
|
||||
} // namespace lsp::service
|
||||
|
|
|
|||
Loading…
Reference in New Issue