summaryrefslogtreecommitdiffstats
path: root/source/slang/slang-language-server-completion.cpp
diff options
context:
space:
mode:
authorYong He <yonghe@outlook.com>2022-06-22 19:58:34 -0700
committerGitHub <noreply@github.com>2022-06-22 19:58:34 -0700
commit07a380d72a13899a84cbdc35692be7a3d9246dcb (patch)
tree68e77f2e9682b3b7c3debd745604a494439e5b25 /source/slang/slang-language-server-completion.cpp
parente5a75563a1ba2e378353af8b937b8b7bb0fe2c2b (diff)
More Language Server Improvements. (#2289)
Diffstat (limited to 'source/slang/slang-language-server-completion.cpp')
-rw-r--r--source/slang/slang-language-server-completion.cpp599
1 files changed, 244 insertions, 355 deletions
diff --git a/source/slang/slang-language-server-completion.cpp b/source/slang/slang-language-server-completion.cpp
index c47e951fb..5eee1dba7 100644
--- a/source/slang/slang-language-server-completion.cpp
+++ b/source/slang/slang-language-server-completion.cpp
@@ -5,18 +5,29 @@
#include "slang-language-server.h"
#include "slang-ast-all.h"
-#include "slang-syntax.h"
#include "slang-check-impl.h"
+#include "slang-syntax.h"
namespace Slang
{
-static bool _isIdentifierChar(char ch)
-{
- return ch >= '0' && ch <= '9' || ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z' || ch == '_';
-}
-
-static bool _isWhitespaceChar(char ch) { return ch == ' ' || ch == '\r' || ch == '\n' || ch == '\t'; }
+static const char* kDeclKeywords[] = {
+ "throws", "static", "const", "in", "out", "inout",
+ "ref", "__subscript", "__init", "property", "get", "set",
+ "class", "struct", "interface", "public", "private", "internal",
+ "protected", "typedef", "typealias", "uniform", "export", "groupshared",
+ "extension", "associatedtype", "namespace", "This", "using",
+ "__generic", "__exported", "import", "enum", "cbuffer", "tbuffer", "func"};
+static const char* kStmtKeywords[] = {
+ "if", "else", "switch", "case", "default", "return",
+ "try", "throw", "throws", "catch", "while", "for",
+ "do", "static", "const", "in", "out", "inout",
+ "ref", "__subscript", "__init", "property", "get", "set",
+ "class", "struct", "interface", "public", "private", "internal",
+ "protected", "typedef", "typealias", "uniform", "export", "groupshared",
+ "extension", "associatedtype", "this", "namespace", "This", "using",
+ "__generic", "__exported", "import", "enum", "break", "continue",
+ "discard", "defer", "cbuffer", "tbuffer", "func"};
static const char* hlslSemanticNames[] = {
"register",
@@ -54,416 +65,294 @@ static const char* hlslSemanticNames[] = {
SlangResult CompletionContext::tryCompleteHLSLSemantic()
{
- auto findResult = findASTNodesAt(
- doc,
- version->linkage->getSourceManager(),
- parsedModule->getModuleDecl(),
- ASTLookupType::Decl,
- canonicalPath,
- line,
- col);
- if (findResult.getCount() == 1 && findResult[0].path.getCount() != 0)
- {
- if (auto semantic = as<HLSLSemantic>(findResult[0].path.getLast()))
- {
- List<LanguageServerProtocol::CompletionItem> items;
- for (auto name : hlslSemanticNames)
- {
- LanguageServerProtocol::CompletionItem item;
- item.label = name;
- item.kind = LanguageServerProtocol::kCompletionItemKindKeyword;
- for (auto ch : getCommitChars())
- item.commitCharacters.add(ch);
- items.add(item);
- }
- server->m_connection->sendResult(&items, responseId);
- return SLANG_OK;
- }
- }
- return SLANG_FAIL;
-}
-
-SlangResult CompletionContext::tryCompleteMember()
-{
- // Scan backward until we locate a '.' or ':'.
- if (cursorOffset > 0)
- cursorOffset--;
- while (cursorOffset > 0 && _isWhitespaceChar(doc->getText()[cursorOffset]))
- {
- cursorOffset--;
- }
- while (cursorOffset > 0 && _isIdentifierChar(doc->getText()[cursorOffset]))
- {
- cursorOffset--;
- }
- while (cursorOffset > 0 && _isWhitespaceChar(doc->getText()[cursorOffset]))
- {
- cursorOffset--;
- }
- if (cursorOffset > 0 && doc->getText()[cursorOffset] == ':')
- cursorOffset--;
- if (cursorOffset <= 0 ||
- (doc->getText()[cursorOffset] != '.' && doc->getText()[cursorOffset] != ':'))
- {
- return SLANG_FAIL;
- }
- doc->offsetToLineCol(cursorOffset, line, col);
- auto findResult = findASTNodesAt(
- doc,
- version->linkage->getSourceManager(),
- parsedModule->getModuleDecl(),
- ASTLookupType::Decl,
- canonicalPath,
- line,
- col);
- if (findResult.getCount() != 1)
+ if (version->linkage->contentAssistInfo.completionSuggestions.scopeKind !=
+ CompletionSuggestions::ScopeKind::HLSLSemantics)
{
return SLANG_FAIL;
}
- if (findResult[0].path.getCount() == 0)
- {
- return SLANG_FAIL;
- }
- Expr* baseExpr = nullptr;
- if (auto memberExpr = as<MemberExpr>(findResult[0].path.getLast()))
- {
- baseExpr = memberExpr->baseExpression;
- }
- else if (auto staticMemberExpr = as<StaticMemberExpr>(findResult[0].path.getLast()))
+ List<LanguageServerProtocol::CompletionItem> items;
+ for (auto name : hlslSemanticNames)
{
- baseExpr = staticMemberExpr->baseExpression;
+ LanguageServerProtocol::CompletionItem item;
+ item.label = name;
+ item.kind = LanguageServerProtocol::kCompletionItemKindKeyword;
+ items.add(item);
}
- else if (auto swizzleExpr = as<SwizzleExpr>(findResult[0].path.getLast()))
- {
- baseExpr = swizzleExpr->base;
- }
- else if (auto matSwizzleExpr = as<MatrixSwizzleExpr>(findResult[0].path.getLast()))
- {
- baseExpr = matSwizzleExpr->base;
- }
- if (!baseExpr || !baseExpr->type.type ||
- baseExpr->type.type->equals(version->linkage->getASTBuilder()->getErrorType()))
+ server->m_connection->sendResult(&items, responseId);
+ return SLANG_OK;
+}
+
+SlangResult CompletionContext::tryCompleteAttributes()
+{
+ if (version->linkage->contentAssistInfo.completionSuggestions.scopeKind !=
+ CompletionSuggestions::ScopeKind::Attribute)
{
return SLANG_FAIL;
}
-
- List<LanguageServerProtocol::CompletionItem> items = collectMembers(baseExpr);
+ List<LanguageServerProtocol::CompletionItem> items = collectAttributes();
server->m_connection->sendResult(&items, responseId);
return SLANG_OK;
}
-// The following collectMember* functions implement the logic to collect all members from a parsed type.]
-// The flow is mostly the same as `lookupMemberInType`, but instead of looking for a specific name,
-// we collect all members we see.
-
-struct MemberCollectingContext
+SlangResult CompletionContext::tryCompleteMemberAndSymbol()
{
- ASTBuilder* astBuilder;
- List<Decl*> members;
- bool includeInstanceMembers = true;
- SharedSemanticsContext semanticsContext;
- MemberCollectingContext(Linkage* linkage, Module* module, DiagnosticSink* sink)
- : semanticsContext(linkage, module, sink)
- {}
-};
-
-void collectMembersInTypeDeclImpl(MemberCollectingContext* context, DeclRef<Decl> declRef);
-
-void collectMembersInType(MemberCollectingContext* context, Type* type);
+ List<LanguageServerProtocol::CompletionItem> items = collectMembersAndSymbols();
+ server->m_connection->sendResult(&items, responseId);
+ return SLANG_OK;
+}
-void collectMembersInType(MemberCollectingContext* context, Type* type)
+List<LanguageServerProtocol::CompletionItem> CompletionContext::collectMembersAndSymbols()
{
- if (auto pointerLikeType = as<PointerLikeType>(type))
- {
- collectMembersInType(context, pointerLikeType->elementType);
- return;
- }
-
- if (auto declRefType = as<DeclRefType>(type))
- {
- auto declRef = declRefType->declRef;
-
- collectMembersInTypeDeclImpl(
- context,
- declRef);
- }
- else if (auto nsType = as<NamespaceType>(type))
- {
- auto declRef = nsType->declRef;
-
- collectMembersInTypeDeclImpl(context, declRef);
- }
- else if (auto extractExistentialType = as<ExtractExistentialType>(type))
- {
- // We want lookup to be performed on the underlying interface type of the existential,
- // but we need to have a this-type substitution applied to ensure that the result of
- // lookup will have a comparable substitution applied (allowing things like associated
- // types, etc. used in the signature of a method to resolve correctly).
- //
- auto interfaceDeclRef = extractExistentialType->getSpecializedInterfaceDeclRef();
- collectMembersInTypeDeclImpl(context, interfaceDeclRef);
- }
- else if (auto thisType = as<ThisType>(type))
+ auto linkage = version->linkage;
+ if (linkage->contentAssistInfo.completionSuggestions.scopeKind ==
+ CompletionSuggestions::ScopeKind::Swizzle)
{
- auto interfaceType = DeclRefType::create(context->astBuilder, thisType->interfaceDeclRef);
- collectMembersInType(context, interfaceType);
+ return createSwizzleCandidates(
+ linkage->contentAssistInfo.completionSuggestions.swizzleBaseType,
+ linkage->contentAssistInfo.completionSuggestions.elementCount);
}
- else if (auto andType = as<AndType>(type))
+ List<LanguageServerProtocol::CompletionItem> result;
+ bool useCommitChars = true;
+ bool addKeywords = false;
+ switch (linkage->contentAssistInfo.completionSuggestions.scopeKind)
{
- auto leftType = andType->left;
- auto rightType = andType->right;
- collectMembersInType(context, leftType);
- collectMembersInType(context, rightType);
+ case CompletionSuggestions::ScopeKind::Member:
+ useCommitChars = (commitCharacterBehavior == CommitCharacterBehavior::MembersOnly || commitCharacterBehavior == CommitCharacterBehavior::All);
+ break;
+ case CompletionSuggestions::ScopeKind::Expr:
+ case CompletionSuggestions::ScopeKind::Decl:
+ case CompletionSuggestions::ScopeKind::Stmt:
+ useCommitChars = (commitCharacterBehavior == CommitCharacterBehavior::All);
+ addKeywords = true;
+ break;
+ default:
+ return result;
}
-}
-
-void collectMembersInTypeDeclImpl(
- MemberCollectingContext* context,
- DeclRef<Decl> declRef)
-{
- if (declRef.getDecl()->checkState.getState() < DeclCheckState::ReadyForLookup)
- return;
-
- if (auto genericTypeParamDeclRef = declRef.as<GenericTypeParamDecl>())
+ HashSet<String> deduplicateSet;
+ for (Index i = 0;
+ i < linkage->contentAssistInfo.completionSuggestions.candidateItems.getCount();
+ i++)
{
- // If the type we are doing lookup in is a generic type parameter,
- // then the members it provides can only be discovered by looking
- // at the constraints that are placed on that type.
- auto genericDeclRef = genericTypeParamDeclRef.getParent().as<GenericDecl>();
- assert(genericDeclRef);
-
- for (auto constraintDeclRef : getMembersOfType<GenericTypeConstraintDecl>(genericDeclRef))
+ auto& suggestedItem = linkage->contentAssistInfo.completionSuggestions.candidateItems[i];
+ auto member = suggestedItem.declRef.getDecl();
+ if (auto genericDecl = as<GenericDecl>(member))
+ member = genericDecl->inner;
+ if (!member)
+ continue;
+ if (!member->getName())
+ continue;
+ LanguageServerProtocol::CompletionItem item;
+ item.label = member->getName()->text;
+ item.kind = LanguageServerProtocol::kCompletionItemKindKeyword;
+ if (as<TypeConstraintDecl>(member))
{
- if (constraintDeclRef.decl->checkState.getState() < DeclCheckState::ReadyForLookup)
- {
- continue;
- }
+ continue;
+ }
+ if (as<ConstructorDecl>(member))
+ {
+ continue;
+ }
+ if (as<SubscriptDecl>(member))
+ {
+ continue;
+ }
+ if (item.label.getLength() == 0)
+ continue;
+ if (!_isIdentifierChar(item.label[0]))
+ continue;
+ if (item.label.startsWith("$"))
+ continue;
+ if (!deduplicateSet.Add(item.label))
+ continue;
- collectMembersInType(
- context,
- getSup(context->astBuilder, constraintDeclRef));
+ if (as<StructDecl>(member))
+ {
+ item.kind = LanguageServerProtocol::kCompletionItemKindStruct;
}
- }
- else if (declRef.as<AssocTypeDecl>() || declRef.as<GlobalGenericParamDecl>())
- {
- for (auto constraintDeclRef :
- getMembersOfType<TypeConstraintDecl>(declRef.as<ContainerDecl>()))
+ else if (as<ClassDecl>(member))
{
- if (constraintDeclRef.decl->checkState.getState() < DeclCheckState::ReadyForLookup)
- {
- continue;
- }
- collectMembersInType(context, getSup(context->astBuilder, constraintDeclRef));
+ item.kind = LanguageServerProtocol::kCompletionItemKindClass;
}
- }
- else if (auto namespaceDecl = declRef.as<NamespaceDecl>())
- {
- for (auto member : namespaceDecl.getDecl()->members)
+ else if (as<InterfaceDecl>(member))
{
- if (member->getName())
- {
- context->members.add(member);
- }
+ item.kind = LanguageServerProtocol::kCompletionItemKindInterface;
+ }
+ else if (as<SimpleTypeDecl>(member))
+ {
+ item.kind = LanguageServerProtocol::kCompletionItemKindClass;
+ }
+ else if (as<PropertyDecl>(member))
+ {
+ item.kind = LanguageServerProtocol::kCompletionItemKindProperty;
+ }
+ else if (as<EnumDecl>(member))
+ {
+ item.kind = LanguageServerProtocol::kCompletionItemKindEnum;
+ }
+ else if (as<VarDeclBase>(member))
+ {
+ item.kind = LanguageServerProtocol::kCompletionItemKindVariable;
+ }
+ else if (as<EnumCaseDecl>(member))
+ {
+ item.kind = LanguageServerProtocol::kCompletionItemKindEnumMember;
+ }
+ else if (as<CallableDecl>(member))
+ {
+ item.kind = LanguageServerProtocol::kCompletionItemKindMethod;
+ }
+ else if (as<AssocTypeDecl>(member))
+ {
+ item.kind = LanguageServerProtocol::kCompletionItemKindClass;
}
+ item.data = String(i);
+ result.add(item);
}
- else if (auto aggTypeDeclBaseRef = declRef.as<AggTypeDeclBase>())
+ if (addKeywords)
{
- // In this case we are peforming lookup in the context of an aggregate
- // type or an `extension`, so the first thing to do is to look for
- // matching members declared directly in the body of the type/`extension`.
- //
- for (auto member : aggTypeDeclBaseRef.getDecl()->members)
+ if (linkage->contentAssistInfo.completionSuggestions.scopeKind ==
+ CompletionSuggestions::ScopeKind::Decl)
{
- if (member->getName())
+ for (auto keyword : kDeclKeywords)
{
- if (!context->includeInstanceMembers)
- {
- // Skip non-static members.
- if (as<PropertyDecl>(member))
- continue;
- if (as<SubscriptDecl>(member))
- continue;
- if (as<VarDeclBase>(member) || as<FuncDecl>(member))
- {
- if (!member->findModifier<HLSLStaticModifier>())
- {
- continue;
- }
- }
- }
- context->members.add(member);
+ if (!deduplicateSet.Add(keyword))
+ continue;
+ LanguageServerProtocol::CompletionItem item;
+ item.label = keyword;
+ item.kind = LanguageServerProtocol::kCompletionItemKindKeyword;
+ item.data = "-1";
+ result.add(item);
}
}
-
- if (auto aggTypeDeclRef = aggTypeDeclBaseRef.as<AggTypeDecl>())
+ else
{
- auto extensions =
- context->semanticsContext.getCandidateExtensionsForTypeDecl(aggTypeDeclRef);
- for (auto extDecl : extensions)
+ for (auto keyword : kStmtKeywords)
{
- // TODO: check if the extension can be applied before including its members.
- // TODO: eventually we need to insert a breadcrumb here so that
- // the constructed result can somehow indicate that a member
- // was found through an extension.
- //
- collectMembersInTypeDeclImpl(
- context,
- DeclRef<Decl>(extDecl, nullptr));
+ if (!deduplicateSet.Add(keyword))
+ continue;
+ LanguageServerProtocol::CompletionItem item;
+ item.label = keyword;
+ item.kind = LanguageServerProtocol::kCompletionItemKindKeyword;
+ item.data = "-1";
+ result.add(item);
}
}
-
- // For both aggregate types and their `extension`s, we want lookup to follow
- // through the declared inheritance relationships on each declaration.
- //
- for (auto inheritanceDeclRef : getMembersOfType<InheritanceDecl>(aggTypeDeclBaseRef))
+
+ for (auto& def : linkage->contentAssistInfo.preprocessorInfo.macroDefinitions)
{
- // Some things that are syntactically `InheritanceDecl`s don't actually
- // represent a subtype/supertype relationship, and thus we shouldn't
- // include members from the base type when doing lookup in the
- // derived type.
- //
- if (inheritanceDeclRef.getDecl()->hasModifier<IgnoreForLookupModifier>())
+ if (!def.name)
continue;
-
- collectMembersInType(
- context, getSup(context->astBuilder, inheritanceDeclRef));
+ auto& text = def.name->text;
+ if (!deduplicateSet.Add(text))
+ continue;
+ LanguageServerProtocol::CompletionItem item;
+ item.label = text;
+ item.kind = LanguageServerProtocol::kCompletionItemKindKeyword;
+ item.data = "-1";
+ result.add(item);
}
}
+ if (useCommitChars)
+ {
+ for (auto& item : result)
+ {
+ for (auto ch : getCommitChars())
+ item.commitCharacters.add(ch);
+ }
+ }
+ return result;
}
-List<LanguageServerProtocol::CompletionItem> CompletionContext::collectMembers(Expr* baseExpr)
+List<LanguageServerProtocol::CompletionItem> CompletionContext::createSwizzleCandidates(
+ Type* type, IntegerLiteralValue elementCount[2])
{
List<LanguageServerProtocol::CompletionItem> result;
- auto linkage = version->linkage;
- Type* type = baseExpr->type.type;
- bool isInstance = true;
- if (auto typeType = as<TypeType>(type))
+ // Hard code members for vector and matrix types.
+ result.clear();
+ if (auto vectorType = as<VectorExpressionType>(type))
{
- type = typeType->type;
- isInstance = false;
+ const char* memberNames[4] = {"x", "y", "z", "w"};
+ Type* elementType = nullptr;
+ elementType = vectorType->elementType;
+ String typeStr;
+ if (elementType)
+ typeStr = elementType->toString();
+ auto count = Math::Min((int)elementCount[0], 4);
+ for (int i = 0; i < count; i++)
+ {
+ LanguageServerProtocol::CompletionItem item;
+ item.data = 0;
+ item.detail = typeStr;
+ item.kind = LanguageServerProtocol::kCompletionItemKindVariable;
+ item.label = memberNames[i];
+ result.add(item);
+ }
}
- version->currentCompletionItems.clear();
- if (type)
+ else if (auto matrixType = as<MatrixExpressionType>(type))
{
- if (isInstance && as<ArithmeticExpressionType>(type))
+ Type* elementType = nullptr;
+ elementType = matrixType->getElementType();
+ String typeStr;
+ if (elementType)
{
- // Hard code members for vector and matrix types.
- result.clear();
- version->currentCompletionItems.clear();
- int elementCount = 0;
- Type* elementType = nullptr;
- const char* memberNames[4] = {"x", "y", "z", "w"};
- if (auto vectorType = as<VectorExpressionType>(type))
- {
- if (auto elementCountVal = as<ConstantIntVal>(vectorType->elementCount))
- {
- elementCount = (int)elementCountVal->value;
- elementType = vectorType->elementType;
- }
- }
- else if (auto matrixType = as<MatrixExpressionType>(type))
- {
- if (auto elementCountVal = as<ConstantIntVal>(matrixType->getRowCount()))
- {
- elementCount = (int)elementCountVal->value;
- elementType = matrixType->getRowType();
- }
- }
- String typeStr;
- if (elementType)
- typeStr = elementType->toString();
- elementCount = Math::Min(elementCount, 4);
- for (int i = 0; i < elementCount; i++)
+ typeStr = elementType->toString();
+ }
+ int rowCount = Math::Min((int)elementCount[0], 4);
+ int colCount = Math::Min((int)elementCount[1], 4);
+ StringBuilder nameSB;
+ for (int i = 0; i < rowCount; i++)
+ {
+ for (int j = 0; j < colCount; j++)
{
LanguageServerProtocol::CompletionItem item;
item.data = 0;
item.detail = typeStr;
item.kind = LanguageServerProtocol::kCompletionItemKindVariable;
- item.label = memberNames[i];
+ nameSB.Clear();
+ nameSB << "_m" << i << j;
+ item.label = nameSB.ToString();
+ result.add(item);
+ nameSB.Clear();
+ nameSB << "_" << i + 1 << j + 1;
+ item.label = nameSB.ToString();
result.add(item);
}
}
- else
+ }
+ for (auto& item : result)
+ {
+ for (auto ch : getCommitChars())
+ item.commitCharacters.add(ch);
+ }
+ return result;
+}
+
+List<LanguageServerProtocol::CompletionItem> CompletionContext::collectAttributes()
+{
+ List<LanguageServerProtocol::CompletionItem> result;
+ for (auto& item : version->linkage->contentAssistInfo.completionSuggestions.candidateItems)
+ {
+ if (auto attrDecl = as<AttributeDecl>(item.declRef.getDecl()))
{
- DiagnosticSink sink;
- MemberCollectingContext context(linkage, parsedModule, &sink);
- context.astBuilder = linkage->getASTBuilder();
- context.includeInstanceMembers = isInstance;
- collectMembersInType(&context, type);
- HashSet<String> deduplicateSet;
- for (auto member : context.members)
+ if (attrDecl->getName())
{
- LanguageServerProtocol::CompletionItem item;
- item.label = member->getName()->text;
- item.kind = 0;
- if (as<TypeConstraintDecl>(member))
- {
- continue;
- }
- if (as<ConstructorDecl>(member))
- {
- continue;
- }
- if (as<SubscriptDecl>(member))
- {
- continue;
- }
-
- if (item.label.startsWith("$"))
- continue;
- if (!deduplicateSet.Add(item.label))
- continue;
-
- if (as<StructDecl>(member))
- {
- item.kind = LanguageServerProtocol::kCompletionItemKindStruct;
- }
- else if (as<ClassDecl>(member))
- {
- item.kind = LanguageServerProtocol::kCompletionItemKindClass;
- }
- else if (as<InterfaceDecl>(member))
- {
- item.kind = LanguageServerProtocol::kCompletionItemKindInterface;
- }
- else if (as<SimpleTypeDecl>(member))
- {
- item.kind = LanguageServerProtocol::kCompletionItemKindClass;
- }
- else if (as<PropertyDecl>(member))
- {
- item.kind = LanguageServerProtocol::kCompletionItemKindProperty;
- }
- else if (as<EnumDecl>(member))
- {
- item.kind = LanguageServerProtocol::kCompletionItemKindEnum;
- }
- else if (as<VarDeclBase>(member))
- {
- item.kind = LanguageServerProtocol::kCompletionItemKindVariable;
- }
- else if (as<EnumCaseDecl>(member))
- {
- item.kind = LanguageServerProtocol::kCompletionItemKindEnumMember;
- }
- else if (as<CallableDecl>(member))
- {
- item.kind = LanguageServerProtocol::kCompletionItemKindMethod;
- }
- else if (as<AssocTypeDecl>(member))
- {
- item.kind = LanguageServerProtocol::kCompletionItemKindClass;
- }
- item.data = String(version->currentCompletionItems.getCount());
- result.add(item);
- version->currentCompletionItems.add(member);
+ LanguageServerProtocol::CompletionItem resultItem;
+ resultItem.kind = LanguageServerProtocol::kCompletionItemKindKeyword;
+ resultItem.label = attrDecl->getName()->text;
+ result.add(resultItem);
}
}
-
- for (auto& item : result)
+ else if (auto decl = as<AggTypeDecl>(item.declRef.getDecl()))
{
- for (auto ch : getCommitChars())
- item.commitCharacters.add(ch);
+ if (decl->getName())
+ {
+ LanguageServerProtocol::CompletionItem resultItem;
+ resultItem.kind = LanguageServerProtocol::kCompletionItemKindStruct;
+ resultItem.label = decl->getName()->text;
+ if (resultItem.label.endsWith("Attribute"))
+ resultItem.label.reduceLength(resultItem.label.getLength() - 9);
+ result.add(resultItem);
+ }
}
}
return result;