Esempio n. 1
0
        private bool TryGetOrAddDocument(AbsolutePath path, VersionedTextDocumentIdentifier documentIdentifier, TextDocumentContentChangeEvent[] changeEvents, out TextDocumentItem document)
        {
            if (!m_documents.TryGetValue(path, out document))
            {
                // The document is missing from the internal storage and the changeset is empty.
                // Can't do anything here.
                if (changeEvents.Length == 0)
                {
                    return(false);
                }

                lock (m_documents)
                {
                    if (!m_documents.TryGetValue(path, out document))
                    {
                        document = new TextDocumentItem()
                        {
                            Version = documentIdentifier.Version,
                            Uri     = documentIdentifier.Uri,
                            Text    = changeEvents.Last().Text,
                        };

                        document = m_documents.GetOrAdd(path, document);
                        return(true);
                    }
                }
            }

            return(true);
        }
Esempio n. 2
0
        public async Task ChangingCorrectDocumentToOneWithSyntaxErrorsReportsTheSyntaxErrors()
        {
            var source       = @"
method Multiply(x: int, y: int) returns (product: int)
  requires y >= 0 && x >= 0
  decreases y
  ensures product == x * y && product >= 0
{
  if y == 0 {
    product := 0;
  } else {
    var step := Multiply(x, y - 1);
    product := x + step;
  }
}".TrimStart();
            var documentItem = CreateTestDocument(source);

            _client.OpenDocument(documentItem);
            var reportAfterOpening = await _diagnosticReceiver.AwaitNextPublishDiagnostics(CancellationToken);

            var diagnosticsAfterOpening = reportAfterOpening.Diagnostics.ToArray();

            Assert.AreEqual(0, diagnosticsAfterOpening.Length);

            _client.DidChangeTextDocument(new DidChangeTextDocumentParams {
                TextDocument = new VersionedTextDocumentIdentifier {
                    Uri     = documentItem.Uri,
                    Version = documentItem.Version + 1
                },
                ContentChanges = new[] {
Esempio n. 3
0
        /// <summary>
        /// Notify that the document with a given path has changed.
        /// </summary>
        public void Change(AbsolutePath path, VersionedTextDocumentIdentifier documentIdentifier, TextDocumentContentChangeEvent[] changeEvents)
        {
            Contract.Requires(path.IsValid);
            Contract.Requires(changeEvents != null);

            if (changeEvents.Length == 0)
            {
                return;
            }

            // In some cases it is possible to get 'Change' event without getting 'Add' event.
            // In this case we consider the document as a new one.

            if (!TryGetOrAddDocument(path, documentIdentifier, changeEvents, out var document))
            {
                // Need to log this, IMO.
                return;
            }

            var version = documentIdentifier.Version;

            if (document.Version >= version)
            {
                return;
            }

            foreach (var ev in changeEvents)
            {
                Apply(document, ev);
            }

            document.Version = version;
            OnChanged(document);
        }
Esempio n. 4
0
        private static RazorCodeAction CreateFQNCodeAction(
            RazorCodeActionContext context,
            Diagnostic fqnDiagnostic,
            RazorCodeAction codeAction,
            string fullyQualifiedName)
        {
            var codeDocumentIdentifier = new VersionedTextDocumentIdentifier()
            {
                Uri = context.Request.TextDocument.Uri
            };

            var fqnTextEdit = new TextEdit()
            {
                NewText = fullyQualifiedName,
                Range   = fqnDiagnostic.Range
            };

            var fqnWorkspaceEditDocumentChange = new WorkspaceEditDocumentChange(new TextDocumentEdit()
            {
                TextDocument = codeDocumentIdentifier,
                Edits        = new[] { fqnTextEdit },
            });

            var fqnWorkspaceEdit = new WorkspaceEdit()
            {
                DocumentChanges = new[] { fqnWorkspaceEditDocumentChange }
            };

            return(new RazorCodeAction()
            {
                Title = codeAction.Title,
                Edit = fqnWorkspaceEdit
            });
        }
Esempio n. 5
0
        /// <summary>
        /// Returns the given edit for the specified file as WorkspaceEdit.
        /// Throws an ArgumentNullException if the given file or any of the given edits is null.
        /// </summary>
        private static WorkspaceEdit GetWorkspaceEdit(this FileContentManager file, params TextEdit[] edits)
        {
            if (file == null)
            {
                throw new ArgumentNullException(nameof(file));
            }
            if (edits == null || edits.Any(edit => edit == null))
            {
                throw new ArgumentNullException(nameof(edits));
            }

            var versionedFileId = new VersionedTextDocumentIdentifier {
                Uri = file.Uri, Version = 1
            };                                                                                         // setting version to null here won't work in VS Code ...

            return(new WorkspaceEdit
            {
                DocumentChanges = new[] { new TextDocumentEdit {
                                              TextDocument = versionedFileId, Edits = edits
                                          } },
                Changes = new Dictionary <string, TextEdit[]> {
                    { file.FileName.Value, edits }
                }
            });
        }
Esempio n. 6
0
        private static void TryAddNamespaceDirective(RazorCodeDocument codeDocument, Uri newComponentUri, List <WorkspaceEditDocumentChange> documentChanges)
        {
            var syntaxTree         = codeDocument.GetSyntaxTree();
            var namespaceDirective = syntaxTree.Root.DescendantNodes()
                                     .Where(n => n.Kind == SyntaxKind.RazorDirective)
                                     .Cast <RazorDirectiveSyntax>()
                                     .Where(n => n.DirectiveDescriptor == NamespaceDirective.Directive)
                                     .FirstOrDefault();

            if (namespaceDirective != null)
            {
                var documentIdentifier = new VersionedTextDocumentIdentifier {
                    Uri = newComponentUri
                };
                documentChanges.Add(new WorkspaceEditDocumentChange(new TextDocumentEdit
                {
                    TextDocument = documentIdentifier,
                    Edits        = new[]
                    {
                        new TextEdit()
                        {
                            NewText = namespaceDirective.GetContent(),
                            Range   = new Range(new Position(0, 0), new Position(0, 0)),
                        }
                    }
                }));
            }
        }
Esempio n. 7
0
            static async Task AddTextDocumentEdits <T>(
                ArrayBuilder <TextDocumentEdit> textDocumentEdits,
                ApplyChangesOperation applyChangesOperation,
                Solution solution,
                IEnumerable <DocumentId> changedDocuments,
                Func <DocumentId, T?> getNewDocumentFunc,
                Func <DocumentId, T?> getOldDocumentFunc,
                CancellationToken cancellationToken)
                where T : TextDocument
            {
                foreach (var docId in changedDocuments)
                {
                    var newDoc = getNewDocumentFunc(docId);
                    var oldDoc = getOldDocumentFunc(docId);

                    Contract.ThrowIfNull(oldDoc);
                    Contract.ThrowIfNull(newDoc);

                    var oldText = await oldDoc.GetTextAsync(cancellationToken).ConfigureAwait(false);

                    var newText = await newDoc.GetTextAsync(cancellationToken).ConfigureAwait(false);

                    var textChanges = newText.GetTextChanges(oldText);

                    var edits = textChanges.Select(tc => ProtocolConversions.TextChangeToTextEdit(tc, oldText)).ToArray();
                    var documentIdentifier = new VersionedTextDocumentIdentifier {
                        Uri = newDoc.GetURI()
                    };
                    textDocumentEdits.Add(new TextDocumentEdit {
                        TextDocument = documentIdentifier, Edits = edits.ToArray()
                    });
                }
            }
Esempio n. 8
0
        private static WorkspaceEditDocumentChange GenerateSingleUsingEditsAtTop(
            RazorCodeDocument codeDocument,
            VersionedTextDocumentIdentifier codeDocumentIdentifier,
            string newUsingNamespace)
        {
            // If we don't have usings, insert after the last namespace or page directive, which ever comes later
            var head           = new Position(1, 0);
            var syntaxTreeRoot = codeDocument.GetSyntaxTree().Root;
            var lastNamespaceOrPageDirective = syntaxTreeRoot
                                               .DescendantNodes()
                                               .Where(n => IsNamespaceOrPageDirective(n))
                                               .LastOrDefault();

            if (lastNamespaceOrPageDirective != null)
            {
                var lineIndex = GetLineIndexOrEnd(codeDocument, lastNamespaceOrPageDirective.Span.End - 1) + 1;
                head = new Position(lineIndex, 0);
            }

            // Insert all usings at the given point
            var range = new Range(head, head);

            return(new WorkspaceEditDocumentChange(new TextDocumentEdit
            {
                TextDocument = codeDocumentIdentifier,
                Edits = new[]
                {
                    new TextEdit()
                    {
                        NewText = string.Concat($"@using {newUsingNamespace}{Environment.NewLine}"),
                        Range = range,
                    }
                }
            }));
        }
Esempio n. 9
0
        internal static DidChangeTextDocumentParams GetChangedFileParams(string filename, TextDocumentContentChangeEvent[] changes)
        {
            var fileId = new VersionedTextDocumentIdentifier {
                Uri = GetUri(filename)
            };

            return(new DidChangeTextDocumentParams
            {
                TextDocument = fileId, ContentChanges = changes
            });
        }
        public override async Task <WorkspaceEdit> ResolveAsync(JObject data, CancellationToken cancellationToken)
        {
            if (data is null)
            {
                return(null);
            }

            var actionParams = data.ToObject <AddUsingsCodeActionParams>();

            if (actionParams is null)
            {
                return(null);
            }

            var path = actionParams.Uri.GetAbsoluteOrUNCPath();

            var document = await Task.Factory.StartNew(() =>
            {
                _documentResolver.TryResolveDocument(path, out var documentSnapshot);
                return(documentSnapshot);
            }, cancellationToken, TaskCreationOptions.None, _foregroundDispatcher.ForegroundScheduler).ConfigureAwait(false);

            if (document is null)
            {
                return(null);
            }

            var text = await document.GetTextAsync().ConfigureAwait(false);

            if (text is null)
            {
                return(null);
            }

            var codeDocument = await document.GetGeneratedOutputAsync().ConfigureAwait(false);

            if (codeDocument.IsUnsupported())
            {
                return(null);
            }

            if (!FileKinds.IsComponent(codeDocument.GetFileKind()))
            {
                return(null);
            }

            var codeDocumentIdentifier = new VersionedTextDocumentIdentifier()
            {
                Uri = actionParams.Uri
            };

            return(CreateAddUsingWorkspaceEdit(actionParams.Namespace, codeDocument, codeDocumentIdentifier));
        }
        public void UpdateBuffer(VersionedTextDocumentIdentifier id, int position, string newText, int charactersToRemove = 0)
        {
            if (!_buffers.TryGetValue(id.Uri, out Buffer buffer))
            {
                return;
            }

            if (charactersToRemove > 0)
            {
                buffer.Data.Remove(position, charactersToRemove);
            }

            buffer.Data.Insert(position, newText);
        }
        public void SimpleTest(string expected)
        {
            var model = new VersionedTextDocumentIdentifier {
                Uri     = new Uri("file:///abc/123.cs"),
                Version = 12
            };
            var result = Fixture.SerializeObject(model);

            result.Should().Be(expected);

            var deresult = new LspSerializer(ClientVersion.Lsp3).DeserializeObject <VersionedTextDocumentIdentifier>(expected);

            deresult.Should().BeEquivalentTo(model);
        }
Esempio n. 13
0
        private static WorkspaceEditDocumentChange GenerateSingleUsingEditsInterpolated(
            RazorCodeDocument codeDocument,
            VersionedTextDocumentIdentifier codeDocumentIdentifier,
            string newUsingNamespace,
            List <RazorUsingDirective> existingUsingDirectives)
        {
            var edits   = new List <TextEdit>();
            var newText = $"@using {newUsingNamespace}{Environment.NewLine}";

            foreach (var usingDirective in existingUsingDirectives)
            {
                // Skip System directives; if they're at the top we don't want to insert before them
                var usingDirectiveNamespace = usingDirective.Statement.ParsedNamespace;
                if (usingDirectiveNamespace.StartsWith("System", StringComparison.Ordinal))
                {
                    continue;
                }

                if (newUsingNamespace.CompareTo(usingDirectiveNamespace) < 0)
                {
                    var usingDirectiveLineIndex = codeDocument.Source.Lines.GetLocation(usingDirective.Node.Span.Start).LineIndex;
                    var head = new Position(usingDirectiveLineIndex, 0);
                    var edit = new TextEdit()
                    {
                        Range = new Range(head, head), NewText = newText
                    };
                    edits.Add(edit);
                    break;
                }
            }

            // If we haven't actually found a place to insert the using directive, do so at the end
            if (edits.Count == 0)
            {
                var endIndex  = existingUsingDirectives.Last().Node.Span.End;
                var lineIndex = GetLineIndexOrEnd(codeDocument, endIndex - 1) + 1;
                var head      = new Position(lineIndex, 0);
                var edit      = new TextEdit()
                {
                    Range = new Range(head, head), NewText = newText
                };
                edits.Add(edit);
            }

            return(new WorkspaceEditDocumentChange(new TextDocumentEdit()
            {
                TextDocument = codeDocumentIdentifier,
                Edits = edits,
            }));
        }
Esempio n. 14
0
        /// <summary>
        /// Returns the given edit for the specified file as WorkspaceEdit.
        /// </summary>
        private static WorkspaceEdit GetWorkspaceEdit(this FileContentManager file, params TextEdit[] edits)
        {
            var versionedFileId = new VersionedTextDocumentIdentifier {
                Uri = file.Uri, Version = 1
            };                                                                                         // setting version to null here won't work in VS Code ...

            return(new WorkspaceEdit
            {
                DocumentChanges = new[] { new TextDocumentEdit {
                                              TextDocument = versionedFileId, Edits = edits
                                          } },
                Changes = new Dictionary <string, TextEdit[]> {
                    { file.FileName, edits }
                }
            });
        }
Esempio n. 15
0
        internal void DidChangeTextDocument(VersionedTextDocumentIdentifier document, Container <TextDocumentContentChangeEvent> changes)
        {
            _logger.LogInformation($"Document changed {document.Uri.GetFileSystemPath()} ({document.Version})");

            Uri docUri = document.Uri;

            if (!Documents.ContainsKey(docUri))
            {
                return;
            }

            var doc = Documents[docUri];

            // We only handle a full document update right now
            doc.Document.Version = document.Version;
            doc.Document.Text    = changes.First().Text;
        }
Esempio n. 16
0
        public void AddEditsForCodeDocument(
            List <WorkspaceEditDocumentChange> documentChanges,
            IReadOnlyList <TagHelperDescriptor> originTagHelpers,
            string newName,
            DocumentUri uri,
            RazorCodeDocument codeDocument)
        {
            var documentIdentifier = new VersionedTextDocumentIdentifier {
                Uri = uri
            };
            var tagHelperElements = codeDocument.GetSyntaxTree().Root
                                    .DescendantNodes()
                                    .Where(n => n.Kind == SyntaxKind.MarkupTagHelperElement)
                                    .OfType <MarkupTagHelperElementSyntax>();

            for (var i = 0; i < originTagHelpers.Count; i++)
            {
                var editedName      = newName;
                var originTagHelper = originTagHelpers[i];
                if (originTagHelper?.IsComponentFullyQualifiedNameMatch() == true)
                {
                    // Fully qualified binding, our "new name" needs to be fully qualified.
                    if (!DefaultRazorTagHelperBinderPhase.ComponentDirectiveVisitor.TrySplitNamespaceAndType(originTagHelper.Name, out var namespaceSpan, out _))
                    {
                        return;
                    }

                    var namespaceString = originTagHelper.Name.Substring(namespaceSpan.Start, namespaceSpan.Length);

                    // The origin TagHelper was fully qualified so any fully qualified rename locations we find will need a fully qualified renamed edit.
                    editedName = $"{namespaceString}.{newName}";
                }

                foreach (var node in tagHelperElements)
                {
                    if (node is MarkupTagHelperElementSyntax tagHelperElement && BindingContainsTagHelper(originTagHelper, tagHelperElement.TagHelperInfo.BindingResult))
                    {
                        documentChanges.Add(new WorkspaceEditDocumentChange(new TextDocumentEdit
                        {
                            TextDocument = documentIdentifier,
                            Edits        = CreateEditsForMarkupTagHelperElement(tagHelperElement, codeDocument, editedName)
                        }));
                    }
                }
            }
        }
        private static WorkspaceEdit CreateRenameTagEdit(RazorCodeActionContext context, MarkupStartTagSyntax startTag, string newTagName)
        {
            var textEdits = new List <TextEdit>();
            var codeDocumentIdentifier = new VersionedTextDocumentIdentifier()
            {
                Uri = context.Request.TextDocument.Uri
            };

            var startTagTextEdit = new TextEdit
            {
                Range   = startTag.Name.GetRange(context.CodeDocument.Source),
                NewText = newTagName,
            };

            textEdits.Add(startTagTextEdit);

            var endTag = (startTag.Parent as MarkupElementSyntax).EndTag;

            if (endTag != null)
            {
                var endTagTextEdit = new TextEdit
                {
                    Range   = endTag.Name.GetRange(context.CodeDocument.Source),
                    NewText = newTagName,
                };

                textEdits.Add(endTagTextEdit);
            }

            return(new WorkspaceEdit
            {
                DocumentChanges = new List <WorkspaceEditDocumentChange>()
                {
                    new WorkspaceEditDocumentChange(
                        new TextDocumentEdit()
                    {
                        TextDocument = codeDocumentIdentifier,
                        Edits = textEdits,
                    }
                        )
                }
            });
        }
        public void AddEditsForCodeDocument(List <WorkspaceEditDocumentChange> documentChanges, TagHelperBinding originTagHelperBinding, string newName, Uri uri, RazorCodeDocument codeDocument)
        {
            var documentIdentifier = new VersionedTextDocumentIdentifier {
                Uri = uri
            };
            var tagHelperElements = codeDocument.GetSyntaxTree().Root
                                    .DescendantNodes()
                                    .Where(n => n.Kind == SyntaxKind.MarkupTagHelperElement)
                                    .OfType <MarkupTagHelperElementSyntax>();

            foreach (var node in tagHelperElements)
            {
                if (node is MarkupTagHelperElementSyntax tagHelperElement && BindingsMatch(originTagHelperBinding, tagHelperElement.TagHelperInfo.BindingResult))
                {
                    documentChanges.Add(new WorkspaceEditDocumentChange(new TextDocumentEdit
                    {
                        TextDocument = documentIdentifier,
                        Edits        = CreateEditsForMarkupTagHelperElement(tagHelperElement, codeDocument, newName)
                    }));
                }
            }
        }
Esempio n. 19
0
        public void DocumentTextChanged(FilePath fileName, string text, int version)
        {
            var textDocument = new VersionedTextDocumentIdentifier {
                uri     = fileName,
                version = version
            };

            var messageParams = new DidChangeTextDocumentParams {
                textDocument   = textDocument,
                contentChanges = new [] {
                    new TextDocumentContentChangeEvent {
                        text = text
                    }
                }
            };

            var notification = new NotificationMessage {
                method  = "textDocument/didChange",
                @params = messageParams
            };

            client.SendMessage(notification);
        }
 public void CreateBuffer(VersionedTextDocumentIdentifier id, string data)
 {
     _buffers[id.Uri] = new Buffer(id, data);
 }
Esempio n. 21
0
        public override async Task <WorkspaceEdit> ResolveAsync(JObject data, CancellationToken cancellationToken)
        {
            if (data is null)
            {
                return(null);
            }

            var actionParams = data.ToObject <AddUsingsCodeActionParams>();
            var path         = actionParams.Uri.GetAbsoluteOrUNCPath();

            var document = await Task.Factory.StartNew(() =>
            {
                _documentResolver.TryResolveDocument(path, out var documentSnapshot);
                return(documentSnapshot);
            }, cancellationToken, TaskCreationOptions.None, _foregroundDispatcher.ForegroundScheduler).ConfigureAwait(false);

            if (document is null)
            {
                return(null);
            }

            var text = await document.GetTextAsync().ConfigureAwait(false);

            if (text is null)
            {
                return(null);
            }

            var codeDocument = await document.GetGeneratedOutputAsync().ConfigureAwait(false);

            if (codeDocument.IsUnsupported())
            {
                return(null);
            }

            if (!FileKinds.IsComponent(codeDocument.GetFileKind()))
            {
                return(null);
            }

            var codeDocumentIdentifier = new VersionedTextDocumentIdentifier()
            {
                Uri = actionParams.Uri
            };
            var documentChanges = new List <WorkspaceEditDocumentChange>();

            /* The heuristic is as follows:
             *
             * - If no @using, @namespace, or @page directives are present, insert the statements at the top of the
             *   file in alphabetical order.
             * - If a @namespace or @page are present, the statements are inserted after the last line-wise in
             *   alphabetical order.
             * - If @using directives are present and alphabetized with System directives at the top, the statements
             *   will be placed in the correct locations according to that ordering.
             * - Otherwise it's kinda undefined; it's only geared to insert based on alphabetization.
             *
             * This is generally sufficient for our current situation (inserting a single @using statement to include a
             * component), however it has holes if we eventually use it for other purposes. If we want to deal with
             * that now I can come up with a more sophisticated heuristic (something along the lines of checking if
             * there's already an ordering, etc.).
             */
            var usingDirectives = FindUsingDirectives(codeDocument);

            if (usingDirectives.Count > 0)
            {
                // Interpolate based on existing @using statements
                var edits = GenerateSingleUsingEditsInterpolated(codeDocument, codeDocumentIdentifier, actionParams.Namespace, usingDirectives);
                documentChanges.Add(edits);
            }
            else
            {
                // Just throw them at the top
                var edits = GenerateSingleUsingEditsAtTop(codeDocument, codeDocumentIdentifier, actionParams.Namespace);
                documentChanges.Add(edits);
            }

            return(new WorkspaceEdit()
            {
                DocumentChanges = documentChanges
            });
        }
Esempio n. 22
0
        public override async Task <WorkspaceEdit> ResolveAsync(JObject data, CancellationToken cancellationToken)
        {
            if (data is null)
            {
                return(null);
            }

            var actionParams = data.ToObject <ExtractToCodeBehindCodeActionParams>();
            var path         = _filePathNormalizer.Normalize(actionParams.Uri.GetAbsoluteOrUNCPath());

            var document = await Task.Factory.StartNew(() =>
            {
                _documentResolver.TryResolveDocument(path, out var documentSnapshot);
                return(documentSnapshot);
            }, cancellationToken, TaskCreationOptions.None, _foregroundDispatcher.ForegroundScheduler).ConfigureAwait(false);

            if (document is null)
            {
                return(null);
            }

            var codeDocument = await document.GetGeneratedOutputAsync().ConfigureAwait(false);

            if (codeDocument.IsUnsupported())
            {
                return(null);
            }

            if (!FileKinds.IsComponent(codeDocument.GetFileKind()))
            {
                return(null);
            }

            var codeBehindPath = GenerateCodeBehindPath(path);
            var codeBehindUri  = new UriBuilder
            {
                Scheme = Uri.UriSchemeFile,
                Path   = codeBehindPath,
                Host   = string.Empty,
            }.Uri;

            var text = await document.GetTextAsync().ConfigureAwait(false);

            if (text is null)
            {
                return(null);
            }

            var className         = Path.GetFileNameWithoutExtension(path);
            var codeBlockContent  = text.GetSubTextString(new CodeAnalysis.Text.TextSpan(actionParams.ExtractStart, actionParams.ExtractEnd - actionParams.ExtractStart));
            var codeBehindContent = GenerateCodeBehindClass(className, codeBlockContent, codeDocument);

            var start       = codeDocument.Source.Lines.GetLocation(actionParams.RemoveStart);
            var end         = codeDocument.Source.Lines.GetLocation(actionParams.RemoveEnd);
            var removeRange = new Range(
                new Position(start.LineIndex, start.CharacterIndex),
                new Position(end.LineIndex, end.CharacterIndex));

            var codeDocumentIdentifier = new VersionedTextDocumentIdentifier {
                Uri = actionParams.Uri
            };
            var codeBehindDocumentIdentifier = new VersionedTextDocumentIdentifier {
                Uri = codeBehindUri
            };

            var documentChanges = new List <WorkspaceEditDocumentChange>
            {
                new WorkspaceEditDocumentChange(new CreateFile {
                    Uri = codeBehindUri.ToString()
                }),
                new WorkspaceEditDocumentChange(new TextDocumentEdit
                {
                    TextDocument = codeDocumentIdentifier,
                    Edits        = new[]
                    {
                        new TextEdit
                        {
                            NewText = string.Empty,
                            Range   = removeRange,
                        }
                    },
                }),
                new WorkspaceEditDocumentChange(new TextDocumentEdit
                {
                    TextDocument = codeBehindDocumentIdentifier,
                    Edits        = new[]
                    {
                        new TextEdit
                        {
                            NewText = codeBehindContent,
                            Range   = StartOfDocumentRange,
                        }
                    },
                })
            };

            return(new WorkspaceEdit
            {
                DocumentChanges = documentChanges,
            });
        }
        internal static WorkspaceEdit CreateAddUsingWorkspaceEdit(string @namespace, RazorCodeDocument codeDocument, VersionedTextDocumentIdentifier codeDocumentIdentifier)
        {
            /* The heuristic is as follows:
             *
             * - If no @using, @namespace, or @page directives are present, insert the statements at the top of the
             *   file in alphabetical order.
             * - If a @namespace or @page are present, the statements are inserted after the last line-wise in
             *   alphabetical order.
             * - If @using directives are present and alphabetized with System directives at the top, the statements
             *   will be placed in the correct locations according to that ordering.
             * - Otherwise it's kinda undefined; it's only geared to insert based on alphabetization.
             *
             * This is generally sufficient for our current situation (inserting a single @using statement to include a
             * component), however it has holes if we eventually use it for other purposes. If we want to deal with
             * that now I can come up with a more sophisticated heuristic (something along the lines of checking if
             * there's already an ordering, etc.).
             */
            var documentChanges = new List <WorkspaceEditDocumentChange>();
            var usingDirectives = FindUsingDirectives(codeDocument);

            if (usingDirectives.Count > 0)
            {
                // Interpolate based on existing @using statements
                var edits = GenerateSingleUsingEditsInterpolated(codeDocument, codeDocumentIdentifier, @namespace, usingDirectives);
                documentChanges.Add(edits);
            }
            else
            {
                // Just throw them at the top
                var edits = GenerateSingleUsingEditsAtTop(codeDocument, codeDocumentIdentifier, @namespace);
                documentChanges.Add(edits);
            }

            return(new WorkspaceEdit()
            {
                DocumentChanges = documentChanges
            });
        }
Esempio n. 24
0
 public void Update(VersionedTextDocumentIdentifier identifier)
 {
     _documentVersions.AddOrUpdate(identifier.Uri, identifier.Version ?? 0, (uri, i) => identifier.Version ?? 0);
 }
Esempio n. 25
0
        public async override Task <RazorCodeAction> ResolveAsync(
            CSharpCodeActionParams csharpParams,
            RazorCodeAction codeAction,
            CancellationToken cancellationToken)
        {
            if (csharpParams is null)
            {
                throw new ArgumentNullException(nameof(csharpParams));
            }

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

            var resolvedCodeAction = await ResolveCodeActionWithServerAsync(codeAction, cancellationToken);

            if (resolvedCodeAction.Edit?.DocumentChanges is null)
            {
                // Unable to resolve code action with server, return original code action
                return(codeAction);
            }

            if (resolvedCodeAction.Edit.DocumentChanges.Count() != 1)
            {
                // We don't yet support multi-document code actions, return original code action
                return(codeAction);
            }

            cancellationToken.ThrowIfCancellationRequested();

            var documentSnapshot = await Task.Factory.StartNew(() =>
            {
                _documentResolver.TryResolveDocument(csharpParams.RazorFileUri.GetAbsoluteOrUNCPath(), out var documentSnapshot);
                return(documentSnapshot);
            }, cancellationToken, TaskCreationOptions.None, _foregroundDispatcher.ForegroundScheduler).ConfigureAwait(false);

            if (documentSnapshot is null)
            {
                return(codeAction);
            }

            var documentChanged = resolvedCodeAction.Edit.DocumentChanges.First();

            if (!documentChanged.IsTextDocumentEdit)
            {
                // Only Text Document Edit changes are supported currently, return original code action
                return(codeAction);
            }

            cancellationToken.ThrowIfCancellationRequested();

            var csharpTextEdits = documentChanged.TextDocumentEdit.Edits.ToArray();

            // Remaps the text edits from the generated C# to the razor file,
            // as well as applying appropriate formatting.
            var formattedEdits = await _razorFormattingService.ApplyFormattedEditsAsync(
                csharpParams.RazorFileUri,
                documentSnapshot,
                RazorLanguageKind.CSharp,
                csharpTextEdits,
                DefaultFormattingOptions,
                cancellationToken,
                bypassValidationPasses : true);

            cancellationToken.ThrowIfCancellationRequested();

            var documentVersion = await Task.Factory.StartNew(() =>
            {
                _documentVersionCache.TryGetDocumentVersion(documentSnapshot, out var version);
                return(version);
            }, cancellationToken, TaskCreationOptions.None, _foregroundDispatcher.ForegroundScheduler).ConfigureAwait(false);

            var codeDocumentIdentifier = new VersionedTextDocumentIdentifier()
            {
                Uri     = csharpParams.RazorFileUri,
                Version = documentVersion.Value
            };

            resolvedCodeAction.Edit = new WorkspaceEdit()
            {
                DocumentChanges = new[] {
                    new WorkspaceEditDocumentChange(
                        new TextDocumentEdit()
                    {
                        TextDocument = codeDocumentIdentifier,
                        Edits        = formattedEdits,
                    }
                        )
                }
            };

            return(resolvedCodeAction);
        }
Esempio n. 26
0
 public static string GetDocumentPath(this VersionedTextDocumentIdentifier doc)
 {
     return(GetDocumentPath(doc.uri));
 }
 public Buffer(VersionedTextDocumentIdentifier id, string initialString)
 {
     Data.Append(initialString);
     Url     = id.Uri;
     Version = (int)id.Version;
 }
        public async override Task <CodeAction> ResolveAsync(
            CSharpCodeActionParams csharpParams,
            CodeAction codeAction,
            CancellationToken cancellationToken)
        {
            if (csharpParams is null)
            {
                throw new ArgumentNullException(nameof(csharpParams));
            }

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

            cancellationToken.ThrowIfCancellationRequested();

            var resolvedCodeAction = await ResolveCodeActionWithServerAsync(codeAction, cancellationToken).ConfigureAwait(false);

            if (resolvedCodeAction.Edit?.DocumentChanges is null)
            {
                // Unable to resolve code action with server, return original code action
                return(codeAction);
            }

            if (resolvedCodeAction.Edit.DocumentChanges.Count() != 1)
            {
                // We don't yet support multi-document code actions, return original code action
                return(codeAction);
            }

            var documentChanged = resolvedCodeAction.Edit.DocumentChanges.First();

            if (!documentChanged.IsTextDocumentEdit)
            {
                // Only Text Document Edit changes are supported currently, return original code action
                return(codeAction);
            }

            var addUsingTextEdit = documentChanged.TextDocumentEdit.Edits.FirstOrDefault();

            if (addUsingTextEdit is null)
            {
                // No text edit available
                return(codeAction);
            }

            if (!AddUsingsCodeActionProviderFactory.TryExtractNamespace(addUsingTextEdit.NewText, out var @namespace))
            {
                // Invalid text edit, missing namespace
                return(codeAction);
            }

            var documentSnapshot = await Task.Factory.StartNew(() =>
            {
                _documentResolver.TryResolveDocument(csharpParams.RazorFileUri.GetAbsoluteOrUNCPath(), out var documentSnapshot);
                return(documentSnapshot);
            }, cancellationToken, TaskCreationOptions.None, _foregroundDispatcher.ForegroundScheduler).ConfigureAwait(false);

            if (documentSnapshot is null)
            {
                return(codeAction);
            }

            var text = await documentSnapshot.GetTextAsync().ConfigureAwait(false);

            if (text is null)
            {
                return(null);
            }

            var codeDocument = await documentSnapshot.GetGeneratedOutputAsync().ConfigureAwait(false);

            if (codeDocument.IsUnsupported())
            {
                return(null);
            }

            var documentVersion = await Task.Factory.StartNew(() =>
            {
                _documentVersionCache.TryGetDocumentVersion(documentSnapshot, out var version);
                return(version);
            }, cancellationToken, TaskCreationOptions.None, _foregroundDispatcher.ForegroundScheduler).ConfigureAwait(false);

            var codeDocumentIdentifier = new VersionedTextDocumentIdentifier()
            {
                Uri     = csharpParams.RazorFileUri,
                Version = documentVersion.Value
            };

            resolvedCodeAction.Edit = AddUsingsCodeActionResolver.CreateAddUsingWorkspaceEdit(@namespace, codeDocument, codeDocumentIdentifier);

            return(resolvedCodeAction);
        }
        public async override Task <CodeAction> ResolveAsync(
            CSharpCodeActionParams csharpParams,
            CodeAction codeAction,
            CancellationToken cancellationToken)
        {
            if (csharpParams is null)
            {
                throw new ArgumentNullException(nameof(csharpParams));
            }

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

            cancellationToken.ThrowIfCancellationRequested();

            var resolvedCodeAction = await ResolveCodeActionWithServerAsync(codeAction, cancellationToken).ConfigureAwait(false);

            if (resolvedCodeAction.Edit?.DocumentChanges is null)
            {
                // Unable to resolve code action with server, return original code action
                return(codeAction);
            }

            if (resolvedCodeAction.Edit.DocumentChanges.Count() != 1)
            {
                // We don't yet support multi-document code actions, return original code action
                Debug.Fail($"Encountered an unsupported multi-document code action edit with ${codeAction.Title}.");
                return(codeAction);
            }

            var documentChanged = resolvedCodeAction.Edit.DocumentChanges.First();

            if (!documentChanged.IsTextDocumentEdit)
            {
                // Only Text Document Edit changes are supported currently, return original code action
                return(codeAction);
            }

            var textEdit = documentChanged.TextDocumentEdit.Edits.FirstOrDefault();

            if (textEdit is null)
            {
                // No text edit available
                return(codeAction);
            }

            var(documentSnapshot, documentVersion) = await Task.Factory.StartNew(() =>
            {
                _documentResolver.TryResolveDocument(csharpParams.RazorFileUri.ToUri().GetAbsoluteOrUNCPath(), out var documentSnapshot);

                _documentVersionCache.TryGetDocumentVersion(documentSnapshot, out var version);

                return(documentSnapshot, version);
            }, cancellationToken, TaskCreationOptions.None, _foregroundDispatcher.ForegroundScheduler).ConfigureAwait(false);

            if (documentSnapshot is null)
            {
                return(codeAction);
            }

            var codeDocument = await documentSnapshot.GetGeneratedOutputAsync().ConfigureAwait(false);

            if (codeDocument.IsUnsupported())
            {
                return(codeAction);
            }

            if (!_documentMappingService.TryMapFromProjectedDocumentRange(codeDocument, textEdit.Range, MappingBehavior.Inclusive, out var originalRange))
            {
                // Text edit failed to map
                return(codeAction);
            }

            textEdit.Range = originalRange;

            var codeDocumentIdentifier = new VersionedTextDocumentIdentifier()
            {
                Uri     = csharpParams.RazorFileUri,
                Version = documentVersion
            };

            resolvedCodeAction.Edit = new WorkspaceEdit()
            {
                DocumentChanges = new[] {
                    new WorkspaceEditDocumentChange(
                        new TextDocumentEdit()
                    {
                        TextDocument = codeDocumentIdentifier,
                        Edits        = new[] { textEdit },
                    }
                        )
                }
            };

            return(resolvedCodeAction);
        }