summaryrefslogtreecommitdiffstats
path: root/source/slang/slang-language-server.cpp
diff options
context:
space:
mode:
authorYong He <yonghe@outlook.com>2022-06-27 15:36:00 -0700
committerGitHub <noreply@github.com>2022-06-27 15:36:00 -0700
commitb7638b8fffe78ade657f361cadc08dffc8c10acf (patch)
treee27a141cfc6a9cc77356b8cba27b41c495d4ee27 /source/slang/slang-language-server.cpp
parent62d16a23b0ecd72dc624abd7e10b373c40adaa90 (diff)
Language server fixes and improvements (#2304)
* Language server: Inlay hints. * Signature help for base exprs that is not a declref. * Fix checking of jvp operator. * Fix. * Add clang-format based auto formatting. * Fix clang error. * Fix clang-format discovery logic. * Fine tune auto formatting and completion experience. * Update macos workflow. * Fixes to configurations. * Fix parser recovery to trigger completion for index exprs. * Typo fix. Co-authored-by: Yong He <yhe@nvidia.com>
Diffstat (limited to 'source/slang/slang-language-server.cpp')
-rw-r--r--source/slang/slang-language-server.cpp292
1 files changed, 280 insertions, 12 deletions
diff --git a/source/slang/slang-language-server.cpp b/source/slang/slang-language-server.cpp
index 619aaaacd..0818aea07 100644
--- a/source/slang/slang-language-server.cpp
+++ b/source/slang/slang-language-server.cpp
@@ -10,6 +10,7 @@
#include <time.h>
#include "../core/slang-secure-crt.h"
#include "../core/slang-range.h"
+#include "../core/slang-char-util.h"
#include "../../slang-com-helper.h"
#include "../compiler-core/slang-json-native.h"
#include "../compiler-core/slang-json-rpc-connection.h"
@@ -20,6 +21,7 @@
#include "slang-language-server-completion.h"
#include "slang-language-server-semantic-tokens.h"
#include "slang-language-server-document-symbols.h"
+#include "slang-language-server-inlay-hints.h"
#include "slang-ast-print.h"
#include "slang-doc-markdown-writer.h"
#include "slang-mangle.h"
@@ -103,7 +105,7 @@ SlangResult LanguageServer::parseNextMessage()
InitializeResult result;
result.serverInfo.name = "SlangLanguageServer";
- result.serverInfo.version = "1.0";
+ result.serverInfo.version = "1.3";
result.capabilities.positionEncoding = "utf-16";
result.capabilities.textDocumentSync.openClose = true;
result.capabilities.textDocumentSync.change = (int)TextDocumentSyncKind::Incremental;
@@ -112,13 +114,17 @@ SlangResult LanguageServer::parseNextMessage()
result.capabilities.hoverProvider = true;
result.capabilities.definitionProvider = true;
result.capabilities.documentSymbolProvider = true;
+ result.capabilities.inlayHintProvider.resolveProvider = false;
+ result.capabilities.documentFormattingProvider = true;
+ result.capabilities.documentOnTypeFormattingProvider.firstTriggerCharacter = "}";
+ result.capabilities.documentOnTypeFormattingProvider.moreTriggerCharacter.add(";");
+ result.capabilities.documentOnTypeFormattingProvider.moreTriggerCharacter.add(":");
+ result.capabilities.documentRangeFormattingProvider = true;
result.capabilities.completionProvider.triggerCharacters.add(".");
result.capabilities.completionProvider.triggerCharacters.add(":");
result.capabilities.completionProvider.triggerCharacters.add("[");
result.capabilities.completionProvider.resolveProvider = true;
result.capabilities.completionProvider.workDoneToken = "";
- for (auto ch : getCommitChars())
- result.capabilities.completionProvider.allCommitCharacters.add(ch);
result.capabilities.semanticTokensProvider.full = true;
result.capabilities.semanticTokensProvider.range = false;
result.capabilities.signatureHelpProvider.triggerCharacters.add("(");
@@ -130,8 +136,8 @@ SlangResult LanguageServer::parseNextMessage()
}
else if (call.method == "initialized")
{
- sendConfigRequest();
registerCapability("workspace/didChangeConfiguration");
+ sendConfigRequest();
m_initialized = true;
return SLANG_OK;
}
@@ -153,12 +159,14 @@ SlangResult LanguageServer::parseNextMessage()
if (response.result.getKind() == JSONValue::Kind::Array)
{
auto arr = m_connection->getContainer()->getArray(response.result);
- if (arr.getCount() == 4)
+ if (arr.getCount() == 8)
{
updatePredefinedMacros(arr[0]);
updateSearchPaths(arr[1]);
updateSearchInWorkspace(arr[2]);
updateCommitCharacters(arr[3]);
+ updateFormattingOptions(arr[4], arr[5]);
+ updateInlayHintOptions(arr[6], arr[7]);
}
}
break;
@@ -515,6 +523,21 @@ SlangResult LanguageServer::completion(
m_connection->sendResult(NullResponse::get(), responseId);
return SLANG_OK;
}
+
+ // Don't show completion at case label.
+ if (args.context.triggerKind ==
+ LanguageServerProtocol::kCompletionTriggerKindTriggerCharacter &&
+ args.context.triggerCharacter == ":")
+ {
+ auto line = doc->getLine((Int)args.position.line + 1);
+ auto prevCharPos = args.position.character - 2;
+ if (prevCharPos >= 0 && prevCharPos < line.getLength() && line[prevCharPos] != ':')
+ {
+ m_connection->sendResult(NullResponse::get(), responseId);
+ return SLANG_OK;
+ }
+ }
+
Index utf8Line, utf8Col;
doc->zeroBasedUTF16LocToOneBasedUTF8Loc(
args.position.line, args.position.character, utf8Line, utf8Col);
@@ -532,6 +555,13 @@ SlangResult LanguageServer::completion(
cursorOffset--;
}
+ // Never show suggestions when the user is typing a number.
+ if (cursorOffset + 1 >= 0 && cursorOffset + 1 < doc->getText().getLength() && CharUtil::isDigit(doc->getText()[cursorOffset + 1]))
+ {
+ m_connection->sendResult(NullResponse::get(), responseId);
+ return SLANG_OK;
+ }
+
// Insert a completion request token at cursor position.
auto originalText = doc->getText();
StringBuilder newText;
@@ -553,7 +583,7 @@ SlangResult LanguageServer::completion(
return SLANG_OK;
}
- CompletionContext context;
+ Slang::CompletionContext context;
context.server = this;
context.cursorOffset = cursorOffset;
context.version = version;
@@ -568,6 +598,16 @@ SlangResult LanguageServer::completion(
{
return SLANG_OK;
}
+
+ // Don't general completion suggestions after typing '['.
+ if (args.context.triggerKind ==
+ LanguageServerProtocol::kCompletionTriggerKindTriggerCharacter &&
+ args.context.triggerCharacter == "[")
+ {
+ m_connection->sendResult(NullResponse::get(), responseId);
+ return SLANG_OK;
+ }
+
if (SLANG_SUCCEEDED(context.tryCompleteHLSLSemantic()))
{
return SLANG_OK;
@@ -760,6 +800,43 @@ SlangResult LanguageServer::signatureHelp(
}
response.signatures.add(sigInfo);
};
+
+ auto addFuncType = [&](FuncType* funcType)
+ {
+ SignatureInformation sigInfo;
+
+ List<Slang::Range<Index>> paramRanges;
+ ASTPrinter printer(
+ version->linkage->getASTBuilder(),
+ ASTPrinter::OptionFlag::ParamNames | ASTPrinter::OptionFlag::NoInternalKeywords |
+ ASTPrinter::OptionFlag::SimplifiedBuiltinType);
+
+ printer.getStringBuilder() << "func (";
+ bool isFirst = true;
+ for (auto param : funcType->paramTypes)
+ {
+ if (!isFirst)
+ printer.getStringBuilder() << ", ";
+ Slang::Range<Index> range;
+ range.begin = printer.getStringBuilder().getLength();
+ printer.addType(param);
+ range.end = printer.getStringBuilder().getLength();
+ paramRanges.add(range);
+ isFirst = false;
+ }
+ printer.getStringBuilder() << ") -> ";
+ printer.addType(funcType->getResultType());
+ sigInfo.label = printer.getString();
+ for (auto& range : paramRanges)
+ {
+ ParameterInformation paramInfo;
+ paramInfo.label[0] = (uint32_t)range.begin;
+ paramInfo.label[1] = (uint32_t)range.end;
+ sigInfo.parameters.add(paramInfo);
+ }
+ response.signatures.add(sigInfo);
+ };
+
if (auto declRefExpr = as<DeclRefExpr>(funcExpr))
{
if (auto aggDecl = as<AggTypeDecl>(declRefExpr->declRef.getDecl()))
@@ -782,6 +859,10 @@ SlangResult LanguageServer::signatureHelp(
addDeclRef(item.declRef);
}
}
+ else if (auto funcType = as<FuncType>(funcExpr->type.type))
+ {
+ addFuncType(funcType);
+ }
response.activeSignature = 0;
response.activeParameter = 0;
for (int i = 1; i < appExpr->argumentDelimeterLocs.getCount(); i++)
@@ -822,6 +903,114 @@ SlangResult LanguageServer::documentSymbol(
return SLANG_OK;
}
+SlangResult LanguageServer::inlayHint(const LanguageServerProtocol::InlayHintParams& args, const JSONValue& responseId)
+{
+ String canonicalPath = uriToCanonicalPath(args.textDocument.uri);
+ RefPtr<DocumentVersion> doc;
+ if (!m_workspace->openedDocuments.TryGetValue(canonicalPath, doc))
+ {
+ m_connection->sendResult(NullResponse::get(), responseId);
+ return SLANG_OK;
+ }
+ auto version = m_workspace->getCurrentVersion();
+ Module* parsedModule = version->getOrLoadModule(canonicalPath);
+ if (!parsedModule)
+ {
+ m_connection->sendResult(NullResponse::get(), responseId);
+ return SLANG_OK;
+ }
+ List<InlayHint> hints = getInlayHints(
+ version->linkage,
+ parsedModule,
+ canonicalPath.getUnownedSlice(),
+ doc.Ptr(),
+ args.range,
+ m_inlayHintOptions);
+ m_connection->sendResult(&hints, responseId);
+ return SLANG_OK;
+}
+
+List<LanguageServerProtocol::TextEdit> translateTextEdits(DocumentVersion* doc, List<Edit>& edits)
+{
+ List<LanguageServerProtocol::TextEdit> result;
+ for (auto& edit : edits)
+ {
+ LanguageServerProtocol::TextEdit tedit;
+ Index line, col;
+ Index zeroBasedLine, zeroBasedCol;
+ doc->offsetToLineCol(edit.offset, line, col);
+ doc->oneBasedUTF8LocToZeroBasedUTF16Loc(line, col, zeroBasedLine, zeroBasedCol);
+ tedit.range.start.line = (int)zeroBasedLine;
+ tedit.range.start.character = (int)zeroBasedCol;
+ doc->offsetToLineCol(edit.offset + edit.length, line, col);
+ doc->oneBasedUTF8LocToZeroBasedUTF16Loc(line, col, zeroBasedLine, zeroBasedCol);
+ tedit.range.end.line = (int)zeroBasedLine;
+ tedit.range.end.character = (int)zeroBasedCol;
+ tedit.newText = edit.text;
+ result.add(_Move(tedit));
+ }
+ return result;
+}
+
+SlangResult LanguageServer::formatting(const LanguageServerProtocol::DocumentFormattingParams& args, const JSONValue& responseId)
+{
+ String canonicalPath = uriToCanonicalPath(args.textDocument.uri);
+ RefPtr<DocumentVersion> doc;
+ if (!m_workspace->openedDocuments.TryGetValue(canonicalPath, doc))
+ {
+ m_connection->sendResult(NullResponse::get(), responseId);
+ return SLANG_OK;
+ }
+ if (m_formatOptions.clangFormatLocation.getLength() == 0)
+ m_formatOptions.clangFormatLocation = findClangFormatTool();
+ auto edits = formatSource(doc->getText().getUnownedSlice(), -1, -1, -1, m_formatOptions);
+ auto textEdits = translateTextEdits(doc, edits);
+ m_connection->sendResult(&textEdits, responseId);
+ return SLANG_OK;
+}
+
+SlangResult LanguageServer::rangeFormatting(const LanguageServerProtocol::DocumentRangeFormattingParams& args, const JSONValue& responseId)
+{
+ String canonicalPath = uriToCanonicalPath(args.textDocument.uri);
+ RefPtr<DocumentVersion> doc;
+ if (!m_workspace->openedDocuments.TryGetValue(canonicalPath, doc))
+ {
+ m_connection->sendResult(NullResponse::get(), responseId);
+ return SLANG_OK;
+ }
+ if (m_formatOptions.clangFormatLocation.getLength() == 0)
+ m_formatOptions.clangFormatLocation = findClangFormatTool();
+ auto edits = formatSource(doc->getText().getUnownedSlice(), args.range.start.line, args.range.end.line, -1, m_formatOptions);
+ auto textEdits = translateTextEdits(doc, edits);
+ m_connection->sendResult(&textEdits, responseId);
+ return SLANG_OK;
+}
+
+SlangResult LanguageServer::onTypeFormatting(const LanguageServerProtocol::DocumentOnTypeFormattingParams& args, const JSONValue& responseId)
+{
+ String canonicalPath = uriToCanonicalPath(args.textDocument.uri);
+ RefPtr<DocumentVersion> doc;
+ if (!m_workspace->openedDocuments.TryGetValue(canonicalPath, doc))
+ {
+ m_connection->sendResult(NullResponse::get(), responseId);
+ return SLANG_OK;
+ }
+ if (args.ch == ":" && !doc->getLine((Int)args.position.line + 1).trim().startsWith("case "))
+ {
+ m_connection->sendResult(NullResponse::get(), responseId);
+ return SLANG_OK;
+ }
+ if (m_formatOptions.clangFormatLocation.getLength() == 0)
+ m_formatOptions.clangFormatLocation = findClangFormatTool();
+ Index line, col;
+ doc->zeroBasedUTF16LocToOneBasedUTF8Loc(args.position.line, args.position.character, line, col);
+ auto cursorOffset = doc->getOffset(line, col);
+ auto edits = formatSource(doc->getText().getUnownedSlice(), args.position.line, args.position.line, cursorOffset, m_formatOptions);
+ auto textEdits = translateTextEdits(doc, edits);
+ m_connection->sendResult(&textEdits, responseId);
+ return SLANG_OK;
+}
+
void LanguageServer::publishDiagnostics()
{
time_t timeNow = 0;
@@ -866,6 +1055,14 @@ void LanguageServer::publishDiagnostics()
}
}
+void sendRefreshRequests(JSONRPCConnection* connection)
+{
+ connection->sendCall(
+ UnownedStringSlice("workspace/semanticTokens/refresh"), JSONValue::makeInt(0));
+ connection->sendCall(
+ UnownedStringSlice("workspace/inlayHint/refresh"), JSONValue::makeInt(0));
+}
+
void LanguageServer::updatePredefinedMacros(const JSONValue& macros)
{
if (macros.isValid())
@@ -877,8 +1074,7 @@ void LanguageServer::updatePredefinedMacros(const JSONValue& macros)
{
if (m_workspace->updatePredefinedMacros(predefinedMacros))
{
- m_connection->sendCall(
- UnownedStringSlice("workspace/semanticTokens/refresh"), JSONValue::makeInt(0));
+ sendRefreshRequests(m_connection);
}
}
}
@@ -895,8 +1091,7 @@ void LanguageServer::updateSearchPaths(const JSONValue& value)
{
if (m_workspace->updateSearchPaths(searchPaths))
{
- m_connection->sendCall(
- UnownedStringSlice("workspace/semanticTokens/refresh"), JSONValue::makeInt(0));
+ sendRefreshRequests(m_connection);
}
}
}
@@ -913,8 +1108,7 @@ void LanguageServer::updateSearchInWorkspace(const JSONValue& value)
{
if (m_workspace->updateSearchInWorkspace(searchPaths))
{
- m_connection->sendCall(
- UnownedStringSlice("workspace/semanticTokens/refresh"), JSONValue::makeInt(0));
+ sendRefreshRequests(m_connection);
}
}
}
@@ -945,6 +1139,32 @@ void LanguageServer::updateCommitCharacters(const JSONValue& jsonValue)
}
}
+void LanguageServer::updateFormattingOptions(const JSONValue& clangFormatLoc, const JSONValue& clangFormatStyle)
+{
+ auto container = m_connection->getContainer();
+ JSONToNativeConverter converter(container, m_connection->getSink());
+ converter.convert(clangFormatLoc, &m_formatOptions.clangFormatLocation);
+ converter.convert(clangFormatStyle, &m_formatOptions.style);
+ if (m_formatOptions.style.getLength() == 0)
+ m_formatOptions.style = Slang::FormatOptions().style;
+}
+
+void LanguageServer::updateInlayHintOptions(const JSONValue& deducedTypes, const JSONValue& parameterNames)
+{
+ auto container = m_connection->getContainer();
+ JSONToNativeConverter converter(container, m_connection->getSink());
+ bool showDeducedType = false;
+ bool showParameterNames = false;
+ converter.convert(deducedTypes, &showDeducedType);
+ converter.convert(parameterNames, &showParameterNames);
+ if (showDeducedType != m_inlayHintOptions.showDeducedType || showParameterNames != m_inlayHintOptions.showParameterNames)
+ {
+ m_connection->sendCall(
+ UnownedStringSlice("workspace/inlayHint/refresh"), JSONValue::makeInt(0));
+ }
+ m_inlayHintOptions.showDeducedType = showDeducedType;
+ m_inlayHintOptions.showParameterNames = showParameterNames;
+}
void LanguageServer::sendConfigRequest()
{
@@ -958,6 +1178,14 @@ void LanguageServer::sendConfigRequest()
args.items.add(item);
item.section = "slang.enableCommitCharactersInAutoCompletion";
args.items.add(item);
+ item.section = "slang.format.clangFormatLocation";
+ args.items.add(item);
+ item.section = "slang.format.clangFormatStyle";
+ args.items.add(item);
+ item.section = "slang.inlayHints.deducedTypes";
+ args.items.add(item);
+ item.section = "slang.inlayHints.parameterNames";
+ args.items.add(item);
m_connection->sendCall(
ConfigurationParams::methodName,
&args,
@@ -1148,6 +1376,24 @@ SlangResult LanguageServer::queueJSONCall(JSONRPCCall call)
SLANG_RETURN_ON_FAIL(m_connection->toNativeArgsOrSendError(call.params, &args, call.id));
cmd.documentSymbolArgs = args;
}
+ else if (call.method == DocumentFormattingParams::methodName)
+ {
+ DocumentFormattingParams args;
+ SLANG_RETURN_ON_FAIL(m_connection->toNativeArgsOrSendError(call.params, &args, call.id));
+ cmd.formattingArgs = args;
+ }
+ else if (call.method == DocumentRangeFormattingParams::methodName)
+ {
+ DocumentRangeFormattingParams args;
+ SLANG_RETURN_ON_FAIL(m_connection->toNativeArgsOrSendError(call.params, &args, call.id));
+ cmd.rangeFormattingArgs = args;
+ }
+ else if (call.method == DocumentOnTypeFormattingParams::methodName)
+ {
+ DocumentOnTypeFormattingParams args;
+ SLANG_RETURN_ON_FAIL(m_connection->toNativeArgsOrSendError(call.params, &args, call.id));
+ cmd.onTypeFormattingArgs = args;
+ }
else if (call.method == DidChangeConfigurationParams::methodName)
{
DidChangeConfigurationParams args;
@@ -1156,6 +1402,12 @@ SlangResult LanguageServer::queueJSONCall(JSONRPCCall call)
// This is because there is reference to JSONValue that is only available here.
return didChangeConfiguration(args);
}
+ else if (call.method == InlayHintParams::methodName)
+ {
+ InlayHintParams args;
+ SLANG_RETURN_ON_FAIL(m_connection->toNativeArgsOrSendError(call.params, &args, call.id));
+ cmd.inlayHintArgs = args;
+ }
else if (call.method == "$/cancelRequest")
{
CancelParams args;
@@ -1213,6 +1465,22 @@ SlangResult LanguageServer::runCommand(Command& call)
{
return didChangeConfiguration(call.changeConfigArgs.get());
}
+ else if (call.method == InlayHintParams::methodName)
+ {
+ return inlayHint(call.inlayHintArgs.get(), call.id);
+ }
+ else if (call.method == DocumentOnTypeFormattingParams::methodName)
+ {
+ return onTypeFormatting(call.onTypeFormattingArgs.get(), call.id);
+ }
+ else if (call.method == DocumentRangeFormattingParams::methodName)
+ {
+ return rangeFormatting(call.rangeFormattingArgs.get(), call.id);
+ }
+ else if (call.method == DocumentFormattingParams::methodName)
+ {
+ return formatting(call.formattingArgs.get(), call.id);
+ }
else if (call.method.startsWith("$/"))
{
// Ignore.