♻️ 重构符号表的代码

This commit is contained in:
csh 2025-11-17 17:45:39 +08:00
parent 99f735f2df
commit 3274af67d5
29 changed files with 3656 additions and 3962 deletions

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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(), [&params](const protocol::SymbolInformation& a, const protocol::SymbolInformation& b) {
std::sort(symbols.begin(), symbols.end(),
[&params](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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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