Пример #1
0
        public AndConstraint <CompletionItemAssertions> HaveInsertTextFormat(InsertTextFormat insertTextFormat, string because = "", params object[] reasonArgs)
        {
            Execute.Assertion.ForCondition(Subject.insertTextFormat == insertTextFormat)
            .BecauseOf(because, reasonArgs)
            .FailWith($"Expected '{Subject.label}' completion to have insert text format '{insertTextFormat}'{{reason}}, but it has '{Subject.insertTextFormat}'");

            return(new AndConstraint <CompletionItemAssertions>(this));
        }
Пример #2
0
 private static void SetTextEditInternal(CompletionItem item, Range range, InsertTextFormat format, string text)
 {
     item.InsertTextFormat = format;
     item.TextEdit         = new TextEdit
     {
         Range   = range,
         NewText = text
     };
 }
Пример #3
0
        public CompletionItemBuilder WithInsertText(string insertText)
        {
            this.AssertNoTextEdit();

            this.insertText       = insertText;
            this.insertTextFormat = InsertTextFormat.PlainText;
            this.insertTextMode   = InsertTextMode.AdjustIndentation;

            return(this);
        }
Пример #4
0
 private void SetTextEditInternal(Range range, InsertTextFormat format, string text)
 {
     this.insertTextFormat = format;
     this.textEdit         = new TextEdit
     {
         Range   = range,
         NewText = text
     };
     this.insertTextMode = InsertTextMode.AdjustIndentation;
 }
Пример #5
0
        public CompletionItemBuilder WithSnippet(string snippet)
        {
            this.AssertNoTextEdit();

            this.insertText       = snippet;
            this.insertTextFormat = InsertTextFormat.Snippet;
            this.insertTextMode   = InsertTextMode.AdjustIndentation;

            return(this);
        }
Пример #6
0
        public void SimpleTest(string expected)
        {
            var model  = new InsertTextFormat();
            var result = Fixture.SerializeObject(model);

            result.Should().Be(expected);

            var deresult = JsonConvert.DeserializeObject <InsertTextFormat>(expected);

            deresult.ShouldBeEquivalentTo(model);
        }
        public void SimpleTest(string expected)
        {
            var model  = new InsertTextFormat();
            var result = Fixture.SerializeObject(model);

            result.Should().Be(expected);

            var deresult = new Serializer(ClientVersion.Lsp3).DeserializeObject <InsertTextFormat>(expected);

            deresult.Should().BeEquivalentTo(model);
        }
 public abstract bool TryResolveInsertion(Position position, FormattingContext context, [NotNullWhen(true)] out TextEdit?edit, out InsertTextFormat format);
Пример #9
0
        public override bool TryResolveInsertion(Position position, FormattingContext context, out TextEdit edit, out InsertTextFormat format)
        {
            if (position is null)
            {
                throw new ArgumentNullException(nameof(position));
            }

            if (context is null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (!_optionsMonitor.CurrentValue.AutoClosingTags)
            {
                format = default;
                edit   = default;
                return(false);
            }

            var afterCloseAngleIndex = position.GetAbsoluteIndex(context.SourceText);

            if (!TryResolveAutoClosingBehavior(context, afterCloseAngleIndex, out var tagName, out var autoClosingBehavior))
            {
                format = default;
                edit   = default;
                return(false);
            }

            if (autoClosingBehavior == AutoClosingBehavior.EndTag)
            {
                format = InsertTextFormat.Snippet;
                edit   = new TextEdit()
                {
                    NewText = $"$0</{tagName}>",
                    Range   = new Range(position, position)
                };
            }
            else
            {
                Debug.Assert(autoClosingBehavior == AutoClosingBehavior.SelfClosing);

                format = InsertTextFormat.PlainText;

                // Need to replace the `>` with ' />$0' or '/>$0' depending on if there's prefixed whitespace.
                var insertionText     = char.IsWhiteSpace(context.SourceText[afterCloseAngleIndex - 2]) ? "/" : " /";
                var insertionPosition = new Position(position.Line, position.Character - 1);
                var insertionRange    = new Range(
                    start: insertionPosition,
                    end: insertionPosition);
                edit = new TextEdit()
                {
                    NewText = insertionText,
                    Range   = insertionRange
                };
            }

            return(true);
        }
Пример #10
0
        public override bool TryResolveInsertion(Position position, FormattingContext context, out TextEdit edit, out InsertTextFormat format)
        {
            if (position is null)
            {
                throw new ArgumentNullException(nameof(position));
            }

            if (context is null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (!IsAtAttributeValueStart(context, position))
            {
                format = default;
                edit   = default;
                return(false);
            }

            // We've just typed a Razor comment start.
            format = InsertTextFormat.Snippet;
            edit   = new TextEdit()
            {
                NewText = "\"$0\"",
                Range   = new Range(position, position)
            };

            return(true);
        }
 public abstract bool TryResolveInsertion(Position position, FormattingContext context, out TextEdit edit, out InsertTextFormat format);
Пример #12
0
            // Disabling because [NotNullWhen] is available in two Assemblies and causes warnings
#pragma warning disable CS8765 // Nullability of type of parameter doesn't match overridden member (possibly because of nullability attributes).
            public override bool TryResolveInsertion(Position position, FormattingContext context, out TextEdit?edit, out InsertTextFormat format)
#pragma warning restore CS8765 // Nullability of type of parameter doesn't match overridden member (possibly because of nullability attributes).
            {
                Called = true;
                edit   = ResolvedTextEdit !;
                format = default;
                return(_canResolve);
            }
Пример #13
0
        public override bool TryResolveInsertion(Position position, FormattingContext context, out TextEdit edit, out InsertTextFormat format)
        {
            if (position is null)
            {
                throw new ArgumentNullException(nameof(position));
            }

            if (context is null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (!_optionsMonitor.CurrentValue.AutoClosingTags)
            {
                // We currently only support auto-closing tags our onType formatter.
                format = default;
                edit   = default;
                return(false);
            }

            if (!IsAtTextTag(context, position))
            {
                format = default;
                edit   = default;
                return(false);
            }

            // This is a text tag.
            format = InsertTextFormat.Snippet;
            edit   = new TextEdit()
            {
                NewText = $"$0</{SyntaxConstants.TextTagName}>",
                Range   = new Range(position, position)
            };

            return(true);
        }
        private static CompletionItem CreateCompletionItem(
            CompletionDetails completionDetails,
            BufferRange completionRange,
            int sortIndex)
        {
            string           detailString        = null;
            string           documentationString = null;
            string           completionText      = completionDetails.CompletionText;
            InsertTextFormat insertTextFormat    = InsertTextFormat.PlainText;

            switch (completionDetails.CompletionType)
            {
            case CompletionType.Type:
            case CompletionType.Namespace:
            case CompletionType.ParameterValue:
            case CompletionType.Method:
            case CompletionType.Property:
                detailString = completionDetails.ToolTipText;
                break;

            case CompletionType.Variable:
            case 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 how 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;
                }
                // The comparison operators (-eq, -not, -gt, etc) are unfortunately fall into ParameterName
                // but they don't have a type associated to them. This allows those tooltips to show up.
                else if (!string.IsNullOrEmpty(completionDetails.ToolTipText))
                {
                    detailString = completionDetails.ToolTipText;
                }
                break;

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

                break;

            case CompletionType.Folder:
                // 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.
                if (EndsWithQuote(completionText))
                {
                    var sb = new StringBuilder(completionDetails.CompletionText)
                             .Replace(@"\", @"\\")
                             .Replace(@"}", @"\}")
                             .Replace(@"$", @"\$");
                    completionText   = sb.Insert(sb.Length - 1, "$0").ToString();
                    insertTextFormat = InsertTextFormat.Snippet;
                }

                break;
            }

            // 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
                        }
                    }
                }
            });
        }
Пример #15
0
 /// <summary>
 /// Initializes static members of the <see cref="TextFormatTypesQueryEngine"/> class.
 /// </summary>
 static TextFormatTypesQueryEngine()
 {
     _insertTextFormat = new StoredProcedures().InsertTextFormat;
 }
 public override bool TryResolveInsertion(Position position, FormattingContext context, out TextEdit edit, out InsertTextFormat format)
 {
     Called = true;
     edit   = ResolvedTextEdit;
     format = default;
     return(_canResolve);
 }
        public override bool TryResolveInsertion(Position position, FormattingContext context, out TextEdit edit, out InsertTextFormat format)
        {
            if (position is null)
            {
                throw new ArgumentNullException(nameof(position));
            }

            if (context is null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            var syntaxTree = context.CodeDocument.GetSyntaxTree();

            var absoluteIndex = position.GetAbsoluteIndex(context.SourceText);
            var change        = new SourceChange(absoluteIndex, 0, string.Empty);
            var owner         = syntaxTree.Root.LocateOwner(change);

            if (!IsAtEnterRuleLocation(owner))
            {
                format = default;
                edit   = default;
                return(false);
            }

            // We're currently at:
            // <someTag>
            // |</someTag>

            context.SourceText.GetLineAndOffset(owner.SpanStart, out var lineNumber, out _);

            var existingIndentation        = context.Indentations[lineNumber].ExistingIndentation;
            var existingIndentationString  = context.GetIndentationString(existingIndentation);
            var increasedIndentationString = context.GetIndentationLevelString(indentationLevel: 1);
            var innerIndentationString     = string.Concat(increasedIndentationString, existingIndentationString);

            // We mark start position at the beginning of the line in order to remove any pre-existing whitespace.
            var startPosition = new Position(position.Line, 0);

            format = InsertTextFormat.Snippet;
            edit   = new TextEdit()
            {
                NewText = $"{innerIndentationString}$0{Environment.NewLine}{existingIndentationString}",
                Range   = new Range(startPosition, position)
            };
            return(true);
        }
        internal static async Task <(IReadOnlyList <CompletionItem>, bool)> BuildCompletionItemsAsync(
            Document document,
            SourceText sourceText,
            long cacheId,
            int position,
            CSharpCompletionService completionService,
            CSharpCompletionList completions,
            TextSpan typedSpan,
            bool expectingImportedItems, bool isSuggestionMode)
        {
            var completionsBuilder            = new List <CompletionItem>(completions.Items.Length);
            var seenUnimportedCompletions     = false;
            var commitCharacterRuleCache      = new Dictionary <ImmutableArray <CharacterSetModificationRule>, IReadOnlyList <char> >();
            var commitCharacterRuleBuilder    = new HashSet <char>();
            var isOverrideOrPartialCompletion = completions.Items.Length > 0 &&
                                                completions.Items[0].GetProviderName() is OverrideCompletionProvider or PartialMethodCompletionProvider;

            for (int i = 0; i < completions.Items.Length; i++)
            {
                var    completion = completions.Items[i];
                string labelText  = completion.DisplayTextPrefix + completion.DisplayText + completion.DisplayTextSuffix;
                string?insertText;
                string?filterText = null;
                List <LinePositionSpanTextChange>?additionalTextEdits = null;
                InsertTextFormat insertTextFormat = InsertTextFormat.PlainText;
                TextSpan         changeSpan;
                string?          sortText;
                bool             hasAfterInsertStep = false;
                if (completion.IsComplexTextEdit)
                {
                    // The completion is somehow expensive. Currently, this one of two categories: import completion, or override/partial
                    // completion.
                    Debug.Assert(completion.GetProviderName() is OverrideCompletionProvider or PartialMethodCompletionProvider
                                 or TypeImportCompletionProvider or ExtensionMethodImportCompletionProvider);

                    changeSpan = typedSpan;

                    if (isOverrideOrPartialCompletion)
                    {
                        // For override and partial completion, we don't want to use the DisplayText as the insert text because they contain
                        // characters that will affect our ability to asynchronously resolve the change later.
                        insertText         = completion.FilterText;
                        sortText           = GetSortText(completion, labelText, expectingImportedItems);
                        hasAfterInsertStep = true;
                    }
                    else
                    {
                        insertText = completion.DisplayText;
                        sortText   = '1' + completion.SortText;
                        seenUnimportedCompletions = true;
                    }
                }
                else
                {
                    // For non-complex completions, just await the text edit. It's cheap enough that it doesn't impact our ability
                    // to pop completions quickly

                    // If the completion item is the misc project name, skip it.
                    if (completion.DisplayText == Configuration.OmniSharpMiscProjectName)
                    {
                        continue;
                    }

                    GetCompletionInfo(
                        sourceText,
                        position,
                        completion,
                        await completionService.GetChangeAsync(document, completion),
                        typedSpan,
                        labelText,
                        expectingImportedItems,
                        out insertText, out filterText, out sortText, out insertTextFormat, out changeSpan, out additionalTextEdits);
                }

                var commitCharacters = BuildCommitCharacters(completion.Rules.CommitCharacterRules, isSuggestionMode, commitCharacterRuleCache, commitCharacterRuleBuilder);

                completionsBuilder.Add(new CompletionItem
                {
                    Label               = labelText,
                    TextEdit            = GetChangeForTextAndSpan(insertText !, changeSpan, sourceText),
                    InsertTextFormat    = insertTextFormat,
                    AdditionalTextEdits = additionalTextEdits,
                    SortText            = sortText,
                    FilterText          = filterText,
                    Kind               = GetCompletionItemKind(completion.Tags),
                    Detail             = completion.InlineDescription,
                    Data               = (cacheId, i),
                    Preselect          = completion.Rules.SelectionBehavior == CompletionItemSelectionBehavior.HardSelection,
                    CommitCharacters   = commitCharacters,
                    HasAfterInsertStep = hasAfterInsertStep,
                });