Additional code cleanup following initial check-in.

Enable more ScriptFile code and hook in stub for error parsing.
This commit is contained in:
Karl Burtram
2016-07-16 02:37:49 -07:00
parent 4ad506f6f2
commit ee664949c5
7 changed files with 231 additions and 218 deletions

View File

@@ -10,11 +10,18 @@ using System.Threading.Tasks;
using Microsoft.SqlTools.EditorServices.Utility;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Linq;
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 EditorSession editorSession;
/// <param name="hostDetails">
@@ -118,21 +125,20 @@ namespace Microsoft.SqlTools.EditorServices.Protocol.Server
ScriptFile changedFile = editorSession.Workspace.GetFile(fileUri);
// changedFile.ApplyChange(
// GetFileChangeDetails(
// textChange.Range.Value,
// textChange.Text));
changedFile.ApplyChange(
GetFileChangeDetails(
textChange.Range.Value,
textChange.Text));
// changedFiles.Add(changedFile);
changedFiles.Add(changedFile);
}
Logger.Write(LogLevel.Normal, msg.ToString());
// // TODO: Get all recently edited files in the workspace
// this.RunScriptDiagnostics(
// changedFiles.ToArray(),
// editorSession,
// eventContext);
this.RunScriptDiagnostics(
changedFiles.ToArray(),
editorSession,
eventContext);
return Task.FromResult(true);
}
@@ -231,6 +237,214 @@ namespace Microsoft.SqlTools.EditorServices.Protocol.Server
{
Logger.Write(LogLevel.Normal, "HandleWorkspaceSymbolRequest");
await Task.FromResult(true);
}
}
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)
// {
// // TODO: Catch a more specific exception!
// 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.
// TODO: Is there a better way to do this?
existingRequestCancellation = new CancellationTokenSource();
Task.Factory.StartNew(
() =>
DelayThenInvokeDiagnostics(
750,
filesToAnalyze,
editorSession,
eventContext,
existingRequestCancellation.Token),
CancellationToken.None,
TaskCreationOptions.None,
TaskScheduler.Default);
return Task.FromResult(true);
}
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.AnalysisService != null)
// {
// Logger.Write(LogLevel.Verbose, "Analyzing script file: " + scriptFile.FilePath);
// semanticMarkers =
// editorSession.AnalysisService.GetSemanticMarkers(
// scriptFile);
// Logger.Write(LogLevel.Verbose, "Analysis complete.");
// }
// else
{
// Semantic markers aren't available if the AnalysisService
// isn't available
semanticMarkers = new ScriptFileMarker[0];
// semanticMarkers = new ScriptFileMarker[1];
// semanticMarkers[0] = new ScriptFileMarker()
// {
// Message = "Error message",
// Level = ScriptFileMarkerLevel.Error,
// ScriptRegion = new ScriptRegion()
// {
// File = scriptFile.FilePath,
// StartLineNumber = 2,
// StartColumnNumber = 2,
// StartOffset = 0,
// EndLineNumber = 4,
// EndColumnNumber = 10,
// EndOffset = 0
// }
// };
}
await PublishScriptDiagnostics(
scriptFile,
semanticMarkers,
eventContext);
}
}
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()
});
}
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
}
}
};
}
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;
}
}
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
};
}
}
}

View File

@@ -75,10 +75,6 @@ namespace Microsoft.SqlTools.EditorServices
#if false
#region Properties
/// <summary>
/// Gets the LanguageService instance for this session.
/// </summary>

View File

@@ -108,4 +108,3 @@ namespace Microsoft.SqlTools.EditorServices
#endregion
}
}

View File

@@ -11,14 +11,6 @@ namespace Microsoft.SqlTools.EditorServices
/// </summary>
public class FilePosition : BufferPosition
{
public FilePosition(
ScriptFile scriptFile,
int line,
int column)
: base(line, column)
{
}
#if false
#region Private Fields
private ScriptFile scriptFile;
@@ -112,7 +104,7 @@ namespace Microsoft.SqlTools.EditorServices
}
#endregion
#endif
}
}

View File

@@ -8,8 +8,6 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
//using System.Management.Automation;
//using System.Management.Automation.Language;
namespace Microsoft.SqlTools.EditorServices
{
@@ -18,34 +16,8 @@ namespace Microsoft.SqlTools.EditorServices
/// </summary>
public class ScriptFile
{
public ScriptFile(
string filePath,
string clientFilePath,
TextReader textReader,
Version SqlToolsVersion)
{
}
/// <summary>
/// Creates a new ScriptFile instance with the specified file contents.
/// </summary>
/// <param name="filePath">The path at which the script file resides.</param>
/// <param name="clientFilePath">The path which the client uses to identify the file.</param>
/// <param name="initialBuffer">The initial contents of the script file.</param>
/// <param name="SqlToolsVersion">The version of SqlTools for which the script is being parsed.</param>
public ScriptFile(
string filePath,
string clientFilePath,
string initialBuffer,
Version SqlToolsVersion)
{
}
#if false
#region Private Fields
private Token[] scriptTokens;
private Version SqlToolsVersion;
#endregion
@@ -121,23 +93,6 @@ namespace Microsoft.SqlTools.EditorServices
private set;
}
/// <summary>
/// Gets the ScriptBlockAst representing the parsed script contents.
/// </summary>
public ScriptBlockAst ScriptAst
{
get;
private set;
}
/// <summary>
/// Gets the array of Tokens representing the parsed script contents.
/// </summary>
public Token[] ScriptTokens
{
get { return this.scriptTokens; }
}
/// <summary>
/// Gets the array of filepaths dot sourced in this ScriptFile
/// </summary>
@@ -502,6 +457,7 @@ namespace Microsoft.SqlTools.EditorServices
/// </summary>
private void ParseFileContents()
{
#if false
ParseError[] parseErrors = null;
// First, get the updated file range
@@ -574,9 +530,9 @@ namespace Microsoft.SqlTools.EditorServices
//Get all dot sourced referenced files and store them
this.ReferencedFiles =
AstOperations.FindDotSourcedIncludes(this.ScriptAst);
#endif
}
#endregion
#endif
}
}

View File

@@ -3,14 +3,6 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using Microsoft.SqlTools.EditorServices.Utility;
using System;
//using System.Management.Automation.Language;
#if ScriptAnalyzer
using Microsoft.Windows.SqlTools.ScriptAnalyzer.Generic;
#endif
namespace Microsoft.SqlTools.EditorServices
{
/// <summary>
@@ -59,60 +51,6 @@ namespace Microsoft.SqlTools.EditorServices
public ScriptRegion ScriptRegion { get; set; }
#endregion
#region Public Methods
#if false
internal static ScriptFileMarker FromParseError(
ParseError parseError)
{
Validate.IsNotNull("parseError", parseError);
return new ScriptFileMarker
{
Message = parseError.Message,
Level = ScriptFileMarkerLevel.Error,
ScriptRegion = ScriptRegion.Create(parseError.Extent)
};
}
#endif
#if ScriptAnalyzer
internal static ScriptFileMarker FromDiagnosticRecord(
DiagnosticRecord diagnosticRecord)
{
Validate.IsNotNull("diagnosticRecord", diagnosticRecord);
return new ScriptFileMarker
{
Message = diagnosticRecord.Message,
Level = GetMarkerLevelFromDiagnosticSeverity(diagnosticRecord.Severity),
ScriptRegion = ScriptRegion.Create(diagnosticRecord.Extent)
};
}
private static ScriptFileMarkerLevel GetMarkerLevelFromDiagnosticSeverity(
DiagnosticSeverity diagnosticSeverity)
{
switch (diagnosticSeverity)
{
case DiagnosticSeverity.Information:
return ScriptFileMarkerLevel.Information;
case DiagnosticSeverity.Warning:
return ScriptFileMarkerLevel.Warning;
case DiagnosticSeverity.Error:
return ScriptFileMarkerLevel.Error;
default:
throw new ArgumentException(
string.Format(
"The provided DiagnosticSeverity value '{0}' is unknown.",
diagnosticSeverity),
"diagnosticSeverity");
}
}
#endif
#endregion
}
}

View File

@@ -9,6 +9,7 @@ using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Linq;
namespace Microsoft.SqlTools.EditorServices
{
@@ -149,15 +150,7 @@ namespace Microsoft.SqlTools.EditorServices
return Regex.Replace(path, @"`(?=[ \[\]])", "");
}
#endregion
#if false
#region Public Methods
/// <summary>
/// <summary>
/// Gets a new ScriptFile instance which is identified by the given file
/// path and initially contains the given buffer contents.
/// </summary>
@@ -211,80 +204,6 @@ namespace Microsoft.SqlTools.EditorServices
this.workspaceFiles.Remove(scriptFile.Id);
}
/// <summary>
/// Gets all file references by recursively searching
/// through referenced files in a scriptfile
/// </summary>
/// <param name="scriptFile">Contains the details and contents of an open script file</param>
/// <returns>A scriptfile array where the first file
/// in the array is the "root file" of the search</returns>
public ScriptFile[] ExpandScriptReferences(ScriptFile scriptFile)
{
Dictionary<string, ScriptFile> referencedScriptFiles = new Dictionary<string, ScriptFile>();
List<ScriptFile> expandedReferences = new List<ScriptFile>();
// add original file so it's not searched for, then find all file references
referencedScriptFiles.Add(scriptFile.Id, scriptFile);
RecursivelyFindReferences(scriptFile, referencedScriptFiles);
// remove original file from referened file and add it as the first element of the
// expanded referenced list to maintain order so the original file is always first in the list
referencedScriptFiles.Remove(scriptFile.Id);
expandedReferences.Add(scriptFile);
if (referencedScriptFiles.Count > 0)
{
expandedReferences.AddRange(referencedScriptFiles.Values);
}
return expandedReferences.ToArray();
}
#endregion
#region Private Methods
/// <summary>
/// Recusrively searches through referencedFiles in scriptFiles
/// and builds a Dictonary of the file references
/// </summary>
/// <param name="scriptFile">Details an contents of "root" script file</param>
/// <param name="referencedScriptFiles">A Dictionary of referenced script files</param>
private void RecursivelyFindReferences(
ScriptFile scriptFile,
Dictionary<string, ScriptFile> referencedScriptFiles)
{
// Get the base path of the current script for use in resolving relative paths
string baseFilePath =
GetBaseFilePath(
scriptFile.FilePath);
ScriptFile referencedFile;
foreach (string referencedFileName in scriptFile.ReferencedFiles)
{
string resolvedScriptPath =
this.ResolveRelativeScriptPath(
baseFilePath,
referencedFileName);
// Make sure file exists before trying to get the file
if (File.Exists(resolvedScriptPath))
{
// Get the referenced file if it's not already in referencedScriptFiles
referencedFile = this.GetFile(resolvedScriptPath);
// Normalize the resolved script path and add it to the
// referenced files list if it isn't there already
resolvedScriptPath = resolvedScriptPath.ToLower();
if (!referencedScriptFiles.ContainsKey(resolvedScriptPath))
{
referencedScriptFiles.Add(resolvedScriptPath, referencedFile);
RecursivelyFindReferences(referencedFile, referencedScriptFiles);
}
}
}
}
private string GetBaseFilePath(string filePath)
{
if (IsPathInMemory(filePath))
@@ -324,7 +243,6 @@ namespace Microsoft.SqlTools.EditorServices
return combinedPath;
}
#endregion
#endif
#endregion
}
}