Skip to content

Commit 0c731b1

Browse files
committed
Move CSharpLanguageServer.FormatUtil -> CSharpLanguageServer.Roslyn.Document
1 parent c10e132 commit 0c731b1

File tree

8 files changed

+136
-116
lines changed

8 files changed

+136
-116
lines changed

src/CSharpLanguageServer/CSharpLanguageServer.fsproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@
2424
<Compile Include="Logging.fs" />
2525
<Compile Include="Util.fs" />
2626
<Compile Include="Types.fs" />
27-
<Compile Include="FormatUtil.fs" />
2827
<Compile Include="ProgressReporter.fs" />
2928
<Compile Include="Roslyn/Conversions.fs" />
3029
<Compile Include="Roslyn/WorkspaceServices.fs" />
3130
<Compile Include="Roslyn/Solution.fs" />
31+
<Compile Include="Roslyn/Document.fs" />
3232
<Compile Include="Roslyn/Symbol.fs" />
3333
<Compile Include="DocumentationUtil.fs" />
3434
<Compile Include="Diagnostics.fs" />

src/CSharpLanguageServer/FormatUtil.fs

Lines changed: 0 additions & 104 deletions
This file was deleted.

src/CSharpLanguageServer/Handlers/DocumentFormatting.fs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,27 @@ open Microsoft.CodeAnalysis.Formatting
44
open Ionide.LanguageServerProtocol.Types
55
open Ionide.LanguageServerProtocol.JsonRpc
66

7-
open CSharpLanguageServer
87
open CSharpLanguageServer.State
8+
open CSharpLanguageServer.Roslyn.Document
99

1010
[<RequireQualifiedAccess>]
1111
module DocumentFormatting =
1212
let provider (clientCapabilities: ClientCapabilities) : U2<bool, DocumentFormattingOptions> option =
1313
Some(U2.C1 true)
1414

1515
let handle (context: ServerRequestContext) (p: DocumentFormattingParams) : AsyncLspResult<TextEdit[] option> = async {
16+
let lspFormattingOptions =
17+
if context.State.Settings.ApplyFormattingOptions then
18+
Some p.Options
19+
else
20+
None
21+
1622
match context.GetUserDocument p.TextDocument.Uri with
1723
| None -> return None |> LspResult.success
1824
| Some doc ->
1925
let! ct = Async.CancellationToken
20-
let! options = FormatUtil.getFormattingOptions context.State.Settings doc p.Options
26+
let! options = getDocumentFormattingOptionSet doc lspFormattingOptions
2127
let! newDoc = Formatter.FormatAsync(doc, options, cancellationToken = ct) |> Async.AwaitTask
22-
let! textEdits = FormatUtil.getChanges newDoc doc
28+
let! textEdits = getDocumentDiffAsLspTextEdits newDoc doc
2329
return textEdits |> Some |> LspResult.success
2430
}

src/CSharpLanguageServer/Handlers/DocumentOnTypeFormatting.fs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ open Microsoft.CodeAnalysis.Formatting
77
open Ionide.LanguageServerProtocol.Types
88
open Ionide.LanguageServerProtocol.JsonRpc
99

10-
open CSharpLanguageServer
1110
open CSharpLanguageServer.State
1211
open CSharpLanguageServer.Roslyn.Conversions
12+
open CSharpLanguageServer.Roslyn.Document
1313

1414
[<RequireQualifiedAccess>]
1515
module DocumentOnTypeFormatting =
@@ -42,10 +42,16 @@ module DocumentOnTypeFormatting =
4242
| _ -> None
4343

4444
let handle (context: ServerRequestContext) (p: DocumentOnTypeFormattingParams) : AsyncLspResult<TextEdit[] option> = async {
45+
let lspFormattingOptions =
46+
if context.State.Settings.ApplyFormattingOptions then
47+
Some p.Options
48+
else
49+
None
50+
4551
match context.GetUserDocument p.TextDocument.Uri with
4652
| None -> return None |> LspResult.success
4753
| Some doc ->
48-
let! options = FormatUtil.getFormattingOptions context.State.Settings doc p.Options
54+
let! options = getDocumentFormattingOptionSet doc lspFormattingOptions
4955
let! ct = Async.CancellationToken
5056
let! sourceText = doc.GetTextAsync(ct) |> Async.AwaitTask
5157
let pos = Position.toRoslynPosition sourceText.Lines p.Position
@@ -69,7 +75,7 @@ module DocumentOnTypeFormatting =
6975
)
7076
|> Async.AwaitTask
7177

72-
let! textEdits = FormatUtil.getChanges newDoc doc
78+
let! textEdits = getDocumentDiffAsLspTextEdits newDoc doc
7379
return textEdits |> Some |> LspResult.success
7480
| _ -> return None |> LspResult.success
7581
}

src/CSharpLanguageServer/Handlers/DocumentRangeFormatting.fs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,27 @@ open Microsoft.CodeAnalysis.Text
55
open Ionide.LanguageServerProtocol.Types
66
open Ionide.LanguageServerProtocol.JsonRpc
77

8-
open CSharpLanguageServer
98
open CSharpLanguageServer.State
109
open CSharpLanguageServer.Roslyn.Conversions
10+
open CSharpLanguageServer.Roslyn.Document
1111

1212
[<RequireQualifiedAccess>]
1313
module DocumentRangeFormatting =
14-
let provider (_: ClientCapabilities) : U2<bool, DocumentRangeFormattingOptions> option = Some(U2.C1 true)
14+
let provider (_cc: ClientCapabilities) : U2<bool, DocumentRangeFormattingOptions> option = Some(U2.C1 true)
1515

1616
let handle (context: ServerRequestContext) (p: DocumentRangeFormattingParams) : AsyncLspResult<TextEdit[] option> = async {
17+
let lspFormattingOptions =
18+
if context.State.Settings.ApplyFormattingOptions then
19+
Some p.Options
20+
else
21+
None
22+
1723
match context.GetUserDocument p.TextDocument.Uri with
1824
| None -> return None |> LspResult.success
1925
| Some doc ->
2026
let! ct = Async.CancellationToken
21-
let! options = FormatUtil.getFormattingOptions context.State.Settings doc p.Options
27+
28+
let! options = getDocumentFormattingOptionSet doc lspFormattingOptions
2229
let! sourceText = doc.GetTextAsync(ct) |> Async.AwaitTask
2330
let startPos = Position.toRoslynPosition sourceText.Lines p.Range.Start
2431
let endPos = Position.toRoslynPosition sourceText.Lines p.Range.End
@@ -29,6 +36,6 @@ module DocumentRangeFormatting =
2936
Formatter.FormatAsync(doc, TextSpan.FromBounds(tokenStart, endPos), options, cancellationToken = ct)
3037
|> Async.AwaitTask
3138

32-
let! textEdits = FormatUtil.getChanges newDoc doc
39+
let! textEdits = getDocumentDiffAsLspTextEdits newDoc doc
3340
return textEdits |> Some |> LspResult.success
3441
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
module CSharpLanguageServer.Roslyn.Document
2+
3+
open Microsoft.CodeAnalysis
4+
open Microsoft.CodeAnalysis.CSharp.Formatting
5+
open Microsoft.CodeAnalysis.Options
6+
open Microsoft.CodeAnalysis.Text
7+
open Microsoft.CodeAnalysis.Formatting
8+
open Ionide.LanguageServerProtocol.Types
9+
10+
open CSharpLanguageServer.Types
11+
12+
let private processChange (oldText: SourceText) (change: TextChange) : TextEdit =
13+
let mapToTextEdit (linePosition: LinePositionSpan, newText: string) : TextEdit =
14+
{ NewText = newText
15+
Range =
16+
{ Start =
17+
{ Line = uint32 linePosition.Start.Line
18+
Character = uint32 linePosition.Start.Character }
19+
End =
20+
{ Line = uint32 linePosition.End.Line
21+
Character = uint32 linePosition.End.Character } } }
22+
23+
let defaultTextEdit (oldText: SourceText, change: TextChange) : TextEdit =
24+
let linePosition = oldText.Lines.GetLinePositionSpan change.Span
25+
mapToTextEdit (linePosition, change.NewText)
26+
27+
let padLeft (span: TextSpan) : TextSpan =
28+
TextSpan.FromBounds(span.Start - 1, span.End)
29+
30+
let padRight (span: TextSpan) : TextSpan =
31+
TextSpan.FromBounds(span.Start, span.End + 1)
32+
33+
let rec checkSpanLineEndings (newText: string, oldText: SourceText, span: TextSpan, prefix: string) : TextEdit =
34+
if span.Start > 0 && newText[0].Equals '\n' && oldText[span.Start - 1].Equals '\r' then
35+
checkSpanLineEndings (newText, oldText, padLeft span, "\r") |> ignore
36+
37+
if
38+
span.End < oldText.Length - 1
39+
&& newText[newText.Length - 1].Equals '\r'
40+
&& oldText[span.End].Equals '\n'
41+
then
42+
let linePosition = oldText.Lines.GetLinePositionSpan(padRight span)
43+
mapToTextEdit (linePosition, prefix + newText.ToString() + "\n")
44+
else
45+
let linePosition = oldText.Lines.GetLinePositionSpan span
46+
mapToTextEdit (linePosition, newText.ToString())
47+
48+
let newText = change.NewText
49+
50+
if newText.Length > 0 then
51+
checkSpanLineEndings (newText, oldText, change.Span, "")
52+
else
53+
defaultTextEdit (oldText, change)
54+
55+
56+
let private convert (oldText: SourceText) (changes: TextChange[]) : TextEdit[] =
57+
//why doesnt it pick up that TextSpan implements IComparable<T>?
58+
//one of life's many mysteries
59+
let comparer (lhs: TextChange) (rhs: TextChange) : int = lhs.Span.CompareTo rhs.Span
60+
61+
changes
62+
|> Seq.sortWith comparer
63+
|> Seq.map (fun x -> processChange oldText x)
64+
|> Seq.toArray
65+
66+
67+
let getDocumentDiffAsLspTextEdits (doc: Document) (oldDoc: Document) : Async<TextEdit[]> = async {
68+
let! ct = Async.CancellationToken
69+
let! changes = doc.GetTextChangesAsync(oldDoc, ct) |> Async.AwaitTask
70+
let! oldText = oldDoc.GetTextAsync ct |> Async.AwaitTask
71+
return convert oldText (changes |> Seq.toArray)
72+
}
73+
74+
75+
let getDocumentFormattingOptionSet
76+
(doc: Document)
77+
(lspFormattingOptions: FormattingOptions option)
78+
: Async<OptionSet> =
79+
async {
80+
let! docOptions = doc.GetOptionsAsync() |> Async.AwaitTask
81+
82+
return
83+
match lspFormattingOptions with
84+
| None -> docOptions
85+
| Some lspFormattingOptions ->
86+
docOptions
87+
|> _.WithChangedOption(
88+
FormattingOptions.IndentationSize,
89+
LanguageNames.CSharp,
90+
int lspFormattingOptions.TabSize
91+
)
92+
|> _.WithChangedOption(
93+
FormattingOptions.UseTabs,
94+
LanguageNames.CSharp,
95+
not lspFormattingOptions.InsertSpaces
96+
)
97+
|> match lspFormattingOptions.InsertFinalNewline with
98+
| Some insertFinalNewline ->
99+
_.WithChangedOption(CSharpFormattingOptions.NewLineForFinally, insertFinalNewline)
100+
| None -> id
101+
|> match lspFormattingOptions.TrimFinalNewlines with
102+
| Some trimFinalNewlines ->
103+
_.WithChangedOption(CSharpFormattingOptions.NewLineForFinally, not trimFinalNewlines)
104+
| None -> id
105+
}

tests/CSharpLanguageServer.Tests/DocumentFormattingTests.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ let testEditorConfigFormatting () =
2929
| Some tes ->
3030
let expectedClassContents =
3131
File
32-
.ReadAllText(Path.Combine(client.ProjectDir, "Project", "ExpectedFormatting.cs.txt"))
32+
.ReadAllText(Path.Combine(client.ProjectDir, "Project", "Class.cs.formatted.txt"))
3333
.ReplaceLineEndings("\n")
3434

3535
let actualClassContents =

0 commit comments

Comments
 (0)