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
                        }
                    }
                }
            };
        }