diff --git a/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/AutoCompleteHelper.cs b/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/AutoCompleteHelper.cs
index 67fb0a15..74aeccce 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/AutoCompleteHelper.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/AutoCompleteHelper.cs
@@ -3,6 +3,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
+using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
@@ -31,89 +32,42 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
private static Regex ValidSqlNameRegex = new Regex(@"^[\p{L}_][\p{L}\p{N}@$#_]{0,127}$");
+ private static CompletionItem[] emptyCompletionList = new CompletionItem[0];
+
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",
+ {
+ "all",
"alter",
- "always",
- "ansi_null_default",
- "ansi_nulls",
- "ansi_padding",
- "ansi_warnings",
- "application",
- "arithabort",
+ "and",
+ "apply",
"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",
+ "cross",
"current_date",
"cursor",
"cursor_close_on_commit",
@@ -122,45 +76,31 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
"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",
@@ -171,20 +111,14 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
"filegroup",
"filename",
"filestream",
- "fillfactor",
"filter",
"first",
"float",
"for",
"foreign",
- "freetext",
- "freetexttable",
"from",
"full",
- "fullscan",
- "fulltext",
"function",
- "generated",
"geography",
"get",
"global",
@@ -200,30 +134,26 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
"holdlock",
"hours",
"identity",
- "identity_insert",
"identitycol",
"if",
- "ignore_dup_key",
"image",
"immediate",
"include",
"index",
- "inflectional",
- "insensitive",
+ "inner",
"insert",
"instead",
"int",
"integer",
- "integrated",
"intersect",
"into",
"isolation",
+ "join",
"json",
"key",
- "kill",
"language",
"last",
- "legacy_cardinality_estimation",
+ "left",
"level",
"lineno",
"load",
@@ -232,16 +162,12 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
"location",
"login",
"masked",
- "master",
"maxdop",
- "memory_optimized",
"merge",
"message",
"modify",
"move",
- "multi_user",
"namespace",
- "national",
"native_compilation",
"nchar",
"next",
@@ -252,8 +178,8 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
"none",
"norecompute",
"now",
+ "null",
"numeric",
- "numeric_roundabort",
"object",
"of",
"off",
@@ -261,21 +187,15 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
"on",
"online",
"open",
- "opendatasource",
- "openquery",
"openrowset",
"openxml",
"option",
+ "or",
"order",
"out",
"output",
"over",
"owner",
- "pad_index",
- "page",
- "page_verify",
- "parameter_sniffing",
- "parameterization",
"partial",
"partition",
"password",
@@ -286,7 +206,6 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
"persisted",
"plan",
"policy",
- "population",
"precision",
"predicate",
"primary",
@@ -295,7 +214,6 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
"proc",
"procedure",
"public",
- "query_optimizer_hotfixes",
"query_store",
"quoted_identifier",
"raiserror",
@@ -318,7 +236,6 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
"relative",
"remove",
"reorganize",
- "replication",
"required",
"restart",
"restore",
@@ -328,7 +245,6 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
"returns",
"revert",
"revoke",
- "role",
"rollback",
"rollup",
"row",
@@ -344,11 +260,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
"scroll",
"secondary",
"security",
- "securityaudit",
"select",
- "semantickeyphrasetable",
- "semanticsimilaritydetailstable",
- "semanticsimilaritytable",
"send",
"sent",
"sequence",
@@ -357,12 +269,10 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
"set",
"sets",
"setuser",
- "shutdown",
"simple",
"smallint",
"smallmoney",
"snapshot",
- "sort_in_tempdb",
"sql",
"standard",
"start",
@@ -374,20 +284,13 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
"statistics_norecompute",
"status",
"stopped",
- "supported",
- "symmetric",
"sysname",
"system",
"system_time",
- "system_versioning",
"table",
- "tablesample",
"take",
"target",
- "textimage_on",
- "textsize",
"then",
- "thesaurus",
"throw",
"time",
"timestamp",
@@ -398,14 +301,13 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
"transaction",
"trigger",
"truncate",
- "trustworthy",
"try",
"tsql",
"type",
+ "uncommitted",
"union",
"unique",
"uniqueidentifier",
- "unlimited",
"updatetext",
"use",
"user",
@@ -413,24 +315,32 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
"value",
"values",
"varchar",
- "varying",
"version",
"view",
"waitfor",
- "weight",
"when",
"where",
"while",
"with",
"within",
- "within group",
"without",
"writetext",
"xact_abort",
"xml",
- "zone"
};
+ ///
+ /// Gets a static instance of an empty completion list to avoid
+ // unneeded memory allocations
+ ///
+ internal static CompletionItem[] EmptyCompletionList
+ {
+ get
+ {
+ return AutoCompleteHelper.emptyCompletionList;
+ }
+ }
+
///
/// Gets or sets the current workspace service instance
/// Setter for internal testing purposes only
@@ -449,7 +359,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
{
AutoCompleteHelper.workspaceServiceInstance = value;
}
- }
+ }
///
/// Get the default completion list from hard-coded list
@@ -462,17 +372,47 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
int row,
int startColumn,
int endColumn,
- bool useLowerCase)
+ bool useLowerCase,
+ string tokenText = null)
{
- var completionItems = new CompletionItem[DefaultCompletionText.Length];
- for (int i = 0; i < DefaultCompletionText.Length; ++i)
+ // determine how many default completion items there will be
+ int listSize = DefaultCompletionText.Length;
+ if (!string.IsNullOrWhiteSpace(tokenText))
{
- completionItems[i] = CreateDefaultCompletionItem(
- useLowerCase ? DefaultCompletionText[i].ToLower() : DefaultCompletionText[i].ToUpper(),
- row,
- startColumn,
- endColumn);
+ listSize = 0;
+ foreach (var completionText in DefaultCompletionText)
+ {
+ if (completionText.StartsWith(tokenText, StringComparison.OrdinalIgnoreCase))
+ {
+ ++listSize;
+ }
+ }
}
+
+ // special case empty list to avoid unneed array allocations
+ if (listSize == 0)
+ {
+ return emptyCompletionList;
+ }
+
+ // build the default completion list
+ var completionItems = new CompletionItem[listSize];
+ int completionItemIndex = 0;
+ foreach (var completionText in DefaultCompletionText)
+ {
+ // add item to list if the tokenText is null (meaning return whole list)
+ // or if the completion item begins with the tokenText
+ if (string.IsNullOrWhiteSpace(tokenText) || completionText.StartsWith(tokenText, StringComparison.OrdinalIgnoreCase))
+ {
+ completionItems[completionItemIndex] = CreateDefaultCompletionItem(
+ useLowerCase ? completionText.ToLower() : completionText.ToUpper(),
+ row,
+ startColumn,
+ endColumn);
+ ++completionItemIndex;
+ }
+ }
+
return completionItems;
}
@@ -559,8 +499,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
int row,
int startColumn,
int endColumn)
- {
-
+ {
List completions = new List();
foreach (var autoCompleteItem in suggestions)
@@ -596,8 +535,6 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
completions.Add(CreateCompletionItem(autoCompleteItem.Title, autoCompleteItem.Title, insertText, kind, row, startColumn, endColumn));
}
-
-
return completions.ToArray();
}
@@ -636,6 +573,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
QueueItem queueItem = bindingQueue.QueueBindingOperation(
key: scriptInfo.ConnectionKey,
bindingTimeout: AutoCompleteHelper.PrepopulateBindTimeout,
+ waitForLockTimeout: AutoCompleteHelper.PrepopulateBindTimeout,
bindOperation: (bindingContext, cancelToken) =>
{
// parse a simple statement that returns common metadata
diff --git a/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/BindingQueue.cs b/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/BindingQueue.cs
index 29310269..6058ec42 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/BindingQueue.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/BindingQueue.cs
@@ -61,7 +61,8 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
string key,
Func bindOperation,
Func timeoutOperation = null,
- int? bindingTimeout = null)
+ int? bindingTimeout = null,
+ int? waitForLockTimeout = null)
{
// don't add null operations to the binding queue
if (bindOperation == null)
@@ -74,7 +75,8 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
Key = key,
BindOperation = bindOperation,
TimeoutOperation = timeoutOperation,
- BindingTimeout = bindingTimeout
+ BindingTimeout = bindingTimeout,
+ WaitForLockTimeout = waitForLockTimeout
};
lock (this.bindingQueueLock)
@@ -198,7 +200,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
int bindTimeout = queueItem.BindingTimeout ?? bindingContext.BindingTimeout;
// handle the case a previous binding operation is still running
- if (!bindingContext.BindingLock.WaitOne(0))
+ if (!bindingContext.BindingLock.WaitOne(queueItem.WaitForLockTimeout ?? 0))
{
queueItem.Result = queueItem.TimeoutOperation != null
? queueItem.TimeoutOperation(bindingContext)
@@ -266,8 +268,15 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
}
finally
{
- // reset the item queued event since we've processed all the pending items
- this.itemQueuedEvent.Reset();
+ lock (this.bindingQueueLock)
+ {
+ // verify the binding queue is still empty
+ if (this.bindingQueue.Count == 0)
+ {
+ // reset the item queued event since we've processed all the pending items
+ this.itemQueuedEvent.Reset();
+ }
+ }
}
}
}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/LanguageService.cs b/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/LanguageService.cs
index a5d27b52..dba3d0aa 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/LanguageService.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/LanguageService.cs
@@ -583,28 +583,45 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
///
internal CompletionItem ResolveCompletionItem(CompletionItem completionItem)
{
- try
+ var scriptParseInfo = LanguageService.Instance.currentCompletionParseInfo;
+ if (scriptParseInfo != null && scriptParseInfo.CurrentSuggestions != null)
{
- var scriptParseInfo = LanguageService.Instance.currentCompletionParseInfo;
- if (scriptParseInfo != null && scriptParseInfo.CurrentSuggestions != null)
+ if (Monitor.TryEnter(scriptParseInfo.BuildingMetadataLock))
{
- foreach (var suggestion in scriptParseInfo.CurrentSuggestions)
+ try
{
- if (string.Equals(suggestion.Title, completionItem.Label))
- {
- completionItem.Detail = suggestion.DatabaseQualifiedName;
- completionItem.Documentation = suggestion.Description;
- break;
- }
+ QueueItem queueItem = this.BindingQueue.QueueBindingOperation(
+ key: scriptParseInfo.ConnectionKey,
+ bindingTimeout: LanguageService.BindingTimeout,
+ bindOperation: (bindingContext, cancelToken) =>
+ {
+ foreach (var suggestion in scriptParseInfo.CurrentSuggestions)
+ {
+ if (string.Equals(suggestion.Title, completionItem.Label))
+ {
+ completionItem.Detail = suggestion.DatabaseQualifiedName;
+ completionItem.Documentation = suggestion.Description;
+ break;
+ }
+ }
+ return completionItem;
+ });
+
+ queueItem.ItemProcessed.WaitOne();
}
+ catch (Exception ex)
+ {
+ // if any exceptions are raised looking up extended completion metadata
+ // then just return the original completion item
+ Logger.Write(LogLevel.Error, "Exeception in ResolveCompletionItem " + ex.ToString());
+ }
+ finally
+ {
+ Monitor.Exit(scriptParseInfo.BuildingMetadataLock);
+ }
}
}
- catch (Exception ex)
- {
- // if any exceptions are raised looking up extended completion metadata
- // then just return the original completion item
- Logger.Write(LogLevel.Error, "Exeception in ResolveCompletionItem " + ex.ToString());
- }
+
return completionItem;
}
@@ -674,27 +691,32 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
ScriptFile scriptFile,
ConnectionInfo connInfo)
{
+ // initialize some state to parse and bind the current script file
+ this.currentCompletionParseInfo = null;
+ CompletionItem[] resultCompletionItems = null;
string filePath = textDocumentPosition.TextDocument.Uri;
int startLine = textDocumentPosition.Position.Line;
+ int parserLine = textDocumentPosition.Position.Line + 1;
int startColumn = TextUtilities.PositionOfPrevDelimeter(
scriptFile.Contents,
textDocumentPosition.Position.Line,
textDocumentPosition.Position.Character);
- int endColumn = textDocumentPosition.Position.Character;
+ int endColumn = TextUtilities.PositionOfNextDelimeter(
+ scriptFile.Contents,
+ textDocumentPosition.Position.Line,
+ textDocumentPosition.Position.Character);
+ int parserColumn = textDocumentPosition.Position.Character + 1;
bool useLowerCaseSuggestions = this.CurrentSettings.SqlTools.IntelliSense.LowerCaseSuggestions.Value;
- this.currentCompletionParseInfo = null;
- CompletionItem[] defaultCompletionItems = AutoCompleteHelper.GetDefaultCompletionItems(startLine, startColumn, endColumn, useLowerCaseSuggestions);
- CompletionItem[] resultCompletionItems = defaultCompletionItems;
- CompletionItem[] emptyCompletionItems = new CompletionItem[0];
- int line = textDocumentPosition.Position.Line + 1;
- int column = textDocumentPosition.Position.Character + 1;
-
// get the current script parse info object
ScriptParseInfo scriptParseInfo = GetScriptParseInfo(textDocumentPosition.TextDocument.Uri);
- if (connInfo == null || scriptParseInfo == null)
+ if (scriptParseInfo == null)
{
- return defaultCompletionItems;
+ return AutoCompleteHelper.GetDefaultCompletionItems(
+ startLine,
+ startColumn,
+ endColumn,
+ useLowerCaseSuggestions);
}
// reparse and bind the SQL statement if needed
@@ -703,14 +725,23 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
ParseAndBind(scriptFile, connInfo);
}
+ // if the parse failed then return the default list
if (scriptParseInfo.ParseResult == null)
{
- return defaultCompletionItems;
+ return AutoCompleteHelper.GetDefaultCompletionItems(
+ startLine,
+ startColumn,
+ endColumn,
+ useLowerCaseSuggestions);
}
- Token token = GetToken(scriptParseInfo, line, column);
+
+ // need to adjust line & column for base-1 parser indices
+ Token token = GetToken(scriptParseInfo, parserLine, parserColumn);
+ string tokenText = token != null ? token.Text : null;
+ // check if the file is connected and the file lock is available
if (scriptParseInfo.IsConnected && Monitor.TryEnter(scriptParseInfo.BuildingMetadataLock))
- {
+ {
try
{
// queue the completion task with the binding queue
@@ -719,33 +750,35 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
bindingTimeout: LanguageService.BindingTimeout,
bindOperation: (bindingContext, cancelToken) =>
{
- CompletionItem[] completions = null;
-
// get the completion list from SQL Parser
scriptParseInfo.CurrentSuggestions = Resolver.FindCompletions(
scriptParseInfo.ParseResult,
- line,
- column,
+ parserLine,
+ parserColumn,
bindingContext.MetadataDisplayInfoProvider);
// cache the current script parse info object to resolve completions later
this.currentCompletionParseInfo = scriptParseInfo;
// convert the suggestion list to the VS Code format
- completions = AutoCompleteHelper.ConvertDeclarationsToCompletionItems(
+ return AutoCompleteHelper.ConvertDeclarationsToCompletionItems(
scriptParseInfo.CurrentSuggestions,
startLine,
startColumn,
- endColumn
- );
-
- return completions;
+ endColumn);
},
timeoutOperation: (bindingContext) =>
{
- return defaultCompletionItems;
+ // return the default list if the connected bind fails
+ return AutoCompleteHelper.GetDefaultCompletionItems(
+ startLine,
+ startColumn,
+ endColumn,
+ useLowerCaseSuggestions,
+ tokenText);
});
+ // wait for the queue item
queueItem.ItemProcessed.WaitOne();
var completionItems = queueItem.GetResultAsT();
@@ -755,7 +788,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
}
else if (!ShouldShowCompletionList(token))
{
- resultCompletionItems = emptyCompletionItems;
+ resultCompletionItems = AutoCompleteHelper.EmptyCompletionList;
}
}
finally
@@ -763,7 +796,18 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
Monitor.Exit(scriptParseInfo.BuildingMetadataLock);
}
}
- //resultCompletionItems = AutoCompleteHelper.AddTokenToItems(resultCompletionItems, token, startLine, startColumn, endColumn);
+
+ // if there are no completions then provide the default list
+ if (resultCompletionItems == null)
+ {
+ resultCompletionItems = AutoCompleteHelper.GetDefaultCompletionItems(
+ startLine,
+ startColumn,
+ endColumn,
+ useLowerCaseSuggestions,
+ tokenText);
+ }
+
return resultCompletionItems;
}
@@ -774,7 +818,16 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
var tokenIndex = scriptParseInfo.ParseResult.Script.TokenManager.FindToken(startLine, startColumn);
if (tokenIndex >= 0)
{
- return scriptParseInfo.ParseResult.Script.Tokens.ToList()[tokenIndex];
+ // return the current token
+ int currentIndex = 0;
+ foreach (var token in scriptParseInfo.ParseResult.Script.Tokens)
+ {
+ if (currentIndex == tokenIndex)
+ {
+ return token;
+ }
+ ++currentIndex;
+ }
}
}
return null;
@@ -930,6 +983,11 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
// Get the requested files
foreach (ScriptFile scriptFile in filesToAnalyze)
{
+ if (IsPreviewWindow(scriptFile))
+ {
+ continue;
+ }
+
Logger.Write(LogLevel.Verbose, "Analyzing script file: " + scriptFile.FilePath);
ScriptFileMarker[] semanticMarkers = GetSemanticMarkers(scriptFile);
Logger.Write(LogLevel.Verbose, "Analysis complete.");
diff --git a/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/QueueItem.cs b/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/QueueItem.cs
index 931bf524..a320f842 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/QueueItem.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/QueueItem.cs
@@ -51,6 +51,11 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
///
public int? BindingTimeout { get; set; }
+ ///
+ /// Gets or sets the timeout for how long to wait for the binding lock
+ ///
+ public int? WaitForLockTimeout { get; set; }
+
///
/// Converts the result of the execution to type T
///
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Utility/TextUtilities.cs b/src/Microsoft.SqlTools.ServiceLayer/Utility/TextUtilities.cs
index 0da84f43..29c9fabc 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Utility/TextUtilities.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/Utility/TextUtilities.cs
@@ -6,7 +6,34 @@
namespace Microsoft.SqlTools.ServiceLayer.Utility
{
public static class TextUtilities
- {
+ {
+ ///
+ /// Find the position of the cursor in the SQL script content buffer and return previous new line position
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static int PositionOfCursor(string sql, int startRow, int startColumn, out int prevNewLine)
+ {
+ prevNewLine = 0;
+ if (string.IsNullOrWhiteSpace(sql))
+ {
+ return 1;
+ }
+
+ for (int i = 0; i < startRow; ++i)
+ {
+ while (prevNewLine < sql.Length && sql[prevNewLine] != '\n')
+ {
+ ++prevNewLine;
+ }
+ ++prevNewLine;
+ }
+
+ return startColumn + prevNewLine;
+ }
+
///
/// 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.
@@ -14,49 +41,70 @@ namespace Microsoft.SqlTools.ServiceLayer.Utility
///
///
///
- ///
+ ///
public static int PositionOfPrevDelimeter(string sql, int startRow, int startColumn)
- {
- if (string.IsNullOrWhiteSpace(sql))
- {
- return 1;
- }
+ {
+ int prevNewLine;
+ int delimeterPos = PositionOfCursor(sql, startRow, startColumn, out prevNewLine);
- int prevLineColumns = 0;
- for (int i = 0; i < startRow; ++i)
+ if (delimeterPos - 1 < sql.Length)
{
- while (sql[prevLineColumns] != '\n' && prevLineColumns < sql.Length)
+ while (--delimeterPos >= prevNewLine)
{
- ++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] == '%')
+ if (IsCharacterDelimeter(sql[delimeterPos]))
{
break;
}
}
+
+ delimeterPos = delimeterPos + 1 - prevNewLine;
}
- return startColumn + 1 - prevLineColumns;
+ return delimeterPos;
+ }
+
+ ///
+ /// Find the position of the next delimeter for autocomplete token replacement.
+ ///
+ ///
+ ///
+ ///
+ public static int PositionOfNextDelimeter(string sql, int startRow, int startColumn)
+ {
+ int prevNewLine;
+ int delimeterPos = PositionOfCursor(sql, startRow, startColumn, out prevNewLine);
+
+ while (delimeterPos < sql.Length)
+ {
+ if (IsCharacterDelimeter(sql[delimeterPos]))
+ {
+ break;
+ }
+ ++delimeterPos;
+ }
+
+ return delimeterPos - prevNewLine;
+ }
+
+ ///
+ /// Determine if the character is a SQL token delimiter
+ ///
+ ///
+ private static bool IsCharacterDelimeter(char ch)
+ {
+ return ch == ' '
+ || ch == '\t'
+ || ch == '\n'
+ || ch == '.'
+ || ch == '+'
+ || ch == '-'
+ || ch == '*'
+ || ch == '>'
+ || ch == '<'
+ || ch == '='
+ || ch == '/'
+ || ch == '%'
+ || ch == ',';
}
}
}