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