✨ feat(lsp_completion): support unit-qualified constructors
- Support `new UnitA.ClassName` and `new unit(UnitA).ClassName` completion.
- Support `createobject("UnitA.ClassName"` and `createobject("unit(UnitA).ClassName"` completion.
- Apply `uses` ordering when resolving ambiguous ClassName (last wins).
- Improve `obj.` member completion using qualified type strings.
This commit is contained in:
parent
3106ab9ce4
commit
171d5e2064
|
|
@ -11,6 +11,77 @@ import lsp.utils.string;
|
||||||
|
|
||||||
namespace lsp::language::semantic
|
namespace lsp::language::semantic
|
||||||
{
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
std::optional<std::string> UnquoteStringLiteral(std::string value)
|
||||||
|
{
|
||||||
|
if (value.size() < 2)
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
char first = value.front();
|
||||||
|
char last = value.back();
|
||||||
|
if ((first == '"' && last == '"') || (first == '\'' && last == '\''))
|
||||||
|
{
|
||||||
|
value.erase(value.begin());
|
||||||
|
value.pop_back();
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::pair<std::string, std::string>> ParseQualifiedTypeName(const std::string& text)
|
||||||
|
{
|
||||||
|
std::string trimmed = utils::Trim(text);
|
||||||
|
if (trimmed.empty())
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string lower = utils::ToLower(trimmed);
|
||||||
|
if (lower.starts_with("unit("))
|
||||||
|
{
|
||||||
|
std::string after_unit = trimmed.substr(5);
|
||||||
|
std::size_t close_paren = after_unit.find(')');
|
||||||
|
if (close_paren == std::string::npos)
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string unit_name = utils::Trim(after_unit.substr(0, close_paren));
|
||||||
|
std::size_t dot_pos = after_unit.find('.', close_paren);
|
||||||
|
if (dot_pos == std::string::npos || dot_pos + 1 >= after_unit.size())
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string class_name = utils::Trim(after_unit.substr(dot_pos + 1));
|
||||||
|
if (unit_name.empty() || class_name.empty())
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::make_pair(std::move(unit_name), std::move(class_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t dot_pos = trimmed.find_last_of('.');
|
||||||
|
if (dot_pos == std::string::npos || dot_pos == 0 || dot_pos + 1 >= trimmed.size())
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string unit_name = utils::Trim(trimmed.substr(0, dot_pos));
|
||||||
|
std::string class_name = utils::Trim(trimmed.substr(dot_pos + 1));
|
||||||
|
if (unit_name.empty() || class_name.empty())
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::make_pair(std::move(unit_name), std::move(class_name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Analyzer::Analyzer(symbol::SymbolTable& symbol_table,
|
Analyzer::Analyzer(symbol::SymbolTable& symbol_table,
|
||||||
SemanticModel& semantic_model) : symbol_table_(symbol_table),
|
SemanticModel& semantic_model) : symbol_table_(symbol_table),
|
||||||
|
|
@ -737,7 +808,74 @@ namespace lsp::language::semantic
|
||||||
}
|
}
|
||||||
else if ([[maybe_unused]] auto* attr = dynamic_cast<ast::AttributeExpression*>(node.target.get()))
|
else if ([[maybe_unused]] auto* attr = dynamic_cast<ast::AttributeExpression*>(node.target.get()))
|
||||||
{
|
{
|
||||||
// 处理限定名的类型(如 SomeUnit.SomeClass)
|
const auto* class_ident = dynamic_cast<ast::Identifier*>(attr->attribute.get());
|
||||||
|
if (class_ident && !class_ident->name.empty())
|
||||||
|
{
|
||||||
|
std::optional<std::string> unit_name;
|
||||||
|
if (const auto* unit_ident = dynamic_cast<ast::Identifier*>(attr->object.get()))
|
||||||
|
{
|
||||||
|
unit_name = unit_ident->name;
|
||||||
|
}
|
||||||
|
else if (const auto* unit_call = dynamic_cast<ast::CallExpression*>(attr->object.get()))
|
||||||
|
{
|
||||||
|
if (const auto* callee_ident = dynamic_cast<ast::Identifier*>(unit_call->callee.get()))
|
||||||
|
{
|
||||||
|
if (utils::IEquals(callee_ident->name, "unit") && !unit_call->arguments.empty() && unit_call->arguments[0].value)
|
||||||
|
{
|
||||||
|
if (const auto* arg_ident = dynamic_cast<ast::Identifier*>(unit_call->arguments[0].value.get()))
|
||||||
|
{
|
||||||
|
unit_name = arg_ident->name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unit_name && !unit_name->empty())
|
||||||
|
{
|
||||||
|
auto unit_id = ResolveIdentifier(*unit_name, class_ident->location);
|
||||||
|
if (unit_id)
|
||||||
|
{
|
||||||
|
const auto* unit_symbol = symbol_table_.definition(*unit_id);
|
||||||
|
if (unit_symbol && unit_symbol->Is<symbol::Unit>())
|
||||||
|
{
|
||||||
|
auto scope_id = FindScopeOwnedBy(*unit_id);
|
||||||
|
if (scope_id)
|
||||||
|
{
|
||||||
|
auto member_id = symbol_table_.scopes().FindSymbolInScope(*scope_id, class_ident->name);
|
||||||
|
if (member_id)
|
||||||
|
{
|
||||||
|
const auto* member_symbol = symbol_table_.definition(*member_id);
|
||||||
|
if (member_symbol && member_symbol->kind() == protocol::SymbolKind::Class)
|
||||||
|
{
|
||||||
|
TrackReference(*member_id, class_ident->location, false);
|
||||||
|
|
||||||
|
auto class_scope_id = FindScopeOwnedBy(*member_id);
|
||||||
|
if (class_scope_id)
|
||||||
|
{
|
||||||
|
auto ctor_id = symbol_table_.scopes().FindSymbolInScope(*class_scope_id, class_ident->name);
|
||||||
|
if (ctor_id)
|
||||||
|
{
|
||||||
|
TrackCall(*ctor_id, node.span);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TrackCall(*member_id, node.span);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TrackCall(*member_id, node.span);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fallback
|
||||||
VisitExpression(*node.target);
|
VisitExpression(*node.target);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -1150,6 +1288,56 @@ namespace lsp::language::semantic
|
||||||
return type_system.CreateClassType(*class_id);
|
return type_system.CreateClassType(*class_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (auto* attr = dynamic_cast<ast::AttributeExpression*>(new_expr->target.get()))
|
||||||
|
{
|
||||||
|
const auto* class_ident = dynamic_cast<ast::Identifier*>(attr->attribute.get());
|
||||||
|
if (class_ident && !class_ident->name.empty())
|
||||||
|
{
|
||||||
|
std::optional<std::string> unit_name;
|
||||||
|
if (const auto* unit_ident = dynamic_cast<ast::Identifier*>(attr->object.get()))
|
||||||
|
{
|
||||||
|
unit_name = unit_ident->name;
|
||||||
|
}
|
||||||
|
else if (const auto* unit_call = dynamic_cast<ast::CallExpression*>(attr->object.get()))
|
||||||
|
{
|
||||||
|
if (const auto* callee_ident = dynamic_cast<ast::Identifier*>(unit_call->callee.get()))
|
||||||
|
{
|
||||||
|
if (utils::IEquals(callee_ident->name, "unit") && !unit_call->arguments.empty() && unit_call->arguments[0].value)
|
||||||
|
{
|
||||||
|
if (const auto* arg_ident = dynamic_cast<ast::Identifier*>(unit_call->arguments[0].value.get()))
|
||||||
|
{
|
||||||
|
unit_name = arg_ident->name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unit_name && !unit_name->empty())
|
||||||
|
{
|
||||||
|
auto unit_id = ResolveIdentifier(*unit_name, class_ident->location);
|
||||||
|
if (unit_id)
|
||||||
|
{
|
||||||
|
const auto* unit_symbol = symbol_table_.definition(*unit_id);
|
||||||
|
if (unit_symbol && unit_symbol->Is<symbol::Unit>())
|
||||||
|
{
|
||||||
|
auto scope_id = FindScopeOwnedBy(*unit_id);
|
||||||
|
if (scope_id)
|
||||||
|
{
|
||||||
|
auto member_id = symbol_table_.scopes().FindSymbolInScope(*scope_id, class_ident->name);
|
||||||
|
if (member_id)
|
||||||
|
{
|
||||||
|
const auto* member_symbol = symbol_table_.definition(*member_id);
|
||||||
|
if (member_symbol && member_symbol->kind() == protocol::SymbolKind::Class)
|
||||||
|
{
|
||||||
|
return type_system.CreateClassType(*member_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return type_system.GetUnknownType();
|
return type_system.GetUnknownType();
|
||||||
}
|
}
|
||||||
|
|
@ -1166,7 +1354,39 @@ namespace lsp::language::semantic
|
||||||
{
|
{
|
||||||
if (lit->literal_kind == ast::LiteralKind::kString)
|
if (lit->literal_kind == ast::LiteralKind::kString)
|
||||||
{
|
{
|
||||||
auto class_id = ResolveClassSymbol(lit->value, lit->location);
|
std::string type_name = lit->value;
|
||||||
|
if (auto unquoted = UnquoteStringLiteral(type_name))
|
||||||
|
{
|
||||||
|
type_name = *unquoted;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto qualified = ParseQualifiedTypeName(type_name))
|
||||||
|
{
|
||||||
|
auto unit_id = ResolveIdentifier(qualified->first, lit->location);
|
||||||
|
if (unit_id)
|
||||||
|
{
|
||||||
|
const auto* unit_symbol = symbol_table_.definition(*unit_id);
|
||||||
|
if (unit_symbol && unit_symbol->Is<symbol::Unit>())
|
||||||
|
{
|
||||||
|
auto scope_id = FindScopeOwnedBy(*unit_id);
|
||||||
|
if (scope_id)
|
||||||
|
{
|
||||||
|
auto member_id = symbol_table_.scopes().FindSymbolInScope(*scope_id, qualified->second);
|
||||||
|
if (member_id)
|
||||||
|
{
|
||||||
|
const auto* member_symbol = symbol_table_.definition(*member_id);
|
||||||
|
if (member_symbol && member_symbol->kind() == protocol::SymbolKind::Class)
|
||||||
|
{
|
||||||
|
return type_system.CreateClassType(*member_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto class_id = ResolveClassSymbol(type_name, lit->location);
|
||||||
if (class_id)
|
if (class_id)
|
||||||
{
|
{
|
||||||
return type_system.CreateClassType(*class_id);
|
return type_system.CreateClassType(*class_id);
|
||||||
|
|
@ -1174,6 +1394,7 @@ namespace lsp::language::semantic
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return type_system.GetUnknownType();
|
return type_system.GetUnknownType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,8 +50,45 @@ namespace lsp::language::symbol
|
||||||
|
|
||||||
if (const auto* attr = dynamic_cast<const ast::AttributeExpression*>(expr))
|
if (const auto* attr = dynamic_cast<const ast::AttributeExpression*>(expr))
|
||||||
{
|
{
|
||||||
// Handle `unit(x).ClassName` by taking last attribute segment.
|
// Prefer preserving unit-qualified names like `UnitA.ClassName` / `unit(UnitA).ClassName`,
|
||||||
return ExtractClassNameFromExpression(attr->attribute.get());
|
// fallback to last attribute segment if we can't extract a qualifier.
|
||||||
|
auto attr_name = ExtractClassNameFromExpression(attr->attribute.get());
|
||||||
|
if (!attr_name || attr_name->empty())
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> qualifier;
|
||||||
|
if (const auto* obj_ident = dynamic_cast<const ast::Identifier*>(attr->object.get()))
|
||||||
|
{
|
||||||
|
if (!obj_ident->name.empty())
|
||||||
|
{
|
||||||
|
qualifier = obj_ident->name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (const auto* obj_call = dynamic_cast<const ast::CallExpression*>(attr->object.get()))
|
||||||
|
{
|
||||||
|
if (const auto* callee_ident = dynamic_cast<const ast::Identifier*>(obj_call->callee.get()))
|
||||||
|
{
|
||||||
|
if (utils::IEquals(callee_ident->name, "unit") && !obj_call->arguments.empty() && obj_call->arguments[0].value)
|
||||||
|
{
|
||||||
|
if (const auto* arg_ident = dynamic_cast<const ast::Identifier*>(obj_call->arguments[0].value.get()))
|
||||||
|
{
|
||||||
|
if (!arg_ident->name.empty())
|
||||||
|
{
|
||||||
|
qualifier = "unit(" + arg_ident->name + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (qualifier && !qualifier->empty())
|
||||||
|
{
|
||||||
|
return *qualifier + "." + *attr_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return attr_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (const auto* call = dynamic_cast<const ast::CallExpression*>(expr))
|
if (const auto* call = dynamic_cast<const ast::CallExpression*>(expr))
|
||||||
|
|
|
||||||
|
|
@ -610,6 +610,76 @@ namespace lsp::provider::text_document
|
||||||
std::string member_prefix;
|
std::string member_prefix;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct QualifiedPrefix
|
||||||
|
{
|
||||||
|
std::string unit_name;
|
||||||
|
std::string member_prefix;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::optional<QualifiedPrefix> ParseUnitQualifiedPrefix(const std::string& text)
|
||||||
|
{
|
||||||
|
std::string trimmed = utils::Trim(text);
|
||||||
|
if (trimmed.empty())
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string lower = utils::ToLower(trimmed);
|
||||||
|
|
||||||
|
// unit(UnitA).Prefix
|
||||||
|
if (lower.starts_with("unit("))
|
||||||
|
{
|
||||||
|
std::string after_unit = trimmed.substr(5);
|
||||||
|
std::size_t close_paren = after_unit.find(')');
|
||||||
|
if (close_paren == std::string::npos)
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string unit_name = utils::Trim(after_unit.substr(0, close_paren));
|
||||||
|
std::size_t dot_pos = after_unit.find('.', close_paren);
|
||||||
|
std::string member_prefix;
|
||||||
|
if (dot_pos != std::string::npos && dot_pos + 1 <= after_unit.size())
|
||||||
|
{
|
||||||
|
member_prefix = utils::Trim(after_unit.substr(dot_pos + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unit_name.empty())
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QualifiedPrefix{
|
||||||
|
.unit_name = std::move(unit_name),
|
||||||
|
.member_prefix = std::move(member_prefix),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnitA.Prefix
|
||||||
|
std::size_t dot_pos = trimmed.find_last_of('.');
|
||||||
|
if (dot_pos != std::string::npos)
|
||||||
|
{
|
||||||
|
std::string unit_name = utils::Trim(trimmed.substr(0, dot_pos));
|
||||||
|
std::string member_prefix;
|
||||||
|
if (dot_pos + 1 <= trimmed.size())
|
||||||
|
{
|
||||||
|
member_prefix = utils::Trim(trimmed.substr(dot_pos + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unit_name.empty())
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QualifiedPrefix{
|
||||||
|
.unit_name = std::move(unit_name),
|
||||||
|
.member_prefix = std::move(member_prefix),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<DotAccessContext> DetectDotAccessContext(const std::string& line)
|
std::optional<DotAccessContext> DetectDotAccessContext(const std::string& line)
|
||||||
{
|
{
|
||||||
// unit(xxx).prefix
|
// unit(xxx).prefix
|
||||||
|
|
@ -667,6 +737,88 @@ namespace lsp::provider::text_document
|
||||||
return ctx;
|
return ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> CollectUnitSearchOrder(const language::symbol::SymbolTable& table,
|
||||||
|
const protocol::Position& position,
|
||||||
|
const std::string& content)
|
||||||
|
{
|
||||||
|
std::vector<std::string> imports;
|
||||||
|
|
||||||
|
std::uint32_t offset = utils::text_coordinates::ToOffset(position, content);
|
||||||
|
language::ast::Location loc{};
|
||||||
|
loc.start_line = loc.end_line = position.line;
|
||||||
|
loc.start_column = loc.end_column = position.character;
|
||||||
|
loc.start_offset = loc.end_offset = offset;
|
||||||
|
|
||||||
|
auto add_imports = [&imports](const auto& vec) {
|
||||||
|
imports.reserve(imports.size() + vec.size());
|
||||||
|
for (const auto& imp : vec)
|
||||||
|
{
|
||||||
|
imports.push_back(imp.unit_name);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (auto id_opt = table.FindSymbolAt(loc))
|
||||||
|
{
|
||||||
|
if (const auto* sym = table.definition(*id_opt))
|
||||||
|
{
|
||||||
|
if (const auto* func = sym->As<language::symbol::Function>())
|
||||||
|
add_imports(func->imports);
|
||||||
|
else if (const auto* method = sym->As<language::symbol::Method>())
|
||||||
|
add_imports(method->imports);
|
||||||
|
else if (const auto* cls = sym->As<language::symbol::Class>())
|
||||||
|
add_imports(cls->imports);
|
||||||
|
else if (const auto* unit = sym->As<language::symbol::Unit>())
|
||||||
|
{
|
||||||
|
add_imports(unit->interface_imports);
|
||||||
|
add_imports(unit->implementation_imports);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (imports.empty())
|
||||||
|
{
|
||||||
|
for (const auto& wrapper : table.all_definitions())
|
||||||
|
{
|
||||||
|
const auto& sym = wrapper.get();
|
||||||
|
if (sym.kind() == protocol::SymbolKind::Module)
|
||||||
|
{
|
||||||
|
if (const auto* unit = sym.As<language::symbol::Unit>())
|
||||||
|
{
|
||||||
|
add_imports(unit->interface_imports);
|
||||||
|
add_imports(unit->implementation_imports);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> order;
|
||||||
|
std::unordered_set<std::string, utils::IHasher, utils::IEqualTo> seen;
|
||||||
|
|
||||||
|
std::string module_name = GetModuleName(table);
|
||||||
|
if (!module_name.empty())
|
||||||
|
{
|
||||||
|
order.push_back(module_name);
|
||||||
|
seen.insert(module_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto it = imports.rbegin(); it != imports.rend(); ++it)
|
||||||
|
{
|
||||||
|
if (it->empty())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (seen.find(*it) != seen.end())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
order.push_back(*it);
|
||||||
|
seen.insert(*it);
|
||||||
|
}
|
||||||
|
|
||||||
|
return order;
|
||||||
|
}
|
||||||
|
|
||||||
void AppendClassMethods(const language::symbol::SymbolTable& table, const language::symbol::Class& cls, const std::string& prefix, CompletionSource source, std::vector<SourcedCompletionItem>& out)
|
void AppendClassMethods(const language::symbol::SymbolTable& table, const language::symbol::Class& cls, const std::string& prefix, CompletionSource source, std::vector<SourcedCompletionItem>& out)
|
||||||
{
|
{
|
||||||
auto scope_id = FindScopeOwnedBy(table, language::symbol::ScopeKind::kClass, cls.id);
|
auto scope_id = FindScopeOwnedBy(table, language::symbol::ScopeKind::kClass, cls.id);
|
||||||
|
|
@ -1044,7 +1196,7 @@ namespace lsp::provider::text_document
|
||||||
close == std::string::npos ? after.size() : close,
|
close == std::string::npos ? after.size() : close,
|
||||||
dot == std::string::npos ? after.size() : dot);
|
dot == std::string::npos ? after.size() : dot);
|
||||||
context.unit_name = utils::Trim(after.substr(0, name_end));
|
context.unit_name = utils::Trim(after.substr(0, name_end));
|
||||||
if (!context.unit_name.empty() && IsUnitVisible(visible_units_set, context.unit_name))
|
if (!context.unit_name.empty())
|
||||||
{
|
{
|
||||||
context.is_unit_scoped_new = true;
|
context.is_unit_scoped_new = true;
|
||||||
if (dot != std::string::npos && dot + 1 < after.size())
|
if (dot != std::string::npos && dot + 1 < after.size())
|
||||||
|
|
@ -1055,11 +1207,11 @@ namespace lsp::provider::text_document
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto dot = context.prefix.find('.');
|
auto dot = context.prefix.find_last_of('.');
|
||||||
if (dot != std::string::npos)
|
if (dot != std::string::npos)
|
||||||
{
|
{
|
||||||
auto alias = utils::Trim(context.prefix.substr(0, dot));
|
auto alias = utils::Trim(context.prefix.substr(0, dot));
|
||||||
if (!alias.empty() && IsUnitVisible(visible_units_set, alias))
|
if (!alias.empty())
|
||||||
{
|
{
|
||||||
context.is_unit_scoped_new = true;
|
context.is_unit_scoped_new = true;
|
||||||
context.unit_name = alias;
|
context.unit_name = alias;
|
||||||
|
|
@ -1136,28 +1288,60 @@ namespace lsp::provider::text_document
|
||||||
{
|
{
|
||||||
if (auto type_name = ResolveTypeNameInScope(*editing_table, editing_semantic, params.position, *content, context.object_name))
|
if (auto type_name = ResolveTypeNameInScope(*editing_table, editing_semantic, params.position, *content, context.object_name))
|
||||||
{
|
{
|
||||||
bool found_class = false;
|
std::string type_text = utils::Trim(*type_name);
|
||||||
|
std::string class_name = type_text;
|
||||||
|
std::vector<std::string> unit_candidates;
|
||||||
|
|
||||||
auto try_append = [&](const language::symbol::SymbolTable& table, CompletionSource source) {
|
if (auto qualified = ParseUnitQualifiedPrefix(type_text))
|
||||||
auto cls_opt = FindClassSymbol(table, *type_name);
|
{
|
||||||
|
if (!qualified->unit_name.empty() && !qualified->member_prefix.empty())
|
||||||
|
{
|
||||||
|
unit_candidates.push_back(qualified->unit_name);
|
||||||
|
class_name = qualified->member_prefix;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!class_name.empty())
|
||||||
|
{
|
||||||
|
if (unit_candidates.empty())
|
||||||
|
{
|
||||||
|
unit_candidates = CollectUnitSearchOrder(*editing_table, params.position, *content);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool found_class = false;
|
||||||
|
for (const auto& unit_name : unit_candidates)
|
||||||
|
{
|
||||||
|
if (found_class)
|
||||||
|
break;
|
||||||
|
|
||||||
|
auto try_table = [&](const language::symbol::SymbolTable& table, CompletionSource source) {
|
||||||
|
if (!utils::IEquals(GetModuleName(table), unit_name))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto cls_opt = FindClassSymbol(table, class_name);
|
||||||
if (!cls_opt)
|
if (!cls_opt)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
AppendInstanceMembers(table, (*cls_opt)->id(), context.member_prefix, source, collected);
|
AppendInstanceMembers(table, (*cls_opt)->id(), context.member_prefix, source, collected);
|
||||||
found_class = true;
|
found_class = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
try_append(*editing_table, CompletionSource::kEditing);
|
try_table(*editing_table, CompletionSource::kEditing);
|
||||||
for (const auto* table : workspace_tables)
|
for (const auto* table : workspace_tables)
|
||||||
try_append(*table, CompletionSource::kWorkspace);
|
try_table(*table, CompletionSource::kWorkspace);
|
||||||
for (const auto* table : system_tables)
|
for (const auto* table : system_tables)
|
||||||
try_append(*table, CompletionSource::kSystem);
|
try_table(*table, CompletionSource::kSystem);
|
||||||
|
}
|
||||||
|
|
||||||
(void)found_class;
|
(void)found_class;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else if (context.is_new_context)
|
else if (context.is_new_context)
|
||||||
{
|
{
|
||||||
if (context.is_unit_scoped_new && !context.unit_name.empty())
|
if (context.is_unit_scoped_new && !context.unit_name.empty())
|
||||||
|
|
@ -1170,6 +1354,31 @@ namespace lsp::provider::text_document
|
||||||
AppendClasses(*table, context.prefix, CompletionSource::kSystem, collected, true, context.unit_name);
|
AppendClasses(*table, context.prefix, CompletionSource::kSystem, collected, true, context.unit_name);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
if (editing_table && content)
|
||||||
|
{
|
||||||
|
auto unit_order = CollectUnitSearchOrder(*editing_table, params.position, *content);
|
||||||
|
if (!unit_order.empty())
|
||||||
|
{
|
||||||
|
for (const auto& unit_name : unit_order)
|
||||||
|
{
|
||||||
|
AppendClasses(*editing_table, context.prefix, CompletionSource::kEditing, collected, true, unit_name);
|
||||||
|
for (const auto* table : workspace_tables)
|
||||||
|
AppendClasses(*table, context.prefix, CompletionSource::kWorkspace, collected, true, unit_name);
|
||||||
|
for (const auto* table : system_tables)
|
||||||
|
AppendClasses(*table, context.prefix, CompletionSource::kSystem, collected, true, unit_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AppendClasses(*editing_table, context.prefix, CompletionSource::kEditing, collected, true);
|
||||||
|
for (const auto* table : workspace_tables)
|
||||||
|
AppendClasses(*table, context.prefix, CompletionSource::kWorkspace, collected, true);
|
||||||
|
for (const auto* table : system_tables)
|
||||||
|
AppendClasses(*table, context.prefix, CompletionSource::kSystem, collected, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
if (editing_table)
|
if (editing_table)
|
||||||
AppendClasses(*editing_table, context.prefix, CompletionSource::kEditing, collected, true);
|
AppendClasses(*editing_table, context.prefix, CompletionSource::kEditing, collected, true);
|
||||||
|
|
@ -1179,7 +1388,42 @@ namespace lsp::provider::text_document
|
||||||
AppendClasses(*table, context.prefix, CompletionSource::kSystem, collected, true);
|
AppendClasses(*table, context.prefix, CompletionSource::kSystem, collected, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else if (context.is_createobject_context)
|
else if (context.is_createobject_context)
|
||||||
|
{
|
||||||
|
if (auto qualified = ParseUnitQualifiedPrefix(context.prefix))
|
||||||
|
{
|
||||||
|
if (editing_table)
|
||||||
|
AppendCreateObjectClasses(*editing_table, qualified->member_prefix, CompletionSource::kEditing, collected, context.createobject_has_open_quote, context.createobject_quote, qualified->unit_name);
|
||||||
|
for (const auto* table : workspace_tables)
|
||||||
|
AppendCreateObjectClasses(*table, qualified->member_prefix, CompletionSource::kWorkspace, collected, context.createobject_has_open_quote, context.createobject_quote, qualified->unit_name);
|
||||||
|
for (const auto* table : system_tables)
|
||||||
|
AppendCreateObjectClasses(*table, qualified->member_prefix, CompletionSource::kSystem, collected, context.createobject_has_open_quote, context.createobject_quote, qualified->unit_name);
|
||||||
|
}
|
||||||
|
else if (editing_table && content)
|
||||||
|
{
|
||||||
|
auto unit_order = CollectUnitSearchOrder(*editing_table, params.position, *content);
|
||||||
|
if (!unit_order.empty())
|
||||||
|
{
|
||||||
|
for (const auto& unit_name : unit_order)
|
||||||
|
{
|
||||||
|
AppendCreateObjectClasses(*editing_table, context.prefix, CompletionSource::kEditing, collected, context.createobject_has_open_quote, context.createobject_quote, unit_name);
|
||||||
|
for (const auto* table : workspace_tables)
|
||||||
|
AppendCreateObjectClasses(*table, context.prefix, CompletionSource::kWorkspace, collected, context.createobject_has_open_quote, context.createobject_quote, unit_name);
|
||||||
|
for (const auto* table : system_tables)
|
||||||
|
AppendCreateObjectClasses(*table, context.prefix, CompletionSource::kSystem, collected, context.createobject_has_open_quote, context.createobject_quote, unit_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AppendCreateObjectClasses(*editing_table, context.prefix, CompletionSource::kEditing, collected, context.createobject_has_open_quote, context.createobject_quote);
|
||||||
|
for (const auto* table : workspace_tables)
|
||||||
|
AppendCreateObjectClasses(*table, context.prefix, CompletionSource::kWorkspace, collected, context.createobject_has_open_quote, context.createobject_quote);
|
||||||
|
for (const auto* table : system_tables)
|
||||||
|
AppendCreateObjectClasses(*table, context.prefix, CompletionSource::kSystem, collected, context.createobject_has_open_quote, context.createobject_quote);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
if (editing_table)
|
if (editing_table)
|
||||||
AppendCreateObjectClasses(*editing_table, context.prefix, CompletionSource::kEditing, collected, context.createobject_has_open_quote, context.createobject_quote);
|
AppendCreateObjectClasses(*editing_table, context.prefix, CompletionSource::kEditing, collected, context.createobject_has_open_quote, context.createobject_quote);
|
||||||
|
|
@ -1188,6 +1432,7 @@ namespace lsp::provider::text_document
|
||||||
for (const auto* table : system_tables)
|
for (const auto* table : system_tables)
|
||||||
AppendCreateObjectClasses(*table, context.prefix, CompletionSource::kSystem, collected, context.createobject_has_open_quote, context.createobject_quote);
|
AppendCreateObjectClasses(*table, context.prefix, CompletionSource::kSystem, collected, context.createobject_has_open_quote, context.createobject_quote);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else if (context.is_unit_context)
|
else if (context.is_unit_context)
|
||||||
{
|
{
|
||||||
if (!visible_units_set.empty())
|
if (!visible_units_set.empty())
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue