diff --git a/ReSharper.FSharp/ReSharper.FSharp.sln b/ReSharper.FSharp/ReSharper.FSharp.sln
index fc5dcbe0f8..e851548b5c 100644
--- a/ReSharper.FSharp/ReSharper.FSharp.sln
+++ b/ReSharper.FSharp/ReSharper.FSharp.sln
@@ -25,6 +25,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FSharp.TypeProviders.Host",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FSharp.TypeProviders.Protocol", "src\FSharp.TypeProviders.Protocol\FSharp.TypeProviders.Protocol.csproj", "{912A7F5F-73F2-4DFE-B81A-C13A4F58F31C}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FSharp.Fantomas.Host", "src\FSharp.Fantomas.Host\FSharp.Fantomas.Host.csproj", "{CF12A71A-1296-418B-8C0A-CC4B398AAB16}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FSharp.Fantomas.Protocol", "src\FSharp.Fantomas.Protocol\FSharp.Fantomas.Protocol.csproj", "{D8DE097E-04DC-4A9B-88BF-10923FF44F8B}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -67,6 +71,14 @@ Global
{912A7F5F-73F2-4DFE-B81A-C13A4F58F31C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{912A7F5F-73F2-4DFE-B81A-C13A4F58F31C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{912A7F5F-73F2-4DFE-B81A-C13A4F58F31C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {CF12A71A-1296-418B-8C0A-CC4B398AAB16}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CF12A71A-1296-418B-8C0A-CC4B398AAB16}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CF12A71A-1296-418B-8C0A-CC4B398AAB16}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CF12A71A-1296-418B-8C0A-CC4B398AAB16}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D8DE097E-04DC-4A9B-88BF-10923FF44F8B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D8DE097E-04DC-4A9B-88BF-10923FF44F8B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D8DE097E-04DC-4A9B-88BF-10923FF44F8B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D8DE097E-04DC-4A9B-88BF-10923FF44F8B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -81,5 +93,7 @@ Global
{83C5104C-057E-4AF8-B3FE-3437968CA621} = {7B41BA27-ACA5-43D2-9EBA-2E604D63A4E2}
{4EB01188-F511-4991-9A0F-604EE28E8F9B} = {2E19336B-3133-4C1A-8E65-E2F7C465663B}
{912A7F5F-73F2-4DFE-B81A-C13A4F58F31C} = {2E19336B-3133-4C1A-8E65-E2F7C465663B}
+ {CF12A71A-1296-418B-8C0A-CC4B398AAB16} = {2E19336B-3133-4C1A-8E65-E2F7C465663B}
+ {D8DE097E-04DC-4A9B-88BF-10923FF44F8B} = {2E19336B-3133-4C1A-8E65-E2F7C465663B}
EndGlobalSection
EndGlobal
diff --git a/ReSharper.FSharp/src/FSharp.Fantomas.Host/FSharp.Fantomas.Host.csproj b/ReSharper.FSharp/src/FSharp.Fantomas.Host/FSharp.Fantomas.Host.csproj
new file mode 100644
index 0000000000..18cdd6de94
--- /dev/null
+++ b/ReSharper.FSharp/src/FSharp.Fantomas.Host/FSharp.Fantomas.Host.csproj
@@ -0,0 +1,35 @@
+
+
+
+ net461
+ JetBrains.ReSharper.Plugins.FSharp.Fantomas.Host
+ Exe
+ $(CSharpLanguageVersion)
+ JetBrains.ReSharper.Plugins.FSharp.Fantomas.Host
+
+
+
+
+ AnyCPU
+
+
+ AnyCPU
+
+
+
+
+
+
+
+
+
+
+
+
+ Always
+
+
+
+
+
+
diff --git a/ReSharper.FSharp/src/FSharp.Fantomas.Host/JetBrains.ReSharper.Plugins.FSharp.Fantomas.Host.runtimeconfig.json b/ReSharper.FSharp/src/FSharp.Fantomas.Host/JetBrains.ReSharper.Plugins.FSharp.Fantomas.Host.runtimeconfig.json
new file mode 100644
index 0000000000..0228bbe4b3
--- /dev/null
+++ b/ReSharper.FSharp/src/FSharp.Fantomas.Host/JetBrains.ReSharper.Plugins.FSharp.Fantomas.Host.runtimeconfig.json
@@ -0,0 +1,10 @@
+{
+ "runtimeOptions": {
+ "tfm": "netcoreapp5.0",
+ "framework": {
+ "name": "Microsoft.NETCore.App",
+ "version": "5.0.0"
+ },
+ "rollForward": "Major"
+ }
+}
diff --git a/ReSharper.FSharp/src/FSharp.Fantomas.Host/src/FantomasAssemblyResolver.cs b/ReSharper.FSharp/src/FSharp.Fantomas.Host/src/FantomasAssemblyResolver.cs
new file mode 100644
index 0000000000..eaceb5c582
--- /dev/null
+++ b/ReSharper.FSharp/src/FSharp.Fantomas.Host/src/FantomasAssemblyResolver.cs
@@ -0,0 +1,43 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+
+namespace JetBrains.ReSharper.Plugins.FSharp.Fantomas.Host
+{
+ public static class FantomasAssemblyResolver
+ {
+ private const string AdditionalProbingPathsEnvVar = "RIDER_PLUGIN_ADDITIONAL_PROBING_PATHS";
+ private static readonly List OurAdditionalProbingPaths = new List();
+
+ static FantomasAssemblyResolver()
+ {
+ var paths = Environment.GetEnvironmentVariable(AdditionalProbingPathsEnvVar);
+ if (string.IsNullOrWhiteSpace(paths)) return;
+
+ foreach (var path in paths.Split(';'))
+ {
+ if (!string.IsNullOrEmpty(path)) OurAdditionalProbingPaths.Add(path);
+ }
+ }
+
+ public static Assembly Resolve(object sender, ResolveEventArgs eventArgs)
+ {
+ var assemblyName = $"{new AssemblyName(eventArgs.Name).Name}.dll";
+
+ foreach (var path in OurAdditionalProbingPaths)
+ {
+ var assemblyPath = Path.Combine(path, assemblyName);
+ if (!File.Exists(assemblyPath)) continue;
+
+ var assembly = Assembly.LoadFrom(assemblyPath);
+ return assembly;
+ }
+
+ Console.Error.Write($"\nFailed to resolve assembly by name '{eventArgs.Name}'" +
+ $"\n Requesting assembly: {eventArgs.RequestingAssembly?.FullName}" +
+ $"\n Probing paths: {string.Join("\n", OurAdditionalProbingPaths)}");
+ return null;
+ }
+ }
+}
diff --git a/ReSharper.FSharp/src/FSharp.Fantomas.Host/src/FantomasCodeFormatter.cs b/ReSharper.FSharp/src/FSharp.Fantomas.Host/src/FantomasCodeFormatter.cs
new file mode 100644
index 0000000000..5ccfae8bbf
--- /dev/null
+++ b/ReSharper.FSharp/src/FSharp.Fantomas.Host/src/FantomasCodeFormatter.cs
@@ -0,0 +1,55 @@
+using Fantomas;
+using FSharp.Compiler.CodeAnalysis;
+using FSharp.Compiler.Diagnostics;
+using FSharp.Compiler.Text;
+using JetBrains.ReSharper.Plugins.FSharp.Fantomas.Server;
+using Microsoft.FSharp.Collections;
+using Microsoft.FSharp.Control;
+
+namespace JetBrains.ReSharper.Plugins.FSharp.Fantomas.Host
+{
+ internal class FantomasCodeFormatter
+ {
+ private readonly FSharpChecker myChecker =
+ FSharpChecker.Create(null, null, null, null, null, null, null, null, null, null);
+
+ private readonly FormatConfig.FormatConfig myDefaultFormatConfig = FormatConfig.FormatConfig.Default;
+
+ public string FormatSelection(RdFormatSelectionArgs args) =>
+ FSharpAsync.StartAsTask(
+ CodeFormatter.FormatSelectionAsync(args.FileName, Convert(args.Range),
+ SourceOrigin.SourceOrigin.NewSourceString(args.Source), Convert(args.FormatConfig),
+ Convert(args.ParsingOptions), myChecker), null, null)
+ .Result.Replace("\r\n", args.NewLineText);
+
+ public string FormatDocument(RdFormatDocumentArgs args) =>
+ FSharpAsync.StartAsTask(
+ CodeFormatter.FormatDocumentAsync(args.FileName, SourceOrigin.SourceOrigin.NewSourceString(args.Source),
+ Convert(args.FormatConfig), Convert(args.ParsingOptions), myChecker), null, null)
+ .Result.Replace("\r\n", args.NewLineText);
+
+ private static Range Convert(RdFcsRange range) =>
+ CodeFormatter.MakeRange(range.FileName, range.StartLine, range.StartCol, range.EndLine, range.EndCol);
+
+ private static FSharpParsingOptions Convert(RdFcsParsingOptions options) =>
+ new FSharpParsingOptions(new[] {options.LastSourceFile},
+ ListModule.OfArray(options.ConditionalCompilationDefines), FSharpDiagnosticOptions.Default, false,
+ options.LightSyntax, false, options.IsExe);
+
+ private FormatConfig.FormatConfig Convert(RdFantomasFormatConfig config) =>
+ new FormatConfig.FormatConfig(config.IndentSize, config.MaxLineLength, config.SemicolonAtEndOfLine,
+ config.SpaceBeforeParameter, config.SpaceBeforeLowercaseInvocation, config.SpaceBeforeUppercaseInvocation,
+ config.SpaceBeforeClassConstructor, config.SpaceBeforeMember, config.SpaceBeforeColon, config.SpaceAfterComma,
+ config.SpaceBeforeSemicolon, config.SpaceAfterSemicolon, config.IndentOnTryWith, config.SpaceAroundDelimiter,
+ config.MaxIfThenElseShortWidth, config.MaxInfixOperatorExpression, config.MaxRecordWidth,
+ myDefaultFormatConfig.MaxRecordNumberOfItems, myDefaultFormatConfig.RecordMultilineFormatter,
+ config.MaxArrayOrListWidth, myDefaultFormatConfig.MaxArrayOrListNumberOfItems,
+ myDefaultFormatConfig.ArrayOrListMultilineFormatter, config.MaxValueBindingWidth,
+ config.MaxFunctionBindingWidth, myDefaultFormatConfig.MaxDotGetExpressionWidth,
+ config.MultilineBlockBracketsOnSameColumn, config.NewlineBetweenTypeDefinitionAndMembers,
+ config.KeepIfThenInSameLine, config.MaxElmishWidth, config.SingleArgumentWebMode,
+ config.AlignFunctionSignatureToIndentation, config.AlternativeLongMemberDefinitions,
+ myDefaultFormatConfig.MultiLineLambdaClosingNewline, myDefaultFormatConfig.DisableElmishSyntax,
+ myDefaultFormatConfig.EndOfLine, myDefaultFormatConfig.StrictMode);
+ }
+}
diff --git a/ReSharper.FSharp/src/FSharp.Fantomas.Host/src/FantomasEndPoint.cs b/ReSharper.FSharp/src/FSharp.Fantomas.Host/src/FantomasEndPoint.cs
new file mode 100644
index 0000000000..7831ebda55
--- /dev/null
+++ b/ReSharper.FSharp/src/FSharp.Fantomas.Host/src/FantomasEndPoint.cs
@@ -0,0 +1,46 @@
+using JetBrains.Collections.Viewable;
+using JetBrains.Diagnostics;
+using JetBrains.Lifetimes;
+using JetBrains.Platform.RdFramework.ExternalProcess;
+using JetBrains.Platform.RdFramework.ExternalProcess.Util;
+using JetBrains.Rd.Impl;
+using JetBrains.Util;
+using JetBrains.Rd.Tasks;
+using JetBrains.ReSharper.Plugins.FSharp.Fantomas.Protocol;
+using JetBrains.ReSharper.Plugins.FSharp.Fantomas.Server;
+
+namespace JetBrains.ReSharper.Plugins.FSharp.Fantomas.Host
+{
+ internal class ExternalFormatterEndPoint : ProtocolEndPoint
+ {
+ private readonly FantomasCodeFormatter myCodeFormatter;
+ protected override string ProtocolName => "External Formatter Host";
+
+ public ExternalFormatterEndPoint() : base(FantomasProtocolConstants.PARENT_PROCESS_PID_ENV_VARIABLE)
+ {
+ myCodeFormatter = new FantomasCodeFormatter();
+ }
+
+ protected override RdSimpleDispatcher InitDispatcher(Lifetime lifetime, ILogger logger) =>
+ new RdSimpleDispatcher(lifetime, logger);
+
+ protected override void InitLogger(Lifetime lifetime, string path) =>
+ ProtocolEndPointUtil.InitLogger(path, lifetime, LoggingLevel.TRACE);
+
+ protected override RdFantomasModel InitModel(Lifetime lifetime, JetBrains.Rd.Impl.Protocol protocol)
+ {
+ var model = new RdFantomasModel(lifetime, protocol);
+
+ model.FormatSelection.Set(FormatSelection);
+ model.FormatDocument.Set(FormatDocument);
+ model.Exit.Advise(lifetime, Terminate);
+
+ return model;
+ }
+
+ private string FormatSelection(RdFormatSelectionArgs args) => myCodeFormatter.FormatSelection(args);
+ private string FormatDocument(RdFormatDocumentArgs args) => myCodeFormatter.FormatDocument(args);
+
+ protected override void Run(Lifetime lifetime, RdSimpleDispatcher dispatcher) => dispatcher.Run();
+ }
+}
diff --git a/ReSharper.FSharp/src/FSharp.Fantomas.Host/src/Program.cs b/ReSharper.FSharp/src/FSharp.Fantomas.Host/src/Program.cs
new file mode 100644
index 0000000000..794ab4ea6b
--- /dev/null
+++ b/ReSharper.FSharp/src/FSharp.Fantomas.Host/src/Program.cs
@@ -0,0 +1,27 @@
+using System;
+
+namespace JetBrains.ReSharper.Plugins.FSharp.Fantomas.Host
+{
+ public static class Program
+ {
+ public static void Main(string[] args)
+ {
+ AppDomain.CurrentDomain.AssemblyResolve += FantomasAssemblyResolver.Resolve;
+ MainInternal(args);
+ }
+
+ private static void MainInternal(string[] args)
+ {
+ var endPoint = new ExternalFormatterEndPoint();
+
+ var portValue = args[0];
+ var logPath = string.Empty;
+ if (args.Length > 1)
+ {
+ logPath = args[1];
+ }
+
+ endPoint.Start(portValue, logPath);
+ }
+ }
+}
diff --git a/ReSharper.FSharp/src/FSharp.Fantomas.Protocol/FSharp.Fantomas.Protocol.csproj b/ReSharper.FSharp/src/FSharp.Fantomas.Protocol/FSharp.Fantomas.Protocol.csproj
new file mode 100644
index 0000000000..d948d35971
--- /dev/null
+++ b/ReSharper.FSharp/src/FSharp.Fantomas.Protocol/FSharp.Fantomas.Protocol.csproj
@@ -0,0 +1,12 @@
+
+
+
+ net461
+ $(CSharpLanguageVersion)
+ JetBrains.ReSharper.Plugins.FSharp.Fantomas.Protocol
+ JetBrains.ReSharper.Plugins.FSharp.Fantomas.Protocol
+
+
+
+
+
diff --git a/ReSharper.FSharp/src/FSharp.Fantomas.Protocol/src/FantomasConnection.cs b/ReSharper.FSharp/src/FSharp.Fantomas.Protocol/src/FantomasConnection.cs
new file mode 100644
index 0000000000..943f696362
--- /dev/null
+++ b/ReSharper.FSharp/src/FSharp.Fantomas.Protocol/src/FantomasConnection.cs
@@ -0,0 +1,17 @@
+using JetBrains.DataFlow;
+using JetBrains.Lifetimes;
+using JetBrains.Platform.RdFramework.ExternalProcess;
+using JetBrains.Rd;
+using JetBrains.ReSharper.Plugins.FSharp.Fantomas.Client;
+
+namespace JetBrains.ReSharper.Plugins.FSharp.Fantomas.Protocol
+{
+ public class FantomasConnection : ProtocolConnection
+ {
+ public FantomasConnection(Lifetime lifetime, RdFantomasModel protocolModel,
+ IProtocol protocol, StartupOutputWriter startupOutputWriter, int processId, ISignal processUnexpectedExited)
+ : base(lifetime, protocolModel, protocol, startupOutputWriter, processId, processUnexpectedExited)
+ {
+ }
+ }
+}
diff --git a/ReSharper.FSharp/src/FSharp.Fantomas.Protocol/src/FantomasProcess.cs b/ReSharper.FSharp/src/FSharp.Fantomas.Protocol/src/FantomasProcess.cs
new file mode 100644
index 0000000000..21beda8840
--- /dev/null
+++ b/ReSharper.FSharp/src/FSharp.Fantomas.Protocol/src/FantomasProcess.cs
@@ -0,0 +1,70 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using JetBrains.Application.Processes;
+using JetBrains.Application.Threading;
+using JetBrains.Core;
+using JetBrains.DataFlow;
+using JetBrains.Diagnostics;
+using JetBrains.Lifetimes;
+using JetBrains.Platform.RdFramework.ExternalProcess;
+using JetBrains.Rd;
+using JetBrains.ReSharper.Plugins.FSharp.Fantomas.Client;
+using JetBrains.Util;
+
+namespace JetBrains.ReSharper.Plugins.FSharp.Fantomas.Protocol
+{
+ public class FantomasProcess : ProtocolExternalProcess
+ {
+ protected override string Name => "External Formatter";
+
+ protected override RdFantomasModel CreateModel(Lifetime lifetime, IProtocol protocol) =>
+ new RdFantomasModel(lifetime, protocol);
+
+ protected override FantomasConnection CreateConnection(Lifetime lifetime,
+ RdFantomasModel model, IProtocol protocol, StartupOutputWriter outputWriter, int processId,
+ Signal processUnexpectedExited) =>
+ new FantomasConnection(lifetime, model, protocol, outputWriter, processId,
+ processUnexpectedExited);
+
+ protected override ProcessStartInfo GetProcessStartInfo(int port)
+ {
+ var launchPath = GetType().Assembly.GetPath().Directory.Combine(FantomasProtocolConstants.PROCESS_FILENAME);
+ Assertion.Assert(launchPath.ExistsFile, $"can't find '{FantomasProtocolConstants.PROCESS_FILENAME}'");
+
+ return new ProcessStartInfo
+ {
+ Arguments =
+ $"{port} \"{FantomasProtocolConstants.LogFolder.Combine($"{DateTime.UtcNow:yyyy_MM_dd_HH_mm_ss_ffff}.log")}\"",
+ FileName = launchPath.FullPath
+ };
+ }
+
+ protected override IDictionary GetAdditionalProcessEnvVars()
+ {
+ return new Dictionary()
+ {
+ {
+ "RIDER_PLUGIN_ADDITIONAL_PROBING_PATHS",
+ Environment.GetEnvironmentVariable("RIDER_PLUGIN_ADDITIONAL_PROBING_PATHS")
+ },
+ {
+ FantomasProtocolConstants.PARENT_PROCESS_PID_ENV_VARIABLE,
+ Process.GetCurrentProcess().Id.ToString()
+ },
+ };
+ }
+
+ protected override bool Shutdown(RdFantomasModel model)
+ {
+ model.Proto.Scheduler.Queue(() => model.Exit.Fire(Unit.Instance));
+ return true;
+ }
+
+ public FantomasProcess(Lifetime lifetime, ILogger logger, IShellLocks locks,
+ IProcessStartInfoPatcher processInfoPatcher, JetProcessRuntimeRequest request)
+ : base(lifetime, logger, locks, processInfoPatcher, request)
+ {
+ }
+ }
+}
diff --git a/ReSharper.FSharp/src/FSharp.Fantomas.Protocol/src/FantomasProcessFactory.cs b/ReSharper.FSharp/src/FSharp.Fantomas.Protocol/src/FantomasProcessFactory.cs
new file mode 100644
index 0000000000..5280a6ada4
--- /dev/null
+++ b/ReSharper.FSharp/src/FSharp.Fantomas.Protocol/src/FantomasProcessFactory.cs
@@ -0,0 +1,36 @@
+using JetBrains.Annotations;
+using JetBrains.Application.Processes;
+using JetBrains.Application.Threading;
+using JetBrains.Lifetimes;
+using JetBrains.ProjectModel;
+using JetBrains.Util;
+
+namespace JetBrains.ReSharper.Plugins.FSharp.Fantomas.Protocol
+{
+ [SolutionComponent]
+ public class FantomasProcessFactory
+ {
+ [NotNull] private readonly ISolutionProcessStartInfoPatcher mySolutionProcessStartInfoPatcher;
+ [NotNull] private readonly ILogger myLogger;
+ [NotNull] private readonly IShellLocks myShellLocks;
+
+ public FantomasProcessFactory(
+ [NotNull] ISolutionProcessStartInfoPatcher solutionProcessStartInfoPatcher,
+ [NotNull] ILogger logger,
+ [NotNull] IShellLocks shellLocks)
+ {
+ mySolutionProcessStartInfoPatcher = solutionProcessStartInfoPatcher;
+ myLogger = logger;
+ myShellLocks = shellLocks;
+ }
+
+ public FantomasProcess Create(Lifetime lifetime)
+ {
+ return new FantomasProcess(lifetime,
+ myLogger,
+ myShellLocks,
+ mySolutionProcessStartInfoPatcher,
+ JetProcessRuntimeRequest.CreateInternalRuntime());
+ }
+ }
+}
diff --git a/ReSharper.FSharp/src/FSharp.Fantomas.Protocol/src/FantomasProtocolConstants.cs b/ReSharper.FSharp/src/FSharp.Fantomas.Protocol/src/FantomasProtocolConstants.cs
new file mode 100644
index 0000000000..ebdb75ea40
--- /dev/null
+++ b/ReSharper.FSharp/src/FSharp.Fantomas.Protocol/src/FantomasProtocolConstants.cs
@@ -0,0 +1,12 @@
+using JetBrains.Util;
+using JetBrains.Util.Logging;
+
+namespace JetBrains.ReSharper.Plugins.FSharp.Fantomas.Protocol
+{
+ public static class FantomasProtocolConstants
+ {
+ public const string PROCESS_FILENAME = "JetBrains.ReSharper.Plugins.FSharp.Fantomas.Host.exe";
+ public const string PARENT_PROCESS_PID_ENV_VARIABLE = "FSHARP_EXTERNAL_FORMATTER_PROCESS_PID";
+ public static readonly FileSystemPath LogFolder = Logger.LogFolderPath.Combine("ExternalFormatter");
+ }
+}
diff --git a/ReSharper.FSharp/src/FSharp.Psi.Features/FSharp.Psi.Features.fsproj b/ReSharper.FSharp/src/FSharp.Psi.Features/FSharp.Psi.Features.fsproj
index c2f09e7232..b7ff0b101f 100644
--- a/ReSharper.FSharp/src/FSharp.Psi.Features/FSharp.Psi.Features.fsproj
+++ b/ReSharper.FSharp/src/FSharp.Psi.Features/FSharp.Psi.Features.fsproj
@@ -105,6 +105,7 @@
+
@@ -230,11 +231,11 @@
-
+
diff --git a/ReSharper.FSharp/src/FSharp.Psi.Features/src/Formatter/FSharpReformatCode.fs b/ReSharper.FSharp/src/FSharp.Psi.Features/src/Formatter/FSharpReformatCode.fs
index 37b82b1f2c..13c3a23fae 100644
--- a/ReSharper.FSharp/src/FSharp.Psi.Features/src/Formatter/FSharpReformatCode.fs
+++ b/ReSharper.FSharp/src/FSharp.Psi.Features/src/Formatter/FSharpReformatCode.fs
@@ -1,14 +1,11 @@
namespace JetBrains.ReSharper.Plugins.FSharp.Services.Formatter
-open FSharp.Compiler.Text
-open Fantomas
-open Fantomas.FormatConfig
open JetBrains.Application.Infra
open JetBrains.DocumentModel
open JetBrains.DocumentModel.Impl
+open JetBrains.ProjectModel
open JetBrains.ReSharper.Feature.Services.CSharp.CodeCleanup
open JetBrains.ReSharper.Feature.Services.CodeCleanup
-open JetBrains.ReSharper.Plugins.FSharp
open JetBrains.ReSharper.Plugins.FSharp.Psi
open JetBrains.ReSharper.Plugins.FSharp.Util
open JetBrains.ReSharper.Psi
@@ -39,84 +36,40 @@ type FSharpReformatCode() =
match fsFile.ParseTree with // todo: completion on enter after with
| None -> ()
- | Some parseTree ->
+ | Some _ ->
let filePath = sourceFile.GetLocation().FullPath
let document = sourceFile.Document :?> DocumentBase
let text = document.GetText()
- let source = SourceOrigin.SourceText(SourceText.ofString(document.GetText()))
let checkerService = fsFile.CheckerService
-
let solution = fsFile.GetSolution()
let settings = sourceFile.GetSettingsStoreWithEditorConfig()
let languageService = fsFile.Language.LanguageServiceNotNull()
let formatter = languageService.CodeFormatter
+ let codeFormatterProvider = solution.GetComponent()
let settings =
formatter.GetFormatterSettings(solution, sourceFile, settings, false) :?> FSharpFormatSettingsKey
- let formatConfig =
- { FormatConfig.Default with
- IndentSize = settings.INDENT_SIZE
- MaxLineLength = settings.WRAP_LIMIT
- SpaceBeforeParameter = settings.SpaceBeforeParameter
- SpaceBeforeLowercaseInvocation = settings.SpaceBeforeLowercaseInvocation
- SpaceBeforeUppercaseInvocation = settings.SpaceBeforeUppercaseInvocation
- SpaceBeforeClassConstructor = settings.SpaceBeforeClassConstructor
- SpaceBeforeMember = settings.SpaceBeforeMember
- SpaceBeforeColon = settings.SpaceBeforeColon
- SpaceAfterComma = settings.SpaceAfterComma
- SpaceBeforeSemicolon = settings.SpaceBeforeSemicolon
- SpaceAfterSemicolon = settings.SpaceAfterSemicolon
- IndentOnTryWith = settings.IndentOnTryWith
- SpaceAroundDelimiter = settings.SpaceAroundDelimiter
- MaxIfThenElseShortWidth = settings.MaxIfThenElseShortWidth
- MaxInfixOperatorExpression = settings.MaxInfixOperatorExpression
- MaxRecordWidth = settings.MaxRecordWidth
- MaxArrayOrListWidth = settings.MaxArrayOrListWidth
- MaxValueBindingWidth = settings.MaxValueBindingWidth
- MaxFunctionBindingWidth = settings.MaxFunctionBindingWidth
- MultilineBlockBracketsOnSameColumn = settings.MultilineBlockBracketsOnSameColumn
- NewlineBetweenTypeDefinitionAndMembers = settings.NewlineBetweenTypeDefinitionAndMembers
- KeepIfThenInSameLine = settings.KeepIfThenInSameLine
- MaxElmishWidth = settings.MaxElmishWidth
- SingleArgumentWebMode = settings.SingleArgumentWebMode
- AlignFunctionSignatureToIndentation = settings.AlignFunctionSignatureToIndentation
- AlternativeLongMemberDefinitions = settings.AlternativeLongMemberDefinitions }
-
let stamp = document.LastModificationStamp
let modificationSide = TextModificationSide.NotSpecified
let newLineText = sourceFile.DetectLineEnding().GetPresentation()
let parsingOptions = checkerService.FcsProjectProvider.GetParsingOptions(sourceFile)
- let checker = checkerService.Checker
let change =
if isNotNull rangeMarker then
try
let range = ofDocumentRange rangeMarker.DocumentRange
-
let formatted =
- CodeFormatter
- .FormatSelectionAsync(filePath, range, source, formatConfig, parsingOptions, checker)
- .RunAsTask()
- .Replace("\r\n", newLineText)
+ codeFormatterProvider.FormatSelection(filePath, range, text, settings, parsingOptions, newLineText)
let offset = rangeMarker.DocumentRange.StartOffset.Offset
let oldLength = rangeMarker.DocumentRange.Length
Some(DocumentChange(document, offset, oldLength, formatted, stamp, modificationSide))
with _ -> None
else
- let parsingOptions = checkerService.FcsProjectProvider.GetParsingOptions(sourceFile)
- let defines = parsingOptions.ConditionalCompilationDefines
- let formatTask =
- if List.isEmpty defines
- then CodeFormatter.FormatASTAsync(parseTree, filePath, defines, Some source, formatConfig)
- else CodeFormatter.FormatDocumentAsync(filePath, source, formatConfig, parsingOptions, checker)
-
let formatted =
- formatTask
- .RunAsTask()
- .Replace("\r\n", newLineText)
+ codeFormatterProvider.FormatDocument(filePath, text, settings, parsingOptions, newLineText)
Some(DocumentChange(document, 0, text.Length, formatted, stamp, modificationSide))
match change with
diff --git a/ReSharper.FSharp/src/FSharp.Psi.Features/src/Formatter/FantomasFormatterProvider.fs b/ReSharper.FSharp/src/FSharp.Psi.Features/src/Formatter/FantomasFormatterProvider.fs
new file mode 100644
index 0000000000..3319a817e8
--- /dev/null
+++ b/ReSharper.FSharp/src/FSharp.Psi.Features/src/Formatter/FantomasFormatterProvider.fs
@@ -0,0 +1,64 @@
+namespace JetBrains.ReSharper.Plugins.FSharp.Services.Formatter
+
+open System
+open FSharp.Compiler.CodeAnalysis
+open FSharp.Compiler.Text
+open JetBrains.Lifetimes
+open JetBrains.ProjectModel
+open JetBrains.Rd.Tasks
+open JetBrains.ReSharper.Plugins.FSharp.Fantomas.Client
+open JetBrains.ReSharper.Plugins.FSharp.Fantomas.Protocol
+
+[]
+type FantomasFormatterProvider(solution: ISolution, fantomasFactory: FantomasProcessFactory) =
+ let mutable connection: FantomasConnection = null
+
+ let isConnectionAlive () =
+ isNotNull connection && connection.IsActive
+
+ let connect () =
+ if isConnectionAlive () then () else
+ let formatterHostLifetime = Lifetime.Define(solution.GetLifetime())
+ connection <- fantomasFactory.Create(formatterHostLifetime.Lifetime).Run()
+
+ let execute (action: unit -> string) =
+ connect ()
+ connection.Execute(action)
+
+ let convertRange (range: range) =
+ RdFcsRange(range.FileName, range.StartLine, range.StartColumn, range.EndLine, range.EndColumn)
+
+ let convertFormatSettings (settings: FSharpFormatSettingsKey) =
+ RdFantomasFormatConfig
+ (settings.INDENT_SIZE, settings.WRAP_LIMIT, settings.SpaceBeforeParameter,
+ settings.SpaceBeforeLowercaseInvocation, settings.SpaceBeforeUppercaseInvocation,
+ settings.SpaceBeforeClassConstructor, settings.SpaceBeforeMember, settings.SpaceBeforeColon,
+ settings.SpaceAfterComma, settings.SpaceBeforeSemicolon, settings.SpaceAfterSemicolon,
+ settings.IndentOnTryWith, settings.SpaceAroundDelimiter, settings.MaxIfThenElseShortWidth,
+ settings.MaxInfixOperatorExpression, settings.MaxRecordWidth, settings.MaxArrayOrListWidth,
+ settings.MaxValueBindingWidth, settings.MaxFunctionBindingWidth,
+ settings.MultilineBlockBracketsOnSameColumn, settings.NewlineBetweenTypeDefinitionAndMembers,
+ settings.KeepIfThenInSameLine, settings.MaxElmishWidth, settings.SingleArgumentWebMode,
+ settings.AlignFunctionSignatureToIndentation, settings.AlternativeLongMemberDefinitions,
+ settings.SemicolonAtEndOfLine)
+
+ let convertParsingOptions (options: FSharpParsingOptions) =
+ let lightSyntax =
+ match options.LightSyntax with
+ | Some x -> Nullable x
+ | None -> Nullable()
+
+ RdFcsParsingOptions(Array.last options.SourceFiles, lightSyntax,
+ List.toArray options.ConditionalCompilationDefines, options.IsExe)
+
+ member x.FormatSelection(filePath, range, source, settings, options, newLineText) =
+ let args = RdFormatSelectionArgs(convertRange range, filePath, source, convertFormatSettings settings,
+ convertParsingOptions options, newLineText)
+
+ execute (fun () -> connection.ProtocolModel.FormatSelection.Sync(args, RpcTimeouts.Maximal))
+
+ member x.FormatDocument(filePath, source, settings, options, newLineText) =
+ let args = RdFormatDocumentArgs(filePath, source, convertFormatSettings settings, convertParsingOptions options,
+ newLineText)
+
+ execute (fun () -> connection.ProtocolModel.FormatDocument.Sync(args, RpcTimeouts.Maximal))
diff --git a/ReSharper.FSharp/src/FSharp.TypeProviders.Host/src/TypeProvidersEndPoint.cs b/ReSharper.FSharp/src/FSharp.TypeProviders.Host/src/TypeProvidersEndPoint.cs
index a0634c10b6..9bb640d78e 100644
--- a/ReSharper.FSharp/src/FSharp.TypeProviders.Host/src/TypeProvidersEndPoint.cs
+++ b/ReSharper.FSharp/src/FSharp.TypeProviders.Host/src/TypeProvidersEndPoint.cs
@@ -2,12 +2,12 @@
using JetBrains.Diagnostics;
using JetBrains.Lifetimes;
using JetBrains.Platform.RdFramework.ExternalProcess;
+using JetBrains.Platform.RdFramework.ExternalProcess.Util;
using JetBrains.Rd.Impl;
using JetBrains.ReSharper.Plugins.FSharp.TypeProviders.Host.Hosts;
using JetBrains.ReSharper.Plugins.FSharp.TypeProviders.Protocol;
using JetBrains.Rider.FSharp.TypeProviders.Protocol.Server;
using JetBrains.Util;
-using JetBrains.Util.Logging;
namespace JetBrains.ReSharper.Plugins.FSharp.TypeProviders.Host
{
@@ -18,7 +18,7 @@ public class
protected override string ProtocolName => "Out-of-Process Type Providers Host";
- public TypeProvidersEndPoint() : base(ProtocolConstants.TypeProvidersHostPid)
+ public TypeProvidersEndPoint() : base(TypeProvidersProtocolConstants.TypeProvidersHostPid)
{
}
@@ -30,12 +30,7 @@ protected override RdSimpleDispatcher InitDispatcher(Lifetime lifetime, ILogger
protected override void InitLogger(Lifetime lifetime, string path)
{
- LogManager.Instance.SetConfig(new XmlLogConfigModel());
- var logPath = FileSystemPath.TryParse(path);
- if (logPath.IsNullOrEmpty()) return;
-
- var logEventListener = new FileLogEventListener(logPath);
- LogManager.Instance.AddOmnipresentLogger(lifetime, logEventListener, LoggingLevel.TRACE);
+ ProtocolEndPointUtil.InitLogger(path, lifetime, LoggingLevel.TRACE);
Logger.Log(LoggingLevel.INFO, $"Process Runtime: {RuntimeInformation.FrameworkDescription}");
}
diff --git a/ReSharper.FSharp/src/FSharp.TypeProviders.Protocol/src/TypeProvidersExternalProcess.cs b/ReSharper.FSharp/src/FSharp.TypeProviders.Protocol/src/TypeProvidersExternalProcess.cs
index e3216d74b3..77b794468d 100644
--- a/ReSharper.FSharp/src/FSharp.TypeProviders.Protocol/src/TypeProvidersExternalProcess.cs
+++ b/ReSharper.FSharp/src/FSharp.TypeProviders.Protocol/src/TypeProvidersExternalProcess.cs
@@ -47,8 +47,8 @@ protected override ProcessStartInfo GetProcessStartInfo(int port)
private ProcessStartInfo GetCoreProcessStartInfo(int port, FileSystemPath basePath)
{
var sdkMajorVersion = myNuGetVersion.Major < 3 ? 3 : myNuGetVersion.Major;
- var runtimeConfigPath = basePath.Combine(ProtocolConstants.CoreRuntimeConfigFilename(sdkMajorVersion));
- var fileSystemPath = basePath.Combine(ProtocolConstants.TypeProvidersHostCoreFilename);
+ var runtimeConfigPath = basePath.Combine(TypeProvidersProtocolConstants.CoreRuntimeConfigFilename(sdkMajorVersion));
+ var fileSystemPath = basePath.Combine(TypeProvidersProtocolConstants.TypeProvidersHostCoreFilename);
var dotnetArgs = $"--runtimeconfig \"{runtimeConfigPath}\"";
Assertion.Assert(fileSystemPath.ExistsFile, $"can't find '{fileSystemPath.FullPath}'");
@@ -57,7 +57,7 @@ private ProcessStartInfo GetCoreProcessStartInfo(int port, FileSystemPath basePa
var processStartInfo = new ProcessStartInfo
{
Arguments =
- $"{dotnetArgs} \"{fileSystemPath.FullPath}\" {port} \"{ProtocolConstants.LogFolder.Combine($"{DateTime.UtcNow:yyyy_MM_dd_HH_mm_ss_ffff}.log")}\"",
+ $"{dotnetArgs} \"{fileSystemPath.FullPath}\" {port} \"{TypeProvidersProtocolConstants.LogFolder.Combine($"{DateTime.UtcNow:yyyy_MM_dd_HH_mm_ss_ffff}.log")}\"",
FileName = "exec"
};
@@ -66,13 +66,13 @@ private ProcessStartInfo GetCoreProcessStartInfo(int port, FileSystemPath basePa
private static ProcessStartInfo GetFrameworkProcessStartInfo(int port, FileSystemPath basePath)
{
- var fileSystemPath = basePath.Combine(ProtocolConstants.TypeProvidersHostFrameworkFilename);
+ var fileSystemPath = basePath.Combine(TypeProvidersProtocolConstants.TypeProvidersHostFrameworkFilename);
Assertion.Assert(fileSystemPath.ExistsFile, $"can't find '{fileSystemPath.FullPath}'");
var processStartInfo = new ProcessStartInfo
{
Arguments =
- $"{port} \"{ProtocolConstants.LogFolder.Combine($"{DateTime.UtcNow:yyyy_MM_dd_HH_mm_ss_ffff}.log")}\"",
+ $"{port} \"{TypeProvidersProtocolConstants.LogFolder.Combine($"{DateTime.UtcNow:yyyy_MM_dd_HH_mm_ss_ffff}.log")}\"",
FileName = fileSystemPath.FullPath
};
@@ -88,7 +88,7 @@ protected override IDictionary GetAdditionalProcessEnvVars()
Environment.GetEnvironmentVariable("RIDER_PLUGIN_ADDITIONAL_PROBING_PATHS")
},
{
- ProtocolConstants.TypeProvidersHostPid,
+ TypeProvidersProtocolConstants.TypeProvidersHostPid,
Process.GetCurrentProcess().Id.ToString()
}
};
diff --git a/ReSharper.FSharp/src/FSharp.TypeProviders.Protocol/src/ProtocolConstants.cs b/ReSharper.FSharp/src/FSharp.TypeProviders.Protocol/src/TypeProvidersProtocolConstants.cs
similarity index 93%
rename from ReSharper.FSharp/src/FSharp.TypeProviders.Protocol/src/ProtocolConstants.cs
rename to ReSharper.FSharp/src/FSharp.TypeProviders.Protocol/src/TypeProvidersProtocolConstants.cs
index fab0435497..734eb5ffd3 100644
--- a/ReSharper.FSharp/src/FSharp.TypeProviders.Protocol/src/ProtocolConstants.cs
+++ b/ReSharper.FSharp/src/FSharp.TypeProviders.Protocol/src/TypeProvidersProtocolConstants.cs
@@ -3,7 +3,7 @@
namespace JetBrains.ReSharper.Plugins.FSharp.TypeProviders.Protocol
{
- public static class ProtocolConstants
+ public static class TypeProvidersProtocolConstants
{
public const string TypeProvidersHostPid = "TypeProvidersHost";
diff --git a/rider-fsharp/build.gradle.kts b/rider-fsharp/build.gradle.kts
index 303c4dfbcf..1e0a1cf8b9 100644
--- a/rider-fsharp/build.gradle.kts
+++ b/rider-fsharp/build.gradle.kts
@@ -107,8 +107,7 @@ val libFiles = listOf(
"FSharp.Common/bin/$buildConfiguration/net461/FSharp.Core.xml",
"FSharp.Common/bin/$buildConfiguration/net461/FSharp.Compiler.Service.dll", // todo: add pdb after next repack
"FSharp.Common/bin/$buildConfiguration/net461/FSharp.DependencyManager.Nuget.dll",
- "FSharp.Common/bin/$buildConfiguration/net461/FSharp.Compiler.Interactive.Settings.dll",
- "FSharp.Psi.Features/bin/$buildConfiguration/net461/Fantomas.dll")
+ "FSharp.Common/bin/$buildConfiguration/net461/FSharp.Compiler.Interactive.Settings.dll")
val pluginFiles = listOf(
"FSharp.ProjectModelBase/bin/$buildConfiguration/net461/JetBrains.ReSharper.Plugins.FSharp.ProjectModelBase",
@@ -129,6 +128,14 @@ val typeProvidersFiles = listOf(
"FSharp.TypeProviders.Host/bin/$buildConfiguration/netcoreapp3.1/tploader5.win.runtimeconfig.json",
"FSharp.TypeProviders.Host/bin/$buildConfiguration/netcoreapp3.1/tploader5.unix.runtimeconfig.json")
+val fantomasFiles = listOf(
+ "FSharp.Fantomas.Protocol/bin/$buildConfiguration/net461/JetBrains.ReSharper.Plugins.FSharp.Fantomas.Protocol.dll",
+ "FSharp.Fantomas.Protocol/bin/$buildConfiguration/net461/JetBrains.ReSharper.Plugins.FSharp.Fantomas.Protocol.pdb",
+ "FSharp.Fantomas.Host/bin/$buildConfiguration/net461/JetBrains.ReSharper.Plugins.FSharp.Fantomas.Host.exe",
+ "FSharp.Fantomas.Host/bin/$buildConfiguration/net461/JetBrains.ReSharper.Plugins.FSharp.Fantomas.Host.runtimeconfig.json",
+ "FSharp.Fantomas.Host/bin/$buildConfiguration/net461/JetBrains.ReSharper.Plugins.FSharp.Fantomas.Host.pdb",
+ "FSharp.Fantomas.Host/bin/$buildConfiguration/net461/Fantomas.dll")
+
val dotNetSdkPath by lazy {
val sdkPath = intellij.ideaDependency.classes.resolve("lib").resolve("DotNetSdkForRdPlugins")
if (sdkPath.isDirectory.not()) error("$sdkPath does not exist or not a directory")
@@ -159,6 +166,9 @@ configure {
val typeProviderClientOutput = File(repoRoot, "ReSharper.FSharp/src/FSharp.TypeProviders.Protocol/src/Client")
val typeProviderServerOutput = File(repoRoot, "ReSharper.FSharp/src/FSharp.TypeProviders.Protocol/src/Server")
+ val fantomasServerOutput = File(repoRoot, "ReSharper.FSharp/src/FSharp.Fantomas.Protocol/src/Server")
+ val fantomasClientOutput = File(repoRoot, "ReSharper.FSharp/src/FSharp.Fantomas.Protocol/src/Client")
+
verbose = true
hashFolder = "build/rdgen"
logger.info("Configuring rdgen params")
@@ -202,11 +212,26 @@ configure {
namespace = "JetBrains.Rider.FSharp.TypeProviders.Protocol.Server"
directory = "$typeProviderServerOutput"
}
+
+ generator {
+ language = "csharp"
+ transform = "asis"
+ root = "model.RdFantomasModel"
+ namespace = "JetBrains.ReSharper.Plugins.FSharp.Fantomas.Client"
+ directory = "$fantomasClientOutput"
+ }
+ generator {
+ language = "csharp"
+ transform = "reversed"
+ root = "model.RdFantomasModel"
+ namespace = "JetBrains.ReSharper.Plugins.FSharp.Fantomas.Server"
+ directory = "$fantomasServerOutput"
+ }
}
tasks {
withType {
- var files = libFiles + pluginFiles.map { "$it.dll" } + pluginFiles.map { "$it.pdb" } + typeProvidersFiles
+ var files = libFiles + pluginFiles.map { "$it.dll" } + pluginFiles.map { "$it.pdb" } + typeProvidersFiles + fantomasFiles
files = files.map { "$resharperPluginPath/src/$it" }
if (name == IntelliJPlugin.PREPARE_TESTING_SANDBOX_TASK_NAME) {
diff --git a/rider-fsharp/protocol/src/kotlin/model/RdFantomasModel.kt b/rider-fsharp/protocol/src/kotlin/model/RdFantomasModel.kt
new file mode 100644
index 0000000000..78af82e7a5
--- /dev/null
+++ b/rider-fsharp/protocol/src/kotlin/model/RdFantomasModel.kt
@@ -0,0 +1,73 @@
+package model
+
+import com.jetbrains.rider.model.nova.ide.SolutionModel
+import com.jetbrains.rd.generator.nova.*
+import com.jetbrains.rd.generator.nova.PredefinedType.*
+import com.jetbrains.rd.generator.nova.csharp.CSharp50Generator
+import java.io.File
+
+@Suppress("unused")
+object RdFantomasModel : Root() {
+
+ private val rdFcsParsingOptions = structdef {
+ field("lastSourceFile", string)
+ field("lightSyntax", bool.nullable)
+ field("conditionalCompilationDefines", array(string))
+ field("isExe", bool)
+ }
+
+ private val rdFcsRange = structdef {
+ field("fileName", string)
+ field("startLine", int)
+ field("startCol", int)
+ field("endLine", int)
+ field("endCol", int)
+ }
+
+ private val rdFantomasFormatConfig = structdef {
+ field("indentSize", int)
+ field("maxLineLength", int)
+ field("spaceBeforeParameter", bool)
+ field("spaceBeforeLowercaseInvocation", bool)
+ field("spaceBeforeUppercaseInvocation", bool)
+ field("spaceBeforeClassConstructor", bool)
+ field("spaceBeforeMember", bool)
+ field("spaceBeforeColon", bool)
+ field("spaceAfterComma", bool)
+ field("spaceBeforeSemicolon", bool)
+ field("spaceAfterSemicolon", bool)
+ field("indentOnTryWith", bool)
+ field("spaceAroundDelimiter", bool)
+ field("maxIfThenElseShortWidth", int)
+ field("maxInfixOperatorExpression", int)
+ field("maxRecordWidth", int)
+ field("maxArrayOrListWidth", int)
+ field("maxValueBindingWidth", int)
+ field("maxFunctionBindingWidth", int)
+ field("multilineBlockBracketsOnSameColumn", bool)
+ field("newlineBetweenTypeDefinitionAndMembers", bool)
+ field("keepIfThenInSameLine", bool)
+ field("maxElmishWidth", int)
+ field("singleArgumentWebMode", bool)
+ field("alignFunctionSignatureToIndentation", bool)
+ field("alternativeLongMemberDefinitions", bool)
+ field("semicolonAtEndOfLine", bool)
+ }
+
+ private val rdFormatArgs = basestruct {
+ field("fileName", string)
+ field("source", string)
+ field("formatConfig", rdFantomasFormatConfig)
+ field("parsingOptions", rdFcsParsingOptions)
+ field("newLineText", string)
+ }
+
+ init {
+ call("formatDocument", structdef("rdFormatDocumentArgs") extends rdFormatArgs {}, string)
+ call("formatSelection", structdef("rdFormatSelectionArgs") extends rdFormatArgs {
+ field("range", rdFcsRange)
+ }, string)
+
+ signal("exit", void)
+ }
+}
diff --git a/rider-fsharp/src/test/kotlin/Extensions.kt b/rider-fsharp/src/test/kotlin/Extensions.kt
index f1a6861602..d00fb79990 100644
--- a/rider-fsharp/src/test/kotlin/Extensions.kt
+++ b/rider-fsharp/src/test/kotlin/Extensions.kt
@@ -1,8 +1,10 @@
+import com.intellij.openapi.project.Project
import com.jetbrains.rdclient.protocol.protocolHost
import com.jetbrains.rider.inTests.TestHost
import com.jetbrains.rider.plugins.fsharp.rdFSharpModel
import com.jetbrains.rider.projectView.solution
import com.jetbrains.rider.test.base.BaseTestWithSolution
+import com.jetbrains.rider.test.base.EditorTestBase
import com.jetbrains.rider.test.scriptingApi.dumpSevereHighlighters
import java.io.PrintStream
@@ -14,12 +16,19 @@ fun com.intellij.openapi.editor.Editor.dumpTypeProviders(stream: PrintStream) {
}
}
-fun BaseTestWithSolution.withTypeProviders(function: () -> Unit) {
- val typeProvidersSetting = "FSharp/FSharpOptions/FSharpExperimentalFeatures/OutOfProcessTypeProviders/@EntryValue"
- TestHost.getInstance(project.protocolHost).setSetting(typeProvidersSetting, "true")
+fun withSetting(project: Project, setting: String, function: () -> Unit) {
+ TestHost.getInstance(project.protocolHost).setSetting(setting, "true")
try {
function()
} finally {
- TestHost.getInstance(project.protocolHost).setSetting(typeProvidersSetting, "false")
+ TestHost.getInstance(project.protocolHost).setSetting(setting, "false")
}
}
+
+fun BaseTestWithSolution.withTypeProviders(function: () -> Unit) {
+ withSetting(project, "FSharp/FSharpOptions/FSharpExperimentalFeatures/OutOfProcessTypeProviders/@EntryValue", function)
+}
+
+fun withEditorConfig(project: Project, function: () -> Unit) {
+ withSetting(project, "CodeStyle/EditorConfig/EnableEditorConfigSupport", function)
+}
diff --git a/rider-fsharp/src/test/kotlin/fantomas/FantomasTest.kt b/rider-fsharp/src/test/kotlin/fantomas/FantomasTest.kt
new file mode 100644
index 0000000000..f4f4c10159
--- /dev/null
+++ b/rider-fsharp/src/test/kotlin/fantomas/FantomasTest.kt
@@ -0,0 +1,39 @@
+package fantomas
+
+import com.jetbrains.rdclient.testFramework.executeWithGold
+import com.jetbrains.rdclient.testFramework.waitForDaemon
+import com.jetbrains.rider.test.annotations.TestEnvironment
+import com.jetbrains.rider.test.base.EditorTestBase
+import com.jetbrains.rider.test.enums.CoreVersion
+import com.jetbrains.rider.test.scriptingApi.dumpOpenedDocument
+import com.jetbrains.rider.test.scriptingApi.reformatCode
+import com.jetbrains.rider.test.scriptingApi.withOpenedEditor
+import org.testng.annotations.Test
+import withEditorConfig
+
+@Test
+@TestEnvironment(coreVersion = CoreVersion.DEFAULT)
+class FantomasTest : EditorTestBase() {
+ override fun getSolutionDirectoryName() = "FormatCodeApp"
+
+ @Test
+ fun withEditorConfig() = doTest("EditorConfig.fs")
+
+ @Test
+ fun simpleFormatting() = doTest("Simple.fs")
+
+ @Test
+ fun formatLastFile() = doTest("Program.fs")
+
+ private fun doTest(fileName: String) {
+ withEditorConfig(project) {
+ withOpenedEditor(fileName) {
+ waitForDaemon()
+ reformatCode()
+ executeWithGold(testGoldFile) {
+ dumpOpenedDocument(it, project!!, false)
+ }
+ }
+ }
+ }
+}
diff --git a/rider-fsharp/testData/fantomas/FantomasTest/formatLastFile/gold/formatLastFile.gold b/rider-fsharp/testData/fantomas/FantomasTest/formatLastFile/gold/formatLastFile.gold
new file mode 100644
index 0000000000..5b77edbd9d
--- /dev/null
+++ b/rider-fsharp/testData/fantomas/FantomasTest/formatLastFile/gold/formatLastFile.gold
@@ -0,0 +1,2 @@
+[]
+let main argv = 0
diff --git a/rider-fsharp/testData/fantomas/FantomasTest/simpleFormatting/gold/simpleFormatting.gold b/rider-fsharp/testData/fantomas/FantomasTest/simpleFormatting/gold/simpleFormatting.gold
new file mode 100644
index 0000000000..ed46f25e8d
--- /dev/null
+++ b/rider-fsharp/testData/fantomas/FantomasTest/simpleFormatting/gold/simpleFormatting.gold
@@ -0,0 +1,5 @@
+module Simple
+
+type A() =
+ class
+ end
diff --git a/rider-fsharp/testData/fantomas/FantomasTest/withEditorConfig/gold/withEditorConfig.gold b/rider-fsharp/testData/fantomas/FantomasTest/withEditorConfig/gold/withEditorConfig.gold
new file mode 100644
index 0000000000..8aea603375
--- /dev/null
+++ b/rider-fsharp/testData/fantomas/FantomasTest/withEditorConfig/gold/withEditorConfig.gold
@@ -0,0 +1,5 @@
+module EditorConfig
+
+type A () =
+ class
+ end
diff --git a/rider-fsharp/testData/solutions/FormatCodeApp/FormatCodeApp.sln b/rider-fsharp/testData/solutions/FormatCodeApp/FormatCodeApp.sln
new file mode 100644
index 0000000000..4994f64918
--- /dev/null
+++ b/rider-fsharp/testData/solutions/FormatCodeApp/FormatCodeApp.sln
@@ -0,0 +1,16 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FormatCodeApp", "FormatCodeApp\FormatCodeApp.fsproj", "{CB87B1A7-9600-4AE2-986C-4333196C6AFE}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {CB87B1A7-9600-4AE2-986C-4333196C6AFE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CB87B1A7-9600-4AE2-986C-4333196C6AFE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CB87B1A7-9600-4AE2-986C-4333196C6AFE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CB87B1A7-9600-4AE2-986C-4333196C6AFE}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+EndGlobal
diff --git a/rider-fsharp/testData/solutions/FormatCodeApp/FormatCodeApp/Folder/.editorconfig b/rider-fsharp/testData/solutions/FormatCodeApp/FormatCodeApp/Folder/.editorconfig
new file mode 100644
index 0000000000..4337b664d0
--- /dev/null
+++ b/rider-fsharp/testData/solutions/FormatCodeApp/FormatCodeApp/Folder/.editorconfig
@@ -0,0 +1,2 @@
+[*.fs]
+fsharp_space_before_class_constructor=true
diff --git a/rider-fsharp/testData/solutions/FormatCodeApp/FormatCodeApp/Folder/EditorConfig.fs b/rider-fsharp/testData/solutions/FormatCodeApp/FormatCodeApp/Folder/EditorConfig.fs
new file mode 100644
index 0000000000..f226a682d1
--- /dev/null
+++ b/rider-fsharp/testData/solutions/FormatCodeApp/FormatCodeApp/Folder/EditorConfig.fs
@@ -0,0 +1,3 @@
+module EditorConfig
+
+ type A() = class end
diff --git a/rider-fsharp/testData/solutions/FormatCodeApp/FormatCodeApp/FormatCodeApp.fsproj b/rider-fsharp/testData/solutions/FormatCodeApp/FormatCodeApp/FormatCodeApp.fsproj
new file mode 100644
index 0000000000..b57bfd3108
--- /dev/null
+++ b/rider-fsharp/testData/solutions/FormatCodeApp/FormatCodeApp/FormatCodeApp.fsproj
@@ -0,0 +1,15 @@
+
+
+
+ Exe
+ netcoreapp3.1
+
+
+
+
+
+
+
+
+
+
diff --git a/rider-fsharp/testData/solutions/FormatCodeApp/FormatCodeApp/Program.fs b/rider-fsharp/testData/solutions/FormatCodeApp/FormatCodeApp/Program.fs
new file mode 100644
index 0000000000..1caf613841
--- /dev/null
+++ b/rider-fsharp/testData/solutions/FormatCodeApp/FormatCodeApp/Program.fs
@@ -0,0 +1,2 @@
+ []
+ let main argv = 0
diff --git a/rider-fsharp/testData/solutions/FormatCodeApp/FormatCodeApp/Simple.fs b/rider-fsharp/testData/solutions/FormatCodeApp/FormatCodeApp/Simple.fs
new file mode 100644
index 0000000000..d3ccaaec1d
--- /dev/null
+++ b/rider-fsharp/testData/solutions/FormatCodeApp/FormatCodeApp/Simple.fs
@@ -0,0 +1,3 @@
+module Simple
+
+ type A() = class end