From 4288b902c258f5106f3bc42295c7c1233e8d5d78 Mon Sep 17 00:00:00 2001 From: Alex Berezhnykh Date: Fri, 24 Feb 2023 01:28:27 +0300 Subject: [PATCH 1/8] wip --- .../src/FantomasCodeFormatter.cs | 59 +++++++++++++++---- .../src/FantomasEndPoint.cs | 2 +- .../src/CodeCleanup/FSharpReformatCode.fs | 51 +++++++++------- .../src/CodeCleanup/FantomasHost.fs | 10 +++- .../src/kotlin/model/RdFantomasModel.kt | 13 +++- 5 files changed, 99 insertions(+), 36 deletions(-) diff --git a/ReSharper.FSharp/src/FSharp.Fantomas.Host/src/FantomasCodeFormatter.cs b/ReSharper.FSharp/src/FSharp.Fantomas.Host/src/FantomasCodeFormatter.cs index a9b9addc0a..34fbe5c767 100644 --- a/ReSharper.FSharp/src/FSharp.Fantomas.Host/src/FantomasCodeFormatter.cs +++ b/ReSharper.FSharp/src/FSharp.Fantomas.Host/src/FantomasCodeFormatter.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; +using Fantomas.Core; +using FSharp.Compiler.Text; using JetBrains.Diagnostics; using JetBrains.Extension; using JetBrains.ReSharper.Plugins.FSharp.Fantomas.Protocol; @@ -24,6 +26,7 @@ internal static class FantomasCodeFormatter private static readonly Version Version45 = Version.Parse("4.5"); private static readonly Version Version46 = Version.Parse("4.6"); + private static readonly Version Version60 = Version.Parse("6.0"); private static Type GetCodeFormatter() => FantomasAssembly @@ -100,12 +103,18 @@ private static Type GetFSharpParsingOptions() return Type.GetType(qualifiedName).NotNull($"{qualifiedName} must exist"); } - private static Type GetFormatConfigType() => - FantomasAssembly + private static Type GetFormatConfigType() + { + var formatConfig = FantomasAssembly .GetType($"{FantomasAssemblyName}.FormatConfig") - .NotNull("FormatConfig must exist") - .GetNestedType("FormatConfig") - .NotNull(); + .NotNull("FormatConfig must exist"); + + return CurrentVersion >= Version60 + ? formatConfig + : formatConfig + .GetNestedType("FormatConfig") + .NotNull(); + } private static readonly Type CodeFormatterType = GetCodeFormatter(); private static readonly Type FSharpParsingOptionsType = GetFSharpParsingOptions(); @@ -119,8 +128,8 @@ private static readonly ConstructorInfo CreateFSharpParsingOptions = FSharpParsingOptionsType?.GetConstructors().Single(); private static readonly MethodInfo FormatSelectionMethod = CodeFormatterType.GetMethod("FormatSelectionAsync"); - private static readonly MethodInfo FormatDocumentMethod = CodeFormatterType.GetMethod("FormatDocumentAsync"); private static readonly MethodInfo MakeRangeMethod = CodeFormatterType.GetMethod("MakeRange"); + private static readonly MethodInfo MakePositionMethod = CodeFormatterType.GetMethod("MakePosition"); private static readonly MethodInfo SourceOriginConstructor = GetSourceOriginStringConstructor(); private static readonly MethodInfo CreateOptionMethod = @@ -173,14 +182,41 @@ public static string FormatSelection(RdFantomasFormatSelectionArgs args) .Result.Replace("\r\n", args.NewLineText); } - public static string FormatDocument(RdFantomasFormatDocumentArgs args) => - FSharpAsync.StartAsTask( - FormatDocumentMethod.Invoke(null, GetFormatDocumentOptions(args)) as FSharpAsync, - null, null) - .Result.Replace("\r\n", args.NewLineText); + public static RdFormatResult FormatDocument(RdFantomasFormatDocumentArgs args) + { + var formatDocumentOptions = GetFormatDocumentOptions(args); + var formatDocumentAsync = CodeFormatterType.InvokeMember("FormatDocumentAsync", + BindingFlags.Static | BindingFlags.InvokeMethod | BindingFlags.Public, Type.DefaultBinder, null, + formatDocumentOptions); + var formatResult = FSharpAsync.StartAsTask((dynamic)formatDocumentAsync, null, null).Result; + + if (CurrentVersion < Version60) + return new RdFormatResult(formatResult.Replace("\r\n", args.NewLineText), null); + + var formattedCode = formatResult.Code; + var newCursorPosition = formatResult.Cursor == null + ? null + : new RdFcsPos(formatResult.Cursor.Value.Line - 1, formatResult.Cursor.Value.Column); + return new RdFormatResult(formattedCode.Replace("\r\n", args.NewLineText), newCursorPosition); + } private static object[] GetFormatDocumentOptions(RdFantomasFormatDocumentArgs args) { + if (CurrentVersion >= Version60) + { + var cursorPosition = args.CursorPosition is { } pos + ? MakePositionMethod.Invoke(null, new object[] { pos.Row + 1, pos.Column }) + : null; + + return new[] + { + args.FileName.EndsWith(".fsi"), // isSignature + args.Source, + cursorPosition, + ConvertToFormatConfig(args.FormatConfig), + }; + } + if (CurrentVersion >= FantomasProtocolConstants.Fantomas5Version) return new[] { @@ -234,6 +270,7 @@ private static object ConvertToFormatConfig(string[] riderFormatConfigValues) var formatConfig = FSharpValue.MakeRecord(FormatConfigType, formatConfigValues, null); + if (CurrentVersion >= Version60) return formatConfig; return CurrentVersion >= FantomasProtocolConstants.Fantomas5Version ? CreateOptionMethod.Invoke(null, new[] { formatConfig }) //FSharpOption : formatConfig; diff --git a/ReSharper.FSharp/src/FSharp.Fantomas.Host/src/FantomasEndPoint.cs b/ReSharper.FSharp/src/FSharp.Fantomas.Host/src/FantomasEndPoint.cs index c5a57a6bb7..9af40cc965 100644 --- a/ReSharper.FSharp/src/FSharp.Fantomas.Host/src/FantomasEndPoint.cs +++ b/ReSharper.FSharp/src/FSharp.Fantomas.Host/src/FantomasEndPoint.cs @@ -47,7 +47,7 @@ private static string[] GetFormatConfigFields(Unit _) => private static string FormatSelection(RdFantomasFormatSelectionArgs args) => FantomasCodeFormatter.FormatSelection(args); - private static string FormatDocument(RdFantomasFormatDocumentArgs args) => + private static RdFormatResult FormatDocument(RdFantomasFormatDocumentArgs args) => FantomasCodeFormatter.FormatDocument(args); protected override void Run(Lifetime lifetime, RdSimpleDispatcher dispatcher) => dispatcher.Run(); diff --git a/ReSharper.FSharp/src/FSharp.Psi.Features/src/CodeCleanup/FSharpReformatCode.fs b/ReSharper.FSharp/src/FSharp.Psi.Features/src/CodeCleanup/FSharpReformatCode.fs index 3c9cf9e397..b591c15644 100644 --- a/ReSharper.FSharp/src/FSharp.Psi.Features/src/CodeCleanup/FSharpReformatCode.fs +++ b/ReSharper.FSharp/src/FSharp.Psi.Features/src/CodeCleanup/FSharpReformatCode.fs @@ -4,6 +4,7 @@ open JetBrains.Application.Infra open JetBrains.Diagnostics open JetBrains.DocumentModel open JetBrains.DocumentModel.Impl +open JetBrains.Lifetimes open JetBrains.ProjectModel open JetBrains.ReSharper.Feature.Services.CodeCleanup open JetBrains.ReSharper.Plugins.FSharp.Psi @@ -11,11 +12,13 @@ open JetBrains.ReSharper.Plugins.FSharp.Util open JetBrains.ReSharper.Psi open JetBrains.ReSharper.Psi.Tree open JetBrains.ReSharper.Psi.Util -open JetBrains.ReSharper.Resources.Shell +open JetBrains.TextControl open JetBrains.Util.Text +open JetBrains.TextControl.CodeWithMe +open JetBrains.Util.dataStructures.TypedIntrinsics [] -type FSharpReformatCode() = +type FSharpReformatCode(textControlManager: ITextControlManager, codeCleanupService: CodeCleanupService) = let REFORMAT_CODE_DESCRIPTOR = CodeCleanupOptionDescriptor( "FSReformatCode", CodeCleanupLanguage("F#", 2), @@ -64,24 +67,32 @@ type FSharpReformatCode() = let modificationSide = TextModificationSide.NotSpecified let newLineText = sourceFile.DetectLineEnding().GetPresentation() let parsingOptions = fsFile.CheckerService.FcsProjectProvider.GetParsingOptions(sourceFile) + let textControl = textControlManager.LastFocusedTextControlPerClient.ForCurrentClient().NotNull() - let change = - if isNotNull rangeMarker then - try - let range = ofDocumentRange rangeMarker.DocumentRange - let formatted = - fantomasHost.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 formatted = fantomasHost.FormatDocument(filePath, text, settings, parsingOptions, newLineText) - Some(DocumentChange(document, 0, text.Length, formatted, stamp, modificationSide)) + if isNotNull rangeMarker then + try + let range = ofDocumentRange rangeMarker.DocumentRange + let formatted = fantomasHost.FormatSelection(filePath, range, text, settings, parsingOptions, newLineText) + let offset = rangeMarker.DocumentRange.StartOffset.Offset + let oldLength = rangeMarker.DocumentRange.Length + let documentChange = DocumentChange(document, offset, oldLength, formatted, stamp, modificationSide) + document.ChangeDocument(documentChange, TimeStamp.NextValue) + sourceFile.GetPsiServices().Files.CommitAllDocuments() + with _ -> () + else + let cursorPosition = textControl.Caret.Position.Value.ToDocLineColumn(); + let formatResult = fantomasHost.FormatDocument(filePath, text, settings, parsingOptions, newLineText, cursorPosition) + let newCursorPosition = formatResult.CursorPosition - match change with - | Some(change) -> - use cookie = WriteLockCookie.Create() - document.ChangeDocument(change, TimeStamp.NextValue) + document.ReplaceText(document.DocumentRange, formatResult.Code) sourceFile.GetPsiServices().Files.CommitAllDocuments() - | _ -> () + + if isNull newCursorPosition then () else + + // move cursor after current document transaction + let moveCursorLifetime = new LifetimeDefinition() + codeCleanupService.WholeFileCleanupCompletedAfterSave.Advise(moveCursorLifetime.Lifetime, fun _ -> + moveCursorLifetime.Terminate() + textControl.Caret.MoveTo(Int32.op_Explicit(newCursorPosition.Row), + Int32.op_Explicit(newCursorPosition.Column), + CaretVisualPlacement.Generic)) diff --git a/ReSharper.FSharp/src/FSharp.Psi.Features/src/CodeCleanup/FantomasHost.fs b/ReSharper.FSharp/src/FSharp.Psi.Features/src/CodeCleanup/FantomasHost.fs index 00183a83a8..990b0b6da3 100644 --- a/ReSharper.FSharp/src/FSharp.Psi.Features/src/CodeCleanup/FantomasHost.fs +++ b/ReSharper.FSharp/src/FSharp.Psi.Features/src/CodeCleanup/FantomasHost.fs @@ -5,6 +5,7 @@ open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.Text open JetBrains.Application.Settings open JetBrains.Core +open JetBrains.DocumentModel open JetBrains.Lifetimes open JetBrains.ProjectModel open JetBrains.Rd.Tasks @@ -49,6 +50,9 @@ type FantomasHost(solution: ISolution, fantomasFactory: FantomasProcessFactory, let toRdFcsRange (range: range) = RdFcsRange(range.FileName, range.StartLine, range.StartColumn, range.EndLine, range.EndColumn) + let toRdFcsPos (caretPosition: DocumentCoords) = + RdFcsPos(int caretPosition.Line, int caretPosition.Column) + let toRdFormatSettings (settings: FSharpFormatSettingsKey) = [| for field in formatConfigFields -> let fieldName = @@ -79,15 +83,15 @@ type FantomasHost(solution: ISolution, fantomasFactory: FantomasProcessFactory, connect() let args = RdFantomasFormatSelectionArgs(toRdFcsRange range, filePath, source, toRdFormatSettings settings, - toRdFcsParsingOptions options, newLineText) + toRdFcsParsingOptions options, newLineText, null) connection.Execute(fun () -> connection.ProtocolModel.FormatSelection.Sync(args, RpcTimeouts.Maximal)) - member x.FormatDocument(filePath, source, settings, options, newLineText) = + member x.FormatDocument(filePath, source, settings, options, newLineText, cursorPosition: DocumentCoords) = connect() let args = RdFantomasFormatDocumentArgs(filePath, source, toRdFormatSettings settings, toRdFcsParsingOptions options, - newLineText) + newLineText, toRdFcsPos cursorPosition) connection.Execute(fun () -> connection.ProtocolModel.FormatDocument.Sync(args, RpcTimeouts.Maximal)) diff --git a/rider-fsharp/protocol/src/kotlin/model/RdFantomasModel.kt b/rider-fsharp/protocol/src/kotlin/model/RdFantomasModel.kt index 729f9b66a5..03dfab732b 100644 --- a/rider-fsharp/protocol/src/kotlin/model/RdFantomasModel.kt +++ b/rider-fsharp/protocol/src/kotlin/model/RdFantomasModel.kt @@ -25,17 +25,28 @@ object RdFantomasModel : Root() { field("endCol", int) } + private val rdFcsPos = structdef { + field("row", int) + field("column", int) + } + + private val rdFormatResult = structdef { + field("code", string) + field("cursorPosition", rdFcsPos.nullable) + } + private val rdFantomasFormatArgs = basestruct { field("fileName", string) field("source", string) field("formatConfig", array(string)) field("parsingOptions", rdFcsParsingOptions) field("newLineText", string) + field("cursorPosition", rdFcsPos.nullable) } init { call("getFormatConfigFields", void, array(string)) - call("formatDocument", structdef("rdFantomasFormatDocumentArgs") extends rdFantomasFormatArgs {}, string) + call("formatDocument", structdef("rdFantomasFormatDocumentArgs") extends rdFantomasFormatArgs {}, rdFormatResult) call("formatSelection", structdef("rdFantomasFormatSelectionArgs") extends rdFantomasFormatArgs { field("range", rdFcsRange) }, string) From 00ee256e778d1cd89d7fba45f2f1fbfad4ca5482 Mon Sep 17 00:00:00 2001 From: Alex Berezhnykh Date: Fri, 24 Feb 2023 03:20:00 +0300 Subject: [PATCH 2/8] wip --- .../src/FantomasCodeFormatter.cs | 37 +++-- .../src/CodeCleanup/FSharpReformatCode.fs | 8 +- .../cases/fantomas/FantomasRunOptionsTest.kt | 13 ++ .../gold/local tool 6_0 with cursor.gold | 129 ++++++++++++++++++ .../source/LargeFile.fs | 98 +++++++++++++ 5 files changed, 263 insertions(+), 22 deletions(-) create mode 100644 rider-fsharp/testData/fantomas/FantomasRunOptionsTest/local tool 6_0 with cursor/gold/local tool 6_0 with cursor.gold create mode 100644 rider-fsharp/testData/fantomas/FantomasRunOptionsTest/local tool 6_0 with cursor/source/LargeFile.fs diff --git a/ReSharper.FSharp/src/FSharp.Fantomas.Host/src/FantomasCodeFormatter.cs b/ReSharper.FSharp/src/FSharp.Fantomas.Host/src/FantomasCodeFormatter.cs index 34fbe5c767..55ba8b99f9 100644 --- a/ReSharper.FSharp/src/FSharp.Fantomas.Host/src/FantomasCodeFormatter.cs +++ b/ReSharper.FSharp/src/FSharp.Fantomas.Host/src/FantomasCodeFormatter.cs @@ -2,8 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; -using Fantomas.Core; -using FSharp.Compiler.Text; using JetBrains.Diagnostics; using JetBrains.Extension; using JetBrains.ReSharper.Plugins.FSharp.Fantomas.Protocol; @@ -127,7 +125,6 @@ private static Type GetFormatConfigType() private static readonly ConstructorInfo CreateFSharpParsingOptions = FSharpParsingOptionsType?.GetConstructors().Single(); - private static readonly MethodInfo FormatSelectionMethod = CodeFormatterType.GetMethod("FormatSelectionAsync"); private static readonly MethodInfo MakeRangeMethod = CodeFormatterType.GetMethod("MakeRange"); private static readonly MethodInfo MakePositionMethod = CodeFormatterType.GetMethod("MakePosition"); private static readonly MethodInfo SourceOriginConstructor = GetSourceOriginStringConstructor(); @@ -161,24 +158,26 @@ public static string FormatSelection(RdFantomasFormatSelectionArgs args) if (CurrentVersion >= FantomasProtocolConstants.Fantomas5Version) return FSharpAsync.StartAsTask( - FormatSelectionMethod.Invoke(null, new[] - { - args.FileName.EndsWith(".fsi"), // isSignature - args.Source, - range, - ConvertToFormatConfig(args.FormatConfig) - }) as dynamic, null, null) // FSharpAsync> + CodeFormatterType.InvokeMember("FormatSelectionAsync", + BindingFlags.Static | BindingFlags.InvokeMethod | BindingFlags.Public, Type.DefaultBinder, null, new[] + { + args.FileName.EndsWith(".fsi"), // isSignature + args.Source, + range, + ConvertToFormatConfig(args.FormatConfig) + }) as dynamic, null, null) // FSharpAsync> .Result.Item1.Replace("\r\n", args.NewLineText); return FSharpAsync.StartAsTask( - FormatSelectionMethod.Invoke(null, new[] - { - args.FileName, range, - SourceOriginConstructor.Invoke(null, new object[] { args.Source }), - ConvertToFormatConfig(args.FormatConfig), - CreateFSharpParsingOptions.Invoke(GetParsingOptions(args.ParsingOptions).ToArray()), - Checker - }) as FSharpAsync, null, null) + CodeFormatterType.InvokeMember("FormatSelectionAsync", + BindingFlags.Static | BindingFlags.InvokeMethod | BindingFlags.Public, Type.DefaultBinder, null, new[] + { + args.FileName, range, + SourceOriginConstructor.Invoke(null, new object[] { args.Source }), + ConvertToFormatConfig(args.FormatConfig), + CreateFSharpParsingOptions.Invoke(GetParsingOptions(args.ParsingOptions).ToArray()), + Checker + }) as FSharpAsync, null, null) .Result.Replace("\r\n", args.NewLineText); } @@ -212,8 +211,8 @@ private static object[] GetFormatDocumentOptions(RdFantomasFormatDocumentArgs ar { args.FileName.EndsWith(".fsi"), // isSignature args.Source, - cursorPosition, ConvertToFormatConfig(args.FormatConfig), + cursorPosition }; } diff --git a/ReSharper.FSharp/src/FSharp.Psi.Features/src/CodeCleanup/FSharpReformatCode.fs b/ReSharper.FSharp/src/FSharp.Psi.Features/src/CodeCleanup/FSharpReformatCode.fs index b591c15644..b9a179b484 100644 --- a/ReSharper.FSharp/src/FSharp.Psi.Features/src/CodeCleanup/FSharpReformatCode.fs +++ b/ReSharper.FSharp/src/FSharp.Psi.Features/src/CodeCleanup/FSharpReformatCode.fs @@ -12,13 +12,13 @@ open JetBrains.ReSharper.Plugins.FSharp.Util open JetBrains.ReSharper.Psi open JetBrains.ReSharper.Psi.Tree open JetBrains.ReSharper.Psi.Util +open JetBrains.ReSharper.Resources.Shell open JetBrains.TextControl open JetBrains.Util.Text -open JetBrains.TextControl.CodeWithMe open JetBrains.Util.dataStructures.TypedIntrinsics [] -type FSharpReformatCode(textControlManager: ITextControlManager, codeCleanupService: CodeCleanupService) = +type FSharpReformatCode(textControlManager: ITextControlManager) = let REFORMAT_CODE_DESCRIPTOR = CodeCleanupOptionDescriptor( "FSReformatCode", CodeCleanupLanguage("F#", 2), @@ -67,7 +67,6 @@ type FSharpReformatCode(textControlManager: ITextControlManager, codeCleanupServ let modificationSide = TextModificationSide.NotSpecified let newLineText = sourceFile.DetectLineEnding().GetPresentation() let parsingOptions = fsFile.CheckerService.FcsProjectProvider.GetParsingOptions(sourceFile) - let textControl = textControlManager.LastFocusedTextControlPerClient.ForCurrentClient().NotNull() if isNotNull rangeMarker then try @@ -76,10 +75,12 @@ type FSharpReformatCode(textControlManager: ITextControlManager, codeCleanupServ let offset = rangeMarker.DocumentRange.StartOffset.Offset let oldLength = rangeMarker.DocumentRange.Length let documentChange = DocumentChange(document, offset, oldLength, formatted, stamp, modificationSide) + use _ = WriteLockCookie.Create() document.ChangeDocument(documentChange, TimeStamp.NextValue) sourceFile.GetPsiServices().Files.CommitAllDocuments() with _ -> () else + let textControl = textControlManager.VisibleTextControls |> Seq.find(fun c -> c.Document == document) let cursorPosition = textControl.Caret.Position.Value.ToDocLineColumn(); let formatResult = fantomasHost.FormatDocument(filePath, text, settings, parsingOptions, newLineText, cursorPosition) let newCursorPosition = formatResult.CursorPosition @@ -91,6 +92,7 @@ type FSharpReformatCode(textControlManager: ITextControlManager, codeCleanupServ // move cursor after current document transaction let moveCursorLifetime = new LifetimeDefinition() + let codeCleanupService = solution.GetComponent() codeCleanupService.WholeFileCleanupCompletedAfterSave.Advise(moveCursorLifetime.Lifetime, fun _ -> moveCursorLifetime.Terminate() textControl.Caret.MoveTo(Int32.op_Explicit(newCursorPosition.Row), diff --git a/rider-fsharp/src/test/kotlin/com/jetbrains/rider/plugins/fsharp/test/cases/fantomas/FantomasRunOptionsTest.kt b/rider-fsharp/src/test/kotlin/com/jetbrains/rider/plugins/fsharp/test/cases/fantomas/FantomasRunOptionsTest.kt index 37a7039d97..7d46161726 100644 --- a/rider-fsharp/src/test/kotlin/com/jetbrains/rider/plugins/fsharp/test/cases/fantomas/FantomasRunOptionsTest.kt +++ b/rider-fsharp/src/test/kotlin/com/jetbrains/rider/plugins/fsharp/test/cases/fantomas/FantomasRunOptionsTest.kt @@ -194,6 +194,19 @@ class FantomasRunOptionsTest : EditorTestBase() { "5.2.1.0" ) + @Test + fun `local tool 6_0 with cursor`() { + withOpenedEditor("Simple.fs", "LargeFile.fs") { + withFantomasLocalTool("fantomas", "6.0.0-alpha-004") { + executeWithGold(testGoldFile) { + reformatCode() + checkFantomasVersion("6.0.0.0") + dumpOpenedDocument(it, project!!, true) + } + } + } + } + @Test fun `global tool`() { executeWithGold(testGoldFile) { diff --git a/rider-fsharp/testData/fantomas/FantomasRunOptionsTest/local tool 6_0 with cursor/gold/local tool 6_0 with cursor.gold b/rider-fsharp/testData/fantomas/FantomasRunOptionsTest/local tool 6_0 with cursor/gold/local tool 6_0 with cursor.gold new file mode 100644 index 0000000000..a974f09ce0 --- /dev/null +++ b/rider-fsharp/testData/fantomas/FantomasRunOptionsTest/local tool 6_0 with cursor/gold/local tool 6_0 with cursor.gold @@ -0,0 +1,129 @@ +namespace JetBrains.ReSharper.Plugins.FSharp.Services.Formatter + +open JetBrains.Application.Infra +open JetBrains.Diagnostics +open JetBrains.DocumentModel +open JetBrains.DocumentModel.Impl +open JetBrains.Lifetimes +open JetBrains.ProjectModel +open JetBrains.ReSharper.Feature.Services.CodeCleanup +open JetBrains.ReSharper.Plugins.FSharp.Psi +open JetBrains.ReSharper.Plugins.FSharp.Util +open JetBrains.ReSharper.Psi +open JetBrains.ReSharper.Psi.Tree +open JetBrains.ReSharper.Psi.Util +open JetBrains.TextControl +open JetBrains.Util.Text +open JetBrains.Util.dataStructures.TypedIntrinsics + +[] +type FSharpReformatCode(textControlManager: ITextControlManager) = + let REFORMAT_CODE_DESCRIPTOR = + CodeCleanupOptionDescriptor( + "FSReformatCode", + CodeCleanupLanguage("F#", 2), + CodeCleanupOptionDescriptor.ReformatGroup, + displayName = "Reformat code" + ) + + interface IReformatCodeCleanupModule with + member x.Name = "Reformat F#" + member x.LanguageType = FSharpLanguage.Instance :> _ + member x.Descriptors = [| REFORMAT_CODE_DESCRIPTOR |] + member x.IsAvailableOnSelection = true + + member x.SetDefaultSetting(profile, profileType) = + match profileType with + | CodeCleanupService.DefaultProfileType.FULL + | CodeCleanupService.DefaultProfileType.REFORMAT + | CodeCleanupService.DefaultProfileType.CODE_STYLE -> profile.SetSetting(REFORMAT_CODE_DESCRIPTOR, true) + | _ -> Assertion.Fail($"Unexpected cleanup profile type: {nameof (profileType)}") + + member x.IsAvailable(sourceFile: IPsiSourceFile) = + sourceFile.PrimaryPsiLanguage :? FSharpLanguage + + member x.IsAvailable(profile: CodeCleanupProfile) = + profile.GetSetting(REFORMAT_CODE_DESCRIPTOR) + + member x.Process(sourceFile, rangeMarker, _, _, _) = + let fsFile = sourceFile.FSharpFile + + if isNull fsFile then + () + else + + match fsFile.ParseTree with + | None -> () + | Some _ -> + + let filePath = sourceFile.GetLocation().FullPath + let document = sourceFile.Document :?> DocumentBase + let text = document.GetText() + + let solution = fsFile.GetSolution() + let settings = sourceFile.GetSettingsStoreWithEditorConfig() + let formatter = fsFile.Language.LanguageServiceNotNull().CodeFormatter + + let settings = + formatter.GetFormatterSettings(solution, sourceFile, settings, false) :?> _ + + let fantomasHost = solution.GetComponent() + + let stamp = document.LastModificationStamp + let modificationSide = TextModificationSide.NotSpecified + let newLineText = sourceFile.DetectLineEnding().GetPresentation() + + let parsingOptions = + fsFile.CheckerService.FcsProjectProvider.GetParsingOptions(sourceFile) + + if isNotNull rangeMarker then + try + let range = ofDocumentRange rangeMarker.DocumentRange + + let formatted = + fantomasHost.FormatSelection(filePath, range, text, settings, parsingOptions, newLineText) + + let offset = rangeMarker.DocumentRange.StartOffset.Offset + let oldLength = rangeMarker.DocumentRange.Length + + let documentChange = + DocumentChange(document, offset, oldLength, formatted, stamp, modificationSide) + + document.ChangeDocument(documentChange, TimeStamp.NextValue) + sourceFile.GetPsiServices().Files.CommitAllDocuments() + with _ -> + () + else + let textControl = + textControlManager.VisibleTextControls + |> Seq.find (fun c -> c.Document == document) + + let cursorPosition = textControl.Caret.Position.Value.ToDocLineColumn() + + let formatResult = + fantomasHost.FormatDocument(filePath, text, settings, parsingOptions, newLineText, cursorPosition) + + let newCursorPosition = formatResult.CursorPosition + + document.ReplaceText(document.DocumentRange, formatResult.Code) + sourceFile.GetPsiServices().Files.CommitAllDocuments() + + if isNull newCursorPosition then + () + else + + // move cursor after current document transaction + let moveCursorLifetime = new LifetimeDefinition() + let codeCleanupService = solution.GetComponent() + + codeCleanupService.WholeFileCleanupCompletedAfterSave.Advise( + moveCursorLifetime.Lifetime, + fun _ -> + moveCursorLifetime.Terminate() + + textControl.Caret.MoveTo( + Int32.op_Explicit (newCursorPosition.Row), + Int32.op_Explicit (newCursorPosition.Column), + CaretVisualPlacement.Generic + ) + ) diff --git a/rider-fsharp/testData/fantomas/FantomasRunOptionsTest/local tool 6_0 with cursor/source/LargeFile.fs b/rider-fsharp/testData/fantomas/FantomasRunOptionsTest/local tool 6_0 with cursor/source/LargeFile.fs new file mode 100644 index 0000000000..0aa3458daf --- /dev/null +++ b/rider-fsharp/testData/fantomas/FantomasRunOptionsTest/local tool 6_0 with cursor/source/LargeFile.fs @@ -0,0 +1,98 @@ +namespace JetBrains.ReSharper.Plugins.FSharp.Services.Formatter + +open JetBrains.Application.Infra +open JetBrains.Diagnostics +open JetBrains.DocumentModel +open JetBrains.DocumentModel.Impl +open JetBrains.Lifetimes +open JetBrains.ProjectModel +open JetBrains.ReSharper.Feature.Services.CodeCleanup +open JetBrains.ReSharper.Plugins.FSharp.Psi +open JetBrains.ReSharper.Plugins.FSharp.Util +open JetBrains.ReSharper.Psi +open JetBrains.ReSharper.Psi.Tree +open JetBrains.ReSharper.Psi.Util +open JetBrains.TextControl +open JetBrains.Util.Text +open JetBrains.Util.dataStructures.TypedIntrinsics + +[] +type FSharpReformatCode(textControlManager: ITextControlManager) = + let REFORMAT_CODE_DESCRIPTOR = CodeCleanupOptionDescriptor( + "FSReformatCode", + CodeCleanupLanguage("F#", 2), + CodeCleanupOptionDescriptor.ReformatGroup, + displayName = "Reformat code") + + interface IReformatCodeCleanupModule with + member x.Name = "Reformat F#" + member x.LanguageType = FSharpLanguage.Instance :> _ + member x.Descriptors = [| REFORMAT_CODE_DESCRIPTOR |] + member x.IsAvailableOnSelection = true + member x.SetDefaultSetting(profile, profileType) = + match profileType with + | CodeCleanupService.DefaultProfileType.FULL + | CodeCleanupService.DefaultProfileType.REFORMAT + | CodeCleanupService.DefaultProfileType.CODE_STYLE -> + profile.SetSetting(REFORMAT_CODE_DESCRIPTOR, true) + | _ -> + Assertion.Fail($"Unexpected cleanup profile type: {nameof(profileType)}") + + member x.IsAvailable(sourceFile: IPsiSourceFile) = + sourceFile.PrimaryPsiLanguage :? FSharpLanguage + + member x.IsAvailable(profile: CodeCleanupProfile) = + profile.GetSetting(REFORMAT_CODE_DESCRIPTOR) + + member x.Process(sourceFile, rangeMarker, _, _, _) = + let fsFile = sourceFile.FSharpFile + if isNull fsFile then () else + + match fsFile.ParseTree with + | None -> () + | Some _ -> + + let filePath = sourceFile.GetLocation().FullPath + let document = sourceFile.Document :?> DocumentBase + let text = document.GetText() + + let solution = fsFile.GetSolution() + let settings = sourceFile.GetSettingsStoreWithEditorConfig() + let formatter = fsFile.Language.LanguageServiceNotNull().CodeFormatter + let settings = formatter.GetFormatterSettings(solution, sourceFile, settings, false) :?> _ + let fantomasHost = solution.GetComponent() + + let stamp = document.LastModificationStamp + let modificationSide = TextModificationSide.NotSpecified + let newLineText = sourceFile.DetectLineEnding().GetPresentation() + let parsingOptions = fsFile.CheckerService.FcsProjectProvider.GetParsingOptions(sourceFile) + + if isNotNull rangeMarker then + try + let range = ofDocumentRange rangeMarker.DocumentRange + let formatted = fantomasHost.FormatSelection(filePath, range, text, settings, parsingOptions, newLineText) + let offset = rangeMarker.DocumentRange.StartOffset.Offset + let oldLength = rangeMarker.DocumentRange.Length + let documentChange = DocumentChange(document, offset, oldLength, formatted, stamp, modificationSide) + document.ChangeDocument(documentChange, TimeStamp.NextValue) + sourceFile.GetPsiServices().Files.CommitAllDocuments() + with _ -> () + else + let textControl = textControlManager.VisibleTextControls |> Seq.find(fun c -> c.Document == document) + let cursorPosition = textControl.Caret.Position.Value.ToDocLineColumn(); + let formatResult = fantomasHost.FormatDocument(filePath, text, settings, parsingOptions, newLineText, cursorPosition) + let newCursorPosition = formatResult.CursorPosition + + document.ReplaceText(document.DocumentRange, formatResult.Code) + sourceFile.GetPsiServices().Files.CommitAllDocuments() + + if isNull newCursorPosition then () else + + // move cursor after current document transaction + let moveCursorLifetime = new LifetimeDefinition() + let codeCleanupService = solution.GetComponent() + codeCleanupService.WholeFileCleanupCompletedAfterSave.Advise(moveCursorLifetime.Lifetime, fun _ -> + moveCursorLifetime.Terminate() + textControl.Caret.MoveTo(Int32.op_Explicit(newCursorPosition.Row), + Int32.op_Explicit(newCursorPosition.Column), + CaretVisualPlacement.Generic)) From 640ac375c69d9d875cde44139db1278b70b7c143 Mon Sep 17 00:00:00 2001 From: Alex Berezhnykh Date: Fri, 24 Feb 2023 06:36:14 +0300 Subject: [PATCH 3/8] cleanup --- .../FSharp.Psi.Features/src/CodeCleanup/FSharpReformatCode.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReSharper.FSharp/src/FSharp.Psi.Features/src/CodeCleanup/FSharpReformatCode.fs b/ReSharper.FSharp/src/FSharp.Psi.Features/src/CodeCleanup/FSharpReformatCode.fs index b9a179b484..e01d46a63c 100644 --- a/ReSharper.FSharp/src/FSharp.Psi.Features/src/CodeCleanup/FSharpReformatCode.fs +++ b/ReSharper.FSharp/src/FSharp.Psi.Features/src/CodeCleanup/FSharpReformatCode.fs @@ -80,7 +80,7 @@ type FSharpReformatCode(textControlManager: ITextControlManager) = sourceFile.GetPsiServices().Files.CommitAllDocuments() with _ -> () else - let textControl = textControlManager.VisibleTextControls |> Seq.find(fun c -> c.Document == document) + let textControl = textControlManager.VisibleTextControls |> Seq.find (fun c -> c.Document == document) let cursorPosition = textControl.Caret.Position.Value.ToDocLineColumn(); let formatResult = fantomasHost.FormatDocument(filePath, text, settings, parsingOptions, newLineText, cursorPosition) let newCursorPosition = formatResult.CursorPosition From fe0ef024b98b862118d3b61d2ab4f29853ff6da5 Mon Sep 17 00:00:00 2001 From: Alex Berezhnykh Date: Mon, 27 Feb 2023 10:12:47 +0300 Subject: [PATCH 4/8] fix --- .../src/CodeCleanup/FSharpReformatCode.fs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/ReSharper.FSharp/src/FSharp.Psi.Features/src/CodeCleanup/FSharpReformatCode.fs b/ReSharper.FSharp/src/FSharp.Psi.Features/src/CodeCleanup/FSharpReformatCode.fs index e01d46a63c..fe703bac6c 100644 --- a/ReSharper.FSharp/src/FSharp.Psi.Features/src/CodeCleanup/FSharpReformatCode.fs +++ b/ReSharper.FSharp/src/FSharp.Psi.Features/src/CodeCleanup/FSharpReformatCode.fs @@ -15,7 +15,6 @@ open JetBrains.ReSharper.Psi.Util open JetBrains.ReSharper.Resources.Shell open JetBrains.TextControl open JetBrains.Util.Text -open JetBrains.Util.dataStructures.TypedIntrinsics [] type FSharpReformatCode(textControlManager: ITextControlManager) = @@ -36,7 +35,7 @@ type FSharpReformatCode(textControlManager: ITextControlManager) = | CodeCleanupService.DefaultProfileType.REFORMAT | CodeCleanupService.DefaultProfileType.CODE_STYLE -> profile.SetSetting(REFORMAT_CODE_DESCRIPTOR, true) - | _ -> + | _ -> Assertion.Fail($"Unexpected cleanup profile type: {nameof(profileType)}") member x.IsAvailable(sourceFile: IPsiSourceFile) = @@ -95,6 +94,6 @@ type FSharpReformatCode(textControlManager: ITextControlManager) = let codeCleanupService = solution.GetComponent() codeCleanupService.WholeFileCleanupCompletedAfterSave.Advise(moveCursorLifetime.Lifetime, fun _ -> moveCursorLifetime.Terminate() - textControl.Caret.MoveTo(Int32.op_Explicit(newCursorPosition.Row), - Int32.op_Explicit(newCursorPosition.Column), + textControl.Caret.MoveTo(docLine newCursorPosition.Row, + docColumn newCursorPosition.Column, CaretVisualPlacement.Generic)) From c81f67baba29d630840657c326f1ac6427c1c44b Mon Sep 17 00:00:00 2001 From: Alex Berezhnykh Date: Fri, 3 Mar 2023 14:35:40 +0300 Subject: [PATCH 5/8] simplify test data --- .../src/CodeCleanup/FSharpReformatCode.fs | 2 +- .../gold/local tool 6_0 with cursor.gold | 138 +++--------------- .../source/LargeFile.fs | 89 +---------- 3 files changed, 25 insertions(+), 204 deletions(-) diff --git a/ReSharper.FSharp/src/FSharp.Psi.Features/src/CodeCleanup/FSharpReformatCode.fs b/ReSharper.FSharp/src/FSharp.Psi.Features/src/CodeCleanup/FSharpReformatCode.fs index fe703bac6c..657e01c152 100644 --- a/ReSharper.FSharp/src/FSharp.Psi.Features/src/CodeCleanup/FSharpReformatCode.fs +++ b/ReSharper.FSharp/src/FSharp.Psi.Features/src/CodeCleanup/FSharpReformatCode.fs @@ -80,7 +80,7 @@ type FSharpReformatCode(textControlManager: ITextControlManager) = with _ -> () else let textControl = textControlManager.VisibleTextControls |> Seq.find (fun c -> c.Document == document) - let cursorPosition = textControl.Caret.Position.Value.ToDocLineColumn(); + let cursorPosition = textControl.Caret.Position.Value.ToDocLineColumn() let formatResult = fantomasHost.FormatDocument(filePath, text, settings, parsingOptions, newLineText, cursorPosition) let newCursorPosition = formatResult.CursorPosition diff --git a/rider-fsharp/testData/fantomas/FantomasRunOptionsTest/local tool 6_0 with cursor/gold/local tool 6_0 with cursor.gold b/rider-fsharp/testData/fantomas/FantomasRunOptionsTest/local tool 6_0 with cursor/gold/local tool 6_0 with cursor.gold index a974f09ce0..a22310452f 100644 --- a/rider-fsharp/testData/fantomas/FantomasRunOptionsTest/local tool 6_0 with cursor/gold/local tool 6_0 with cursor.gold +++ b/rider-fsharp/testData/fantomas/FantomasRunOptionsTest/local tool 6_0 with cursor/gold/local tool 6_0 with cursor.gold @@ -1,129 +1,29 @@ namespace JetBrains.ReSharper.Plugins.FSharp.Services.Formatter -open JetBrains.Application.Infra -open JetBrains.Diagnostics -open JetBrains.DocumentModel -open JetBrains.DocumentModel.Impl -open JetBrains.Lifetimes -open JetBrains.ProjectModel -open JetBrains.ReSharper.Feature.Services.CodeCleanup -open JetBrains.ReSharper.Plugins.FSharp.Psi -open JetBrains.ReSharper.Plugins.FSharp.Util -open JetBrains.ReSharper.Psi -open JetBrains.ReSharper.Psi.Tree -open JetBrains.ReSharper.Psi.Util -open JetBrains.TextControl -open JetBrains.Util.Text -open JetBrains.Util.dataStructures.TypedIntrinsics - [] type FSharpReformatCode(textControlManager: ITextControlManager) = - let REFORMAT_CODE_DESCRIPTOR = - CodeCleanupOptionDescriptor( - "FSReformatCode", - CodeCleanupLanguage("F#", 2), - CodeCleanupOptionDescriptor.ReformatGroup, - displayName = "Reformat code" - ) - - interface IReformatCodeCleanupModule with - member x.Name = "Reformat F#" - member x.LanguageType = FSharpLanguage.Instance :> _ - member x.Descriptors = [| REFORMAT_CODE_DESCRIPTOR |] - member x.IsAvailableOnSelection = true - - member x.SetDefaultSetting(profile, profileType) = - match profileType with - | CodeCleanupService.DefaultProfileType.FULL - | CodeCleanupService.DefaultProfileType.REFORMAT - | CodeCleanupService.DefaultProfileType.CODE_STYLE -> profile.SetSetting(REFORMAT_CODE_DESCRIPTOR, true) - | _ -> Assertion.Fail($"Unexpected cleanup profile type: {nameof (profileType)}") + member x.Process(sourceFile, rangeMarker, _, _, _) = + if isNotNull rangeMarker then + try + let range = ofDocumentRange rangeMarker.DocumentRange - member x.IsAvailable(sourceFile: IPsiSourceFile) = - sourceFile.PrimaryPsiLanguage :? FSharpLanguage + let formatted = + fantomasHost.FormatSelection(filePath, range, text, settings, parsingOptions, newLineText) - member x.IsAvailable(profile: CodeCleanupProfile) = - profile.GetSetting(REFORMAT_CODE_DESCRIPTOR) + let offset = rangeMarker.DocumentRange.StartOffset.Offset + let oldLength = rangeMarker.DocumentRange.Length - member x.Process(sourceFile, rangeMarker, _, _, _) = - let fsFile = sourceFile.FSharpFile + let documentChange = + DocumentChange(document, offset, oldLength, formatted, stamp, modificationSide) - if isNull fsFile then + use _ = WriteLockCookie.Create() + document.ChangeDocument(documentChange, TimeStamp.NextValue) + sourceFile.GetPsiServices().Files.CommitAllDocuments() + with _ -> () - else - - match fsFile.ParseTree with - | None -> () - | Some _ -> - - let filePath = sourceFile.GetLocation().FullPath - let document = sourceFile.Document :?> DocumentBase - let text = document.GetText() - - let solution = fsFile.GetSolution() - let settings = sourceFile.GetSettingsStoreWithEditorConfig() - let formatter = fsFile.Language.LanguageServiceNotNull().CodeFormatter - - let settings = - formatter.GetFormatterSettings(solution, sourceFile, settings, false) :?> _ - - let fantomasHost = solution.GetComponent() - - let stamp = document.LastModificationStamp - let modificationSide = TextModificationSide.NotSpecified - let newLineText = sourceFile.DetectLineEnding().GetPresentation() - - let parsingOptions = - fsFile.CheckerService.FcsProjectProvider.GetParsingOptions(sourceFile) - - if isNotNull rangeMarker then - try - let range = ofDocumentRange rangeMarker.DocumentRange - - let formatted = - fantomasHost.FormatSelection(filePath, range, text, settings, parsingOptions, newLineText) - - let offset = rangeMarker.DocumentRange.StartOffset.Offset - let oldLength = rangeMarker.DocumentRange.Length - - let documentChange = - DocumentChange(document, offset, oldLength, formatted, stamp, modificationSide) - - document.ChangeDocument(documentChange, TimeStamp.NextValue) - sourceFile.GetPsiServices().Files.CommitAllDocuments() - with _ -> - () - else - let textControl = - textControlManager.VisibleTextControls - |> Seq.find (fun c -> c.Document == document) - - let cursorPosition = textControl.Caret.Position.Value.ToDocLineColumn() - - let formatResult = - fantomasHost.FormatDocument(filePath, text, settings, parsingOptions, newLineText, cursorPosition) - - let newCursorPosition = formatResult.CursorPosition - - document.ReplaceText(document.DocumentRange, formatResult.Code) - sourceFile.GetPsiServices().Files.CommitAllDocuments() - - if isNull newCursorPosition then - () - else - - // move cursor after current document transaction - let moveCursorLifetime = new LifetimeDefinition() - let codeCleanupService = solution.GetComponent() - - codeCleanupService.WholeFileCleanupCompletedAfterSave.Advise( - moveCursorLifetime.Lifetime, - fun _ -> - moveCursorLifetime.Terminate() + else + let textControl = + textControlManager.VisibleTextControls + |> Seq.find (fun c -> c.Document == document) - textControl.Caret.MoveTo( - Int32.op_Explicit (newCursorPosition.Row), - Int32.op_Explicit (newCursorPosition.Column), - CaretVisualPlacement.Generic - ) - ) + cursorPosition = textControl.Caret.Position.Value.ToDocLineColumn() diff --git a/rider-fsharp/testData/fantomas/FantomasRunOptionsTest/local tool 6_0 with cursor/source/LargeFile.fs b/rider-fsharp/testData/fantomas/FantomasRunOptionsTest/local tool 6_0 with cursor/source/LargeFile.fs index 0aa3458daf..2c5e0136bd 100644 --- a/rider-fsharp/testData/fantomas/FantomasRunOptionsTest/local tool 6_0 with cursor/source/LargeFile.fs +++ b/rider-fsharp/testData/fantomas/FantomasRunOptionsTest/local tool 6_0 with cursor/source/LargeFile.fs @@ -1,98 +1,19 @@ namespace JetBrains.ReSharper.Plugins.FSharp.Services.Formatter -open JetBrains.Application.Infra -open JetBrains.Diagnostics -open JetBrains.DocumentModel -open JetBrains.DocumentModel.Impl -open JetBrains.Lifetimes -open JetBrains.ProjectModel -open JetBrains.ReSharper.Feature.Services.CodeCleanup -open JetBrains.ReSharper.Plugins.FSharp.Psi -open JetBrains.ReSharper.Plugins.FSharp.Util -open JetBrains.ReSharper.Psi -open JetBrains.ReSharper.Psi.Tree -open JetBrains.ReSharper.Psi.Util -open JetBrains.TextControl -open JetBrains.Util.Text -open JetBrains.Util.dataStructures.TypedIntrinsics - [] type FSharpReformatCode(textControlManager: ITextControlManager) = - let REFORMAT_CODE_DESCRIPTOR = CodeCleanupOptionDescriptor( - "FSReformatCode", - CodeCleanupLanguage("F#", 2), - CodeCleanupOptionDescriptor.ReformatGroup, - displayName = "Reformat code") - - interface IReformatCodeCleanupModule with - member x.Name = "Reformat F#" - member x.LanguageType = FSharpLanguage.Instance :> _ - member x.Descriptors = [| REFORMAT_CODE_DESCRIPTOR |] - member x.IsAvailableOnSelection = true - member x.SetDefaultSetting(profile, profileType) = - match profileType with - | CodeCleanupService.DefaultProfileType.FULL - | CodeCleanupService.DefaultProfileType.REFORMAT - | CodeCleanupService.DefaultProfileType.CODE_STYLE -> - profile.SetSetting(REFORMAT_CODE_DESCRIPTOR, true) - | _ -> - Assertion.Fail($"Unexpected cleanup profile type: {nameof(profileType)}") - - member x.IsAvailable(sourceFile: IPsiSourceFile) = - sourceFile.PrimaryPsiLanguage :? FSharpLanguage - - member x.IsAvailable(profile: CodeCleanupProfile) = - profile.GetSetting(REFORMAT_CODE_DESCRIPTOR) - - member x.Process(sourceFile, rangeMarker, _, _, _) = - let fsFile = sourceFile.FSharpFile - if isNull fsFile then () else - - match fsFile.ParseTree with - | None -> () - | Some _ -> - - let filePath = sourceFile.GetLocation().FullPath - let document = sourceFile.Document :?> DocumentBase - let text = document.GetText() - - let solution = fsFile.GetSolution() - let settings = sourceFile.GetSettingsStoreWithEditorConfig() - let formatter = fsFile.Language.LanguageServiceNotNull().CodeFormatter - let settings = formatter.GetFormatterSettings(solution, sourceFile, settings, false) :?> _ - let fantomasHost = solution.GetComponent() - - let stamp = document.LastModificationStamp - let modificationSide = TextModificationSide.NotSpecified - let newLineText = sourceFile.DetectLineEnding().GetPresentation() - let parsingOptions = fsFile.CheckerService.FcsProjectProvider.GetParsingOptions(sourceFile) - + member x.Process(sourceFile, rangeMarker, _, _, _) = if isNotNull rangeMarker then - try + try let range = ofDocumentRange rangeMarker.DocumentRange let formatted = fantomasHost.FormatSelection(filePath, range, text, settings, parsingOptions, newLineText) let offset = rangeMarker.DocumentRange.StartOffset.Offset let oldLength = rangeMarker.DocumentRange.Length let documentChange = DocumentChange(document, offset, oldLength, formatted, stamp, modificationSide) + use _ = WriteLockCookie.Create() document.ChangeDocument(documentChange, TimeStamp.NextValue) sourceFile.GetPsiServices().Files.CommitAllDocuments() with _ -> () else - let textControl = textControlManager.VisibleTextControls |> Seq.find(fun c -> c.Document == document) - let cursorPosition = textControl.Caret.Position.Value.ToDocLineColumn(); - let formatResult = fantomasHost.FormatDocument(filePath, text, settings, parsingOptions, newLineText, cursorPosition) - let newCursorPosition = formatResult.CursorPosition - - document.ReplaceText(document.DocumentRange, formatResult.Code) - sourceFile.GetPsiServices().Files.CommitAllDocuments() - - if isNull newCursorPosition then () else - - // move cursor after current document transaction - let moveCursorLifetime = new LifetimeDefinition() - let codeCleanupService = solution.GetComponent() - codeCleanupService.WholeFileCleanupCompletedAfterSave.Advise(moveCursorLifetime.Lifetime, fun _ -> - moveCursorLifetime.Terminate() - textControl.Caret.MoveTo(Int32.op_Explicit(newCursorPosition.Row), - Int32.op_Explicit(newCursorPosition.Column), - CaretVisualPlacement.Generic)) + let textControl = textControlManager.VisibleTextControls |> Seq.find (fun c -> c.Document == document) + cursorPosition = textControl.Caret.Position.Value.ToDocLineColumn(); From cafb480d340aeba0d8c21c3fa9a2747c9e8ee2d4 Mon Sep 17 00:00:00 2001 From: Alex Berezhnykh Date: Fri, 3 Mar 2023 16:34:45 +0300 Subject: [PATCH 6/8] fix --- .../src/CodeCleanup/FSharpReformatCode.fs | 10 +++++----- .../src/CodeCleanup/FantomasHost.fs | 8 +++++--- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/ReSharper.FSharp/src/FSharp.Psi.Features/src/CodeCleanup/FSharpReformatCode.fs b/ReSharper.FSharp/src/FSharp.Psi.Features/src/CodeCleanup/FSharpReformatCode.fs index 657e01c152..752213b6f0 100644 --- a/ReSharper.FSharp/src/FSharp.Psi.Features/src/CodeCleanup/FSharpReformatCode.fs +++ b/ReSharper.FSharp/src/FSharp.Psi.Features/src/CodeCleanup/FSharpReformatCode.fs @@ -79,8 +79,8 @@ type FSharpReformatCode(textControlManager: ITextControlManager) = sourceFile.GetPsiServices().Files.CommitAllDocuments() with _ -> () else - let textControl = textControlManager.VisibleTextControls |> Seq.find (fun c -> c.Document == document) - let cursorPosition = textControl.Caret.Position.Value.ToDocLineColumn() + let textControl = textControlManager.VisibleTextControls |> Seq.tryFind (fun c -> c.Document == document) + let cursorPosition = textControl |> Option.map (fun c -> c.Caret.Position.Value.ToDocLineColumn()) let formatResult = fantomasHost.FormatDocument(filePath, text, settings, parsingOptions, newLineText, cursorPosition) let newCursorPosition = formatResult.CursorPosition @@ -94,6 +94,6 @@ type FSharpReformatCode(textControlManager: ITextControlManager) = let codeCleanupService = solution.GetComponent() codeCleanupService.WholeFileCleanupCompletedAfterSave.Advise(moveCursorLifetime.Lifetime, fun _ -> moveCursorLifetime.Terminate() - textControl.Caret.MoveTo(docLine newCursorPosition.Row, - docColumn newCursorPosition.Column, - CaretVisualPlacement.Generic)) + textControl.Value.Caret.MoveTo(docLine newCursorPosition.Row, + docColumn newCursorPosition.Column, + CaretVisualPlacement.Generic)) diff --git a/ReSharper.FSharp/src/FSharp.Psi.Features/src/CodeCleanup/FantomasHost.fs b/ReSharper.FSharp/src/FSharp.Psi.Features/src/CodeCleanup/FantomasHost.fs index 990b0b6da3..c859753521 100644 --- a/ReSharper.FSharp/src/FSharp.Psi.Features/src/CodeCleanup/FantomasHost.fs +++ b/ReSharper.FSharp/src/FSharp.Psi.Features/src/CodeCleanup/FantomasHost.fs @@ -50,8 +50,10 @@ type FantomasHost(solution: ISolution, fantomasFactory: FantomasProcessFactory, let toRdFcsRange (range: range) = RdFcsRange(range.FileName, range.StartLine, range.StartColumn, range.EndLine, range.EndColumn) - let toRdFcsPos (caretPosition: DocumentCoords) = - RdFcsPos(int caretPosition.Line, int caretPosition.Column) + let toRdFcsPos (caretPosition: DocumentCoords option) = + match caretPosition with + | Some caretPosition -> RdFcsPos(int caretPosition.Line, int caretPosition.Column) + | None -> null let toRdFormatSettings (settings: FSharpFormatSettingsKey) = [| for field in formatConfigFields -> @@ -87,7 +89,7 @@ type FantomasHost(solution: ISolution, fantomasFactory: FantomasProcessFactory, connection.Execute(fun () -> connection.ProtocolModel.FormatSelection.Sync(args, RpcTimeouts.Maximal)) - member x.FormatDocument(filePath, source, settings, options, newLineText, cursorPosition: DocumentCoords) = + member x.FormatDocument(filePath, source, settings, options, newLineText, cursorPosition: DocumentCoords option) = connect() let args = RdFantomasFormatDocumentArgs(filePath, source, toRdFormatSettings settings, toRdFcsParsingOptions options, From 62447441e56e3da9a288f7b76aa4b2a28b8659f1 Mon Sep 17 00:00:00 2001 From: Alex Berezhnykh Date: Fri, 3 Mar 2023 16:44:40 +0300 Subject: [PATCH 7/8] add comment --- .../src/FSharp.Fantomas.Host/src/FantomasCodeFormatter.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ReSharper.FSharp/src/FSharp.Fantomas.Host/src/FantomasCodeFormatter.cs b/ReSharper.FSharp/src/FSharp.Fantomas.Host/src/FantomasCodeFormatter.cs index 55ba8b99f9..241ab9ae79 100644 --- a/ReSharper.FSharp/src/FSharp.Fantomas.Host/src/FantomasCodeFormatter.cs +++ b/ReSharper.FSharp/src/FSharp.Fantomas.Host/src/FantomasCodeFormatter.cs @@ -189,6 +189,9 @@ public static RdFormatResult FormatDocument(RdFantomasFormatDocumentArgs args) formatDocumentOptions); var formatResult = FSharpAsync.StartAsTask((dynamic)formatDocumentAsync, null, null).Result; + // Prior to version 6, Fantomas returns formatted code, + // in version 6, the return FormatResult value includes the formatted code and the new cursor position. + // https://github.com/fsprojects/fantomas/issues/2727 if (CurrentVersion < Version60) return new RdFormatResult(formatResult.Replace("\r\n", args.NewLineText), null); From 9877c8968dbc1d53c23dca6f1664903dc900dd05 Mon Sep 17 00:00:00 2001 From: Alex Berezhnykh Date: Fri, 3 Mar 2023 18:11:43 +0300 Subject: [PATCH 8/8] fix --- .../FSharp.Psi.Features/src/CodeCleanup/FSharpReformatCode.fs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ReSharper.FSharp/src/FSharp.Psi.Features/src/CodeCleanup/FSharpReformatCode.fs b/ReSharper.FSharp/src/FSharp.Psi.Features/src/CodeCleanup/FSharpReformatCode.fs index 752213b6f0..acc55393ac 100644 --- a/ReSharper.FSharp/src/FSharp.Psi.Features/src/CodeCleanup/FSharpReformatCode.fs +++ b/ReSharper.FSharp/src/FSharp.Psi.Features/src/CodeCleanup/FSharpReformatCode.fs @@ -79,7 +79,8 @@ type FSharpReformatCode(textControlManager: ITextControlManager) = sourceFile.GetPsiServices().Files.CommitAllDocuments() with _ -> () else - let textControl = textControlManager.VisibleTextControls |> Seq.tryFind (fun c -> c.Document == document) + let textControl = textControlManager.VisibleTextControls + |> Seq.tryFind (fun c -> c.Document == document && c.Window.IsFocused.Value) let cursorPosition = textControl |> Option.map (fun c -> c.Caret.Position.Value.ToDocLineColumn()) let formatResult = fantomasHost.FormatDocument(filePath, text, settings, parsingOptions, newLineText, cursorPosition) let newCursorPosition = formatResult.CursorPosition