private static CompletionItem CreateCompletionItem( CompletionDetails completionDetails, BufferRange completionRange) { string detailString = null; if (completionDetails.CompletionType == CompletionType.Variable) { // Look for variable type encoded in the tooltip var matches = Regex.Matches(completionDetails.ToolTipText, @"^\[(.+)\]"); if (matches.Count > 0 && matches[0].Groups.Count > 1) { detailString = matches[0].Groups[1].Value; } } return new CompletionItem { Label = completionDetails.CompletionText, Kind = MapCompletionKind(completionDetails.CompletionType), Detail = detailString, TextEdit = new TextEdit { NewText = completionDetails.CompletionText, Range = new Range { Start = new Position { Line = completionRange.Start.Line - 1, Character = completionRange.Start.Column - 1 }, End = new Position { Line = completionRange.End.Line - 1, Character = completionRange.End.Column - 1 } } } }; }
private static CompletionItem CreateCompletionItem( CompletionDetails completionDetails, BufferRange completionRange, int sortIndex) { string detailString = null; string documentationString = null; string labelString = completionDetails.ListItemText; if ((completionDetails.CompletionType == CompletionType.Variable) || (completionDetails.CompletionType == CompletionType.ParameterName)) { // Look for type encoded in the tooltip for parameters and variables. // Display PowerShell type names in [] to be consistent with PowerShell syntax // and now the debugger displays type names. var matches = Regex.Matches(completionDetails.ToolTipText, @"^(\[.+\])"); if ((matches.Count > 0) && (matches[0].Groups.Count > 1)) { detailString = matches[0].Groups[1].Value; } // PowerShell returns ListItemText for parameters & variables that is not prefixed // and it needs to be or the completion will not appear for these CompletionTypes. string prefix = (completionDetails.CompletionType == CompletionType.Variable) ? "$" : "-"; labelString = prefix + completionDetails.ListItemText; } else if ((completionDetails.CompletionType == CompletionType.Method) || (completionDetails.CompletionType == CompletionType.Property)) { // We have a raw signature for .NET members, heck let's display it. It's // better than nothing. documentationString = completionDetails.ToolTipText; } else if (completionDetails.CompletionType == CompletionType.Command) { // For Commands, let's extract the resolved command or the path for an exe // from the ToolTipText - if there is any ToolTipText. if (completionDetails.ToolTipText != null) { // Don't display ToolTipText if it is the same as the ListItemText. // Reject command syntax ToolTipText - it's too much to display as a detailString. if (!completionDetails.ListItemText.Equals( completionDetails.ToolTipText, StringComparison.OrdinalIgnoreCase) && !Regex.IsMatch(completionDetails.ToolTipText, @"^\s*" + completionDetails.ListItemText + @"\s+\[")) { detailString = completionDetails.ToolTipText; } } } // We want a special "sort order" for parameters that is not lexicographical. // Fortunately, PowerShell returns parameters in the preferred sort order by // default (with common params at the end). We just need to make sure the default // order also be the lexicographical order which we do by prefixig the ListItemText // with a leading 0's four digit index. This would not sort correctly for a list // > 999 parameters but surely we won't have so many items in the "parameter name" // completion list. Technically we don't need the ListItemText at all but it may come // in handy during debug. var sortText = (completionDetails.CompletionType == CompletionType.ParameterName) ? string.Format("{0:D3}{1}", sortIndex, completionDetails.ListItemText) : null; return(new CompletionItem { InsertText = completionDetails.CompletionText, Label = labelString, Kind = MapCompletionKind(completionDetails.CompletionType), Detail = detailString, Documentation = documentationString, SortText = sortText, TextEdit = new TextEdit { NewText = completionDetails.CompletionText, Range = new Range { Start = new Position { Line = completionRange.Start.Line - 1, Character = completionRange.Start.Column - 1 }, End = new Position { Line = completionRange.End.Line - 1, Character = completionRange.End.Column - 1 } } } }); }
private static CompletionItem CreateCompletionItem( CompletionDetails completionDetails, BufferRange completionRange, int sortIndex) { string detailString = null; string documentationString = null; string completionText = completionDetails.CompletionText; InsertTextFormat insertTextFormat = InsertTextFormat.PlainText; if ((completionDetails.CompletionType == CompletionType.Variable) || (completionDetails.CompletionType == CompletionType.ParameterName)) { // Look for type encoded in the tooltip for parameters and variables. // Display PowerShell type names in [] to be consistent with PowerShell syntax // and now the debugger displays type names. var matches = Regex.Matches(completionDetails.ToolTipText, @"^(\[.+\])"); if ((matches.Count > 0) && (matches[0].Groups.Count > 1)) { detailString = matches[0].Groups[1].Value; } } else if ((completionDetails.CompletionType == CompletionType.Method) || (completionDetails.CompletionType == CompletionType.Property)) { // We have a raw signature for .NET members, heck let's display it. It's // better than nothing. documentationString = completionDetails.ToolTipText; } else if (completionDetails.CompletionType == CompletionType.Command) { // For Commands, let's extract the resolved command or the path for an exe // from the ToolTipText - if there is any ToolTipText. if (completionDetails.ToolTipText != null) { // Fix for #240 - notepad++.exe in tooltip text caused regex parser to throw. string escapedToolTipText = Regex.Escape(completionDetails.ToolTipText); // Don't display ToolTipText if it is the same as the ListItemText. // Reject command syntax ToolTipText - it's too much to display as a detailString. if (!completionDetails.ListItemText.Equals( completionDetails.ToolTipText, StringComparison.OrdinalIgnoreCase) && !Regex.IsMatch(completionDetails.ToolTipText, @"^\s*" + escapedToolTipText + @"\s+\[")) { detailString = completionDetails.ToolTipText; } } } else if (completionDetails.CompletionType == CompletionType.Folder && EndsWithQuote(completionText)) { // Insert a final "tab stop" as identified by $0 in the snippet provided for completion. // For folder paths, we take the path returned by PowerShell e.g. 'C:\Program Files' and insert // the tab stop marker before the closing quote char e.g. 'C:\Program Files$0'. // This causes the editing cursor to be placed *before* the final quote after completion, // which makes subsequent path completions work. See this part of the LSP spec for details: // https://microsoft.github.io/language-server-protocol/specification#textDocument_completion // Since we want to use a "tab stop" we need to escape a few things for Textmate to render properly. var sb = new StringBuilder(completionDetails.CompletionText) .Replace(@"\", @"\\") .Replace(@"}", @"\}") .Replace(@"$", @"\$"); completionText = sb.Insert(sb.Length - 1, "$0").ToString(); insertTextFormat = InsertTextFormat.Snippet; } // Force the client to maintain the sort order in which the // original completion results were returned. We just need to // make sure the default order also be the lexicographical order // which we do by prefixing the ListItemText with a leading 0's // four digit index. var sortText = $"{sortIndex:D4}{completionDetails.ListItemText}"; return(new CompletionItem { InsertText = completionText, InsertTextFormat = insertTextFormat, Label = completionDetails.ListItemText, Kind = MapCompletionKind(completionDetails.CompletionType), Detail = detailString, Documentation = documentationString, SortText = sortText, FilterText = completionDetails.CompletionText, TextEdit = new TextEdit { NewText = completionText, Range = new Range { Start = new Position { Line = completionRange.Start.Line - 1, Character = completionRange.Start.Column - 1 }, End = new Position { Line = completionRange.End.Line - 1, Character = completionRange.End.Column - 1 } } } }); }
private static CompletionItem CreateCompletionItem( CompletionDetails completionDetails, BufferRange completionRange, int sortIndex) { string detailString = null; string documentationString = null; string labelString = completionDetails.ListItemText; if ((completionDetails.CompletionType == CompletionType.Variable) || (completionDetails.CompletionType == CompletionType.ParameterName)) { // Look for type encoded in the tooltip for parameters and variables. // Display PowerShell type names in [] to be consistent with PowerShell syntax // and now the debugger displays type names. var matches = Regex.Matches(completionDetails.ToolTipText, @"^(\[.+\])"); if ((matches.Count > 0) && (matches[0].Groups.Count > 1)) { detailString = matches[0].Groups[1].Value; } // PowerShell returns ListItemText for parameters & variables that is not prefixed // and it needs to be or the completion will not appear for these CompletionTypes. string prefix = (completionDetails.CompletionType == CompletionType.Variable) ? "$" : "-"; labelString = prefix + completionDetails.ListItemText; } else if ((completionDetails.CompletionType == CompletionType.Method) || (completionDetails.CompletionType == CompletionType.Property)) { // We have a raw signature for .NET members, heck let's display it. It's // better than nothing. documentationString = completionDetails.ToolTipText; } else if (completionDetails.CompletionType == CompletionType.Command) { // For Commands, let's extract the resolved command or the path for an exe // from the ToolTipText - if there is any ToolTipText. if (completionDetails.ToolTipText != null) { // Don't display ToolTipText if it is the same as the ListItemText. // Reject command syntax ToolTipText - it's too much to display as a detailString. if (!completionDetails.ListItemText.Equals( completionDetails.ToolTipText, StringComparison.OrdinalIgnoreCase) && !Regex.IsMatch(completionDetails.ToolTipText, @"^\s*" + completionDetails.ListItemText + @"\s+\[")) { detailString = completionDetails.ToolTipText; } } } // We want a special "sort order" for parameters that is not lexicographical. // Fortunately, PowerShell returns parameters in the preferred sort order by // default (with common params at the end). We just need to make sure the default // order also be the lexicographical order which we do by prefixig the ListItemText // with a leading 0's four digit index. This would not sort correctly for a list // > 999 parameters but surely we won't have so many items in the "parameter name" // completion list. Technically we don't need the ListItemText at all but it may come // in handy during debug. var sortText = (completionDetails.CompletionType == CompletionType.ParameterName) ? string.Format("{0:D3}{1}", sortIndex, completionDetails.ListItemText) : null; return new CompletionItem { InsertText = completionDetails.CompletionText, Label = labelString, Kind = MapCompletionKind(completionDetails.CompletionType), Detail = detailString, Documentation = documentationString, SortText = sortText, TextEdit = new TextEdit { NewText = completionDetails.CompletionText, Range = new Range { Start = new Position { Line = completionRange.Start.Line - 1, Character = completionRange.Start.Column - 1 }, End = new Position { Line = completionRange.End.Line - 1, Character = completionRange.End.Column - 1 } } } }; }