From 18866d99bc9ea1547ab7ab49a7c3da652ed1ac5e Mon Sep 17 00:00:00 2001 From: Yong He Date: Tue, 1 Jul 2025 00:58:10 -0700 Subject: [PATCH 1/6] Misc language server improvements. --- source/compiler-core/slang-json-value.cpp | 3 + source/slang/slang-ast-decl-ref.cpp | 9 ++- source/slang/slang-ast-print.cpp | 18 ++++- source/slang/slang-check-decl.cpp | 2 +- source/slang/slang-check-expr.cpp | 2 +- source/slang/slang-content-assist-info.h | 3 + .../slang/slang-language-server-auto-format.h | 1 + .../slang-language-server-completion.cpp | 32 +++++++++ source/slang/slang-language-server.cpp | 71 ++++++++++++++++--- source/slang/slang-language-server.h | 1 + .../override-completion-2.slang | 17 +++++ 11 files changed, 142 insertions(+), 17 deletions(-) create mode 100644 tests/language-server/override-completion-2.slang diff --git a/source/compiler-core/slang-json-value.cpp b/source/compiler-core/slang-json-value.cpp index 56d003737fd..e2e95a968fd 100644 --- a/source/compiler-core/slang-json-value.cpp +++ b/source/compiler-core/slang-json-value.cpp @@ -655,6 +655,9 @@ bool JSONContainer::asBool(const JSONValue& value) return asInteger(value) != 0; case JSONValue::Type::FloatLexeme: return asFloat(value) != 0.0; + case JSONValue::Type::StringLexeme: + return getTransientString(value).caseInsensitiveEquals(toSlice("true")) || + getTransientString(value).caseInsensitiveEquals(toSlice("1")); default: return value.asBool(); } diff --git a/source/slang/slang-ast-decl-ref.cpp b/source/slang/slang-ast-decl-ref.cpp index 76d0324c6b8..7eec32a3a18 100644 --- a/source/slang/slang-ast-decl-ref.cpp +++ b/source/slang/slang-ast-decl-ref.cpp @@ -131,9 +131,12 @@ DeclRefBase* LookupDeclRef::_substituteImplOverride( void LookupDeclRef::_toTextOverride(StringBuilder& out) { - getLookupSource()->toText(out); - if (out.getLength() && !out.endsWith(".")) - out << "."; + if (!as(getLookupSource())) + { + getLookupSource()->toText(out); + if (out.getLength() && !out.endsWith(".")) + out << "."; + } if (getDecl()->getName() && getDecl()->getName()->text.getLength() != 0) { out << getDecl()->getName()->text; diff --git a/source/slang/slang-ast-print.cpp b/source/slang/slang-ast-print.cpp index 94864b8159d..bbbcfcad5e1 100644 --- a/source/slang/slang-ast-print.cpp +++ b/source/slang/slang-ast-print.cpp @@ -1168,8 +1168,22 @@ void ASTPrinter::_addDeclPathRec(const DeclRef& declRef, Index depth) { auto& sb = m_builder; - // Find the parent declaration - auto parentDeclRef = declRef.getParent(); + // Find the parent declaration. + DeclRef parentDeclRef; + + // If this is a lookup decl ref, prefix with the lookup source type instead of the parent. + if (auto lookupDeclRef = as(declRef.declRefBase)) + { + parentDeclRef = DeclRef(); + if (!as(lookupDeclRef->getLookupSource())) + { + parentDeclRef = isDeclRefTypeOf(lookupDeclRef->getLookupSource()); + } + } + else + { + parentDeclRef = declRef.getParent(); + } // If the immediate parent is a generic, then we probably // want the declaration above that... diff --git a/source/slang/slang-check-decl.cpp b/source/slang/slang-check-decl.cpp index 0dd859bb223..c0595843c04 100644 --- a/source/slang/slang-check-decl.cpp +++ b/source/slang/slang-check-decl.cpp @@ -7898,7 +7898,7 @@ void SemanticsVisitor::calcOverridableCompletionCandidates( contentAssistInfo.completionSuggestions.formatMode = varDeclBase ? CompletionSuggestions::FormatMode::FuncSignatureWithoutReturnType : CompletionSuggestions::FormatMode::FullSignature; - + contentAssistInfo.completionSuggestions.currentPartialDecl = memberDecl; List candidateItems; for (auto facet : inheritanceInfo.facets) { diff --git a/source/slang/slang-check-expr.cpp b/source/slang/slang-check-expr.cpp index b90081af878..c3238bd9b0b 100644 --- a/source/slang/slang-check-expr.cpp +++ b/source/slang/slang-check-expr.cpp @@ -410,7 +410,7 @@ DeclRefExpr* SemanticsVisitor::ConstructDeclRefExpr( SharedTypeExpr* baseTypeExpr = m_astBuilder->create(); baseTypeExpr->base.type = baseExprType; baseTypeExpr->type.type = m_astBuilder->getTypeType(baseExprType); - + baseTypeExpr->base.exp = baseExpr; auto expr = m_astBuilder->create(); expr->loc = loc; expr->type = type; diff --git a/source/slang/slang-content-assist-info.h b/source/slang/slang-content-assist-info.h index 77102bf66e6..c913e154669 100644 --- a/source/slang/slang-content-assist-info.h +++ b/source/slang/slang-content-assist-info.h @@ -32,6 +32,7 @@ struct CompletionSuggestions ScopeKind scopeKind = ScopeKind::Invalid; FormatMode formatMode = FormatMode::Name; + Decl* currentPartialDecl = nullptr; List candidateItems; Type* swizzleBaseType = nullptr; @@ -40,10 +41,12 @@ struct CompletionSuggestions void clear() { scopeKind = ScopeKind::Invalid; + formatMode = FormatMode::Name; candidateItems.clear(); elementCount[0] = 0; elementCount[1] = 0; swizzleBaseType = nullptr; + currentPartialDecl = nullptr; } }; diff --git a/source/slang/slang-language-server-auto-format.h b/source/slang/slang-language-server-auto-format.h index 24e89b05100..d37359730ec 100644 --- a/source/slang/slang-language-server-auto-format.h +++ b/source/slang/slang-language-server-auto-format.h @@ -27,6 +27,7 @@ enum class FormatBehavior struct FormatOptions { + bool enableFormatOnType = true; String clangFormatLocation; String style = "file"; String fallbackStyle = "{BasedOnStyle: Microsoft}"; diff --git a/source/slang/slang-language-server-completion.cpp b/source/slang/slang-language-server-completion.cpp index 9a88fbaa794..b5864d97229 100644 --- a/source/slang/slang-language-server-completion.cpp +++ b/source/slang/slang-language-server-completion.cpp @@ -617,6 +617,16 @@ CompletionResult CompletionContext::collectMembersAndSymbols() default: return result; } + + // If we are completing an override function signature, don't add keywords to the result. + switch (linkage->contentAssistInfo.completionSuggestions.formatMode) + { + case CompletionSuggestions::FormatMode::FullSignature: + case CompletionSuggestions::FormatMode::FuncSignatureWithoutReturnType: + addKeywords = false; + break; + } + HashSet deduplicateSet; for (Index i = 0; i < linkage->contentAssistInfo.completionSuggestions.candidateItems.getCount(); @@ -639,6 +649,28 @@ CompletionResult CompletionContext::collectMembersAndSymbols() nameStart); if (item.label.getLength() == 0) continue; + if (linkage->contentAssistInfo.completionSuggestions.formatMode == + CompletionSuggestions::FormatMode::FullSignature) + { + // If the completion item is a `static` function, but there is no `static` keyword + // on the current incomplete decl, then we will add `static` keyword to the completion + // result. + if (suggestedItem.declRef.getDecl() && + suggestedItem.declRef.getDecl()->findModifier() && + linkage->contentAssistInfo.completionSuggestions.currentPartialDecl && + !linkage->contentAssistInfo.completionSuggestions.currentPartialDecl + ->findModifier()) + { + item.label = "static " + item.label; + nameStart += 7; + // Add an item for 'static' keyword. + LanguageServerProtocol::CompletionItem staticItem; + staticItem.label = "static"; + staticItem.kind = LanguageServerProtocol::kCompletionItemKindKeyword; + staticItem.data = "-1"; // Use -1 to indicate this is a keyword. + result.add(staticItem); + } + } item.kind = LanguageServerProtocol::kCompletionItemKindKeyword; if (as(member)) { diff --git a/source/slang/slang-language-server.cpp b/source/slang/slang-language-server.cpp index 6daeb75ad5d..3cc093ad7a1 100644 --- a/source/slang/slang-language-server.cpp +++ b/source/slang/slang-language-server.cpp @@ -210,15 +210,15 @@ SlangResult LanguageServer::parseNextMessage() if (response.result.getKind() == JSONValue::Kind::Array) { auto arr = m_connection->getContainer()->getArray(response.result); - if (arr.getCount() == 12) + if (arr.getCount() == 13) { updatePredefinedMacros(arr[0]); updateSearchPaths(arr[1]); updateSearchInWorkspace(arr[2]); updateCommitCharacters(arr[3]); - updateFormattingOptions(arr[4], arr[5], arr[6], arr[7], arr[8]); - updateInlayHintOptions(arr[9], arr[10]); - updateTraceOptions(arr[11]); + updateFormattingOptions(arr[4], arr[5], arr[6], arr[7], arr[8], arr[9]); + updateInlayHintOptions(arr[10], arr[11]); + updateTraceOptions(arr[12]); } } break; @@ -1876,6 +1876,9 @@ SlangResult LanguageServer::rangeFormatting( LanguageServerResult> LanguageServerCore::rangeFormatting( const LanguageServerProtocol::DocumentRangeFormattingParams& args) { + if (!m_formatOptions.enableFormatOnType) + return std::nullopt; + String canonicalPath = uriToCanonicalPath(args.textDocument.uri); RefPtr doc; if (!m_workspace->openedDocuments.tryGetValue(canonicalPath, doc)) @@ -1924,6 +1927,9 @@ SlangResult LanguageServer::onTypeFormatting( LanguageServerResult> LanguageServerCore::onTypeFormatting( const LanguageServerProtocol::DocumentOnTypeFormattingParams& args) { + if (!m_formatOptions.enableFormatOnType) + return std::nullopt; + String canonicalPath = uriToCanonicalPath(args.textDocument.uri); RefPtr doc; if (!m_workspace->openedDocuments.tryGetValue(canonicalPath, doc)) @@ -2084,6 +2090,7 @@ void LanguageServer::updateCommitCharacters(const JSONValue& jsonValue) } void LanguageServer::updateFormattingOptions( + const JSONValue& enableFormatOnType, const JSONValue& clangFormatLoc, const JSONValue& clangFormatStyle, const JSONValue& clangFormatFallbackStyle, @@ -2092,6 +2099,8 @@ void LanguageServer::updateFormattingOptions( { auto container = m_connection->getContainer(); JSONToNativeConverter converter(container, &m_typeMap, m_connection->getSink()); + if (enableFormatOnType.isValid()) + converter.convert(enableFormatOnType, &m_core.m_formatOptions.enableFormatOnType); if (clangFormatLoc.isValid()) converter.convert(clangFormatLoc, &m_core.m_formatOptions.clangFormatLocation); if (clangFormatStyle.isValid()) @@ -2162,6 +2171,8 @@ void LanguageServer::sendConfigRequest() args.items.add(item); item.section = "slang.enableCommitCharactersInAutoCompletion"; args.items.add(item); + item.section = "slang.format.enableFormatOnType"; + args.items.add(item); item.section = "slang.format.clangFormatLocation"; args.items.add(item); item.section = "slang.format.clangFormatStyle"; @@ -2611,7 +2622,7 @@ SlangResult LanguageServerCore::didChangeTextDocument(const DidChangeTextDocumen SlangResult LanguageServer::didChangeConfiguration( const LanguageServerProtocol::DidChangeConfigurationParams& args) { - if (args.settings.isValid()) + if (args.settings.isValid() && args.settings.type != JSONValue::Type::Null) { updateConfigFromJSON(args.settings); } @@ -2657,25 +2668,65 @@ void LanguageServer::updateConfigFromJSON(const JSONValue& jsonVal) { updateCommitCharacters(kv.value); } + else if (key == "slang.format.enableFormatOnType") + { + updateFormattingOptions( + kv.value, + JSONValue(), + JSONValue(), + JSONValue(), + JSONValue(), + JSONValue()); + } else if (key == "slang.format.clangFormatLocation") { - updateFormattingOptions(kv.value, JSONValue(), JSONValue(), JSONValue(), JSONValue()); + updateFormattingOptions( + JSONValue(), + kv.value, + JSONValue(), + JSONValue(), + JSONValue(), + JSONValue()); } else if (key == "slang.format.clangFormatStyle") { - updateFormattingOptions(JSONValue(), kv.value, JSONValue(), JSONValue(), JSONValue()); + updateFormattingOptions( + JSONValue(), + JSONValue(), + kv.value, + JSONValue(), + JSONValue(), + JSONValue()); } else if (key == "slang.format.clangFormatFallbackStyle") { - updateFormattingOptions(JSONValue(), JSONValue(), kv.value, JSONValue(), JSONValue()); + updateFormattingOptions( + JSONValue(), + JSONValue(), + JSONValue(), + kv.value, + JSONValue(), + JSONValue()); } else if (key == "slang.format.allowLineBreakChangesInOnTypeFormatting") { - updateFormattingOptions(JSONValue(), JSONValue(), JSONValue(), kv.value, JSONValue()); + updateFormattingOptions( + JSONValue(), + JSONValue(), + JSONValue(), + JSONValue(), + kv.value, + JSONValue()); } else if (key == "slang.format.allowLineBreakChangesInRangeFormatting") { - updateFormattingOptions(JSONValue(), JSONValue(), JSONValue(), JSONValue(), kv.value); + updateFormattingOptions( + JSONValue(), + JSONValue(), + JSONValue(), + JSONValue(), + JSONValue(), + kv.value); } else if (key == "slang.inlayHints.deducedTypes") { diff --git a/source/slang/slang-language-server.h b/source/slang/slang-language-server.h index e31e77acbfe..43c3521eb57 100644 --- a/source/slang/slang-language-server.h +++ b/source/slang/slang-language-server.h @@ -250,6 +250,7 @@ class LanguageServer void updateSearchInWorkspace(const JSONValue& value); void updateCommitCharacters(const JSONValue& value); void updateFormattingOptions( + const JSONValue& enableFormatOnType, const JSONValue& clangFormatLoc, const JSONValue& clangFormatStyle, const JSONValue& clangFormatFallbackStyle, diff --git a/tests/language-server/override-completion-2.slang b/tests/language-server/override-completion-2.slang new file mode 100644 index 00000000000..5bf89577132 --- /dev/null +++ b/tests/language-server/override-completion-2.slang @@ -0,0 +1,17 @@ +//TEST:LANG_SERVER(filecheck=CHECK): +interface IBar{ associatedtype AT; } +interface IFoo +{ + associatedtype B : IBar; +//HOVER:7,12 + static B.AT execute(int x); +} + +struct Impl : IFoo +{ +//COMPLETE:13,14 + override +} + +//CHECK: associatedtype B +//CHECK: static B.AT execute(int x) \ No newline at end of file From 8234d0a38bcdf9bb6124d767ee03f37b143172fd Mon Sep 17 00:00:00 2001 From: Yong He Date: Tue, 1 Jul 2025 08:28:59 -0700 Subject: [PATCH 2/6] Fix. --- source/slang/slang-ast-print.cpp | 14 +++++++++----- tests/language-server/smoke.slang.expected.txt | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/source/slang/slang-ast-print.cpp b/source/slang/slang-ast-print.cpp index bbbcfcad5e1..cd75ced5d44 100644 --- a/source/slang/slang-ast-print.cpp +++ b/source/slang/slang-ast-print.cpp @@ -1174,10 +1174,14 @@ void ASTPrinter::_addDeclPathRec(const DeclRef& declRef, Index depth) // If this is a lookup decl ref, prefix with the lookup source type instead of the parent. if (auto lookupDeclRef = as(declRef.declRefBase)) { - parentDeclRef = DeclRef(); - if (!as(lookupDeclRef->getLookupSource())) + parentDeclRef = isDeclRefTypeOf(lookupDeclRef->getLookupSource()); + if (auto thisType = as(parentDeclRef.getDecl())) { - parentDeclRef = isDeclRefTypeOf(lookupDeclRef->getLookupSource()); + if (auto baseLookupDeclRef = as(parentDeclRef.declRefBase)) + { + // If the base type is a lookup, we want to use its source type + parentDeclRef = isDeclRefTypeOf(baseLookupDeclRef->getLookupSource()); + } } } else @@ -1194,9 +1198,9 @@ void ASTPrinter::_addDeclPathRec(const DeclRef& declRef, Index depth) } // Depending on what the parent is, we may want to format things specially - if (auto aggTypeDeclRef = parentDeclRef.as()) + if (parentDeclRef.as() || parentDeclRef.as()) { - _addDeclPathRec(aggTypeDeclRef, depth + 1); + _addDeclPathRec(parentDeclRef, depth + 1); sb << toSlice("."); } else if (auto namespaceDeclRef = parentDeclRef.as()) diff --git a/tests/language-server/smoke.slang.expected.txt b/tests/language-server/smoke.slang.expected.txt index 212bcdabbe2..7e0e8def758 100644 --- a/tests/language-server/smoke.slang.expected.txt +++ b/tests/language-server/smoke.slang.expected.txt @@ -16,7 +16,7 @@ content: -------- activeParameter: 0 activeSignature: 0 -func IFoo.getSum() -> int: +func T.getSum() -> int: Returns the sum of the contents. From dfa177afcb75ff8ec09db1abdff5b0c42693cc7c Mon Sep 17 00:00:00 2001 From: Yong He Date: Tue, 1 Jul 2025 08:40:07 -0700 Subject: [PATCH 3/6] Fix decl path printing for existential lookup. --- source/slang/slang-ast-print.cpp | 18 +++++++++++++----- .../existential-decl-path.slang | 13 +++++++++++++ 2 files changed, 26 insertions(+), 5 deletions(-) create mode 100644 tests/language-server/existential-decl-path.slang diff --git a/source/slang/slang-ast-print.cpp b/source/slang/slang-ast-print.cpp index cd75ced5d44..e369c4e82cb 100644 --- a/source/slang/slang-ast-print.cpp +++ b/source/slang/slang-ast-print.cpp @@ -1174,13 +1174,21 @@ void ASTPrinter::_addDeclPathRec(const DeclRef& declRef, Index depth) // If this is a lookup decl ref, prefix with the lookup source type instead of the parent. if (auto lookupDeclRef = as(declRef.declRefBase)) { - parentDeclRef = isDeclRefTypeOf(lookupDeclRef->getLookupSource()); - if (auto thisType = as(parentDeclRef.getDecl())) + if (auto extractExistentialType = + as(lookupDeclRef->getLookupSource())) { - if (auto baseLookupDeclRef = as(parentDeclRef.declRefBase)) + parentDeclRef = extractExistentialType->getOriginalInterfaceDeclRef(); + } + else + { + parentDeclRef = isDeclRefTypeOf(lookupDeclRef->getLookupSource()); + if (auto thisType = as(parentDeclRef.getDecl())) { - // If the base type is a lookup, we want to use its source type - parentDeclRef = isDeclRefTypeOf(baseLookupDeclRef->getLookupSource()); + if (auto baseLookupDeclRef = as(parentDeclRef.declRefBase)) + { + // If the base type is a lookup, we want to use its source type + parentDeclRef = isDeclRefTypeOf(baseLookupDeclRef->getLookupSource()); + } } } } diff --git a/tests/language-server/existential-decl-path.slang b/tests/language-server/existential-decl-path.slang new file mode 100644 index 00000000000..9ffda72d692 --- /dev/null +++ b/tests/language-server/existential-decl-path.slang @@ -0,0 +1,13 @@ +//TEST:LANG_SERVER(filecheck=CHECK): +interface IFoo +{ + int getSum(); +} + +void test(IFoo f) +{ +//HOVER:10,9 + f.getSum(); +} + +//CHECK: IFoo.getSum From a4c4cb08d1a4fd5fec2553c6d05479a8acbbce9e Mon Sep 17 00:00:00 2001 From: Yong He Date: Tue, 1 Jul 2025 08:57:22 -0700 Subject: [PATCH 4/6] More existential decl path fix. --- source/slang/slang-ast-print.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/source/slang/slang-ast-print.cpp b/source/slang/slang-ast-print.cpp index e369c4e82cb..8199869bde2 100644 --- a/source/slang/slang-ast-print.cpp +++ b/source/slang/slang-ast-print.cpp @@ -1182,13 +1182,13 @@ void ASTPrinter::_addDeclPathRec(const DeclRef& declRef, Index depth) else { parentDeclRef = isDeclRefTypeOf(lookupDeclRef->getLookupSource()); - if (auto thisType = as(parentDeclRef.getDecl())) + } + if (auto thisType = as(parentDeclRef.getDecl())) + { + if (auto baseLookupDeclRef = as(parentDeclRef.declRefBase)) { - if (auto baseLookupDeclRef = as(parentDeclRef.declRefBase)) - { - // If the base type is a lookup, we want to use its source type - parentDeclRef = isDeclRefTypeOf(baseLookupDeclRef->getLookupSource()); - } + // If the base type is a lookup, we want to use its source type + parentDeclRef = isDeclRefTypeOf(baseLookupDeclRef->getLookupSource()); } } } From 658fa0fb37687d96e3a5440c31502498b0738986 Mon Sep 17 00:00:00 2001 From: Yong He Date: Tue, 1 Jul 2025 09:31:42 -0700 Subject: [PATCH 5/6] Polish. --- source/slang/slang-ast-print.cpp | 8 ++++++-- tests/language-server/override-completion-2.slang | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/source/slang/slang-ast-print.cpp b/source/slang/slang-ast-print.cpp index 8199869bde2..4b5a69f15d8 100644 --- a/source/slang/slang-ast-print.cpp +++ b/source/slang/slang-ast-print.cpp @@ -1183,7 +1183,7 @@ void ASTPrinter::_addDeclPathRec(const DeclRef& declRef, Index depth) { parentDeclRef = isDeclRefTypeOf(lookupDeclRef->getLookupSource()); } - if (auto thisType = as(parentDeclRef.getDecl())) + if (as(parentDeclRef.getDecl())) { if (auto baseLookupDeclRef = as(parentDeclRef.declRefBase)) { @@ -1206,7 +1206,11 @@ void ASTPrinter::_addDeclPathRec(const DeclRef& declRef, Index depth) } // Depending on what the parent is, we may want to format things specially - if (parentDeclRef.as() || parentDeclRef.as()) + if (parentDeclRef.as()) + { + sb << "This."; + } + else if (parentDeclRef.as() || parentDeclRef.as()) { _addDeclPathRec(parentDeclRef, depth + 1); sb << toSlice("."); diff --git a/tests/language-server/override-completion-2.slang b/tests/language-server/override-completion-2.slang index 5bf89577132..c2eb56781da 100644 --- a/tests/language-server/override-completion-2.slang +++ b/tests/language-server/override-completion-2.slang @@ -13,5 +13,5 @@ struct Impl : IFoo override } -//CHECK: associatedtype B +//CHECK: associatedtype IFoo.This.B //CHECK: static B.AT execute(int x) \ No newline at end of file From d543affe6d5c094a0c3b571a61a52b8ae50088f3 Mon Sep 17 00:00:00 2001 From: Yong He Date: Tue, 1 Jul 2025 09:32:03 -0700 Subject: [PATCH 6/6] Fix test. --- tests/language-server/override-completion-2.slang | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/language-server/override-completion-2.slang b/tests/language-server/override-completion-2.slang index c2eb56781da..510af702075 100644 --- a/tests/language-server/override-completion-2.slang +++ b/tests/language-server/override-completion-2.slang @@ -13,5 +13,5 @@ struct Impl : IFoo override } -//CHECK: associatedtype IFoo.This.B +//CHECK: associatedtype This.B //CHECK: static B.AT execute(int x) \ No newline at end of file