summaryrefslogtreecommitdiffstats
path: root/source/slang/slang-language-server.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.cpp
parente5a75563a1ba2e378353af8b937b8b7bb0fe2c2b (diff)
More Language Server Improvements. (#2289)
Diffstat (limited to 'source/slang/slang-language-server.cpp')
-rw-r--r--source/slang/slang-language-server.cpp261
1 files changed, 228 insertions, 33 deletions
diff --git a/source/slang/slang-language-server.cpp b/source/slang/slang-language-server.cpp
index 1ad0e28e9..619aaaacd 100644
--- a/source/slang/slang-language-server.cpp
+++ b/source/slang/slang-language-server.cpp
@@ -8,7 +8,6 @@
#include <stdlib.h>
#include <string.h>
#include <time.h>
-#include <thread>
#include "../core/slang-secure-crt.h"
#include "../core/slang-range.h"
#include "../../slang-com-helper.h"
@@ -23,6 +22,7 @@
#include "slang-language-server-document-symbols.h"
#include "slang-ast-print.h"
#include "slang-doc-markdown-writer.h"
+#include "slang-mangle.h"
#include "../../tools/platform/performance-counter.h"
namespace Slang
@@ -114,6 +114,7 @@ SlangResult LanguageServer::parseNextMessage()
result.capabilities.documentSymbolProvider = 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())
@@ -152,11 +153,12 @@ SlangResult LanguageServer::parseNextMessage()
if (response.result.getKind() == JSONValue::Kind::Array)
{
auto arr = m_connection->getContainer()->getArray(response.result);
- if (arr.getCount() == 3)
+ if (arr.getCount() == 4)
{
updatePredefinedMacros(arr[0]);
updateSearchPaths(arr[1]);
updateSearchInWorkspace(arr[2]);
+ updateCommitCharacters(arr[3]);
}
}
break;
@@ -223,7 +225,7 @@ static String _formatDocumentation(String doc)
auto trimedLine = lines[i].trimStart();
if (i > 0)
{
- if (trimedLine.startsWith("\\") && lines[i - 1].trim().getLength() != 0)
+ if ((trimedLine.startsWith("\\") || trimedLine.startsWith("@")) && lines[i - 1].trim().getLength() != 0)
{
result << " \n";
}
@@ -232,15 +234,19 @@ static String _formatDocumentation(String doc)
result << "\n";
}
}
- if (trimedLine.startsWith("\\returns "))
+ if (trimedLine.startsWith("\\") || trimedLine.startsWith("@"))
{
- trimedLine = trimedLine.subString(9, trimedLine.getLength());
- result << "**returns** ";
- }
- else if (trimedLine.startsWith("\\return "))
- {
- trimedLine = trimedLine.subString(8, trimedLine.getLength());
- result << "**Returns** ";
+ trimedLine = trimedLine.tail(1);
+ if (trimedLine.startsWith("returns "))
+ {
+ trimedLine = trimedLine.subString(8, trimedLine.getLength());
+ result << "**returns** ";
+ }
+ else if (trimedLine.startsWith("return "))
+ {
+ trimedLine = trimedLine.subString(7, trimedLine.getLength());
+ result << "**Returns** ";
+ }
}
result << trimedLine;
}
@@ -294,6 +300,8 @@ SlangResult LanguageServer::hover(
col);
if (findResult.getCount() == 0 || findResult[0].path.getCount() == 0)
{
+ if (SLANG_SUCCEEDED(tryGetMacroHoverInfo(version, doc, line, col, responseId)))
+ return SLANG_OK;
m_connection->sendResult(NullResponse::get(), responseId);
return SLANG_OK;
}
@@ -393,6 +401,10 @@ SlangResult LanguageServer::gotoDefinition(
col);
if (findResult.getCount() == 0 || findResult[0].path.getCount() == 0)
{
+ if (SLANG_SUCCEEDED(tryGotoMacroDefinition(version, doc, line, col, responseId)))
+ return SLANG_OK;
+ if (SLANG_SUCCEEDED(tryGotoFileInclude(version, doc, line, responseId)))
+ return SLANG_OK;
m_connection->sendResult(NullResponse::get(), responseId);
return SLANG_OK;
}
@@ -438,7 +450,22 @@ SlangResult LanguageServer::gotoDefinition(
locations.add(LocationResult{location, name ? (int)name->text.getLength() : 0});
}
}
-
+ }
+ else if (auto importDecl = as<ImportDecl>(leafNode))
+ {
+ if (importDecl->importedModuleDecl)
+ {
+ if (importDecl->importedModuleDecl->members.getCount() &&
+ importDecl->importedModuleDecl->members[0])
+ {
+ auto loc = importDecl->importedModuleDecl->members[0]->loc;
+ if (loc.isValid())
+ {
+ auto location = version->linkage->getSourceManager()->getHumaneLoc(loc, SourceLocType::Actual);
+ locations.add(LocationResult{location, 0});
+ }
+ }
+ }
}
if (locations.getCount() == 0)
{
@@ -467,6 +494,16 @@ SlangResult LanguageServer::gotoDefinition(
}
}
+template <typename Func> struct Deferred
+{
+ Func f;
+ Deferred(const Func& func)
+ : f(func)
+ {}
+ ~Deferred() { f(); }
+};
+template <typename Func> Deferred<Func> makeDeferred(const Func& f) { return Deferred<Func>(f); }
+
SlangResult LanguageServer::completion(
const LanguageServerProtocol::CompletionParams& args, const JSONValue& responseId)
{
@@ -487,8 +524,28 @@ SlangResult LanguageServer::completion(
m_connection->sendResult(NullResponse::get(), responseId);
return SLANG_OK;
}
-
- auto version = m_workspace->getCurrentVersion();
+
+ // Ajust cursor position to the beginning of the current/last identifier.
+ cursorOffset--;
+ while (cursorOffset > 0 && _isIdentifierChar(doc->getText()[cursorOffset]))
+ {
+ cursorOffset--;
+ }
+
+ // Insert a completion request token at cursor position.
+ auto originalText = doc->getText();
+ StringBuilder newText;
+ newText << originalText.getUnownedSlice().head(cursorOffset + 1) << "#?"
+ << originalText.getUnownedSlice().tail(cursorOffset + 1);
+ doc->setText(newText.ProduceString());
+ auto restoreDocText = makeDeferred([&]() { doc->setText(originalText); });
+
+ // Always create a new workspace version for the completion request since we
+ // will use a modified source.
+ auto version = m_workspace->createVersionForCompletion();
+ auto moduleName = getMangledNameFromNameString(canonicalPath.getUnownedSlice());
+ version->linkage->contentAssistInfo.cursorLine = utf8Line;
+ version->linkage->contentAssistInfo.cursorCol = utf8Col;
Module* parsedModule = version->getOrLoadModule(canonicalPath);
if (!parsedModule)
{
@@ -506,7 +563,8 @@ SlangResult LanguageServer::completion(
context.canonicalPath = canonicalPath.getUnownedSlice();
context.line = utf8Line;
context.col = utf8Col;
- if (SLANG_SUCCEEDED(context.tryCompleteMember()))
+ context.commitCharacterBehavior = m_commitCharacterBehavior;
+ if (SLANG_SUCCEEDED(context.tryCompleteAttributes()))
{
return SLANG_OK;
}
@@ -514,6 +572,10 @@ SlangResult LanguageServer::completion(
{
return SLANG_OK;
}
+ if (SLANG_SUCCEEDED(context.tryCompleteMemberAndSymbol()))
+ {
+ return SLANG_OK;
+ }
m_connection->sendResult(NullResponse::get(), responseId);
return SLANG_OK;
}
@@ -523,14 +585,19 @@ SlangResult LanguageServer::completionResolve(
{
LanguageServerProtocol::CompletionItem resolvedItem = args;
int itemId = StringToInt(args.data);
- auto version = m_workspace->getCurrentVersion();
- if (itemId >= 0 && itemId < version->currentCompletionItems.getCount())
+ auto version = m_workspace->getCurrentCompletionVersion();
+ if (!version || !version->linkage)
{
- auto decl = version->currentCompletionItems[itemId];
- resolvedItem.detail = getDeclSignatureString(
- DeclRef<Decl>(decl, nullptr), version->linkage->getASTBuilder());
+ m_connection->sendResult(&resolvedItem, responseId);
+ return SLANG_OK;
+ }
+ auto& candidateItems = version->linkage->contentAssistInfo.completionSuggestions.candidateItems;
+ if (itemId >= 0 && itemId < candidateItems.getCount())
+ {
+ auto declRef = candidateItems[itemId].declRef;
+ resolvedItem.detail = getDeclSignatureString(declRef, version->linkage->getASTBuilder());
StringBuilder docSB;
- _tryGetDocumentation(docSB, version, decl);
+ _tryGetDocumentation(docSB, version, declRef.getDecl());
resolvedItem.documentation.value = docSB.ProduceString();
resolvedItem.documentation.kind = "markdown";
}
@@ -817,14 +884,14 @@ void LanguageServer::updatePredefinedMacros(const JSONValue& macros)
}
}
-void LanguageServer::updateSearchPaths(const JSONValue& macros)
+void LanguageServer::updateSearchPaths(const JSONValue& value)
{
- if (macros.isValid())
+ if (value.isValid())
{
auto container = m_connection->getContainer();
JSONToNativeConverter converter(container, m_connection->getSink());
List<String> searchPaths;
- if (SLANG_SUCCEEDED(converter.convert(macros, &searchPaths)))
+ if (SLANG_SUCCEEDED(converter.convert(value, &searchPaths)))
{
if (m_workspace->updateSearchPaths(searchPaths))
{
@@ -835,14 +902,14 @@ void LanguageServer::updateSearchPaths(const JSONValue& macros)
}
}
-void LanguageServer::updateSearchInWorkspace(const JSONValue& macros)
+void LanguageServer::updateSearchInWorkspace(const JSONValue& value)
{
- if (macros.isValid())
+ if (value.isValid())
{
auto container = m_connection->getContainer();
JSONToNativeConverter converter(container, m_connection->getSink());
bool searchPaths;
- if (SLANG_SUCCEEDED(converter.convert(macros, &searchPaths)))
+ if (SLANG_SUCCEEDED(converter.convert(value, &searchPaths)))
{
if (m_workspace->updateSearchInWorkspace(searchPaths))
{
@@ -853,6 +920,32 @@ void LanguageServer::updateSearchInWorkspace(const JSONValue& macros)
}
}
+void LanguageServer::updateCommitCharacters(const JSONValue& jsonValue)
+{
+ if (jsonValue.isValid())
+ {
+ auto container = m_connection->getContainer();
+ JSONToNativeConverter converter(container, m_connection->getSink());
+ String value;
+ if (SLANG_SUCCEEDED(converter.convert(jsonValue, &value)))
+ {
+ if (value == "on")
+ {
+ m_commitCharacterBehavior = CommitCharacterBehavior::All;
+ }
+ else if (value == "off")
+ {
+ m_commitCharacterBehavior = CommitCharacterBehavior::Disabled;
+ }
+ else
+ {
+ m_commitCharacterBehavior = CommitCharacterBehavior::MembersOnly;
+ }
+ }
+ }
+}
+
+
void LanguageServer::sendConfigRequest()
{
ConfigurationParams args;
@@ -863,6 +956,8 @@ void LanguageServer::sendConfigRequest()
args.items.add(item);
item.section = "slang.searchInAllWorkspaceDirectories";
args.items.add(item);
+ item.section = "slang.enableCommitCharactersInAutoCompletion";
+ args.items.add(item);
m_connection->sendCall(
ConfigurationParams::methodName,
&args,
@@ -888,6 +983,106 @@ void LanguageServer::logMessage(int type, String message)
m_connection->sendCall(LanguageServerProtocol::LogMessageParams::methodName, &args);
}
+SlangResult LanguageServer::tryGetMacroHoverInfo(
+ WorkspaceVersion* version, DocumentVersion* doc, Index line, Index col, JSONValue responseId)
+{
+ Index startOffset = 0;
+ auto identifier = doc->peekIdentifier(line, col, startOffset);
+ if (identifier.getLength() == 0)
+ return SLANG_FAIL;
+ auto def = version->tryGetMacroDefinition(identifier);
+ if (!def)
+ return SLANG_FAIL;
+ LanguageServerProtocol::Hover hover;
+ doc->offsetToLineCol(startOffset, line, col);
+ Index outLine, outCol;
+ doc->oneBasedUTF8LocToZeroBasedUTF16Loc(line, col, outLine, outCol);
+ hover.range.start.line = (int)outLine;
+ hover.range.start.character = (int)outCol;
+ hover.range.end.line = (int)outLine;
+ hover.range.end.character = (int)(outCol + identifier.getLength());
+ StringBuilder sb;
+ sb << "```\n#define " << identifier;
+ if (def->params.getCount())
+ {
+ sb << "(";
+ bool isFirst = true;
+ for (auto param : def->params)
+ {
+ if (!isFirst)
+ sb << ", ";
+ if (param.isVariadic)
+ sb << "...";
+ else if (param.name)
+ sb << param.name->text;
+ isFirst = false;
+ }
+ sb << ")";
+ }
+ for (auto& token : def->tokenList)
+ {
+ sb << " ";
+ sb << token.getContent();
+ }
+ sb << "\n```\n\n";
+ auto humaneLoc =
+ version->linkage->getSourceManager()->getHumaneLoc(def->loc, SourceLocType::Actual);
+ sb << "Defined in " << humaneLoc.pathInfo.foundPath << "(" << humaneLoc.line << ")\n";
+ hover.contents.kind = "markdown";
+ hover.contents.value = sb.ProduceString();
+ m_connection->sendResult(&hover, responseId);
+ return SLANG_OK;
+}
+
+SlangResult LanguageServer::tryGotoMacroDefinition(
+ WorkspaceVersion* version, DocumentVersion* doc, Index line, Index col, JSONValue responseId)
+{
+ Index startOffset = 0;
+ auto identifier = doc->peekIdentifier(line, col, startOffset);
+ if (identifier.getLength() == 0)
+ return SLANG_FAIL;
+ auto def = version->tryGetMacroDefinition(identifier);
+ if (!def)
+ return SLANG_FAIL;
+ auto humaneLoc =
+ version->linkage->getSourceManager()->getHumaneLoc(def->loc, SourceLocType::Actual);
+ LanguageServerProtocol::Location result;
+ result.uri = URI::fromLocalFilePath(humaneLoc.pathInfo.foundPath.getUnownedSlice()).uri;
+ Index outLine, outCol;
+ doc->oneBasedUTF8LocToZeroBasedUTF16Loc(humaneLoc.line, humaneLoc.column, outLine, outCol);
+ result.range.start.line = (int)outLine;
+ result.range.start.character = (int)outCol;
+ result.range.end.line = (int)outLine;
+ result.range.end.character = (int)(outCol + identifier.getLength());
+ m_connection->sendResult(&result, responseId);
+ return SLANG_OK;
+}
+
+SlangResult LanguageServer::tryGotoFileInclude(
+ WorkspaceVersion* version, DocumentVersion* doc, Index line, JSONValue responseId)
+{
+ auto lineContent = doc->getLine(line).trim();
+ if (!lineContent.startsWith("#") || lineContent.indexOf(UnownedStringSlice("include")) == -1)
+ return SLANG_FAIL;
+ for (auto& include : version->linkage->contentAssistInfo.preprocessorInfo.fileIncludes)
+ {
+ auto includeLoc =
+ version->linkage->getSourceManager()->getHumaneLoc(include.loc, SourceLocType::Actual);
+ if (includeLoc.line == line && includeLoc.pathInfo.foundPath == doc->getPath())
+ {
+ LanguageServerProtocol::Location result;
+ result.uri = URI::fromLocalFilePath(include.path.getUnownedSlice()).uri;
+ result.range.start.line = 0;
+ result.range.start.character = 0;
+ result.range.end.line = 0;
+ result.range.end.character = 0;
+ m_connection->sendResult(&result, responseId);
+ return SLANG_OK;
+ }
+ }
+ return SLANG_FAIL;
+}
+
SlangResult LanguageServer::queueJSONCall(JSONRPCCall call)
{
Command cmd;
@@ -943,7 +1138,7 @@ SlangResult LanguageServer::queueJSONCall(JSONRPCCall call)
}
else if (call.method == "completionItem/resolve")
{
- CompletionItem args;
+ Slang::LanguageServerProtocol::CompletionItem args;
SLANG_RETURN_ON_FAIL(m_connection->toNativeArgsOrSendError(call.params, &args, call.id));
cmd.completionResolveArgs = args;
}
@@ -1105,23 +1300,23 @@ SlangResult LanguageServer::execute()
break;
parseNextMessage();
}
- auto parseTime = platform::PerformanceCounter::getElapsedTimeInSeconds(start);
auto parseEnd = platform::PerformanceCounter::now();
processCommands();
- // Now we can use this time to reparse user's code, report diagnostics, etc.
+
+ // Report diagnostics if it hasn't been updated for a while.
update();
+
auto workTime = platform::PerformanceCounter::getElapsedTimeInSeconds(parseEnd);
if (commands.getCount() > 0 && m_initialized)
{
StringBuilder msgBuilder;
- msgBuilder << "Server processed " << commands.getCount() << " commands, parsed in "
- << String(int(parseTime * 1000)) << "ms, executed in "
+ msgBuilder << "Server processed " << commands.getCount() << " commands, executed in "
<< String(int(workTime * 1000)) << "ms";
logMessage(3, msgBuilder.ProduceString());
}
- std::this_thread::sleep_for(std::chrono::milliseconds(10));
+ m_connection->getUnderlyingConnection()->waitForResult(1000);
}
return SLANG_OK;