From 4c21750d09ed4fd8357e58d929574c4f04143b6a Mon Sep 17 00:00:00 2001 From: Ravi Chande Date: Wed, 8 Nov 2017 11:02:40 -0800 Subject: [PATCH 01/12] Support Rename and Open DocumentOperation If a CodeAction returns a RenameDocumentOperation, use reflection to extract the file rename information and apply the requested changes to the workspace. Enable host support by returning new versions of ModifiedFileRequests that indicate the files have been renamed, or in the case of OpenDocumentOperation, opened. --- .../Models/v1/ModifiedFileResponse.cs | 32 ++++++++++- .../Services/Refactoring/RenameService.cs | 2 +- .../Refactoring/V2/RunCodeActionService.cs | 42 +++++++++++++- src/OmniSharp.Roslyn/OmniSharpWorkspace.cs | 57 +++++++++++++++++++ .../CodeActionsV2Facts.cs | 17 ++++++ 5 files changed, 146 insertions(+), 4 deletions(-) diff --git a/src/OmniSharp.Abstractions/Models/v1/ModifiedFileResponse.cs b/src/OmniSharp.Abstractions/Models/v1/ModifiedFileResponse.cs index 2afe8ead5c..00ea152820 100644 --- a/src/OmniSharp.Abstractions/Models/v1/ModifiedFileResponse.cs +++ b/src/OmniSharp.Abstractions/Models/v1/ModifiedFileResponse.cs @@ -1,18 +1,48 @@ using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; namespace OmniSharp.Models { + public enum FileModificationType + { + Modified, + Opened, + Renamed + } + public class ModifiedFileResponse { public ModifiedFileResponse() { } - public ModifiedFileResponse(string fileName) + public ModifiedFileResponse(string fileName, FileModificationType type) { FileName = fileName; + ModificationType = type; } public string FileName { get; set; } public string Buffer { get; set; } public IEnumerable Changes { get; set; } + [JsonConverter(typeof(StringEnumConverter))] + public FileModificationType ModificationType { get; } + } + + public class RenamedFileResponse : ModifiedFileResponse + { + public RenamedFileResponse(string fileName, string newFileName) + : base(fileName, FileModificationType.Renamed) + { + NewFileName = newFileName; + } + + public string NewFileName { get; } + } + + public class OpenFileResponse : ModifiedFileResponse + { + public OpenFileResponse(string fileName) : base(fileName, FileModificationType.Opened) + { + } } } diff --git a/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/RenameService.cs b/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/RenameService.cs index 8b136b0761..2f81e2b6d7 100644 --- a/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/RenameService.cs +++ b/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/RenameService.cs @@ -61,7 +61,7 @@ public async Task Handle(RenameRequest request) if (!changes.TryGetValue(changedDocument.FilePath, out var modifiedFileResponse)) { - modifiedFileResponse = new ModifiedFileResponse(changedDocument.FilePath); + modifiedFileResponse = new ModifiedFileResponse(changedDocument.FilePath, FileModificationType.Modified); changes[changedDocument.FilePath] = modifiedFileResponse; } diff --git a/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/V2/RunCodeActionService.cs b/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/V2/RunCodeActionService.cs index ee7ff601e6..a09fc8a105 100644 --- a/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/V2/RunCodeActionService.cs +++ b/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/V2/RunCodeActionService.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Composition; using System.IO; @@ -57,6 +58,20 @@ public override async Task Handle(RunCodeActionRequest re changes.AddRange(fileChanges); solution = applyChangesOperation.ChangedSolution; } + else if (IsRenameDocumentOperation(o, out var originalDocumentId, out var newDocumentId, out var newFileName)) + { + var originalDocument = solution.GetDocument(originalDocumentId); + string newFilePath = GetNewFilePath(newFileName, originalDocument.FilePath); + var text = await originalDocument.GetTextAsync(); + var temp = solution.RemoveDocument(originalDocumentId); + solution = temp.AddDocument(newDocumentId, newFileName, text, originalDocument.Folders, newFilePath); + changes.Add(new RenamedFileResponse(originalDocument.FilePath, newFilePath)); + } + else if (o is OpenDocumentOperation openDocumentOperation) + { + var document = solution.GetDocument(openDocumentOperation.DocumentId); + changes.Add(new OpenFileResponse(document.FilePath)); + } } if (request.ApplyTextChanges) @@ -71,6 +86,29 @@ public override async Task Handle(RunCodeActionRequest re }; } + private static string GetNewFilePath(string newFileName, string currentFilePath) + { + var directory = Path.GetDirectoryName(currentFilePath); + return Path.Combine(directory, newFileName); + } + + bool IsRenameDocumentOperation(CodeActionOperation o, out DocumentId oldDocumentId, out DocumentId newDocumentId, out string name) + { + var type = Type.GetType("Microsoft.CodeAnalysis.CodeActions.RenameDocumentOperation, Microsoft.CodeAnalysis.Workspaces, Version=2.4.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"); + if (o.GetType() == type) + { + oldDocumentId = (DocumentId)type.GetField("_oldDocumentId", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).GetValue(o); + newDocumentId = (DocumentId)type.GetField("_newDocumentId", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).GetValue(o); + name = (string)type.GetField("_newFileName", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).GetValue(o); + return true; + } + + oldDocumentId = default(DocumentId); + newDocumentId = default(DocumentId); + name = null; + return false; + } + private async Task> GetFileChangesAsync(Solution newSolution, Solution oldSolution, string directory, bool wantTextChanges) { var filePathToResponseMap = new Dictionary(); @@ -88,7 +126,7 @@ private async Task> GetFileChangesAsync(Soluti ? Path.Combine(directory, newDocument.Name) : newDocument.FilePath; - var modifiedFileResponse = new ModifiedFileResponse(newFilePath) + var modifiedFileResponse = new ModifiedFileResponse(newFilePath, FileModificationType.Modified) { Changes = new[] { new LinePositionSpanTextChange @@ -138,7 +176,7 @@ private async Task> GetFileChangesAsync(Soluti if (!filePathToResponseMap.TryGetValue(filePath, out var modifiedFileResponse)) { - modifiedFileResponse = new ModifiedFileResponse(filePath); + modifiedFileResponse = new ModifiedFileResponse(filePath, FileModificationType.Modified); filePathToResponseMap[filePath] = modifiedFileResponse; } diff --git a/src/OmniSharp.Roslyn/OmniSharpWorkspace.cs b/src/OmniSharp.Roslyn/OmniSharpWorkspace.cs index 89ca4a75c6..026ed94a05 100644 --- a/src/OmniSharp.Roslyn/OmniSharpWorkspace.cs +++ b/src/OmniSharp.Roslyn/OmniSharpWorkspace.cs @@ -1,6 +1,9 @@ +using System; using System.Collections.Generic; using System.Composition; +using System.IO; using System.Linq; +using System.Text; using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Text; @@ -142,5 +145,59 @@ public override bool CanApplyChange(ApplyChangesKind feature) { return true; } + + protected override void ApplyDocumentRemoved(DocumentId documentId) + { + var document = this.CurrentSolution.GetDocument(documentId); + if (document != null) + { + DeleteDocumentFile(document.Id, document.FilePath); + this.OnDocumentRemoved(documentId); + } + } + + private void DeleteDocumentFile(DocumentId id, string filePath) + { + try + { + File.Delete(filePath); + } + catch (IOException) { } + catch (NotSupportedException) { } + catch (UnauthorizedAccessException) { } + } + + protected override void ApplyDocumentAdded(DocumentInfo info, SourceText text) + { + var project = this.CurrentSolution.GetProject(info.Id.ProjectId); + var fullPath = info.FilePath; + + this.OnDocumentAdded(info); + + if (text != null) + { + this.SaveDocumentText(info.Id, fullPath, text, text.Encoding ?? Encoding.UTF8); + } + } + + private void SaveDocumentText(DocumentId id, string fullPath, SourceText newText, Encoding encoding) + { + try + { + var dir = Path.GetDirectoryName(fullPath); + if (!Directory.Exists(dir)) + { + Directory.CreateDirectory(dir); + } + + using (var writer = new StreamWriter(fullPath, append: false, encoding: encoding)) + { + newText.Write(writer); + } + } + catch (IOException) + { + } + } } } diff --git a/tests/OmniSharp.Roslyn.CSharp.Tests/CodeActionsV2Facts.cs b/tests/OmniSharp.Roslyn.CSharp.Tests/CodeActionsV2Facts.cs index 0641b12358..7cd0d3dff3 100644 --- a/tests/OmniSharp.Roslyn.CSharp.Tests/CodeActionsV2Facts.cs +++ b/tests/OmniSharp.Roslyn.CSharp.Tests/CodeActionsV2Facts.cs @@ -2,6 +2,7 @@ using System.IO; using System.Linq; using System.Threading.Tasks; +using OmniSharp.Models; using OmniSharp.Models.V2; using OmniSharp.Roslyn.CSharp.Services.Refactoring.V2; using TestUtility; @@ -128,6 +129,22 @@ private static void NewMethod() AssertIgnoringIndent(expected, response.Changes.First().Buffer); } + [Fact] + public async Task Can_send_rename_and_fileOpen_responses_when_codeAction_renames_file() + { + const string code = +@"public class [||]CCC +{ +}"; + + var response = await RunRefactoringAsync(code, "Rename file to CCC.cs"); + var changes = response.Changes.ToArray(); + Assert.Equal(2, changes.Length); + Assert.Equal(FileModificationType.Renamed, changes[0].ModificationType); + Assert.Contains("CCC.cs", ((RenamedFileResponse)changes[0]).NewFileName); + Assert.Equal(FileModificationType.Opened, changes[1].ModificationType); + } + private static void AssertIgnoringIndent(string expected, string actual) { Assert.Equal(TrimLines(expected), TrimLines(actual), false, true, true); From 7c478a4d8c9c9efa7bf7bdb473e08e360b7e5f05 Mon Sep 17 00:00:00 2001 From: Ravi Chande Date: Fri, 10 Nov 2017 10:16:09 -0800 Subject: [PATCH 02/12] Make response factoring clearer --- .../v1/FileOperation/FileModificationType.cs | 9 ++++ .../v1/FileOperation/FileOperationResponse.cs | 19 ++++++++ .../v1/FileOperation/ModifiedFileResponse.cs | 15 ++++++ .../v1/FileOperation/OpenFileResponse.cs | 9 ++++ .../v1/FileOperation/RenamedFileResponse.cs | 13 +++++ .../Models/v1/ModifiedFileResponse.cs | 48 ------------------- .../Models/v2/RunCodeActionResponse.cs | 2 +- .../Services/Refactoring/RenameService.cs | 2 +- .../Refactoring/V2/RunCodeActionService.cs | 6 +-- .../CodeActionsV2Facts.cs | 4 +- 10 files changed, 72 insertions(+), 55 deletions(-) create mode 100644 src/OmniSharp.Abstractions/Models/v1/FileOperation/FileModificationType.cs create mode 100644 src/OmniSharp.Abstractions/Models/v1/FileOperation/FileOperationResponse.cs create mode 100644 src/OmniSharp.Abstractions/Models/v1/FileOperation/ModifiedFileResponse.cs create mode 100644 src/OmniSharp.Abstractions/Models/v1/FileOperation/OpenFileResponse.cs create mode 100644 src/OmniSharp.Abstractions/Models/v1/FileOperation/RenamedFileResponse.cs delete mode 100644 src/OmniSharp.Abstractions/Models/v1/ModifiedFileResponse.cs diff --git a/src/OmniSharp.Abstractions/Models/v1/FileOperation/FileModificationType.cs b/src/OmniSharp.Abstractions/Models/v1/FileOperation/FileModificationType.cs new file mode 100644 index 0000000000..2b60923b41 --- /dev/null +++ b/src/OmniSharp.Abstractions/Models/v1/FileOperation/FileModificationType.cs @@ -0,0 +1,9 @@ +namespace OmniSharp.Models +{ + public enum FileModificationType + { + Modified, + Opened, + Renamed + } +} diff --git a/src/OmniSharp.Abstractions/Models/v1/FileOperation/FileOperationResponse.cs b/src/OmniSharp.Abstractions/Models/v1/FileOperation/FileOperationResponse.cs new file mode 100644 index 0000000000..972baffc41 --- /dev/null +++ b/src/OmniSharp.Abstractions/Models/v1/FileOperation/FileOperationResponse.cs @@ -0,0 +1,19 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace OmniSharp.Models +{ + public abstract class FileOperationResponse + { + public FileOperationResponse(string fileName, FileModificationType type) + { + FileName = fileName; + ModificationType = type; + } + + public string FileName { get; } + + [JsonConverter(typeof(StringEnumConverter))] + public FileModificationType ModificationType { get; } + } +} diff --git a/src/OmniSharp.Abstractions/Models/v1/FileOperation/ModifiedFileResponse.cs b/src/OmniSharp.Abstractions/Models/v1/FileOperation/ModifiedFileResponse.cs new file mode 100644 index 0000000000..a462545b48 --- /dev/null +++ b/src/OmniSharp.Abstractions/Models/v1/FileOperation/ModifiedFileResponse.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace OmniSharp.Models +{ + public class ModifiedFileResponse : FileOperationResponse + { + public ModifiedFileResponse(string fileName) + : base(fileName, FileModificationType.Modified) + { + } + + public string Buffer { get; set; } + public IEnumerable Changes { get; set; } + } +} diff --git a/src/OmniSharp.Abstractions/Models/v1/FileOperation/OpenFileResponse.cs b/src/OmniSharp.Abstractions/Models/v1/FileOperation/OpenFileResponse.cs new file mode 100644 index 0000000000..0cdd23e530 --- /dev/null +++ b/src/OmniSharp.Abstractions/Models/v1/FileOperation/OpenFileResponse.cs @@ -0,0 +1,9 @@ +namespace OmniSharp.Models +{ + public class OpenFileResponse : FileOperationResponse + { + public OpenFileResponse(string fileName) : base(fileName, FileModificationType.Opened) + { + } + } +} diff --git a/src/OmniSharp.Abstractions/Models/v1/FileOperation/RenamedFileResponse.cs b/src/OmniSharp.Abstractions/Models/v1/FileOperation/RenamedFileResponse.cs new file mode 100644 index 0000000000..cba7431c9d --- /dev/null +++ b/src/OmniSharp.Abstractions/Models/v1/FileOperation/RenamedFileResponse.cs @@ -0,0 +1,13 @@ +namespace OmniSharp.Models +{ + public class RenamedFileResponse : FileOperationResponse + { + public RenamedFileResponse(string fileName, string newFileName) + : base(fileName, FileModificationType.Renamed) + { + NewFileName = newFileName; + } + + public string NewFileName { get; } + } +} diff --git a/src/OmniSharp.Abstractions/Models/v1/ModifiedFileResponse.cs b/src/OmniSharp.Abstractions/Models/v1/ModifiedFileResponse.cs deleted file mode 100644 index 00ea152820..0000000000 --- a/src/OmniSharp.Abstractions/Models/v1/ModifiedFileResponse.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System.Collections.Generic; -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; - -namespace OmniSharp.Models -{ - public enum FileModificationType - { - Modified, - Opened, - Renamed - } - - public class ModifiedFileResponse - { - public ModifiedFileResponse() { } - - public ModifiedFileResponse(string fileName, FileModificationType type) - { - FileName = fileName; - ModificationType = type; - } - - public string FileName { get; set; } - public string Buffer { get; set; } - public IEnumerable Changes { get; set; } - [JsonConverter(typeof(StringEnumConverter))] - public FileModificationType ModificationType { get; } - } - - public class RenamedFileResponse : ModifiedFileResponse - { - public RenamedFileResponse(string fileName, string newFileName) - : base(fileName, FileModificationType.Renamed) - { - NewFileName = newFileName; - } - - public string NewFileName { get; } - } - - public class OpenFileResponse : ModifiedFileResponse - { - public OpenFileResponse(string fileName) : base(fileName, FileModificationType.Opened) - { - } - } -} diff --git a/src/OmniSharp.Abstractions/Models/v2/RunCodeActionResponse.cs b/src/OmniSharp.Abstractions/Models/v2/RunCodeActionResponse.cs index 76a41fa0fc..a08557543d 100644 --- a/src/OmniSharp.Abstractions/Models/v2/RunCodeActionResponse.cs +++ b/src/OmniSharp.Abstractions/Models/v2/RunCodeActionResponse.cs @@ -4,6 +4,6 @@ namespace OmniSharp.Models.V2 { public class RunCodeActionResponse { - public IEnumerable Changes { get; set; } + public IEnumerable Changes { get; set; } } } diff --git a/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/RenameService.cs b/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/RenameService.cs index 2f81e2b6d7..8b136b0761 100644 --- a/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/RenameService.cs +++ b/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/RenameService.cs @@ -61,7 +61,7 @@ public async Task Handle(RenameRequest request) if (!changes.TryGetValue(changedDocument.FilePath, out var modifiedFileResponse)) { - modifiedFileResponse = new ModifiedFileResponse(changedDocument.FilePath, FileModificationType.Modified); + modifiedFileResponse = new ModifiedFileResponse(changedDocument.FilePath); changes[changedDocument.FilePath] = modifiedFileResponse; } diff --git a/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/V2/RunCodeActionService.cs b/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/V2/RunCodeActionService.cs index a09fc8a105..5a6a98f452 100644 --- a/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/V2/RunCodeActionService.cs +++ b/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/V2/RunCodeActionService.cs @@ -46,7 +46,7 @@ public override async Task Handle(RunCodeActionRequest re var operations = await availableAction.GetOperationsAsync(CancellationToken.None); var solution = this.Workspace.CurrentSolution; - var changes = new List(); + var changes = new List(); var directory = Path.GetDirectoryName(request.FileName); foreach (var o in operations) @@ -126,7 +126,7 @@ private async Task> GetFileChangesAsync(Soluti ? Path.Combine(directory, newDocument.Name) : newDocument.FilePath; - var modifiedFileResponse = new ModifiedFileResponse(newFilePath, FileModificationType.Modified) + var modifiedFileResponse = new ModifiedFileResponse(newFilePath) { Changes = new[] { new LinePositionSpanTextChange @@ -176,7 +176,7 @@ private async Task> GetFileChangesAsync(Soluti if (!filePathToResponseMap.TryGetValue(filePath, out var modifiedFileResponse)) { - modifiedFileResponse = new ModifiedFileResponse(filePath, FileModificationType.Modified); + modifiedFileResponse = new ModifiedFileResponse(filePath); filePathToResponseMap[filePath] = modifiedFileResponse; } diff --git a/tests/OmniSharp.Roslyn.CSharp.Tests/CodeActionsV2Facts.cs b/tests/OmniSharp.Roslyn.CSharp.Tests/CodeActionsV2Facts.cs index 7cd0d3dff3..26d833d7ac 100644 --- a/tests/OmniSharp.Roslyn.CSharp.Tests/CodeActionsV2Facts.cs +++ b/tests/OmniSharp.Roslyn.CSharp.Tests/CodeActionsV2Facts.cs @@ -80,7 +80,7 @@ public class c {public c() {Guid.NewGuid();}}"; public class c {public c() {Guid.NewGuid();}}"; var response = await RunRefactoringAsync(code, "Remove Unnecessary Usings"); - AssertIgnoringIndent(expected, response.Changes.First().Buffer); + AssertIgnoringIndent(expected, ((ModifiedFileResponse)response.Changes.First()).Buffer); } [Fact] @@ -126,7 +126,7 @@ private static void NewMethod() }"; var response = await RunRefactoringAsync(code, "Extract Method"); - AssertIgnoringIndent(expected, response.Changes.First().Buffer); + AssertIgnoringIndent(expected, ((ModifiedFileResponse)response.Changes.First()).Buffer); } [Fact] From fe2cf016ddfd9cd19a18b87919b0eef484fe51aa Mon Sep 17 00:00:00 2001 From: Ravi Chande Date: Mon, 13 Nov 2017 08:55:40 -0800 Subject: [PATCH 03/12] CR feedback: improve reflection code --- .../Utilities/ReflectionExtensions.cs | 26 +++++++++++++++++ .../Refactoring/V2/RunCodeActionService.cs | 28 +++++++++++++++---- 2 files changed, 48 insertions(+), 6 deletions(-) diff --git a/src/OmniSharp.Abstractions/Utilities/ReflectionExtensions.cs b/src/OmniSharp.Abstractions/Utilities/ReflectionExtensions.cs index e664d991f2..3379121827 100644 --- a/src/OmniSharp.Abstractions/Utilities/ReflectionExtensions.cs +++ b/src/OmniSharp.Abstractions/Utilities/ReflectionExtensions.cs @@ -162,5 +162,31 @@ public static object InvokeStatic(this Lazy lazyMethodInfo, object[] { return lazyMethodInfo.InvokeStatic(args); } + + public static Lazy LazyGetField(this Lazy lazyType, string fieldName, BindingFlags bindingFlags) + { + if (lazyType == null) + { + throw new ArgumentNullException(nameof(lazyType)); + } + + return new Lazy(() => + { + var type = lazyType.Value; + var field = type.GetField(fieldName, bindingFlags); + + if (field == null) + { + throw new InvalidOperationException($"Could not get method '{fieldName}' on type '{type.FullName}'"); + } + + return field; + }); + } + + public static T GetValue(this Lazy lazyFieldInfo, object o) + { + return (T)lazyFieldInfo.Value.GetValue(o); + } } } diff --git a/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/V2/RunCodeActionService.cs b/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/V2/RunCodeActionService.cs index 5a6a98f452..11b67b4958 100644 --- a/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/V2/RunCodeActionService.cs +++ b/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/V2/RunCodeActionService.cs @@ -3,6 +3,7 @@ using System.Composition; using System.IO; using System.Linq; +using System.Reflection; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; @@ -13,7 +14,7 @@ using OmniSharp.Roslyn.CSharp.Services.CodeActions; using OmniSharp.Roslyn.Utilities; using OmniSharp.Services; - +using OmniSharp.Utilities; using RunCodeActionRequest = OmniSharp.Models.V2.RunCodeActionRequest; using RunCodeActionResponse = OmniSharp.Models.V2.RunCodeActionResponse; @@ -22,14 +23,30 @@ namespace OmniSharp.Roslyn.CSharp.Services.Refactoring.V2 [OmniSharpHandler(OmniSharpEndpoints.V2.RunCodeAction, LanguageNames.CSharp)] public class RunCodeActionService : BaseCodeActionService { + private readonly IAssemblyLoader _loader; + private readonly Lazy _workspaceAssembly; + private readonly Lazy _renameDocumentOperation; + private readonly Lazy _oldDocumentId; + private readonly Lazy _newDocumentId; + private readonly Lazy _newFileName; + + private const string RenameDocumentOperation = "Microsoft.CodeAnalysis.CodeActions.RenameDocumentOperation"; + [ImportingConstructor] public RunCodeActionService( + IAssemblyLoader loader, OmniSharpWorkspace workspace, CodeActionHelper helper, [ImportMany] IEnumerable providers, ILoggerFactory loggerFactory) : base(workspace, helper, providers, loggerFactory.CreateLogger()) { + _loader = loader; + _workspaceAssembly = _loader.LazyLoad(Configuration.RoslynWorkspaces); + _renameDocumentOperation = _workspaceAssembly.LazyGetType(RenameDocumentOperation); + _oldDocumentId = _renameDocumentOperation.LazyGetField("_oldDocumentId", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + _newDocumentId = _renameDocumentOperation.LazyGetField("_newDocumentId", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + _newFileName = _renameDocumentOperation.LazyGetField("_newFileName", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); } public override async Task Handle(RunCodeActionRequest request) @@ -94,12 +111,11 @@ private static string GetNewFilePath(string newFileName, string currentFilePath) bool IsRenameDocumentOperation(CodeActionOperation o, out DocumentId oldDocumentId, out DocumentId newDocumentId, out string name) { - var type = Type.GetType("Microsoft.CodeAnalysis.CodeActions.RenameDocumentOperation, Microsoft.CodeAnalysis.Workspaces, Version=2.4.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"); - if (o.GetType() == type) + if (o.GetType() == _renameDocumentOperation.Value) { - oldDocumentId = (DocumentId)type.GetField("_oldDocumentId", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).GetValue(o); - newDocumentId = (DocumentId)type.GetField("_newDocumentId", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).GetValue(o); - name = (string)type.GetField("_newFileName", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).GetValue(o); + oldDocumentId = _oldDocumentId.GetValue(o); + newDocumentId = _newDocumentId.GetValue(o); + name = _newFileName.GetValue(o); return true; } From db0177a952cd81da33d3e1fe6cd9fc51ff629bdd Mon Sep 17 00:00:00 2001 From: Ravi Chande Date: Mon, 13 Nov 2017 09:02:30 -0800 Subject: [PATCH 04/12] CR feedback: log file exceptions --- src/OmniSharp.Roslyn/OmniSharpWorkspace.cs | 33 +++++++++++++++++----- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/src/OmniSharp.Roslyn/OmniSharpWorkspace.cs b/src/OmniSharp.Roslyn/OmniSharpWorkspace.cs index 026ed94a05..ef4236a1b7 100644 --- a/src/OmniSharp.Roslyn/OmniSharpWorkspace.cs +++ b/src/OmniSharp.Roslyn/OmniSharpWorkspace.cs @@ -7,6 +7,7 @@ using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Text; +using Microsoft.Extensions.Logging; using OmniSharp.Roslyn; using OmniSharp.Utilities; @@ -18,11 +19,14 @@ public class OmniSharpWorkspace : Workspace public bool Initialized { get; set; } public BufferManager BufferManager { get; private set; } + private readonly ILogger _logger; + [ImportingConstructor] - public OmniSharpWorkspace(HostServicesAggregator aggregator) + public OmniSharpWorkspace(HostServicesAggregator aggregator, ILoggerFactory loggerFactory) : base(aggregator.CreateHostServices(), "Custom") { BufferManager = new BufferManager(this); + _logger = loggerFactory.CreateLogger(); } public override bool CanOpenDocuments => true; @@ -156,15 +160,29 @@ protected override void ApplyDocumentRemoved(DocumentId documentId) } } - private void DeleteDocumentFile(DocumentId id, string filePath) + private void DeleteDocumentFile(DocumentId id, string fullPath) { try { - File.Delete(filePath); + File.Delete(fullPath); + } + catch (IOException e) + { + LogDeletionException(e, fullPath); + } + catch (NotSupportedException e) + { + LogDeletionException(e, fullPath); } - catch (IOException) { } - catch (NotSupportedException) { } - catch (UnauthorizedAccessException) { } + catch (UnauthorizedAccessException e) + { + LogDeletionException(e, fullPath); + } + } + + private void LogDeletionException(Exception e, string filePath) + { + _logger.LogError(e, $"Error deleting file {filePath}"); } protected override void ApplyDocumentAdded(DocumentInfo info, SourceText text) @@ -195,8 +213,9 @@ private void SaveDocumentText(DocumentId id, string fullPath, SourceText newText newText.Write(writer); } } - catch (IOException) + catch (IOException e) { + _logger.LogError(e, $"Error saving document {fullPath}"); } } } From 9a3ba1807a825ba373caaa32be21c8144cc2ce46 Mon Sep 17 00:00:00 2001 From: Ravi Chande Date: Mon, 13 Nov 2017 09:27:24 -0800 Subject: [PATCH 05/12] Allow hosts to opt in to receiving new code action operations --- .../Models/v2/RunCodeActionRequest.cs | 1 + .../Refactoring/V2/RunCodeActionService.cs | 28 +++++++++++-------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/OmniSharp.Abstractions/Models/v2/RunCodeActionRequest.cs b/src/OmniSharp.Abstractions/Models/v2/RunCodeActionRequest.cs index 1b02fd382a..98415f93bc 100644 --- a/src/OmniSharp.Abstractions/Models/v2/RunCodeActionRequest.cs +++ b/src/OmniSharp.Abstractions/Models/v2/RunCodeActionRequest.cs @@ -9,5 +9,6 @@ public class RunCodeActionRequest : Request, ICodeActionRequest public Range Selection { get; set; } public bool WantsTextChanges { get; set; } public bool ApplyTextChanges { get; set; } = true; + public bool WantsAllCodeActionOperations { get; set; } } } diff --git a/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/V2/RunCodeActionService.cs b/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/V2/RunCodeActionService.cs index 11b67b4958..2c0060cc13 100644 --- a/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/V2/RunCodeActionService.cs +++ b/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/V2/RunCodeActionService.cs @@ -75,19 +75,23 @@ public override async Task Handle(RunCodeActionRequest re changes.AddRange(fileChanges); solution = applyChangesOperation.ChangedSolution; } - else if (IsRenameDocumentOperation(o, out var originalDocumentId, out var newDocumentId, out var newFileName)) - { - var originalDocument = solution.GetDocument(originalDocumentId); - string newFilePath = GetNewFilePath(newFileName, originalDocument.FilePath); - var text = await originalDocument.GetTextAsync(); - var temp = solution.RemoveDocument(originalDocumentId); - solution = temp.AddDocument(newDocumentId, newFileName, text, originalDocument.Folders, newFilePath); - changes.Add(new RenamedFileResponse(originalDocument.FilePath, newFilePath)); - } - else if (o is OpenDocumentOperation openDocumentOperation) + + if (request.WantsAllCodeActionOperations) { - var document = solution.GetDocument(openDocumentOperation.DocumentId); - changes.Add(new OpenFileResponse(document.FilePath)); + if (IsRenameDocumentOperation(o, out var originalDocumentId, out var newDocumentId, out var newFileName)) + { + var originalDocument = solution.GetDocument(originalDocumentId); + string newFilePath = GetNewFilePath(newFileName, originalDocument.FilePath); + var text = await originalDocument.GetTextAsync(); + var temp = solution.RemoveDocument(originalDocumentId); + solution = temp.AddDocument(newDocumentId, newFileName, text, originalDocument.Folders, newFilePath); + changes.Add(new RenamedFileResponse(originalDocument.FilePath, newFilePath)); + } + else if (o is OpenDocumentOperation openDocumentOperation) + { + var document = solution.GetDocument(openDocumentOperation.DocumentId); + changes.Add(new OpenFileResponse(document.FilePath)); + } } } From 64ab1b86a3a40a7608527537ad9fbce6aab08e3d Mon Sep 17 00:00:00 2001 From: Ravi Chande Date: Mon, 13 Nov 2017 09:27:31 -0800 Subject: [PATCH 06/12] Don't stringify --- .../Models/v1/FileOperation/FileOperationResponse.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/OmniSharp.Abstractions/Models/v1/FileOperation/FileOperationResponse.cs b/src/OmniSharp.Abstractions/Models/v1/FileOperation/FileOperationResponse.cs index 972baffc41..872d93a76b 100644 --- a/src/OmniSharp.Abstractions/Models/v1/FileOperation/FileOperationResponse.cs +++ b/src/OmniSharp.Abstractions/Models/v1/FileOperation/FileOperationResponse.cs @@ -13,7 +13,6 @@ public FileOperationResponse(string fileName, FileModificationType type) public string FileName { get; } - [JsonConverter(typeof(StringEnumConverter))] public FileModificationType ModificationType { get; } } } From 646d11c649a8545b5ed3aede35c1ecd8d3eb31a9 Mon Sep 17 00:00:00 2001 From: Ravi Chande Date: Mon, 13 Nov 2017 10:17:27 -0800 Subject: [PATCH 07/12] Fix build and tests --- tests/OmniSharp.Cake.Tests/LineIndexHelperFacts.cs | 3 ++- tests/OmniSharp.Roslyn.CSharp.Tests/BufferFacts.cs | 3 ++- tests/OmniSharp.Roslyn.CSharp.Tests/BufferManagerFacts.cs | 6 ++++-- tests/OmniSharp.Roslyn.CSharp.Tests/CodeActionsV2Facts.cs | 5 +++-- tests/TestUtility/TestHelpers.cs | 2 +- 5 files changed, 12 insertions(+), 7 deletions(-) diff --git a/tests/OmniSharp.Cake.Tests/LineIndexHelperFacts.cs b/tests/OmniSharp.Cake.Tests/LineIndexHelperFacts.cs index 5d8b646384..9d8e59d885 100644 --- a/tests/OmniSharp.Cake.Tests/LineIndexHelperFacts.cs +++ b/tests/OmniSharp.Cake.Tests/LineIndexHelperFacts.cs @@ -58,7 +58,8 @@ private static OmniSharpWorkspace CreateSimpleWorkspace(string fileName, string { var workspace = new OmniSharpWorkspace( new HostServicesAggregator( - Enumerable.Empty(), new LoggerFactory())); + Enumerable.Empty(), new LoggerFactory()), + new LoggerFactory()); var projectInfo = ProjectInfo.Create(ProjectId.CreateNewId(), VersionStamp.Create(), "ProjectNameVal", "AssemblyNameVal", LanguageNames.CSharp); diff --git a/tests/OmniSharp.Roslyn.CSharp.Tests/BufferFacts.cs b/tests/OmniSharp.Roslyn.CSharp.Tests/BufferFacts.cs index d94076b914..e0bc99202c 100644 --- a/tests/OmniSharp.Roslyn.CSharp.Tests/BufferFacts.cs +++ b/tests/OmniSharp.Roslyn.CSharp.Tests/BufferFacts.cs @@ -16,7 +16,8 @@ public class BufferFacts { var workspace = new OmniSharpWorkspace( new HostServicesAggregator( - Enumerable.Empty(), new LoggerFactory())); + Enumerable.Empty(), new LoggerFactory()), + new LoggerFactory()); var service = new ChangeBufferService(workspace); diff --git a/tests/OmniSharp.Roslyn.CSharp.Tests/BufferManagerFacts.cs b/tests/OmniSharp.Roslyn.CSharp.Tests/BufferManagerFacts.cs index ff1bc90030..a868cd9691 100644 --- a/tests/OmniSharp.Roslyn.CSharp.Tests/BufferManagerFacts.cs +++ b/tests/OmniSharp.Roslyn.CSharp.Tests/BufferManagerFacts.cs @@ -96,7 +96,8 @@ public async Task UpdateBufferFindsProjectBasedOnNearestPath() { var workspace = new OmniSharpWorkspace( new HostServicesAggregator( - Enumerable.Empty(), new LoggerFactory())); + Enumerable.Empty(), new LoggerFactory()), + new LoggerFactory()); TestHelpers.AddProjectToWorkspace(workspace, filePath: Path.Combine("src", "root", "foo.csproj"), @@ -162,7 +163,8 @@ private static OmniSharpWorkspace GetWorkspaceWithProjects() { var workspace = new OmniSharpWorkspace( new HostServicesAggregator( - Enumerable.Empty(), new LoggerFactory())); + Enumerable.Empty(), new LoggerFactory()), + new LoggerFactory()); TestHelpers.AddProjectToWorkspace(workspace, filePath: Path.Combine("src", "project.json"), diff --git a/tests/OmniSharp.Roslyn.CSharp.Tests/CodeActionsV2Facts.cs b/tests/OmniSharp.Roslyn.CSharp.Tests/CodeActionsV2Facts.cs index 26d833d7ac..4c1f96485a 100644 --- a/tests/OmniSharp.Roslyn.CSharp.Tests/CodeActionsV2Facts.cs +++ b/tests/OmniSharp.Roslyn.CSharp.Tests/CodeActionsV2Facts.cs @@ -188,7 +188,7 @@ private async Task> FindRefactoringsAsync(strin Column = range.Start.Offset, FileName = BufferPath, Buffer = testFile.Content.Code, - Selection = GetSelection(range) + Selection = GetSelection(range), }; var response = await requestHandler.Handle(request); @@ -216,7 +216,8 @@ private async Task RunRefactoringsAsync(string code, stri FileName = BufferPath, Buffer = testFile.Content.Code, Identifier = identifier, - WantsTextChanges = wantsChanges + WantsTextChanges = wantsChanges, + WantsAllCodeActionOperations = true }; return await requestHandler.Handle(request); diff --git a/tests/TestUtility/TestHelpers.cs b/tests/TestUtility/TestHelpers.cs index 6c8bc4888c..337386ee83 100644 --- a/tests/TestUtility/TestHelpers.cs +++ b/tests/TestUtility/TestHelpers.cs @@ -15,7 +15,7 @@ public static class TestHelpers { public static OmniSharpWorkspace CreateCsxWorkspace(TestFile testFile) { - var workspace = new OmniSharpWorkspace(new HostServicesAggregator(Enumerable.Empty(), new LoggerFactory())); + var workspace = new OmniSharpWorkspace(new HostServicesAggregator(Enumerable.Empty(), new LoggerFactory()), new LoggerFactory()); AddCsxProjectToWorkspace(workspace, testFile); return workspace; } From d7da5e343ff38c5dfe36a81594ff4408c6615cf9 Mon Sep 17 00:00:00 2001 From: Ravi Chande Date: Mon, 13 Nov 2017 14:09:58 -0800 Subject: [PATCH 08/12] Attempt to fix test on linux --- tests/OmniSharp.Roslyn.CSharp.Tests/CodeActionsV2Facts.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/OmniSharp.Roslyn.CSharp.Tests/CodeActionsV2Facts.cs b/tests/OmniSharp.Roslyn.CSharp.Tests/CodeActionsV2Facts.cs index 4c1f96485a..2a1f73a0ad 100644 --- a/tests/OmniSharp.Roslyn.CSharp.Tests/CodeActionsV2Facts.cs +++ b/tests/OmniSharp.Roslyn.CSharp.Tests/CodeActionsV2Facts.cs @@ -173,7 +173,8 @@ private async Task> FindRefactoringNamesAsync(string code) private async Task> FindRefactoringsAsync(string code, IDictionary configurationData = null) { - var testFile = new TestFile(BufferPath, code); + var path = "buffer.cs"; + var testFile = new TestFile(path, code); using (var host = CreateOmniSharpHost(new [] { testFile }, configurationData)) { @@ -186,7 +187,7 @@ private async Task> FindRefactoringsAsync(strin { Line = range.Start.Line, Column = range.Start.Offset, - FileName = BufferPath, + FileName = path, Buffer = testFile.Content.Code, Selection = GetSelection(range), }; From a7d2c2cbccc6a0e1a8d98835e533f64fd966e9b8 Mon Sep 17 00:00:00 2001 From: Ravi Chande Date: Tue, 14 Nov 2017 09:32:51 -0800 Subject: [PATCH 09/12] Revert "Attempt to fix test on linux" This reverts commit d7da5e343ff38c5dfe36a81594ff4408c6615cf9. --- tests/OmniSharp.Roslyn.CSharp.Tests/CodeActionsV2Facts.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/OmniSharp.Roslyn.CSharp.Tests/CodeActionsV2Facts.cs b/tests/OmniSharp.Roslyn.CSharp.Tests/CodeActionsV2Facts.cs index 2a1f73a0ad..4c1f96485a 100644 --- a/tests/OmniSharp.Roslyn.CSharp.Tests/CodeActionsV2Facts.cs +++ b/tests/OmniSharp.Roslyn.CSharp.Tests/CodeActionsV2Facts.cs @@ -173,8 +173,7 @@ private async Task> FindRefactoringNamesAsync(string code) private async Task> FindRefactoringsAsync(string code, IDictionary configurationData = null) { - var path = "buffer.cs"; - var testFile = new TestFile(path, code); + var testFile = new TestFile(BufferPath, code); using (var host = CreateOmniSharpHost(new [] { testFile }, configurationData)) { @@ -187,7 +186,7 @@ private async Task> FindRefactoringsAsync(strin { Line = range.Start.Line, Column = range.Start.Offset, - FileName = path, + FileName = BufferPath, Buffer = testFile.Content.Code, Selection = GetSelection(range), }; From 3a9585c198944bdda100f387e05ee9df753ee5e9 Mon Sep 17 00:00:00 2001 From: Ravi Chande Date: Tue, 14 Nov 2017 13:15:04 -0800 Subject: [PATCH 10/12] Use an on-disk project to test file renaming --- .../ProjectWithMismatchedFileName/Program.cs | 12 ++++++ .../ProjectAndSolution.csproj | 8 ++++ .../ProjectAndSolution.sln | 34 +++++++++++++++++ .../CodeActionsV2Facts.cs | 37 +++++++++++++------ 4 files changed, 80 insertions(+), 11 deletions(-) create mode 100644 test-assets/test-projects/ProjectWithMismatchedFileName/Program.cs create mode 100644 test-assets/test-projects/ProjectWithMismatchedFileName/ProjectAndSolution.csproj create mode 100644 test-assets/test-projects/ProjectWithMismatchedFileName/ProjectAndSolution.sln diff --git a/test-assets/test-projects/ProjectWithMismatchedFileName/Program.cs b/test-assets/test-projects/ProjectWithMismatchedFileName/Program.cs new file mode 100644 index 0000000000..3692177aa0 --- /dev/null +++ b/test-assets/test-projects/ProjectWithMismatchedFileName/Program.cs @@ -0,0 +1,12 @@ +using System; + +namespace ProjectAndSolution +{ + class Class1 + { + static void Main(string[] args) + { + Console.WriteLine("Hello World!"); + } + } +} diff --git a/test-assets/test-projects/ProjectWithMismatchedFileName/ProjectAndSolution.csproj b/test-assets/test-projects/ProjectWithMismatchedFileName/ProjectAndSolution.csproj new file mode 100644 index 0000000000..abb9969a56 --- /dev/null +++ b/test-assets/test-projects/ProjectWithMismatchedFileName/ProjectAndSolution.csproj @@ -0,0 +1,8 @@ + + + + Exe + netcoreapp1.1 + + + diff --git a/test-assets/test-projects/ProjectWithMismatchedFileName/ProjectAndSolution.sln b/test-assets/test-projects/ProjectWithMismatchedFileName/ProjectAndSolution.sln new file mode 100644 index 0000000000..da37db368b --- /dev/null +++ b/test-assets/test-projects/ProjectWithMismatchedFileName/ProjectAndSolution.sln @@ -0,0 +1,34 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProjectAndSolution", "ProjectAndSolution.csproj", "{A4C2694D-AEB4-4CB1-8951-5290424EF883}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A4C2694D-AEB4-4CB1-8951-5290424EF883}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A4C2694D-AEB4-4CB1-8951-5290424EF883}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A4C2694D-AEB4-4CB1-8951-5290424EF883}.Debug|x64.ActiveCfg = Debug|x64 + {A4C2694D-AEB4-4CB1-8951-5290424EF883}.Debug|x64.Build.0 = Debug|x64 + {A4C2694D-AEB4-4CB1-8951-5290424EF883}.Debug|x86.ActiveCfg = Debug|x86 + {A4C2694D-AEB4-4CB1-8951-5290424EF883}.Debug|x86.Build.0 = Debug|x86 + {A4C2694D-AEB4-4CB1-8951-5290424EF883}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A4C2694D-AEB4-4CB1-8951-5290424EF883}.Release|Any CPU.Build.0 = Release|Any CPU + {A4C2694D-AEB4-4CB1-8951-5290424EF883}.Release|x64.ActiveCfg = Release|x64 + {A4C2694D-AEB4-4CB1-8951-5290424EF883}.Release|x64.Build.0 = Release|x64 + {A4C2694D-AEB4-4CB1-8951-5290424EF883}.Release|x86.ActiveCfg = Release|x86 + {A4C2694D-AEB4-4CB1-8951-5290424EF883}.Release|x86.Build.0 = Release|x86 + EndGlobalSection +EndGlobal diff --git a/tests/OmniSharp.Roslyn.CSharp.Tests/CodeActionsV2Facts.cs b/tests/OmniSharp.Roslyn.CSharp.Tests/CodeActionsV2Facts.cs index 4c1f96485a..63c09cea6c 100644 --- a/tests/OmniSharp.Roslyn.CSharp.Tests/CodeActionsV2Facts.cs +++ b/tests/OmniSharp.Roslyn.CSharp.Tests/CodeActionsV2Facts.cs @@ -132,17 +132,32 @@ private static void NewMethod() [Fact] public async Task Can_send_rename_and_fileOpen_responses_when_codeAction_renames_file() { - const string code = -@"public class [||]CCC -{ -}"; - - var response = await RunRefactoringAsync(code, "Rename file to CCC.cs"); - var changes = response.Changes.ToArray(); - Assert.Equal(2, changes.Length); - Assert.Equal(FileModificationType.Renamed, changes[0].ModificationType); - Assert.Contains("CCC.cs", ((RenamedFileResponse)changes[0]).NewFileName); - Assert.Equal(FileModificationType.Opened, changes[1].ModificationType); + using (var testProject = await TestAssets.Instance.GetTestProjectAsync("ProjectWithMismatchedFileName")) + using (var host = CreateOmniSharpHost(testProject.Directory)) + { + var requestHandler = host.GetRequestHandler(OmniSharpEndpoints.V2.RunCodeAction); + var document = host.Workspace.CurrentSolution.Projects.First().Documents.First(); + var buffer = await document.GetTextAsync(); + var path = document.FilePath; + + var request = new RunCodeActionRequest + { + Line = 4, + Column = 10, + FileName = path, + Buffer = buffer.ToString(), + Identifier = "Rename file to Class1.cs", + WantsTextChanges = true, + WantsAllCodeActionOperations = true + }; + + var response = await requestHandler.Handle(request); + var changes = response.Changes.ToArray(); + Assert.Equal(2, changes.Length); + Assert.Equal(FileModificationType.Renamed, changes[0].ModificationType); + Assert.Contains("Class1.cs", ((RenamedFileResponse)changes[0]).NewFileName); + Assert.Equal(FileModificationType.Opened, changes[1].ModificationType); + } } private static void AssertIgnoringIndent(string expected, string actual) From 92dd47ecaedbf9a97adc85d2f05b2894e315a855 Mon Sep 17 00:00:00 2001 From: Ravi Chande Date: Wed, 15 Nov 2017 13:49:43 -0800 Subject: [PATCH 11/12] Add new test project to build.json --- build.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build.json b/build.json index a1d6ee9d54..6e53680975 100644 --- a/build.json +++ b/build.json @@ -36,7 +36,8 @@ "ProjectAndSolutionWithProjectSection", "TwoProjectsWithSolution", "ProjectWithGeneratedFile", - "CSharpAndFSharp" + "CSharpAndFSharp", + "ProjectWithMismatchedFileName" ], "LegacyTestAssets": [ "LegacyNUnitTestProject", From 81b86e533c21b4d7a5eeec67f4f50b438b656d55 Mon Sep 17 00:00:00 2001 From: Dustin Campbell Date: Wed, 15 Nov 2017 15:39:47 -0800 Subject: [PATCH 12/12] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 11c0c342f8..e143120a6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,9 @@ All changes to the project will be documented in this file. ## [1.28.0] - not yet released + * Fixed issue with loading XML documentation for `#r` assembly references in CSX scripts ([#1026](https://github.com/OmniSharp/omnisharp-roslyn/issues/1026), PR: [#1027](https://github.com/OmniSharp/omnisharp-roslyn/pull/1027)) +* Updated the `/v2/runcodeaction` end point to return document "renames" and "opens" that a code action might perform. (PR: [#1023](https://github.com/OmniSharp/omnisharp-roslyn/pull/1023)) ## [1.27.2] - 2017-11-10