diff --git a/src/OmniSharp.Abstractions/Options/OmniSharpOptions.cs b/src/OmniSharp.Abstractions/Options/OmniSharpOptions.cs index d7b5099b91..d2fb9e8ae8 100644 --- a/src/OmniSharp.Abstractions/Options/OmniSharpOptions.cs +++ b/src/OmniSharp.Abstractions/Options/OmniSharpOptions.cs @@ -4,13 +4,8 @@ namespace OmniSharp.Options { public class OmniSharpOptions { - public FormattingOptions FormattingOptions { get; } + public RoslynExtensionsOptions RoslynExtensionsOptions { get; } = new RoslynExtensionsOptions(); - public OmniSharpOptions() : this(new FormattingOptions()) { } - - public OmniSharpOptions(FormattingOptions options) - { - FormattingOptions = options ?? throw new ArgumentNullException(nameof(options)); - } + public FormattingOptions FormattingOptions { get; } = new FormattingOptions(); } } diff --git a/src/OmniSharp.Abstractions/Options/RoslynExtensionsOptions.cs b/src/OmniSharp.Abstractions/Options/RoslynExtensionsOptions.cs new file mode 100644 index 0000000000..0072724225 --- /dev/null +++ b/src/OmniSharp.Abstractions/Options/RoslynExtensionsOptions.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace OmniSharp.Options +{ + public class RoslynExtensionsOptions + { + public string[] LocationPaths { get; set; } + + public IEnumerable GetNormalizedLocationPaths(IOmniSharpEnvironment env) + { + if (LocationPaths == null || LocationPaths.Length == 0) return Enumerable.Empty(); + + var normalizePaths = new HashSet(StringComparer.OrdinalIgnoreCase); + foreach (var locationPath in LocationPaths) + { + if (Path.IsPathRooted(locationPath)) + { + normalizePaths.Add(locationPath); + } + else + { + normalizePaths.Add(Path.Combine(env.TargetDirectory, locationPath)); + } + } + + return normalizePaths; + } + } +} diff --git a/src/OmniSharp.Abstractions/Services/IAssemblyLoader.cs b/src/OmniSharp.Abstractions/Services/IAssemblyLoader.cs index 7044fcfcca..f49859e6cf 100644 --- a/src/OmniSharp.Abstractions/Services/IAssemblyLoader.cs +++ b/src/OmniSharp.Abstractions/Services/IAssemblyLoader.cs @@ -7,6 +7,8 @@ namespace OmniSharp.Services public interface IAssemblyLoader { Assembly Load(AssemblyName name); + + IReadOnlyList LoadAllFrom(string folderPath); } public static class IAssemblyLoaderExtensions diff --git a/src/OmniSharp.Host/Services/AssemblyLoader.cs b/src/OmniSharp.Host/Services/AssemblyLoader.cs index 36e3595bd0..3f3ecf5d48 100644 --- a/src/OmniSharp.Host/Services/AssemblyLoader.cs +++ b/src/OmniSharp.Host/Services/AssemblyLoader.cs @@ -1,4 +1,7 @@ using System; +using System.Collections.Generic; +using System.Linq; +using System.IO; using System.Reflection; using Microsoft.Extensions.Logging; using OmniSharp.Services; @@ -11,7 +14,7 @@ internal class AssemblyLoader : IAssemblyLoader public AssemblyLoader(ILoggerFactory loggerFactory) { - this._logger = loggerFactory.CreateLogger(); + _logger = loggerFactory.CreateLogger(); } public Assembly Load(AssemblyName name) @@ -30,5 +33,43 @@ public Assembly Load(AssemblyName name) _logger.LogTrace($"Assembly loaded: {name}"); return result; } + + public IReadOnlyList LoadAllFrom(string folderPath) + { + if (string.IsNullOrWhiteSpace(folderPath)) return Array.Empty(); + + var assemblies = new List(); + foreach (var filePath in Directory.EnumerateFiles(folderPath, "*.dll")) + { + var assembly = LoadFromPath(filePath); + if (assembly != null) + { + assemblies.Add(assembly); + } + } + + return assemblies; + } + + private Assembly LoadFromPath(string assemblyPath) + { + Assembly assembly = null; + + try + { +#if NET46 + assembly = Assembly.LoadFrom(assemblyPath); +#else + assembly = System.Runtime.Loader.AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyPath); +#endif + } + catch (Exception ex) + { + _logger.LogError(ex, $"Failed to load assembly from path: {assemblyPath}"); + } + + _logger.LogTrace($"Assembly loaded from path: {assemblyPath}"); + return assembly; + } } } diff --git a/src/OmniSharp.Roslyn.CSharp/CodeActions/ExternalCodeActionProvider.cs b/src/OmniSharp.Roslyn.CSharp/CodeActions/ExternalCodeActionProvider.cs new file mode 100644 index 0000000000..a9c2d2c377 --- /dev/null +++ b/src/OmniSharp.Roslyn.CSharp/CodeActions/ExternalCodeActionProvider.cs @@ -0,0 +1,15 @@ +using System.Composition; +using OmniSharp.Services; + +namespace OmniSharp.Roslyn.CSharp.Services.CodeActions +{ + [Export(typeof(ICodeActionProvider))] + public class ExternalCodeActionProvider : AbstractCodeActionProvider + { + [ImportingConstructor] + public ExternalCodeActionProvider(ExternalFeaturesHostServicesProvider featuresHostServicesProvider) + : base("ExternalCodeActions", featuresHostServicesProvider.Assemblies) + { + } + } +} diff --git a/src/OmniSharp.Roslyn.CSharp/Services/ExternalFeaturesHostServicesProvider.cs b/src/OmniSharp.Roslyn.CSharp/Services/ExternalFeaturesHostServicesProvider.cs new file mode 100644 index 0000000000..0a7c457e70 --- /dev/null +++ b/src/OmniSharp.Roslyn.CSharp/Services/ExternalFeaturesHostServicesProvider.cs @@ -0,0 +1,34 @@ +using System.Collections.Immutable; +using System.Composition; +using System.Linq; +using System.Reflection; +using OmniSharp.Options; +using OmniSharp.Services; + +namespace OmniSharp.Roslyn.CSharp.Services +{ + [Export(typeof(IHostServicesProvider))] + [Export(typeof(ExternalFeaturesHostServicesProvider))] + [Shared] + public class ExternalFeaturesHostServicesProvider : IHostServicesProvider + { + public ImmutableArray Assemblies { get; } + + [ImportingConstructor] + public ExternalFeaturesHostServicesProvider(IAssemblyLoader loader, OmniSharpOptions options, IOmniSharpEnvironment env) + { + var builder = ImmutableArray.CreateBuilder(); + + var roslynExtensionsLocations = options.RoslynExtensionsOptions.GetNormalizedLocationPaths(env); + if (roslynExtensionsLocations?.Any() == true) + { + foreach (var roslynExtensionsLocation in roslynExtensionsLocations) + { + builder.AddRange(loader.LoadAllFrom(roslynExtensionsLocation)); + } + } + + Assemblies = builder.ToImmutable(); + } + } +} diff --git a/src/OmniSharp.Roslyn.CSharp/Services/RoslynFeaturesHostServicesProvider.cs b/src/OmniSharp.Roslyn.CSharp/Services/RoslynFeaturesHostServicesProvider.cs index 356812c96d..a0f4cf8b0c 100644 --- a/src/OmniSharp.Roslyn.CSharp/Services/RoslynFeaturesHostServicesProvider.cs +++ b/src/OmniSharp.Roslyn.CSharp/Services/RoslynFeaturesHostServicesProvider.cs @@ -1,12 +1,14 @@ using System.Collections.Immutable; using System.Composition; using System.Reflection; +using OmniSharp.Options; using OmniSharp.Services; namespace OmniSharp.Roslyn.CSharp.Services { [Export(typeof(IHostServicesProvider))] [Export(typeof(RoslynFeaturesHostServicesProvider))] + [Shared] public class RoslynFeaturesHostServicesProvider : IHostServicesProvider { public ImmutableArray Assemblies { get; } @@ -21,7 +23,8 @@ public RoslynFeaturesHostServicesProvider(IAssemblyLoader loader) builder.AddRange(loader.Load(Features, CSharpFeatures)); - this.Assemblies = builder.ToImmutable(); + + Assemblies = builder.ToImmutable(); } } } diff --git a/src/OmniSharp/app.config b/src/OmniSharp/app.config index b886ce469e..7d957b3f31 100644 --- a/src/OmniSharp/app.config +++ b/src/OmniSharp/app.config @@ -3,4 +3,20 @@ + + + + + + + + + + + + + + + + \ No newline at end of file