Skip to content

Commit

Permalink
Add formatSelection.
Browse files Browse the repository at this point in the history
  • Loading branch information
nojaf committed Jul 31, 2021
1 parent ee96b66 commit af2b63a
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 58 deletions.
42 changes: 35 additions & 7 deletions src/Fantomas.Client/Contracts.fs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ module Methods =
[<Literal>]
let FormatDocument = "fantomas/formatDocument"

[<Literal>]
let FormatSelection = "fantomas/formatSelection"

[<Literal>]
let Configuration = "fantomas/configuration"

Expand All @@ -23,10 +26,34 @@ type FormatDocumentRequest =
/// Overrides the found .editorconfig.
Config: IReadOnlyDictionary<string, string> option }

type FormatSourceRange =
class
type FormatDocumentResponse = { Formatted: string }

type FormatSelectionRequest =
{ SourceCode: string
/// File path will be used to identify the .editorconfig options
/// Unless the configuration is passed
FilePath: string
/// Overrides the found .editorconfig.
Config: IReadOnlyDictionary<string, string> option
/// Range follows the same semantics of the FSharp Compiler Range type.
Range: FormatSelectionRange }

and FormatSelectionRange =
struct
val StartLine: int
val StartColumn: int
val EndLine: int
val EndColumn: int

new(startLine: int, startColumn: int, endLine: int, endColumn: int) =
{ StartLine = startLine
StartColumn = startColumn
EndLine = endLine
EndColumn = endColumn }
end

type FormatSelectionResponse = { Formatted: string }

type FantomasOption = { Type: string; DefaultValue: string }

type ConfigurationResponse =
Expand All @@ -35,15 +62,16 @@ type ConfigurationResponse =

type VersionResponse = { Version: string }

type FormatDocumentResponse = { Formatted: string }

type FantomasService =
interface
inherit IDisposable
abstract member Version : CancellationToken option -> Async<VersionResponse>
abstract member VersionAsync : ?cancellationToken: CancellationToken -> Async<VersionResponse>

abstract member FormatDocumentAsync :
FormatDocumentRequest * CancellationToken option -> Async<FormatDocumentResponse>
FormatDocumentRequest * ?cancellationToken: CancellationToken -> Async<FormatDocumentResponse>

abstract member FormatSelectionAsync :
FormatSelectionRequest * ?cancellationToken: CancellationToken -> Async<FormatSelectionResponse>

abstract member Configuration : CancellationToken option -> Async<ConfigurationResponse>
abstract member ConfigurationAsync : ?cancellationToken: CancellationToken -> Async<ConfigurationResponse>
end
69 changes: 31 additions & 38 deletions src/Fantomas.Client/LSPFantomasService.fs
Original file line number Diff line number Diff line change
Expand Up @@ -26,50 +26,43 @@ type LSPFantomasService(daemonStartInfo: ProcessStartInfo) =
client.Dispose()
daemonProcess.Dispose()

member _.Version(?cancellationToken: CancellationToken) : Async<VersionResponse> =
async {
let ct =
orDefaultCancellationToken cancellationToken

return!
client.InvokeWithCancellationAsync<VersionResponse>(Methods.Version, cancellationToken = ct)
|> Async.AwaitTask
}
member _.VersionAsync(?cancellationToken: CancellationToken) : Async<VersionResponse> =
client.InvokeWithCancellationAsync<VersionResponse>(
Methods.Version,
cancellationToken = orDefaultCancellationToken cancellationToken
)
|> Async.AwaitTask

member _.FormatDocumentAsync
(
formatDocumentOptions: FormatDocumentRequest,
?cancellationToken: CancellationToken
) : Async<FormatDocumentResponse> =
async {
let ct =
orDefaultCancellationToken cancellationToken

let! formatDocumentResponse =
client.InvokeWithParameterObjectAsync<FormatDocumentResponse>(
Methods.FormatDocument,
argument = formatDocumentOptions,
cancellationToken = ct
)
|> Async.AwaitTask

return formatDocumentResponse
}

member _.Configuration(?cancellationToken: CancellationToken) : Async<ConfigurationResponse> =
async {
let ct =
orDefaultCancellationToken cancellationToken

let! configurationResponse =
client.InvokeWithCancellationAsync<ConfigurationResponse>(
Methods.Configuration,
cancellationToken = ct
)
|> Async.AwaitTask

return configurationResponse
}
client.InvokeWithParameterObjectAsync<FormatDocumentResponse>(
Methods.FormatDocument,
argument = formatDocumentOptions,
cancellationToken = orDefaultCancellationToken cancellationToken
)
|> Async.AwaitTask

member _.FormatSelectionAsync
(
formatSelectionRequest: FormatSelectionRequest,
?cancellationToken: CancellationToken
) =
client.InvokeWithParameterObjectAsync<FormatSelectionResponse>(
Methods.FormatSelection,
argument = formatSelectionRequest,
cancellationToken = orDefaultCancellationToken cancellationToken
)
|> Async.AwaitTask

member _.ConfigurationAsync(?cancellationToken: CancellationToken) : Async<ConfigurationResponse> =
client.InvokeWithCancellationAsync<ConfigurationResponse>(
Methods.Configuration,
cancellationToken = orDefaultCancellationToken cancellationToken
)
|> Async.AwaitTask

let createForWorkingDirectory (workingDirectory: string) : Result<LSPFantomasService, string> =
if not (Directory.Exists workingDirectory) then
Expand Down
51 changes: 40 additions & 11 deletions src/Fantomas.CoreGlobalTool.Tests/DaemonTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@ open Fantomas
open Fantomas.Client.Contracts
open Fantomas.Client.LSPFantomasService

let private assertFormatted (response: FormatDocumentResponse) (expected: string) : unit =
String.normalizeNewLine response.Formatted
let private assertFormatted (actual: string) (expected: string) : unit =
String.normalizeNewLine actual
|> should equal (String.normalizeNewLine expected)

[<Test>]
let ``compare the version with the public api`` () =
async {
let processStart = getFantomasToolStartInfo "--daemon"
use client = new LSPFantomasService(processStart)
let! { Version = version } = (client :> FantomasService).Version None
let! { Version = version } = (client :> FantomasService).VersionAsync()

version
|> should equal (CodeFormatter.GetVersion())
Expand All @@ -37,10 +37,10 @@ let ``format document`` () =

let! response =
(client :> FantomasService)
.FormatDocumentAsync(request, None)
.FormatDocumentAsync(request)

assertFormatted
response
response.Formatted
"module Foobar
"
}
Expand All @@ -63,10 +63,10 @@ let ``format document respecting .editorconfig file`` () =

let! response =
(client :> FantomasService)
.FormatDocumentAsync(request, None)
.FormatDocumentAsync(request)

assertFormatted
response
response.Formatted
"module Foo
let a = //
Expand All @@ -92,17 +92,47 @@ let ``custom configuration has precedence over .editorconfig file`` () =

let! response =
(client :> FantomasService)
.FormatDocumentAsync(request, None)
.FormatDocumentAsync(request)

assertFormatted
response
response.Formatted
"module Foo
let a = //
4
"
}

[<Test>]
let ``format selection`` () =
async {
let sourceCode =
"""module Foo
let x = 4
let y = 5
"""

let processStart = getFantomasToolStartInfo "--daemon"
use client = new LSPFantomasService(processStart)
use _codeFile = new TemporaryFileCodeSample(sourceCode)

let request: FormatSelectionRequest =
let range = FormatSelectionRange(3, 0, 3, 16)

{ SourceCode = sourceCode
FilePath = "tmp.fsx" // codeFile.Filename
Config = None
Range = range }

let! response =
(client :> FantomasService)
.FormatSelectionAsync(request)

assertFormatted response.Formatted "let x = 4\n"
}


[<Test>]
let ``find fantomas tool from working directory`` () =
async {
Expand All @@ -125,8 +155,7 @@ let ``find fantomas tool from working directory`` () =
.FormatDocumentAsync(
{ SourceCode = originalCode
FilePath = filePath
Config = None },
None
Config = None }
)

let formattedCode = formattedResponse
Expand Down
32 changes: 30 additions & 2 deletions src/Fantomas.CoreGlobalTool/Daemon.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
open System
open System.Diagnostics
open System.IO
open System.Threading
open System.Threading.Tasks
open FSharp.Compiler.Text.Range
open FSharp.Compiler.Text.Pos
open StreamJsonRpc
open Fantomas
open Fantomas.SourceOrigin
open StreamJsonRpc
open System.Threading
open Fantomas.FormatConfig
open Fantomas.Extras.EditorConfig
open Fantomas.Client.Contracts
Expand Down Expand Up @@ -55,6 +57,32 @@ type FantomasDaemon(sender: Stream, reader: Stream) as this =
CodeFormatterImpl.sharedChecker.Value
)

return ({ Formatted = formatted }: FormatDocumentResponse)
}
|> Async.StartAsTask

[<JsonRpcMethod(Methods.FormatSelection, UseSingleObjectParameterDeserialization = true)>]
member _.FormatSelectionAsync(request: FormatSelectionRequest) : Task<FormatSelectionResponse> =
async {
let config =
match request.Config with
| Some configProperties -> parseOptionsFromEditorConfig configProperties
| None -> readConfiguration request.FilePath

let range =
let r = request.Range
mkRange request.FilePath (mkPos r.StartLine r.StartColumn) (mkPos r.EndLine r.EndColumn)

let! formatted =
CodeFormatter.FormatSelectionAsync(
request.FilePath,
range,
SourceString request.SourceCode,
config,
CodeFormatterImpl.createParsingOptionsFromFile request.FilePath,
CodeFormatterImpl.sharedChecker.Value
)

return { Formatted = formatted }
}
|> Async.StartAsTask
Expand Down

0 comments on commit af2b63a

Please sign in to comment.