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); }
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[] {
/// <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); }
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 }); }
/// <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 } } }); }
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)), } } })); } }
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() }); } }
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, } } })); }
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); }
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, })); }
/// <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 } } }); }
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; }
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) })); } } }
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); }
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 }); }
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 }); }
public void Update(VersionedTextDocumentIdentifier identifier) { _documentVersions.AddOrUpdate(identifier.Uri, identifier.Version ?? 0, (uri, i) => identifier.Version ?? 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); }
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); }