diff --git a/src/Microsoft.SqlTools.ServiceLayer/Hosting/ServiceHost.cs b/src/Microsoft.SqlTools.ServiceLayer/Hosting/ServiceHost.cs
index 5f5ef1df..f252d3c6 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Hosting/ServiceHost.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/Hosting/ServiceHost.cs
@@ -136,13 +136,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Hosting
TextDocumentSync = TextDocumentSyncKind.Incremental,
DefinitionProvider = true,
ReferencesProvider = true,
- DocumentHighlightProvider = true,
- DocumentSymbolProvider = true,
- WorkspaceSymbolProvider = true,
+ DocumentHighlightProvider = true,
CompletionProvider = new CompletionOptions
{
ResolveProvider = true,
- TriggerCharacters = new string[] { ".", "-", ":", "\\" }
+ TriggerCharacters = new string[] { ".", "-", ":", "\\", ",", " " }
},
SignatureHelpProvider = new SignatureHelpOptions
{
diff --git a/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/AutoCompleteHelper.cs b/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/AutoCompleteHelper.cs
new file mode 100644
index 00000000..1069e18d
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/AutoCompleteHelper.cs
@@ -0,0 +1,514 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+using System.Collections.Generic;
+using Microsoft.SqlServer.Management.SqlParser.Intellisense;
+using Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts;
+using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts;
+
+namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
+{
+ ///
+ /// Main class for Language Service functionality including anything that reqires knowledge of
+ /// the language to perfom, such as definitions, intellisense, etc.
+ ///
+ public static class AutoCompleteHelper
+ {
+ private static readonly string[] DefaultCompletionText = new string[]
+ {
+ "absolute",
+ "accent_sensitivity",
+ "action",
+ "activation",
+ "add",
+ "address",
+ "admin",
+ "after",
+ "aggregate",
+ "algorithm",
+ "allow_page_locks",
+ "allow_row_locks",
+ "allow_snapshot_isolation",
+ "alter",
+ "always",
+ "ansi_null_default",
+ "ansi_nulls",
+ "ansi_padding",
+ "ansi_warnings",
+ "application",
+ "arithabort",
+ "as",
+ "asc",
+ "assembly",
+ "asymmetric",
+ "at",
+ "atomic",
+ "audit",
+ "authentication",
+ "authorization",
+ "auto",
+ "auto_close",
+ "auto_shrink",
+ "auto_update_statistics",
+ "auto_update_statistics_async",
+ "availability",
+ "backup",
+ "before",
+ "begin",
+ "binary",
+ "bit",
+ "block",
+ "break",
+ "browse",
+ "bucket_count",
+ "bulk",
+ "by",
+ "call",
+ "caller",
+ "card",
+ "cascade",
+ "case",
+ "catalog",
+ "catch",
+ "change_tracking",
+ "changes",
+ "char",
+ "character",
+ "check",
+ "checkpoint",
+ "close",
+ "clustered",
+ "collection",
+ "column",
+ "column_encryption_key",
+ "columnstore",
+ "commit",
+ "compatibility_level",
+ "compress_all_row_groups",
+ "compression",
+ "compression_delay",
+ "compute",
+ "concat_null_yields_null",
+ "configuration",
+ "connect",
+ "constraint",
+ "containstable",
+ "continue",
+ "create",
+ "cube",
+ "current",
+ "current_date",
+ "cursor",
+ "cursor_close_on_commit",
+ "cursor_default",
+ "data",
+ "data_compression",
+ "database",
+ "date",
+ "date_correlation_optimization",
+ "datefirst",
+ "datetime",
+ "datetime2",
+ "days",
+ "db_chaining",
+ "dbcc",
+ "deallocate",
+ "dec",
+ "decimal",
+ "declare",
+ "default",
+ "delayed_durability",
+ "delete",
+ "deny",
+ "desc",
+ "description",
+ "disable_broker",
+ "disabled",
+ "disk",
+ "distinct",
+ "distributed",
+ "double",
+ "drop",
+ "drop_existing",
+ "dump",
+ "durability",
+ "dynamic",
+ "else",
+ "enable",
+ "encrypted",
+ "encryption_type",
+ "end",
+ "end-exec",
+ "entry",
+ "errlvl",
+ "escape",
+ "event",
+ "except",
+ "exec",
+ "execute",
+ "exit",
+ "external",
+ "fast_forward",
+ "fetch",
+ "file",
+ "filegroup",
+ "filename",
+ "filestream",
+ "fillfactor",
+ "filter",
+ "first",
+ "float",
+ "for",
+ "foreign",
+ "freetext",
+ "freetexttable",
+ "from",
+ "full",
+ "fullscan",
+ "fulltext",
+ "function",
+ "generated",
+ "geography",
+ "get",
+ "global",
+ "go",
+ "goto",
+ "grant",
+ "group",
+ "hash",
+ "hashed",
+ "having",
+ "hidden",
+ "hierarchyid",
+ "holdlock",
+ "hours",
+ "identity",
+ "identity_insert",
+ "identitycol",
+ "if",
+ "ignore_dup_key",
+ "image",
+ "immediate",
+ "include",
+ "index",
+ "inflectional",
+ "insensitive",
+ "insert",
+ "instead",
+ "int",
+ "integer",
+ "integrated",
+ "intersect",
+ "into",
+ "isolation",
+ "json",
+ "key",
+ "kill",
+ "language",
+ "last",
+ "legacy_cardinality_estimation",
+ "level",
+ "lineno",
+ "load",
+ "local",
+ "locate",
+ "location",
+ "login",
+ "masked",
+ "master",
+ "maxdop",
+ "memory_optimized",
+ "merge",
+ "message",
+ "modify",
+ "move",
+ "multi_user",
+ "namespace",
+ "national",
+ "native_compilation",
+ "nchar",
+ "next",
+ "no",
+ "nocheck",
+ "nocount",
+ "nonclustered",
+ "none",
+ "norecompute",
+ "now",
+ "numeric",
+ "numeric_roundabort",
+ "object",
+ "of",
+ "off",
+ "offsets",
+ "on",
+ "online",
+ "open",
+ "opendatasource",
+ "openquery",
+ "openrowset",
+ "openxml",
+ "option",
+ "order",
+ "out",
+ "output",
+ "over",
+ "owner",
+ "pad_index",
+ "page",
+ "page_verify",
+ "parameter_sniffing",
+ "parameterization",
+ "partial",
+ "partition",
+ "password",
+ "path",
+ "percent",
+ "percentage",
+ "period",
+ "persisted",
+ "plan",
+ "policy",
+ "population",
+ "precision",
+ "predicate",
+ "primary",
+ "print",
+ "prior",
+ "proc",
+ "procedure",
+ "public",
+ "query_optimizer_hotfixes",
+ "query_store",
+ "quoted_identifier",
+ "raiserror",
+ "range",
+ "raw",
+ "read",
+ "read_committed_snapshot",
+ "read_only",
+ "read_write",
+ "readonly",
+ "readtext",
+ "real",
+ "rebuild",
+ "receive",
+ "reconfigure",
+ "recovery",
+ "recursive",
+ "recursive_triggers",
+ "references",
+ "relative",
+ "remove",
+ "reorganize",
+ "replication",
+ "required",
+ "restart",
+ "restore",
+ "restrict",
+ "resume",
+ "return",
+ "returns",
+ "revert",
+ "revoke",
+ "role",
+ "rollback",
+ "rollup",
+ "row",
+ "rowcount",
+ "rowguidcol",
+ "rows",
+ "rule",
+ "sample",
+ "save",
+ "schema",
+ "schemabinding",
+ "scoped",
+ "scroll",
+ "secondary",
+ "security",
+ "securityaudit",
+ "select",
+ "semantickeyphrasetable",
+ "semanticsimilaritydetailstable",
+ "semanticsimilaritytable",
+ "send",
+ "sent",
+ "sequence",
+ "server",
+ "session",
+ "set",
+ "sets",
+ "setuser",
+ "shutdown",
+ "simple",
+ "smallint",
+ "smallmoney",
+ "snapshot",
+ "sort_in_tempdb",
+ "sql",
+ "standard",
+ "start",
+ "started",
+ "state",
+ "statement",
+ "static",
+ "statistics",
+ "statistics_norecompute",
+ "status",
+ "stopped",
+ "supported",
+ "symmetric",
+ "sysname",
+ "system",
+ "system_time",
+ "system_versioning",
+ "table",
+ "tablesample",
+ "take",
+ "target",
+ "textimage_on",
+ "textsize",
+ "then",
+ "thesaurus",
+ "throw",
+ "time",
+ "timestamp",
+ "tinyint",
+ "to",
+ "top",
+ "tran",
+ "transaction",
+ "trigger",
+ "truncate",
+ "trustworthy",
+ "try",
+ "tsql",
+ "type",
+ "union",
+ "unique",
+ "uniqueidentifier",
+ "unlimited",
+ "updatetext",
+ "use",
+ "user",
+ "using",
+ "value",
+ "values",
+ "varchar",
+ "varying",
+ "version",
+ "view",
+ "waitfor",
+ "weight",
+ "when",
+ "where",
+ "while",
+ "with",
+ "within",
+ "within group",
+ "without",
+ "writetext",
+ "xact_abort",
+ "xml",
+ "zone"
+ };
+
+ internal static CompletionItem[] GetDefaultCompletionItems(
+ int row,
+ int startColumn,
+ int endColumn)
+ {
+ var completionItems = new CompletionItem[DefaultCompletionText.Length];
+ for (int i = 0; i < DefaultCompletionText.Length; ++i)
+ {
+ completionItems[i] = CreateDefaultCompletionItem(
+ DefaultCompletionText[i].ToUpper(),
+ row,
+ startColumn,
+ endColumn);
+ }
+ return completionItems;
+ }
+
+ private static CompletionItem CreateDefaultCompletionItem(
+ string label,
+ int row,
+ int startColumn,
+ int endColumn)
+ {
+ return new CompletionItem()
+ {
+ Label = label,
+ Kind = CompletionItemKind.Keyword,
+ Detail = label + " keyword",
+ TextEdit = new TextEdit
+ {
+ NewText = label,
+ Range = new Range
+ {
+ Start = new Position
+ {
+ Line = row,
+ Character = startColumn
+ },
+ End = new Position
+ {
+ Line = row,
+ Character = endColumn
+ }
+ }
+ }
+ };
+ }
+
+ ///
+ /// Converts a list of Declaration objects to CompletionItem objects
+ /// since VS Code expects CompletionItems but SQL Parser works with Declarations
+ ///
+ ///
+ ///
+ ///
+ ///
+ internal static CompletionItem[] ConvertDeclarationsToCompletionItems(
+ IEnumerable suggestions,
+ int row,
+ int startColumn,
+ int endColumn)
+ {
+ List completions = new List();
+ foreach (var autoCompleteItem in suggestions)
+ {
+ // convert the completion item candidates into CompletionItems
+ completions.Add(new CompletionItem()
+ {
+ Label = autoCompleteItem.Title,
+ Kind = CompletionItemKind.Variable,
+ Detail = autoCompleteItem.Title,
+ Documentation = autoCompleteItem.Description,
+ TextEdit = new TextEdit
+ {
+ NewText = autoCompleteItem.Title,
+ Range = new Range
+ {
+ Start = new Position
+ {
+ Line = row,
+ Character = startColumn
+ },
+ End = new Position
+ {
+ Line = row,
+ Character = endColumn
+ }
+ }
+ }
+ });
+ }
+
+ return completions.ToArray();
+ }
+ }
+}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/AutoCompleteService.cs b/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/AutoCompleteService.cs
deleted file mode 100644
index 5abe27f7..00000000
--- a/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/AutoCompleteService.cs
+++ /dev/null
@@ -1,323 +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 System;
-using System.Collections.Generic;
-using System.Data.SqlClient;
-using System.Threading.Tasks;
-using Microsoft.SqlServer.Management.Common;
-using Microsoft.SqlServer.Management.SmoMetadataProvider;
-using Microsoft.SqlServer.Management.SqlParser.Binder;
-using Microsoft.SqlServer.Management.SqlParser.Intellisense;
-using Microsoft.SqlServer.Management.SqlParser.MetadataProvider;
-using Microsoft.SqlTools.ServiceLayer.Connection;
-using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
-using Microsoft.SqlTools.ServiceLayer.Hosting;
-using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol;
-using Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts;
-using Microsoft.SqlTools.ServiceLayer.SqlContext;
-using Microsoft.SqlTools.ServiceLayer.Workspace;
-using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts;
-
-namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
-{
- ///
- /// Main class for Autocomplete functionality
- ///
- public class AutoCompleteService
- {
- #region Singleton Instance Implementation
-
- ///
- /// Singleton service instance
- ///
- private static Lazy instance
- = new Lazy(() => new AutoCompleteService());
-
- ///
- /// Gets the singleton service instance
- ///
- public static AutoCompleteService Instance
- {
- get
- {
- return instance.Value;
- }
- }
-
- ///
- /// Default, parameterless constructor.
- /// Internal constructor for use in test cases only
- ///
- internal AutoCompleteService()
- {
- }
-
- #endregion
-
- private ConnectionService connectionService = null;
-
- ///
- /// Internal for testing purposes only
- ///
- internal ConnectionService ConnectionServiceInstance
- {
- get
- {
- if(connectionService == null)
- {
- connectionService = ConnectionService.Instance;
- }
- return connectionService;
- }
-
- set
- {
- connectionService = value;
- }
- }
-
- public void InitializeService(ServiceHost serviceHost)
- {
- // Register auto-complete request handler
- serviceHost.SetRequestHandler(CompletionRequest.Type, HandleCompletionRequest);
-
- // Register a callback for when a connection is created
- ConnectionServiceInstance.RegisterOnConnectionTask(UpdateAutoCompleteCache);
-
- // Register a callback for when a connection is closed
- ConnectionServiceInstance.RegisterOnDisconnectTask(RemoveAutoCompleteCacheUriReference);
- }
-
- ///
- /// Auto-complete completion provider request callback
- ///
- ///
- ///
- ///
- private static async Task HandleCompletionRequest(
- TextDocumentPosition textDocumentPosition,
- RequestContext requestContext)
- {
- // get the current list of completion items and return to client
- var scriptFile = WorkspaceService.Instance.Workspace.GetFile(
- textDocumentPosition.TextDocument.Uri);
-
- ConnectionInfo connInfo;
- ConnectionService.Instance.TryFindConnection(
- scriptFile.ClientFilePath,
- out connInfo);
-
- var completionItems = Instance.GetCompletionItems(
- textDocumentPosition, scriptFile, connInfo);
-
- await requestContext.SendResult(completionItems);
- }
-
- ///
- /// Remove a reference to an autocomplete cache from a URI. If
- /// it is the last URI connected to a particular connection,
- /// then remove the cache.
- ///
- public async Task RemoveAutoCompleteCacheUriReference(ConnectionSummary summary)
- {
- // currently this method is disabled, but we need to reimplement now that the
- // implementation of the 'cache' has changed.
- await Task.FromResult(0);
- }
-
- ///
- /// Update the cached autocomplete candidate list when the user connects to a database
- ///
- ///
- public async Task UpdateAutoCompleteCache(ConnectionInfo info)
- {
- await Task.Run( () =>
- {
- if (!LanguageService.Instance.ScriptParseInfoMap.ContainsKey(info.OwnerUri))
- {
- var sqlConn = info.SqlConnection as SqlConnection;
- if (sqlConn != null)
- {
- var srvConn = new ServerConnection(sqlConn);
- var displayInfoProvider = new MetadataDisplayInfoProvider();
- var metadataProvider = SmoMetadataProvider.CreateConnectedProvider(srvConn);
- var binder = BinderProvider.CreateBinder(metadataProvider);
-
- LanguageService.Instance.ScriptParseInfoMap.Add(info.OwnerUri,
- new ScriptParseInfo()
- {
- Binder = binder,
- MetadataProvider = metadataProvider,
- MetadataDisplayInfoProvider = displayInfoProvider
- });
-
- var scriptFile = WorkspaceService.Instance.Workspace.GetFile(info.OwnerUri);
-
- LanguageService.Instance.ParseAndBind(scriptFile, info);
- }
- }
- });
- }
-
- ///
- /// Find the position of the previous delimeter for autocomplete token replacement.
- /// SQL Parser may have similar functionality in which case we'll delete this method.
- ///
- ///
- ///
- ///
- ///
- private int PositionOfPrevDelimeter(string sql, int startRow, int startColumn)
- {
- if (string.IsNullOrWhiteSpace(sql))
- {
- return 1;
- }
-
- int prevLineColumns = 0;
- for (int i = 0; i < startRow; ++i)
- {
- while (sql[prevLineColumns] != '\n' && prevLineColumns < sql.Length)
- {
- ++prevLineColumns;
- }
- ++prevLineColumns;
- }
-
- startColumn += prevLineColumns;
-
- if (startColumn - 1 < sql.Length)
- {
- while (--startColumn >= prevLineColumns)
- {
- if (sql[startColumn] == ' '
- || sql[startColumn] == '\t'
- || sql[startColumn] == '\n'
- || sql[startColumn] == '.'
- || sql[startColumn] == '+'
- || sql[startColumn] == '-'
- || sql[startColumn] == '*'
- || sql[startColumn] == '>'
- || sql[startColumn] == '<'
- || sql[startColumn] == '='
- || sql[startColumn] == '/'
- || sql[startColumn] == '%')
- {
- break;
- }
- }
- }
-
- return startColumn + 1 - prevLineColumns;
- }
-
- ///
- /// Determines whether a reparse and bind is required to provide autocomplete
- ///
- ///
- /// TEMP: Currently hard-coded to false for perf
- private bool RequiresReparse(ScriptParseInfo info)
- {
- return false;
- }
-
- ///
- /// Converts a list of Declaration objects to CompletionItem objects
- /// since VS Code expects CompletionItems but SQL Parser works with Declarations
- ///
- ///
- ///
- ///
- ///
- private CompletionItem[] ConvertDeclarationsToCompletionItems(
- IEnumerable suggestions,
- int row,
- int startColumn,
- int endColumn)
- {
- List completions = new List();
- foreach (var autoCompleteItem in suggestions)
- {
- // convert the completion item candidates into CompletionItems
- completions.Add(new CompletionItem()
- {
- Label = autoCompleteItem.Title,
- Kind = CompletionItemKind.Keyword,
- Detail = autoCompleteItem.Title,
- Documentation = autoCompleteItem.Description,
- TextEdit = new TextEdit
- {
- NewText = autoCompleteItem.Title,
- Range = new Range
- {
- Start = new Position
- {
- Line = row,
- Character = startColumn
- },
- End = new Position
- {
- Line = row,
- Character = endColumn
- }
- }
- }
- });
- }
-
- return completions.ToArray();
- }
-
- ///
- /// Return the completion item list for the current text position.
- /// This method does not await cache builds since it expects to return quickly
- ///
- ///
- public CompletionItem[] GetCompletionItems(
- TextDocumentPosition textDocumentPosition,
- ScriptFile scriptFile,
- ConnectionInfo connInfo)
- {
- string filePath = textDocumentPosition.TextDocument.Uri;
-
- // Take a reference to the list at a point in time in case we update and replace the list
- if (connInfo == null
- || !LanguageService.Instance.ScriptParseInfoMap.ContainsKey(textDocumentPosition.TextDocument.Uri))
- {
- return new CompletionItem[0];
- }
-
- // reparse and bind the SQL statement if needed
- var scriptParseInfo = LanguageService.Instance.ScriptParseInfoMap[textDocumentPosition.TextDocument.Uri];
- if (RequiresReparse(scriptParseInfo))
- {
- LanguageService.Instance.ParseAndBind(scriptFile, connInfo);
- }
-
- if (scriptParseInfo.ParseResult == null)
- {
- return new CompletionItem[0];
- }
-
- // get the completion list from SQL Parser
- var suggestions = Resolver.FindCompletions(
- scriptParseInfo.ParseResult,
- textDocumentPosition.Position.Line + 1,
- textDocumentPosition.Position.Character + 1,
- scriptParseInfo.MetadataDisplayInfoProvider);
-
- // convert the suggestion list to the VS Code format
- return ConvertDeclarationsToCompletionItems(
- suggestions,
- textDocumentPosition.Position.Line,
- PositionOfPrevDelimeter(
- scriptFile.Contents,
- textDocumentPosition.Position.Line,
- textDocumentPosition.Position.Character),
- textDocumentPosition.Position.Character);
- }
- }
-}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/DiagnosticsHelper.cs b/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/DiagnosticsHelper.cs
new file mode 100644
index 00000000..0c493b0f
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/DiagnosticsHelper.cs
@@ -0,0 +1,98 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol;
+using Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts;
+using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts;
+
+namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
+{
+ ///
+ /// Main class for Language Service functionality including anything that reqires knowledge of
+ /// the language to perfom, such as definitions, intellisense, etc.
+ ///
+ public static class DiagnosticsHelper
+ {
+ ///
+ /// Send the diagnostic results back to the host application
+ ///
+ ///
+ ///
+ ///
+ internal 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()
+ });
+ }
+
+ ///
+ /// Convert a ScriptFileMarker to a Diagnostic that is Language Service compatible
+ ///
+ ///
+ ///
+ internal static Diagnostic GetDiagnosticFromMarker(ScriptFileMarker scriptFileMarker)
+ {
+ return new Diagnostic
+ {
+ Severity = MapDiagnosticSeverity(scriptFileMarker.Level),
+ Message = scriptFileMarker.Message,
+ Range = new Range
+ {
+ 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
+ }
+ }
+ };
+ }
+
+ ///
+ /// Map ScriptFileMarker severity to Diagnostic severity
+ ///
+ ///
+ internal 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;
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/LanguageService.cs b/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/LanguageService.cs
index d414b41e..b4da32aa 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/LanguageService.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/LanguageService.cs
@@ -5,22 +5,28 @@
using System;
using System.Collections.Generic;
+using System.Data.SqlClient;
+using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.SqlTools.EditorServices.Utility;
+using Microsoft.SqlTools.ServiceLayer.Connection;
+using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
using Microsoft.SqlTools.ServiceLayer.Hosting;
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol;
using Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts;
using Microsoft.SqlTools.ServiceLayer.SqlContext;
using Microsoft.SqlTools.ServiceLayer.Workspace;
using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts;
-using System.Linq;
-using Microsoft.SqlServer.Management.SqlParser.Parser;
-using Location = Microsoft.SqlTools.ServiceLayer.Workspace.Contracts.Location;
-using Microsoft.SqlTools.ServiceLayer.Connection;
-using Microsoft.SqlServer.Management.SqlParser.Binder;
using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlServer.Management.SqlParser;
+using Microsoft.SqlServer.Management.SqlParser.Binder;
+using Microsoft.SqlServer.Management.SqlParser.Intellisense;
+using Microsoft.SqlServer.Management.SqlParser.MetadataProvider;
+using Microsoft.SqlServer.Management.SqlParser.Parser;
+using Microsoft.SqlServer.Management.SmoMetadataProvider;
+
+using Location = Microsoft.SqlTools.ServiceLayer.Workspace.Contracts.Location;
namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
{
@@ -30,6 +36,42 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
///
public sealed class LanguageService
{
+ public const string DefaultBatchSeperator = "GO";
+
+ private const int DiagnosticParseDelay = 750;
+
+ private const int FindCompletionsTimeout = 3000;
+
+ private const int FindCompletionStartTimeout = 50;
+
+ private const int OnConnectionWaitTimeout = 30000;
+
+ private bool ShouldEnableAutocomplete()
+ {
+ return true;
+ }
+
+ private ConnectionService connectionService = null;
+
+ ///
+ /// Internal for testing purposes only
+ ///
+ internal ConnectionService ConnectionServiceInstance
+ {
+ get
+ {
+ if(connectionService == null)
+ {
+ connectionService = ConnectionService.Instance;
+ }
+ return connectionService;
+ }
+
+ set
+ {
+ connectionService = value;
+ }
+ }
#region Singleton Instance Implementation
@@ -98,8 +140,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
serviceHost.SetRequestHandler(SignatureHelpRequest.Type, HandleSignatureHelpRequest);
serviceHost.SetRequestHandler(DocumentHighlightRequest.Type, HandleDocumentHighlightRequest);
serviceHost.SetRequestHandler(HoverRequest.Type, HandleHoverRequest);
- serviceHost.SetRequestHandler(DocumentSymbolRequest.Type, HandleDocumentSymbolRequest);
- serviceHost.SetRequestHandler(WorkspaceSymbolRequest.Type, HandleWorkspaceSymbolRequest);
+ serviceHost.SetRequestHandler(CompletionRequest.Type, HandleCompletionRequest);
// Register a no-op shutdown task for validation of the shutdown logic
serviceHost.RegisterShutdownTask(async (shutdownParams, shutdownRequestContext) =>
@@ -115,104 +156,47 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
WorkspaceService.Instance.RegisterTextDocChangeCallback(HandleDidChangeTextDocumentNotification);
// Register the file open update handler
- WorkspaceService.Instance.RegisterTextDocOpenCallback(HandleDidOpenTextDocumentNotification);
+ WorkspaceService.Instance.RegisterTextDocOpenCallback(HandleDidOpenTextDocumentNotification);
+
+ // Register a callback for when a connection is created
+ ConnectionServiceInstance.RegisterOnConnectionTask(UpdateLanguageServiceOnConnection);
+
+ // Register a callback for when a connection is closed
+ ConnectionServiceInstance.RegisterOnDisconnectTask(RemoveAutoCompleteCacheUriReference);
// Store the SqlToolsContext for future use
Context = context;
}
+ #endregion
+
+ #region Request Handlers
+
///
- /// Parses the SQL text and binds it to the SMO metadata provider if connected
+ /// Auto-complete completion provider request callback
///
- ///
- ///
+ ///
+ ///
///
- public ParseResult ParseAndBind(ScriptFile scriptFile, ConnectionInfo connInfo)
+ private static async Task HandleCompletionRequest(
+ TextDocumentPosition textDocumentPosition,
+ RequestContext requestContext)
{
- ScriptParseInfo parseInfo = null;
- if (this.ScriptParseInfoMap.ContainsKey(scriptFile.ClientFilePath))
- {
- parseInfo = this.ScriptParseInfoMap[scriptFile.ClientFilePath];
- }
+ // get the current list of completion items and return to client
+ var scriptFile = WorkspaceService.Instance.Workspace.GetFile(
+ textDocumentPosition.TextDocument.Uri);
- // parse current SQL file contents to retrieve a list of errors
- ParseOptions parseOptions = new ParseOptions();
- ParseResult parseResult = Parser.IncrementalParse(
- scriptFile.Contents,
- parseInfo != null ? parseInfo.ParseResult : null,
- parseOptions);
-
- // save previous result for next incremental parse
- if (parseInfo != null)
- {
- parseInfo.ParseResult = parseResult;
- }
-
- if (connInfo != null)
- {
- try
- {
- List parseResults = new List();
- parseResults.Add(parseResult);
- parseInfo.Binder.Bind(
- parseResults,
- connInfo.ConnectionDetails.DatabaseName,
- BindMode.Batch);
- }
- catch (ConnectionException)
- {
- Logger.Write(LogLevel.Error, "Hit connection exception while binding - disposing binder object...");
- }
- catch (SqlParserInternalBinderError)
- {
- Logger.Write(LogLevel.Error, "Hit connection exception while binding - disposing binder object...");
- }
- }
-
- return parseResult;
- }
-
- ///
- /// Gets a list of semantic diagnostic marks for the provided script file
- ///
- ///
- public ScriptFileMarker[] GetSemanticMarkers(ScriptFile scriptFile)
- {
ConnectionInfo connInfo;
ConnectionService.Instance.TryFindConnection(
scriptFile.ClientFilePath,
out connInfo);
-
- var parseResult = ParseAndBind(scriptFile, connInfo);
- // build a list of SQL script file markers from the errors
- List markers = new List();
- 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
- }
- });
- }
+ var completionItems = Instance.GetCompletionItems(
+ textDocumentPosition, scriptFile, connInfo);
- return markers.ToArray();
+ await requestContext.SendResult(completionItems);
}
- #endregion
-
- #region Request Handlers
-
private static async Task HandleDefinitionRequest(
TextDocumentPosition textDocumentPosition,
RequestContext requestContext)
@@ -261,22 +245,6 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
await Task.FromResult(true);
}
- private static async Task HandleDocumentSymbolRequest(
- DocumentSymbolParams documentSymbolParams,
- RequestContext requestContext)
- {
- Logger.Write(LogLevel.Verbose, "HandleDocumentSymbolRequest");
- await Task.FromResult(true);
- }
-
- private static async Task HandleWorkspaceSymbolRequest(
- WorkspaceSymbolParams workspaceSymbolParams,
- RequestContext requestContext)
- {
- Logger.Write(LogLevel.Verbose, "HandleWorkspaceSymbolRequest");
- await Task.FromResult(true);
- }
-
#endregion
#region Handlers for Events from Other Services
@@ -336,7 +304,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
foreach (var scriptFile in WorkspaceService.Instance.Workspace.GetOpenedFiles())
{
- await PublishScriptDiagnostics(scriptFile, emptyAnalysisDiagnostics, eventContext);
+ await DiagnosticsHelper.PublishScriptDiagnostics(scriptFile, emptyAnalysisDiagnostics, eventContext);
}
}
else
@@ -352,7 +320,269 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
#endregion
- #region Private Helpers
+
+ #region "AutoComplete Provider methods"
+
+ ///
+ /// Remove a reference to an autocomplete cache from a URI. If
+ /// it is the last URI connected to a particular connection,
+ /// then remove the cache.
+ ///
+ public async Task RemoveAutoCompleteCacheUriReference(ConnectionSummary summary)
+ {
+ // currently this method is disabled, but we need to reimplement now that the
+ // implementation of the 'cache' has changed.
+ await Task.FromResult(0);
+ }
+
+ ///
+ /// Parses the SQL text and binds it to the SMO metadata provider if connected
+ ///
+ ///
+ ///
+ ///
+ public ParseResult ParseAndBind(ScriptFile scriptFile, ConnectionInfo connInfo)
+ {
+ ScriptParseInfo parseInfo = null;
+ if (this.ScriptParseInfoMap.ContainsKey(scriptFile.ClientFilePath))
+ {
+ parseInfo = this.ScriptParseInfoMap[scriptFile.ClientFilePath];
+ }
+ else
+ {
+ parseInfo = new ScriptParseInfo();
+ this.ScriptParseInfoMap.Add(scriptFile.ClientFilePath, parseInfo);
+ }
+
+ if (parseInfo.BuildingMetadataEvent.WaitOne(LanguageService.FindCompletionsTimeout))
+ {
+ try
+ {
+ parseInfo.BuildingMetadataEvent.Reset();
+
+ // parse current SQL file contents to retrieve a list of errors
+ ParseResult parseResult = Parser.IncrementalParse(
+ scriptFile.Contents,
+ parseInfo.ParseResult,
+ parseInfo.ParseOptions);
+
+ parseInfo.ParseResult = parseResult;
+
+ if (connInfo != null && parseInfo.IsConnected)
+ {
+ try
+ {
+ List parseResults = new List();
+ parseResults.Add(parseResult);
+ parseInfo.Binder.Bind(
+ parseResults,
+ connInfo.ConnectionDetails.DatabaseName,
+ BindMode.Batch);
+ }
+ catch (ConnectionException)
+ {
+ Logger.Write(LogLevel.Error, "Hit connection exception while binding - disposing binder object...");
+ }
+ catch (SqlParserInternalBinderError)
+ {
+ Logger.Write(LogLevel.Error, "Hit connection exception while binding - disposing binder object...");
+ }
+ }
+ }
+ finally
+ {
+ parseInfo.BuildingMetadataEvent.Set();
+ }
+ }
+
+ return parseInfo.ParseResult;
+ }
+
+ ///
+ /// Update the cached autocomplete candidate list when the user connects to a database
+ ///
+ ///
+ public async Task UpdateLanguageServiceOnConnection(ConnectionInfo info)
+ {
+ await Task.Run( () =>
+ {
+ if (ShouldEnableAutocomplete())
+ {
+ ScriptParseInfo scriptInfo =
+ this.ScriptParseInfoMap.ContainsKey(info.OwnerUri)
+ ? this.ScriptParseInfoMap[info.OwnerUri]
+ : new ScriptParseInfo();
+
+ try
+ {
+ scriptInfo.BuildingMetadataEvent.WaitOne(LanguageService.OnConnectionWaitTimeout);
+ scriptInfo.BuildingMetadataEvent.Reset();
+
+ var sqlConn = info.SqlConnection as SqlConnection;
+ if (sqlConn != null)
+ {
+ ServerConnection serverConn = new ServerConnection(sqlConn);
+ scriptInfo.MetadataDisplayInfoProvider = new MetadataDisplayInfoProvider();
+ scriptInfo.MetadataProvider = SmoMetadataProvider.CreateConnectedProvider(serverConn);
+ scriptInfo.Binder = BinderProvider.CreateBinder(scriptInfo.MetadataProvider);
+ scriptInfo.ServerConnection = new ServerConnection(sqlConn);
+ this.ScriptParseInfoMap[info.OwnerUri] = scriptInfo;
+ }
+ }
+ catch (Exception)
+ {
+ scriptInfo.IsConnected = false;
+ }
+ finally
+ {
+ // Set Metadata Build event to Signal state.
+ // (Tell Language Service that I am ready with Metadata Provider Object)
+ scriptInfo.BuildingMetadataEvent.Set();
+ }
+
+ if (scriptInfo.IsConnected)
+ {
+ var scriptFile = WorkspaceService.Instance.Workspace.GetFile(info.OwnerUri);
+ ParseAndBind(scriptFile, info);
+ }
+ }
+ });
+ }
+
+ ///
+ /// Determines whether a reparse and bind is required to provide autocomplete
+ ///
+ ///
+ private bool RequiresReparse(ScriptParseInfo info, ScriptFile scriptFile)
+ {
+ if (info.ParseResult == null)
+ {
+ return true;
+ }
+
+ string prevSqlText = info.ParseResult.Script.Sql;
+ string currentSqlText = scriptFile.Contents;
+
+ return prevSqlText.Length != currentSqlText.Length
+ || !string.Equals(prevSqlText, currentSqlText);
+ }
+
+ ///
+ /// Return the completion item list for the current text position.
+ /// This method does not await cache builds since it expects to return quickly
+ ///
+ ///
+ public CompletionItem[] GetCompletionItems(
+ TextDocumentPosition textDocumentPosition,
+ ScriptFile scriptFile,
+ ConnectionInfo connInfo)
+ {
+ string filePath = textDocumentPosition.TextDocument.Uri;
+ int startLine = textDocumentPosition.Position.Line;
+ int startColumn = TextUtilities.PositionOfPrevDelimeter(
+ scriptFile.Contents,
+ textDocumentPosition.Position.Line,
+ textDocumentPosition.Position.Character);
+ int endColumn = textDocumentPosition.Position.Character;
+
+ // Take a reference to the list at a point in time in case we update and replace the list
+ if (connInfo == null
+ || !LanguageService.Instance.ScriptParseInfoMap.ContainsKey(textDocumentPosition.TextDocument.Uri))
+ {
+ return AutoCompleteHelper.GetDefaultCompletionItems(startLine, startColumn, endColumn);
+ }
+
+ // reparse and bind the SQL statement if needed
+ var scriptParseInfo = ScriptParseInfoMap[textDocumentPosition.TextDocument.Uri];
+ if (RequiresReparse(scriptParseInfo, scriptFile))
+ {
+ ParseAndBind(scriptFile, connInfo);
+ }
+
+ if (scriptParseInfo.ParseResult == null)
+ {
+ return AutoCompleteHelper.GetDefaultCompletionItems(startLine, startColumn, endColumn);
+ }
+
+ if (scriptParseInfo.IsConnected
+ && scriptParseInfo.BuildingMetadataEvent.WaitOne(LanguageService.FindCompletionStartTimeout))
+ {
+ scriptParseInfo.BuildingMetadataEvent.Reset();
+ Task findCompletionsTask = Task.Run(() => {
+ try
+ {
+ // get the completion list from SQL Parser
+ var suggestions = Resolver.FindCompletions(
+ scriptParseInfo.ParseResult,
+ textDocumentPosition.Position.Line + 1,
+ textDocumentPosition.Position.Character + 1,
+ scriptParseInfo.MetadataDisplayInfoProvider);
+
+ // convert the suggestion list to the VS Code format
+ return AutoCompleteHelper.ConvertDeclarationsToCompletionItems(
+ suggestions,
+ startLine,
+ startColumn,
+ endColumn);
+ }
+ finally
+ {
+ scriptParseInfo.BuildingMetadataEvent.Set();
+ }
+ });
+
+ findCompletionsTask.Wait(LanguageService.FindCompletionsTimeout);
+ if (findCompletionsTask.IsCompleted
+ && findCompletionsTask.Result != null
+ && findCompletionsTask.Result.Length > 0)
+ {
+ return findCompletionsTask.Result;
+ }
+ }
+
+ return AutoCompleteHelper.GetDefaultCompletionItems(startLine, startColumn, endColumn);
+ }
+
+ #endregion
+
+ #region Diagnostic Provider methods
+
+ ///
+ /// Gets a list of semantic diagnostic marks for the provided script file
+ ///
+ ///
+ internal ScriptFileMarker[] GetSemanticMarkers(ScriptFile scriptFile)
+ {
+ ConnectionInfo connInfo;
+ ConnectionService.Instance.TryFindConnection(
+ scriptFile.ClientFilePath,
+ out connInfo);
+
+ var parseResult = ParseAndBind(scriptFile, connInfo);
+
+ // build a list of SQL script file markers from the errors
+ List markers = new List();
+ 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();
+ }
///
/// Runs script diagnostics on changed files
@@ -401,7 +631,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
Task.Factory.StartNew(
() =>
DelayThenInvokeDiagnostics(
- 750,
+ LanguageService.DiagnosticParseDelay,
filesToAnalyze,
eventContext,
ExistingRequestCancellation.Token),
@@ -451,85 +681,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
ScriptFileMarker[] semanticMarkers = GetSemanticMarkers(scriptFile);
Logger.Write(LogLevel.Verbose, "Analysis complete.");
- await PublishScriptDiagnostics(scriptFile, semanticMarkers, eventContext);
- }
- }
-
- ///
- /// Send the diagnostic results back to the host application
- ///
- ///
- ///
- ///
- 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()
- });
- }
-
- ///
- /// Convert a ScriptFileMarker to a Diagnostic that is Language Service compatible
- ///
- ///
- ///
- private static Diagnostic GetDiagnosticFromMarker(ScriptFileMarker scriptFileMarker)
- {
- return new Diagnostic
- {
- Severity = MapDiagnosticSeverity(scriptFileMarker.Level),
- Message = scriptFileMarker.Message,
- Range = new Range
- {
- 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
- }
- }
- };
- }
-
- ///
- /// Map ScriptFileMarker severity to Diagnostic severity
- ///
- ///
- 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;
+ await DiagnosticsHelper.PublishScriptDiagnostics(scriptFile, semanticMarkers, eventContext);
}
}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/ScriptParseInfo.cs b/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/ScriptParseInfo.cs
index 4da2c57e..48fb2cce 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/ScriptParseInfo.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/ScriptParseInfo.cs
@@ -3,10 +3,14 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
+using Microsoft.SqlServer.Management.Common;
+using Microsoft.SqlServer.Management.SmoMetadataProvider;
using Microsoft.SqlServer.Management.SqlParser.Binder;
+using Microsoft.SqlServer.Management.SqlParser.Common;
using Microsoft.SqlServer.Management.SqlParser.MetadataProvider;
using Microsoft.SqlServer.Management.SqlParser.Parser;
-using Microsoft.SqlServer.Management.SmoMetadataProvider;
+using System;
+using System.Threading;
namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
{
@@ -15,6 +19,110 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
///
internal class ScriptParseInfo
{
+ private ManualResetEvent buildingMetadataEvent = new ManualResetEvent(initialState: true);
+
+ private ParseOptions parseOptions = new ParseOptions();
+
+ private ServerConnection serverConnection;
+
+ ///
+ /// Event which tells if MetadataProvider is built fully or not
+ ///
+ public ManualResetEvent BuildingMetadataEvent
+ {
+ get { return this.buildingMetadataEvent; }
+ }
+
+
+ ///
+ /// Gets or sets a flag determining is the LanguageService is connected
+ ///
+ public bool IsConnected { get; set; }
+
+ ///
+ /// Gets or sets the LanguageService SMO ServerConnection
+ ///
+ public ServerConnection ServerConnection
+ {
+ get
+ {
+ return this.serverConnection;
+ }
+ set
+ {
+ this.serverConnection = value;
+ this.parseOptions = new ParseOptions(
+ batchSeparator: LanguageService.DefaultBatchSeperator,
+ isQuotedIdentifierSet: true,
+ compatibilityLevel: DatabaseCompatibilityLevel,
+ transactSqlVersion: TransactSqlVersion);
+ this.IsConnected = true;
+ }
+ }
+
+ ///
+ /// Gets the Language Service ServerVersion
+ ///
+ public ServerVersion ServerVersion
+ {
+ get
+ {
+ return this.ServerConnection != null
+ ? this.ServerConnection.ServerVersion
+ : null;
+ }
+ }
+
+ ///
+ /// Gets the current DataEngineType
+ ///
+ public DatabaseEngineType DatabaseEngineType
+ {
+ get
+ {
+ return this.ServerConnection != null
+ ? this.ServerConnection.DatabaseEngineType
+ : DatabaseEngineType.Standalone;
+ }
+ }
+
+ ///
+ /// Gets the current connections TransactSqlVersion
+ ///
+ public TransactSqlVersion TransactSqlVersion
+ {
+ get
+ {
+ return this.IsConnected
+ ? GetTransactSqlVersion(this.ServerVersion)
+ : TransactSqlVersion.Current;
+ }
+ }
+
+ ///
+ /// Gets the current DatabaseCompatibilityLevel
+ ///
+ public DatabaseCompatibilityLevel DatabaseCompatibilityLevel
+ {
+ get
+ {
+ return this.IsConnected
+ ? GetDatabaseCompatibilityLevel(this.ServerVersion)
+ : DatabaseCompatibilityLevel.Current;
+ }
+ }
+
+ ///
+ /// Gets the current ParseOptions
+ ///
+ public ParseOptions ParseOptions
+ {
+ get
+ {
+ return this.parseOptions;
+ }
+ }
+
///
/// Gets or sets the SMO binder for schema-aware intellisense
///
@@ -28,13 +136,63 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
///
/// Gets or set the SMO metadata provider that's bound to the current connection
///
- ///
public SmoMetadataProvider MetadataProvider { get; set; }
///
/// Gets or sets the SMO metadata display info provider
///
- ///
public MetadataDisplayInfoProvider MetadataDisplayInfoProvider { get; set; }
+
+ ///
+ /// Gets the database compatibility level from a server version
+ ///
+ ///
+ private static DatabaseCompatibilityLevel GetDatabaseCompatibilityLevel(ServerVersion serverVersion)
+ {
+ int versionMajor = Math.Max(serverVersion.Major, 8);
+
+ switch (versionMajor)
+ {
+ case 8:
+ return DatabaseCompatibilityLevel.Version80;
+ case 9:
+ return DatabaseCompatibilityLevel.Version90;
+ case 10:
+ return DatabaseCompatibilityLevel.Version100;
+ case 11:
+ return DatabaseCompatibilityLevel.Version110;
+ case 12:
+ return DatabaseCompatibilityLevel.Version120;
+ case 13:
+ return DatabaseCompatibilityLevel.Version130;
+ default:
+ return DatabaseCompatibilityLevel.Current;
+ }
+ }
+
+ ///
+ /// Gets the transaction sql version from a server version
+ ///
+ ///
+ private static TransactSqlVersion GetTransactSqlVersion(ServerVersion serverVersion)
+ {
+ int versionMajor = Math.Max(serverVersion.Major, 9);
+
+ switch (versionMajor)
+ {
+ case 9:
+ case 10:
+ // In case of 10.0 we still use Version 10.5 as it is the closest available.
+ return TransactSqlVersion.Version105;
+ case 11:
+ return TransactSqlVersion.Version110;
+ case 12:
+ return TransactSqlVersion.Version120;
+ case 13:
+ return TransactSqlVersion.Version130;
+ default:
+ return TransactSqlVersion.Current;
+ }
+ }
}
}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Program.cs b/src/Microsoft.SqlTools.ServiceLayer/Program.cs
index f0d2d6e8..377afe9d 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Program.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/Program.cs
@@ -45,7 +45,6 @@ namespace Microsoft.SqlTools.ServiceLayer
// Initialize the services that will be hosted here
WorkspaceService.Instance.InitializeService(serviceHost);
- AutoCompleteService.Instance.InitializeService(serviceHost);
LanguageService.Instance.InitializeService(serviceHost, sqlToolsContext);
ConnectionService.Instance.InitializeService(serviceHost);
CredentialService.Instance.InitializeService(serviceHost);
diff --git a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/ServiceBufferFileStreamReader.cs b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/ServiceBufferFileStreamReader.cs
index 0cfc2466..b6c23349 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/ServiceBufferFileStreamReader.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/ServiceBufferFileStreamReader.cs
@@ -14,13 +14,10 @@ using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
{
///
- /// Reader for SSMS formatted file streams
+ /// Reader for service buffer formatted file streams
///
public class ServiceBufferFileStreamReader : IFileStreamReader
{
- // Most of this code is based on code from the Microsoft.SqlServer.Management.UI.Grid, SSMS DataStorage
- // $\Data Tools\SSMS_XPlat\sql\ssms\core\DataStorage\src\FileStreamReader.cs
-
private const int DefaultBufferSize = 8192;
#region Member Variables
diff --git a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/ServiceBufferFileStreamWriter.cs b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/ServiceBufferFileStreamWriter.cs
index d0a1c2a9..c978bade 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/ServiceBufferFileStreamWriter.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/ServiceBufferFileStreamWriter.cs
@@ -14,13 +14,10 @@ using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
{
///
- /// Writer for SSMS formatted file streams
+ /// Writer for service buffer formatted file streams
///
public class ServiceBufferFileStreamWriter : IFileStreamWriter
{
- // Most of this code is based on code from the Microsoft.SqlServer.Management.UI.Grid, SSMS DataStorage
- // $\Data Tools\SSMS_XPlat\sql\ssms\core\DataStorage\src\FileStreamWriter.cs
-
#region Properties
public const int DefaultBufferLength = 8192;
diff --git a/src/Microsoft.SqlTools.ServiceLayer/SqlContext/SqlToolsSettings.cs b/src/Microsoft.SqlTools.ServiceLayer/SqlContext/SqlToolsSettings.cs
index 198884f2..e21dcb47 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/SqlContext/SqlToolsSettings.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/SqlContext/SqlToolsSettings.cs
@@ -8,12 +8,6 @@ namespace Microsoft.SqlTools.ServiceLayer.SqlContext
///
public class SqlToolsSettings
{
- // TODO: Is this needed? I can't make sense of this comment.
- // NOTE: This property is capitalized as 'SqlTools' because the
- // mode name sent from the client is written as 'SqlTools' and
- // JSON.net is using camelCasing.
- //public ServiceHostSettings SqlTools { get; set; }
-
public SqlToolsSettings()
{
this.ScriptAnalysis = new ScriptAnalysisSettings();
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Utility/TextUtilities.cs b/src/Microsoft.SqlTools.ServiceLayer/Utility/TextUtilities.cs
new file mode 100644
index 00000000..0da32040
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/Utility/TextUtilities.cs
@@ -0,0 +1,62 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+namespace Microsoft.SqlTools.EditorServices.Utility
+{
+ public static class TextUtilities
+ {
+ ///
+ /// Find the position of the previous delimeter for autocomplete token replacement.
+ /// SQL Parser may have similar functionality in which case we'll delete this method.
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static int PositionOfPrevDelimeter(string sql, int startRow, int startColumn)
+ {
+ if (string.IsNullOrWhiteSpace(sql))
+ {
+ return 1;
+ }
+
+ int prevLineColumns = 0;
+ for (int i = 0; i < startRow; ++i)
+ {
+ while (sql[prevLineColumns] != '\n' && prevLineColumns < sql.Length)
+ {
+ ++prevLineColumns;
+ }
+ ++prevLineColumns;
+ }
+
+ startColumn += prevLineColumns;
+
+ if (startColumn - 1 < sql.Length)
+ {
+ while (--startColumn >= prevLineColumns)
+ {
+ if (sql[startColumn] == ' '
+ || sql[startColumn] == '\t'
+ || sql[startColumn] == '\n'
+ || sql[startColumn] == '.'
+ || sql[startColumn] == '+'
+ || sql[startColumn] == '-'
+ || sql[startColumn] == '*'
+ || sql[startColumn] == '>'
+ || sql[startColumn] == '<'
+ || sql[startColumn] == '='
+ || sql[startColumn] == '/'
+ || sql[startColumn] == '%')
+ {
+ break;
+ }
+ }
+ }
+
+ return startColumn + 1 - prevLineColumns;
+ }
+ }
+}
diff --git a/test/Microsoft.SqlTools.ServiceLayer.Test/LanguageServer/LanguageServiceTests.cs b/test/Microsoft.SqlTools.ServiceLayer.Test/LanguageServer/LanguageServiceTests.cs
index 0725c209..bb07c56a 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.Test/LanguageServer/LanguageServiceTests.cs
+++ b/test/Microsoft.SqlTools.ServiceLayer.Test/LanguageServer/LanguageServiceTests.cs
@@ -164,7 +164,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.LanguageServices
textDocument.Position.Character = 7;
scriptFile.Contents = "select ";
- var autoCompleteService = AutoCompleteService.Instance;
+ var autoCompleteService = LanguageService.Instance;
var completions = autoCompleteService.GetCompletionItems(
textDocument,
scriptFile,
diff --git a/test/Microsoft.SqlTools.ServiceLayer.Test/Utility/TestObjects.cs b/test/Microsoft.SqlTools.ServiceLayer.Test/Utility/TestObjects.cs
index 23ff7260..82ffffd0 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.Test/Utility/TestObjects.cs
+++ b/test/Microsoft.SqlTools.ServiceLayer.Test/Utility/TestObjects.cs
@@ -67,14 +67,6 @@ namespace Microsoft.SqlTools.Test.Utility
return new LanguageService();
}
- ///
- /// Creates a test autocomplete service instance
- ///
- public static AutoCompleteService GetAutoCompleteService()
- {
- return AutoCompleteService.Instance;
- }
-
///
/// Creates a test sql connection factory instance
///