// // 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.Binder; using Microsoft.SqlServer.Management.SqlParser.Intellisense; using Microsoft.SqlServer.Management.SqlParser.Parser; using Microsoft.SqlTools.ServiceLayer.Connection; 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 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, 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(); } /// /// Preinitialize the parser and binder with common metadata. /// This should front load the long binding wait to the time the /// connection is established. Once this is completed other binding /// requests should be faster. /// /// /// internal static void PrepopulateCommonMetadata(ConnectionInfo info, ScriptParseInfo scriptInfo) { if (scriptInfo.IsConnected) { var scriptFile = WorkspaceService.Instance.Workspace.GetFile(info.OwnerUri); LanguageService.Instance.ParseAndBind(scriptFile, info); if (scriptInfo.BuildingMetadataEvent.WaitOne(LanguageService.OnConnectionWaitTimeout)) { try { scriptInfo.BuildingMetadataEvent.Reset(); // parse a simple statement that returns common metadata ParseResult parseResult = Parser.Parse( "select ", scriptInfo.ParseOptions); List parseResults = new List(); parseResults.Add(parseResult); scriptInfo.Binder.Bind( parseResults, info.ConnectionDetails.DatabaseName, BindMode.Batch); // get the completion list from SQL Parser var suggestions = Resolver.FindCompletions( parseResult, 1, 8, scriptInfo.MetadataDisplayInfoProvider); // this forces lazy evaluation of the suggestion metadata AutoCompleteHelper.ConvertDeclarationsToCompletionItems(suggestions, 1, 8, 8); parseResult = Parser.Parse( "exec ", scriptInfo.ParseOptions); parseResults = new List(); parseResults.Add(parseResult); scriptInfo.Binder.Bind( parseResults, info.ConnectionDetails.DatabaseName, BindMode.Batch); // get the completion list from SQL Parser suggestions = Resolver.FindCompletions( parseResult, 1, 6, scriptInfo.MetadataDisplayInfoProvider); // this forces lazy evaluation of the suggestion metadata AutoCompleteHelper.ConvertDeclarationsToCompletionItems(suggestions, 1, 6, 6); } catch { } finally { scriptInfo.BuildingMetadataEvent.Set(); } } } } } }