mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-02-16 10:58:30 -05:00
Fix merge build breaks
This commit is contained in:
@@ -1,112 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) Microsoft. All rights reserved.
|
|
||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
||||||
//
|
|
||||||
|
|
||||||
using Microsoft.SqlTools.EditorServices.Connection;
|
|
||||||
using Microsoft.SqlTools.EditorServices.Protocol.LanguageServer;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.LanguageSupport
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Main class for Autocomplete functionality
|
|
||||||
/// </summary>
|
|
||||||
public class AutoCompleteService
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Singleton service instance
|
|
||||||
/// </summary>
|
|
||||||
private static Lazy<AutoCompleteService> instance
|
|
||||||
= new Lazy<AutoCompleteService>(() => new AutoCompleteService());
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The current autocomplete candidate list
|
|
||||||
/// </summary>
|
|
||||||
private IEnumerable<string> autoCompleteList;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the current autocomplete candidate list
|
|
||||||
/// </summary>
|
|
||||||
public IEnumerable<string> AutoCompleteList
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return this.autoCompleteList;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the singleton service instance
|
|
||||||
/// </summary>
|
|
||||||
public static AutoCompleteService Instance
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return instance.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Update the cached autocomplete candidate list when the user connects to a database
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="connection"></param>
|
|
||||||
public void UpdateAutoCompleteCache(ISqlConnection connection)
|
|
||||||
{
|
|
||||||
this.autoCompleteList = connection.GetServerObjects();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Return the completion item list for the current text position
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="textDocumentPosition"></param>
|
|
||||||
public CompletionItem[] GetCompletionItems(TextDocumentPosition textDocumentPosition)
|
|
||||||
{
|
|
||||||
var completions = new List<CompletionItem>();
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
// the completion list will be null is user not connected to server
|
|
||||||
if (this.AutoCompleteList != null)
|
|
||||||
{
|
|
||||||
foreach (var autoCompleteItem in this.AutoCompleteList)
|
|
||||||
{
|
|
||||||
// convert the completion item candidates into CompletionItems
|
|
||||||
completions.Add(new CompletionItem()
|
|
||||||
{
|
|
||||||
Label = autoCompleteItem,
|
|
||||||
Kind = CompletionItemKind.Keyword,
|
|
||||||
Detail = autoCompleteItem + " details",
|
|
||||||
Documentation = autoCompleteItem + " documentation",
|
|
||||||
TextEdit = new TextEdit
|
|
||||||
{
|
|
||||||
NewText = autoCompleteItem,
|
|
||||||
Range = new Range
|
|
||||||
{
|
|
||||||
Start = new Position
|
|
||||||
{
|
|
||||||
Line = textDocumentPosition.Position.Line,
|
|
||||||
Character = textDocumentPosition.Position.Character
|
|
||||||
},
|
|
||||||
End = new Position
|
|
||||||
{
|
|
||||||
Line = textDocumentPosition.Position.Line,
|
|
||||||
Character = textDocumentPosition.Position.Character + 5
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// only show 50 items
|
|
||||||
if (++i == 50)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return completions.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) Microsoft. All rights reserved.
|
|
||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
||||||
//
|
|
||||||
|
|
||||||
using Microsoft.SqlTools.EditorServices;
|
|
||||||
using Microsoft.SqlTools.EditorServices.Session;
|
|
||||||
using Microsoft.SqlServer.Management.SqlParser.Parser;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.LanguageSupport
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Main class for Language Service functionality
|
|
||||||
/// </summary>
|
|
||||||
public class LanguageService
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The cached parse result from previous incremental parse
|
|
||||||
/// </summary>
|
|
||||||
private ParseResult prevParseResult;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the current SQL Tools context
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
private SqlToolsContext Context { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Constructor for the Language Service class
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="context"></param>
|
|
||||||
public LanguageService(SqlToolsContext context)
|
|
||||||
{
|
|
||||||
this.Context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a list of semantic diagnostic marks for the provided script file
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="scriptFile"></param>
|
|
||||||
public ScriptFileMarker[] GetSemanticMarkers(ScriptFile scriptFile)
|
|
||||||
{
|
|
||||||
// parse current SQL file contents to retrieve a list of errors
|
|
||||||
ParseOptions parseOptions = new ParseOptions();
|
|
||||||
ParseResult parseResult = Parser.IncrementalParse(
|
|
||||||
scriptFile.Contents,
|
|
||||||
prevParseResult,
|
|
||||||
parseOptions);
|
|
||||||
|
|
||||||
// save previous result for next incremental parse
|
|
||||||
this.prevParseResult = parseResult;
|
|
||||||
|
|
||||||
// build a list of SQL script file markers from the errors
|
|
||||||
List<ScriptFileMarker> markers = new List<ScriptFileMarker>();
|
|
||||||
foreach (var error in parseResult.Errors)
|
|
||||||
{
|
|
||||||
markers.Add(new ScriptFileMarker()
|
|
||||||
{
|
|
||||||
Message = error.Message,
|
|
||||||
Level = ScriptFileMarkerLevel.Error,
|
|
||||||
ScriptRegion = new ScriptRegion()
|
|
||||||
{
|
|
||||||
File = scriptFile.FilePath,
|
|
||||||
StartLineNumber = error.Start.LineNumber,
|
|
||||||
StartColumnNumber = error.Start.ColumnNumber,
|
|
||||||
StartOffset = 0,
|
|
||||||
EndLineNumber = error.End.LineNumber,
|
|
||||||
EndColumnNumber = error.End.ColumnNumber,
|
|
||||||
EndOffset = 0
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return markers.ToArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,583 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) Microsoft. All rights reserved.
|
|
||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
||||||
//
|
|
||||||
using Microsoft.SqlTools.EditorServices.Protocol.LanguageServer;
|
|
||||||
using Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol;
|
|
||||||
using Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol.Channel;
|
|
||||||
using Microsoft.SqlTools.EditorServices.Session;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.SqlTools.EditorServices.Utility;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Linq;
|
|
||||||
using System;
|
|
||||||
using Microsoft.SqlTools.EditorServices.Connection;
|
|
||||||
using Microsoft.SqlTools.LanguageSupport;
|
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Protocol.Server
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// SQL Tools VS Code Language Server request handler
|
|
||||||
/// </summary>
|
|
||||||
public class LanguageServer : LanguageServerBase
|
|
||||||
{
|
|
||||||
private static CancellationTokenSource existingRequestCancellation;
|
|
||||||
|
|
||||||
private LanguageServerSettings currentSettings = new LanguageServerSettings();
|
|
||||||
|
|
||||||
private EditorSession editorSession;
|
|
||||||
|
|
||||||
/// <param name="hostDetails">
|
|
||||||
/// Provides details about the host application.
|
|
||||||
/// </param>
|
|
||||||
public LanguageServer(HostDetails hostDetails, ProfilePaths profilePaths)
|
|
||||||
: base(new StdioServerChannel())
|
|
||||||
{
|
|
||||||
this.editorSession = new EditorSession();
|
|
||||||
this.editorSession.StartSession(hostDetails, profilePaths);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initialize the VS Code request/response callbacks
|
|
||||||
/// </summary>
|
|
||||||
protected override void Initialize()
|
|
||||||
{
|
|
||||||
// Register all supported message types
|
|
||||||
this.SetRequestHandler(InitializeRequest.Type, this.HandleInitializeRequest);
|
|
||||||
this.SetEventHandler(DidChangeTextDocumentNotification.Type, this.HandleDidChangeTextDocumentNotification);
|
|
||||||
this.SetEventHandler(DidOpenTextDocumentNotification.Type, this.HandleDidOpenTextDocumentNotification);
|
|
||||||
this.SetEventHandler(DidCloseTextDocumentNotification.Type, this.HandleDidCloseTextDocumentNotification);
|
|
||||||
this.SetEventHandler(DidChangeConfigurationNotification<LanguageServerSettingsWrapper>.Type, this.HandleDidChangeConfigurationNotification);
|
|
||||||
|
|
||||||
this.SetRequestHandler(DefinitionRequest.Type, this.HandleDefinitionRequest);
|
|
||||||
this.SetRequestHandler(ReferencesRequest.Type, this.HandleReferencesRequest);
|
|
||||||
this.SetRequestHandler(CompletionRequest.Type, this.HandleCompletionRequest);
|
|
||||||
this.SetRequestHandler(CompletionResolveRequest.Type, this.HandleCompletionResolveRequest);
|
|
||||||
this.SetRequestHandler(SignatureHelpRequest.Type, this.HandleSignatureHelpRequest);
|
|
||||||
this.SetRequestHandler(DocumentHighlightRequest.Type, this.HandleDocumentHighlightRequest);
|
|
||||||
this.SetRequestHandler(HoverRequest.Type, this.HandleHoverRequest);
|
|
||||||
this.SetRequestHandler(DocumentSymbolRequest.Type, this.HandleDocumentSymbolRequest);
|
|
||||||
this.SetRequestHandler(WorkspaceSymbolRequest.Type, this.HandleWorkspaceSymbolRequest);
|
|
||||||
|
|
||||||
this.SetRequestHandler(ConnectionRequest.Type, this.HandleConnectRequest);
|
|
||||||
|
|
||||||
// register an OnConnection callback
|
|
||||||
ConnectionService.Instance.RegisterOnConnectionTask(OnConnection);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Callback for when a user connection is done processing
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="sqlConnection"></param>
|
|
||||||
public Task OnConnection(ISqlConnection sqlConnection)
|
|
||||||
{
|
|
||||||
AutoCompleteService.Instance.UpdateAutoCompleteCache(sqlConnection);
|
|
||||||
return Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Handles the shutdown event for the Language Server
|
|
||||||
/// </summary>
|
|
||||||
protected override async Task Shutdown()
|
|
||||||
{
|
|
||||||
Logger.Write(LogLevel.Normal, "Language service is shutting down...");
|
|
||||||
|
|
||||||
if (this.editorSession != null)
|
|
||||||
{
|
|
||||||
this.editorSession.Dispose();
|
|
||||||
this.editorSession = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
await Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Handles the initialization request
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="initializeParams"></param>
|
|
||||||
/// <param name="requestContext"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
protected async Task HandleInitializeRequest(
|
|
||||||
InitializeRequest initializeParams,
|
|
||||||
RequestContext<InitializeResult> requestContext)
|
|
||||||
{
|
|
||||||
Logger.Write(LogLevel.Verbose, "HandleDidChangeTextDocumentNotification");
|
|
||||||
|
|
||||||
// Grab the workspace path from the parameters
|
|
||||||
editorSession.Workspace.WorkspacePath = initializeParams.RootPath;
|
|
||||||
|
|
||||||
await requestContext.SendResult(
|
|
||||||
new InitializeResult
|
|
||||||
{
|
|
||||||
Capabilities = new ServerCapabilities
|
|
||||||
{
|
|
||||||
TextDocumentSync = TextDocumentSyncKind.Incremental,
|
|
||||||
DefinitionProvider = true,
|
|
||||||
ReferencesProvider = true,
|
|
||||||
DocumentHighlightProvider = true,
|
|
||||||
DocumentSymbolProvider = true,
|
|
||||||
WorkspaceSymbolProvider = true,
|
|
||||||
HoverProvider = true,
|
|
||||||
CompletionProvider = new CompletionOptions
|
|
||||||
{
|
|
||||||
ResolveProvider = true,
|
|
||||||
TriggerCharacters = new string[] { ".", "-", ":", "\\" }
|
|
||||||
},
|
|
||||||
SignatureHelpProvider = new SignatureHelpOptions
|
|
||||||
{
|
|
||||||
TriggerCharacters = new string[] { " " } // TODO: Other characters here?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Handles text document change events
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="textChangeParams"></param>
|
|
||||||
/// <param name="eventContext"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
protected async Task HandleDidChangeTextDocumentNotification(
|
|
||||||
DidChangeTextDocumentParams textChangeParams,
|
|
||||||
EventContext eventContext)
|
|
||||||
{
|
|
||||||
StringBuilder msg = new StringBuilder();
|
|
||||||
msg.Append("HandleDidChangeTextDocumentNotification");
|
|
||||||
List<ScriptFile> changedFiles = new List<ScriptFile>();
|
|
||||||
|
|
||||||
// A text change notification can batch multiple change requests
|
|
||||||
foreach (var textChange in textChangeParams.ContentChanges)
|
|
||||||
{
|
|
||||||
string fileUri = textChangeParams.Uri ?? textChangeParams.TextDocument.Uri;
|
|
||||||
msg.AppendLine();
|
|
||||||
msg.Append(" File: ");
|
|
||||||
msg.Append(fileUri);
|
|
||||||
|
|
||||||
ScriptFile changedFile = editorSession.Workspace.GetFile(fileUri);
|
|
||||||
|
|
||||||
changedFile.ApplyChange(
|
|
||||||
GetFileChangeDetails(
|
|
||||||
textChange.Range.Value,
|
|
||||||
textChange.Text));
|
|
||||||
|
|
||||||
changedFiles.Add(changedFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger.Write(LogLevel.Verbose, msg.ToString());
|
|
||||||
|
|
||||||
await this.RunScriptDiagnostics(
|
|
||||||
changedFiles.ToArray(),
|
|
||||||
editorSession,
|
|
||||||
eventContext);
|
|
||||||
|
|
||||||
await Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Handle the file open notification
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="openParams"></param>
|
|
||||||
/// <param name="eventContext"></param>
|
|
||||||
protected Task HandleDidOpenTextDocumentNotification(
|
|
||||||
DidOpenTextDocumentNotification openParams,
|
|
||||||
EventContext eventContext)
|
|
||||||
{
|
|
||||||
Logger.Write(LogLevel.Verbose, "HandleDidOpenTextDocumentNotification");
|
|
||||||
|
|
||||||
// read the SQL file contents into the ScriptFile
|
|
||||||
ScriptFile openedFile =
|
|
||||||
editorSession.Workspace.GetFileBuffer(
|
|
||||||
openParams.Uri,
|
|
||||||
openParams.Text);
|
|
||||||
|
|
||||||
// run diagnostics on the opened file
|
|
||||||
this.RunScriptDiagnostics(
|
|
||||||
new ScriptFile[] { openedFile },
|
|
||||||
editorSession,
|
|
||||||
eventContext);
|
|
||||||
|
|
||||||
return Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Handle the close document notication
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="closeParams"></param>
|
|
||||||
/// <param name="eventContext"></param>
|
|
||||||
protected Task HandleDidCloseTextDocumentNotification(
|
|
||||||
TextDocumentIdentifier closeParams,
|
|
||||||
EventContext eventContext)
|
|
||||||
{
|
|
||||||
Logger.Write(LogLevel.Verbose, "HandleDidCloseTextDocumentNotification");
|
|
||||||
return Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Handles the configuration change event
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="configChangeParams"></param>
|
|
||||||
/// <param name="eventContext"></param>
|
|
||||||
protected async Task HandleDidChangeConfigurationNotification(
|
|
||||||
DidChangeConfigurationParams<LanguageServerSettingsWrapper> configChangeParams,
|
|
||||||
EventContext eventContext)
|
|
||||||
{
|
|
||||||
Logger.Write(LogLevel.Verbose, "HandleDidChangeConfigurationNotification");
|
|
||||||
|
|
||||||
bool oldLoadProfiles = this.currentSettings.EnableProfileLoading;
|
|
||||||
bool oldScriptAnalysisEnabled =
|
|
||||||
this.currentSettings.ScriptAnalysis.Enable.HasValue;
|
|
||||||
string oldScriptAnalysisSettingsPath =
|
|
||||||
this.currentSettings.ScriptAnalysis.SettingsPath;
|
|
||||||
|
|
||||||
this.currentSettings.Update(
|
|
||||||
configChangeParams.Settings.SqlTools,
|
|
||||||
this.editorSession.Workspace.WorkspacePath);
|
|
||||||
|
|
||||||
// If script analysis settings have changed we need to clear & possibly update the current diagnostic records.
|
|
||||||
if ((oldScriptAnalysisEnabled != this.currentSettings.ScriptAnalysis.Enable))
|
|
||||||
{
|
|
||||||
// If the user just turned off script analysis or changed the settings path, send a diagnostics
|
|
||||||
// event to clear the analysis markers that they already have.
|
|
||||||
if (!this.currentSettings.ScriptAnalysis.Enable.Value)
|
|
||||||
{
|
|
||||||
ScriptFileMarker[] emptyAnalysisDiagnostics = new ScriptFileMarker[0];
|
|
||||||
|
|
||||||
foreach (var scriptFile in editorSession.Workspace.GetOpenedFiles())
|
|
||||||
{
|
|
||||||
await PublishScriptDiagnostics(
|
|
||||||
scriptFile,
|
|
||||||
emptyAnalysisDiagnostics,
|
|
||||||
eventContext);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await this.RunScriptDiagnostics(
|
|
||||||
this.editorSession.Workspace.GetOpenedFiles(),
|
|
||||||
this.editorSession,
|
|
||||||
eventContext);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async Task HandleDefinitionRequest(
|
|
||||||
TextDocumentPosition textDocumentPosition,
|
|
||||||
RequestContext<Location[]> requestContext)
|
|
||||||
{
|
|
||||||
Logger.Write(LogLevel.Verbose, "HandleDefinitionRequest");
|
|
||||||
await Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async Task HandleReferencesRequest(
|
|
||||||
ReferencesParams referencesParams,
|
|
||||||
RequestContext<Location[]> requestContext)
|
|
||||||
{
|
|
||||||
Logger.Write(LogLevel.Verbose, "HandleReferencesRequest");
|
|
||||||
await Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Handles the completion list request
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="textDocumentPosition"></param>
|
|
||||||
/// <param name="requestContext"></param>
|
|
||||||
protected async Task HandleCompletionRequest(
|
|
||||||
TextDocumentPosition textDocumentPosition,
|
|
||||||
RequestContext<CompletionItem[]> requestContext)
|
|
||||||
{
|
|
||||||
Logger.Write(LogLevel.Verbose, "HandleCompletionRequest");
|
|
||||||
|
|
||||||
// get teh current list of completion items and return to client
|
|
||||||
var completionItems = AutoCompleteService.Instance.GetCompletionItems(textDocumentPosition);
|
|
||||||
await requestContext.SendResult(completionItems);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async Task HandleCompletionResolveRequest(
|
|
||||||
CompletionItem completionItem,
|
|
||||||
RequestContext<CompletionItem> requestContext)
|
|
||||||
{
|
|
||||||
Logger.Write(LogLevel.Verbose, "HandleCompletionResolveRequest");
|
|
||||||
await Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async Task HandleSignatureHelpRequest(
|
|
||||||
TextDocumentPosition textDocumentPosition,
|
|
||||||
RequestContext<SignatureHelp> requestContext)
|
|
||||||
{
|
|
||||||
Logger.Write(LogLevel.Verbose, "HandleSignatureHelpRequest");
|
|
||||||
await Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async Task HandleDocumentHighlightRequest(
|
|
||||||
TextDocumentPosition textDocumentPosition,
|
|
||||||
RequestContext<DocumentHighlight[]> requestContext)
|
|
||||||
{
|
|
||||||
Logger.Write(LogLevel.Verbose, "HandleDocumentHighlightRequest");
|
|
||||||
await Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async Task HandleHoverRequest(
|
|
||||||
TextDocumentPosition textDocumentPosition,
|
|
||||||
RequestContext<Hover> requestContext)
|
|
||||||
{
|
|
||||||
Logger.Write(LogLevel.Verbose, "HandleHoverRequest");
|
|
||||||
await Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async Task HandleDocumentSymbolRequest(
|
|
||||||
TextDocumentIdentifier textDocumentIdentifier,
|
|
||||||
RequestContext<SymbolInformation[]> requestContext)
|
|
||||||
{
|
|
||||||
Logger.Write(LogLevel.Verbose, "HandleDocumentSymbolRequest");
|
|
||||||
await Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async Task HandleWorkspaceSymbolRequest(
|
|
||||||
WorkspaceSymbolParams workspaceSymbolParams,
|
|
||||||
RequestContext<SymbolInformation[]> requestContext)
|
|
||||||
{
|
|
||||||
Logger.Write(LogLevel.Verbose, "HandleWorkspaceSymbolRequest");
|
|
||||||
await Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Handle new connection requests
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="connectionDetails"></param>
|
|
||||||
/// <param name="requestContext"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
protected async Task HandleConnectRequest(
|
|
||||||
ConnectionDetails connectionDetails,
|
|
||||||
RequestContext<ConnectionResult> requestContext)
|
|
||||||
{
|
|
||||||
Logger.Write(LogLevel.Verbose, "HandleConnectRequest");
|
|
||||||
|
|
||||||
// open connection base on request details
|
|
||||||
ConnectionResult result = ConnectionService.Instance.Connect(connectionDetails);
|
|
||||||
|
|
||||||
await requestContext.SendResult(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Runs script diagnostics on changed files
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="filesToAnalyze"></param>
|
|
||||||
/// <param name="editorSession"></param>
|
|
||||||
/// <param name="eventContext"></param>
|
|
||||||
private Task RunScriptDiagnostics(
|
|
||||||
ScriptFile[] filesToAnalyze,
|
|
||||||
EditorSession editorSession,
|
|
||||||
EventContext eventContext)
|
|
||||||
{
|
|
||||||
if (!this.currentSettings.ScriptAnalysis.Enable.Value)
|
|
||||||
{
|
|
||||||
// If the user has disabled script analysis, skip it entirely
|
|
||||||
return Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there's an existing task, attempt to cancel it
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (existingRequestCancellation != null)
|
|
||||||
{
|
|
||||||
// Try to cancel the request
|
|
||||||
existingRequestCancellation.Cancel();
|
|
||||||
|
|
||||||
// If cancellation didn't throw an exception,
|
|
||||||
// clean up the existing token
|
|
||||||
existingRequestCancellation.Dispose();
|
|
||||||
existingRequestCancellation = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Logger.Write(
|
|
||||||
LogLevel.Error,
|
|
||||||
string.Format(
|
|
||||||
"Exception while cancelling analysis task:\n\n{0}",
|
|
||||||
e.ToString()));
|
|
||||||
|
|
||||||
TaskCompletionSource<bool> cancelTask = new TaskCompletionSource<bool>();
|
|
||||||
cancelTask.SetCanceled();
|
|
||||||
return cancelTask.Task;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a fresh cancellation token and then start the task.
|
|
||||||
// We create this on a different TaskScheduler so that we
|
|
||||||
// don't block the main message loop thread.
|
|
||||||
existingRequestCancellation = new CancellationTokenSource();
|
|
||||||
Task.Factory.StartNew(
|
|
||||||
() =>
|
|
||||||
DelayThenInvokeDiagnostics(
|
|
||||||
750,
|
|
||||||
filesToAnalyze,
|
|
||||||
editorSession,
|
|
||||||
eventContext,
|
|
||||||
existingRequestCancellation.Token),
|
|
||||||
CancellationToken.None,
|
|
||||||
TaskCreationOptions.None,
|
|
||||||
TaskScheduler.Default);
|
|
||||||
|
|
||||||
return Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Actually run the script diagnostics after waiting for some small delay
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="delayMilliseconds"></param>
|
|
||||||
/// <param name="filesToAnalyze"></param>
|
|
||||||
/// <param name="editorSession"></param>
|
|
||||||
/// <param name="eventContext"></param>
|
|
||||||
/// <param name="cancellationToken"></param>
|
|
||||||
private static async Task DelayThenInvokeDiagnostics(
|
|
||||||
int delayMilliseconds,
|
|
||||||
ScriptFile[] filesToAnalyze,
|
|
||||||
EditorSession editorSession,
|
|
||||||
EventContext eventContext,
|
|
||||||
CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
// First of all, wait for the desired delay period before
|
|
||||||
// analyzing the provided list of files
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await Task.Delay(delayMilliseconds, cancellationToken);
|
|
||||||
}
|
|
||||||
catch (TaskCanceledException)
|
|
||||||
{
|
|
||||||
// If the task is cancelled, exit directly
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we've made it past the delay period then we don't care
|
|
||||||
// about the cancellation token anymore. This could happen
|
|
||||||
// when the user stops typing for long enough that the delay
|
|
||||||
// period ends but then starts typing while analysis is going
|
|
||||||
// on. It makes sense to send back the results from the first
|
|
||||||
// delay period while the second one is ticking away.
|
|
||||||
|
|
||||||
// Get the requested files
|
|
||||||
foreach (ScriptFile scriptFile in filesToAnalyze)
|
|
||||||
{
|
|
||||||
ScriptFileMarker[] semanticMarkers = null;
|
|
||||||
if (editorSession.LanguageService != null)
|
|
||||||
{
|
|
||||||
Logger.Write(LogLevel.Verbose, "Analyzing script file: " + scriptFile.FilePath);
|
|
||||||
semanticMarkers = editorSession.LanguageService.GetSemanticMarkers(scriptFile);
|
|
||||||
Logger.Write(LogLevel.Verbose, "Analysis complete.");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Semantic markers aren't available if the AnalysisService
|
|
||||||
// isn't available
|
|
||||||
semanticMarkers = new ScriptFileMarker[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
await PublishScriptDiagnostics(
|
|
||||||
scriptFile,
|
|
||||||
semanticMarkers,
|
|
||||||
eventContext);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Send the diagnostic results back to the host application
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="scriptFile"></param>
|
|
||||||
/// <param name="semanticMarkers"></param>
|
|
||||||
/// <param name="eventContext"></param>
|
|
||||||
private static async Task PublishScriptDiagnostics(
|
|
||||||
ScriptFile scriptFile,
|
|
||||||
ScriptFileMarker[] semanticMarkers,
|
|
||||||
EventContext eventContext)
|
|
||||||
{
|
|
||||||
var allMarkers = scriptFile.SyntaxMarkers != null
|
|
||||||
? scriptFile.SyntaxMarkers.Concat(semanticMarkers)
|
|
||||||
: semanticMarkers;
|
|
||||||
|
|
||||||
// Always send syntax and semantic errors. We want to
|
|
||||||
// make sure no out-of-date markers are being displayed.
|
|
||||||
await eventContext.SendEvent(
|
|
||||||
PublishDiagnosticsNotification.Type,
|
|
||||||
new PublishDiagnosticsNotification
|
|
||||||
{
|
|
||||||
Uri = scriptFile.ClientFilePath,
|
|
||||||
Diagnostics =
|
|
||||||
allMarkers
|
|
||||||
.Select(GetDiagnosticFromMarker)
|
|
||||||
.ToArray()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Convert a ScriptFileMarker to a Diagnostic that is Language Service compatible
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="scriptFileMarker"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
private static Diagnostic GetDiagnosticFromMarker(ScriptFileMarker scriptFileMarker)
|
|
||||||
{
|
|
||||||
return new Diagnostic
|
|
||||||
{
|
|
||||||
Severity = MapDiagnosticSeverity(scriptFileMarker.Level),
|
|
||||||
Message = scriptFileMarker.Message,
|
|
||||||
Range = new Range
|
|
||||||
{
|
|
||||||
// TODO: What offsets should I use?
|
|
||||||
Start = new Position
|
|
||||||
{
|
|
||||||
Line = scriptFileMarker.ScriptRegion.StartLineNumber - 1,
|
|
||||||
Character = scriptFileMarker.ScriptRegion.StartColumnNumber - 1
|
|
||||||
},
|
|
||||||
End = new Position
|
|
||||||
{
|
|
||||||
Line = scriptFileMarker.ScriptRegion.EndLineNumber - 1,
|
|
||||||
Character = scriptFileMarker.ScriptRegion.EndColumnNumber - 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Map ScriptFileMarker severity to Diagnostic severity
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="markerLevel"></param>
|
|
||||||
private static DiagnosticSeverity MapDiagnosticSeverity(ScriptFileMarkerLevel markerLevel)
|
|
||||||
{
|
|
||||||
switch (markerLevel)
|
|
||||||
{
|
|
||||||
case ScriptFileMarkerLevel.Error:
|
|
||||||
return DiagnosticSeverity.Error;
|
|
||||||
|
|
||||||
case ScriptFileMarkerLevel.Warning:
|
|
||||||
return DiagnosticSeverity.Warning;
|
|
||||||
|
|
||||||
case ScriptFileMarkerLevel.Information:
|
|
||||||
return DiagnosticSeverity.Information;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return DiagnosticSeverity.Error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Switch from 0-based offsets to 1 based offsets
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="changeRange"></param>
|
|
||||||
/// <param name="insertString"></param>
|
|
||||||
private static FileChange GetFileChangeDetails(Range changeRange, string insertString)
|
|
||||||
{
|
|
||||||
// The protocol's positions are zero-based so add 1 to all offsets
|
|
||||||
return new FileChange
|
|
||||||
{
|
|
||||||
InsertString = insertString,
|
|
||||||
Line = changeRange.Start.Line + 1,
|
|
||||||
Offset = changeRange.Start.Character + 1,
|
|
||||||
EndLine = changeRange.End.Line + 1,
|
|
||||||
EndOffset = changeRange.End.Character + 1
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -53,7 +53,7 @@ namespace Microsoft.SqlTools.Test.Utility
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static LanguageService GetTestLanguageService()
|
public static LanguageService GetTestLanguageService()
|
||||||
{
|
{
|
||||||
return new LanguageService(new SqlToolsContext(null, null));
|
return new LanguageService();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
Reference in New Issue
Block a user