Skip to content

Commit

Permalink
Merge branch 'master' into lsp
Browse files Browse the repository at this point in the history
  • Loading branch information
david-driscoll authored Nov 16, 2017
2 parents 25cafe1 + 81b86e5 commit 541be2e
Show file tree
Hide file tree
Showing 20 changed files with 320 additions and 19 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
3 changes: 2 additions & 1 deletion build.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@
"ProjectAndSolutionWithProjectSection",
"TwoProjectsWithSolution",
"ProjectWithGeneratedFile",
"CSharpAndFSharp"
"CSharpAndFSharp",
"ProjectWithMismatchedFileName"
],
"LegacyTestAssets": [
"LegacyNUnitTestProject",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace OmniSharp.Models
{
public enum FileModificationType
{
Modified,
Opened,
Renamed
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
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; }

public FileModificationType ModificationType { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,13 @@

namespace OmniSharp.Models
{
public class ModifiedFileResponse
public class ModifiedFileResponse : FileOperationResponse
{
public ModifiedFileResponse() { }

public ModifiedFileResponse(string fileName)
: base(fileName, FileModificationType.Modified)
{
FileName = fileName;
}

public string FileName { get; set; }
public string Buffer { get; set; }
public IEnumerable<LinePositionSpanTextChange> Changes { get; set; }
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace OmniSharp.Models
{
public class OpenFileResponse : FileOperationResponse
{
public OpenFileResponse(string fileName) : base(fileName, FileModificationType.Opened)
{
}
}
}
Original file line number Diff line number Diff line change
@@ -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; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ namespace OmniSharp.Models.V2
{
public class RunCodeActionResponse
{
public IEnumerable<ModifiedFileResponse> Changes { get; set; }
public IEnumerable<FileOperationResponse> Changes { get; set; }
}
}
26 changes: 26 additions & 0 deletions src/OmniSharp.Abstractions/Utilities/ReflectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -162,5 +162,31 @@ public static object InvokeStatic(this Lazy<MethodInfo> lazyMethodInfo, object[]
{
return lazyMethodInfo.InvokeStatic(args);
}

public static Lazy<FieldInfo> LazyGetField(this Lazy<Type> lazyType, string fieldName, BindingFlags bindingFlags)
{
if (lazyType == null)
{
throw new ArgumentNullException(nameof(lazyType));
}

return new Lazy<FieldInfo>(() =>
{
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<T>(this Lazy<FieldInfo> lazyFieldInfo, object o)
{
return (T)lazyFieldInfo.Value.GetValue(o);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using System;
using System.Collections.Generic;
using System.Composition;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
Expand All @@ -12,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;

Expand All @@ -21,14 +23,30 @@ namespace OmniSharp.Roslyn.CSharp.Services.Refactoring.V2
[OmniSharpHandler(OmniSharpEndpoints.V2.RunCodeAction, LanguageNames.CSharp)]
public class RunCodeActionService : BaseCodeActionService<RunCodeActionRequest, RunCodeActionResponse>
{
private readonly IAssemblyLoader _loader;
private readonly Lazy<Assembly> _workspaceAssembly;
private readonly Lazy<Type> _renameDocumentOperation;
private readonly Lazy<FieldInfo> _oldDocumentId;
private readonly Lazy<FieldInfo> _newDocumentId;
private readonly Lazy<FieldInfo> _newFileName;

private const string RenameDocumentOperation = "Microsoft.CodeAnalysis.CodeActions.RenameDocumentOperation";

[ImportingConstructor]
public RunCodeActionService(
IAssemblyLoader loader,
OmniSharpWorkspace workspace,
CodeActionHelper helper,
[ImportMany] IEnumerable<ICodeActionProvider> providers,
ILoggerFactory loggerFactory)
: base(workspace, helper, providers, loggerFactory.CreateLogger<RunCodeActionService>())
{
_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<RunCodeActionResponse> Handle(RunCodeActionRequest request)
Expand All @@ -45,7 +63,7 @@ public override async Task<RunCodeActionResponse> Handle(RunCodeActionRequest re
var operations = await availableAction.GetOperationsAsync(CancellationToken.None);

var solution = this.Workspace.CurrentSolution;
var changes = new List<ModifiedFileResponse>();
var changes = new List<FileOperationResponse>();
var directory = Path.GetDirectoryName(request.FileName);

foreach (var o in operations)
Expand All @@ -57,6 +75,24 @@ public override async Task<RunCodeActionResponse> Handle(RunCodeActionRequest re
changes.AddRange(fileChanges);
solution = applyChangesOperation.ChangedSolution;
}

if (request.WantsAllCodeActionOperations)
{
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)
Expand All @@ -71,6 +107,28 @@ public override async Task<RunCodeActionResponse> 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)
{
if (o.GetType() == _renameDocumentOperation.Value)
{
oldDocumentId = _oldDocumentId.GetValue<DocumentId>(o);
newDocumentId = _newDocumentId.GetValue<DocumentId>(o);
name = _newFileName.GetValue<string>(o);
return true;
}

oldDocumentId = default(DocumentId);
newDocumentId = default(DocumentId);
name = null;
return false;
}

private async Task<IEnumerable<ModifiedFileResponse>> GetFileChangesAsync(Solution newSolution, Solution oldSolution, string directory, bool wantTextChanges)
{
var filePathToResponseMap = new Dictionary<string, ModifiedFileResponse>();
Expand Down
78 changes: 77 additions & 1 deletion src/OmniSharp.Roslyn/OmniSharpWorkspace.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
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;
using Microsoft.Extensions.Logging;
using OmniSharp.Roslyn;
using OmniSharp.Utilities;

Expand All @@ -15,11 +19,14 @@ public class OmniSharpWorkspace : Workspace
public bool Initialized { get; set; }
public BufferManager BufferManager { get; private set; }

private readonly ILogger<OmniSharpWorkspace> _logger;

[ImportingConstructor]
public OmniSharpWorkspace(HostServicesAggregator aggregator)
public OmniSharpWorkspace(HostServicesAggregator aggregator, ILoggerFactory loggerFactory)
: base(aggregator.CreateHostServices(), "Custom")
{
BufferManager = new BufferManager(this);
_logger = loggerFactory.CreateLogger<OmniSharpWorkspace>();
}

public override bool CanOpenDocuments => true;
Expand Down Expand Up @@ -142,5 +149,74 @@ 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 fullPath)
{
try
{
File.Delete(fullPath);
}
catch (IOException e)
{
LogDeletionException(e, fullPath);
}
catch (NotSupportedException e)
{
LogDeletionException(e, fullPath);
}
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)
{
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 e)
{
_logger.LogError(e, $"Error saving document {fullPath}");
}
}
}
}
12 changes: 12 additions & 0 deletions test-assets/test-projects/ProjectWithMismatchedFileName/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System;

namespace ProjectAndSolution
{
class Class1
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp1.1</TargetFramework>
</PropertyGroup>

</Project>
Loading

0 comments on commit 541be2e

Please sign in to comment.