Skip to content

Commit 25fe21c

Browse files
author
Omar Tawfik
committed
Added Roslyn IndentationService + tests
1 parent b7a3144 commit 25fe21c

File tree

5 files changed

+133
-104
lines changed

5 files changed

+133
-104
lines changed

vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@
3030
</PropertyGroup>
3131
<ItemGroup>
3232
<Compile Include="AssemblyInfo.fs" />
33-
<Compile Include="SmartIndent.fs" />
3433
<Compile Include="ColorizationService.fs" />
3534
<Compile Include="BraceMatchingService.fs" />
35+
<Compile Include="IndentationService.fs" />
3636
<Compile Include="ProjectSiteService.fs" />
3737
<Compile Include="ContentType.fs" />
3838
</ItemGroup>
@@ -60,6 +60,7 @@
6060
<ItemGroup>
6161
<Reference Include="mscorlib" />
6262
<Reference Include="System" />
63+
<Reference Include="PresentationCore" />
6364
<Reference Include="System.ComponentModel.Composition" />
6465
<Reference Include="Microsoft.VisualStudio.Threading, Version=$(RoslynVSBinariesVersion).0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
6566
<HintPath>$(FSharpSourcesRoot)\..\packages\Microsoft.VisualStudio.Threading.14.1.131\lib\net45\Microsoft.VisualStudio.Threading.dll</HintPath>
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
2+
3+
namespace Microsoft.VisualStudio.FSharp.Editor
4+
5+
open System
6+
open System.Composition
7+
open System.Collections.Concurrent
8+
open System.Collections.Generic
9+
open System.Threading
10+
open System.Threading.Tasks
11+
open System.Linq
12+
13+
open Microsoft.CodeAnalysis
14+
open Microsoft.CodeAnalysis.Classification
15+
open Microsoft.CodeAnalysis.Editor
16+
open Microsoft.CodeAnalysis.Editor.Implementation.BraceMatching
17+
open Microsoft.CodeAnalysis.Editor.Shared.Utilities
18+
open Microsoft.CodeAnalysis.Formatting
19+
open Microsoft.CodeAnalysis.Host.Mef
20+
open Microsoft.CodeAnalysis.Text
21+
22+
open Microsoft.VisualStudio.FSharp.LanguageService
23+
open Microsoft.VisualStudio.Text
24+
open Microsoft.VisualStudio.Text.Tagging
25+
26+
open Microsoft.FSharp.Compiler.Parser
27+
open Microsoft.FSharp.Compiler.SourceCodeServices
28+
29+
[<Shared>]
30+
[<ExportLanguageService(typeof<IIndentationService>, FSharpCommonConstants.FSharpLanguageName)>]
31+
type internal FSharpIndentationService() =
32+
33+
static member GetDesiredIndentation(sourceText: SourceText, lineNumber: int, tabSize: int): Option<int> =
34+
if lineNumber = 0 then
35+
// No indentation on the first line of a document
36+
None
37+
else
38+
// Match indentation with previous line
39+
let previousLine = sourceText.Lines.[lineNumber - 1]
40+
let rec loop column spaces =
41+
if previousLine.Start + column >= previousLine.End then
42+
spaces
43+
else match previousLine.Text.[previousLine.Start + column] with
44+
| ' ' -> loop (column + 1) (spaces + 1)
45+
| '\t' -> loop (column + 1) (((spaces / tabSize) + 1) * tabSize)
46+
| _ -> spaces
47+
Some(loop 0 0)
48+
49+
interface IIndentationService with
50+
member this.GetDesiredIndentationAsync(document: Document, lineNumber: int, cancellationToken: CancellationToken): Task<Nullable<IndentationResult>> =
51+
document.GetTextAsync(cancellationToken).ContinueWith(
52+
fun (sourceTextTask: Task<SourceText>) ->
53+
let tabSize = document.Project.Solution.Workspace.Options.GetOption(FormattingOptions.TabSize, FSharpCommonConstants.FSharpLanguageName)
54+
try match FSharpIndentationService.GetDesiredIndentation(sourceTextTask.Result, lineNumber, tabSize) with
55+
| None -> Nullable<IndentationResult>()
56+
| Some(indentation) -> Nullable<IndentationResult>(IndentationResult(sourceTextTask.Result.Lines.[lineNumber].Start, indentation))
57+
with ex ->
58+
Assert.Exception(ex)
59+
reraise()
60+
, TaskContinuationOptions.OnlyOnRanToCompletion)

vsintegration/src/FSharp.Editor/SmartIndent.fs

Lines changed: 0 additions & 103 deletions
This file was deleted.
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
2+
namespace Microsoft.VisualStudio.FSharp.Editor.Tests.Roslyn
3+
4+
open System
5+
open System.Threading
6+
7+
open NUnit.Framework
8+
9+
open Microsoft.CodeAnalysis.Classification
10+
open Microsoft.CodeAnalysis.Editor
11+
open Microsoft.CodeAnalysis.Text
12+
open Microsoft.VisualStudio.FSharp.Editor
13+
14+
[<TestFixture>]
15+
type IndentationServiceTests() =
16+
17+
static let tabSize = 4
18+
19+
static let consoleProjectTemplate = "
20+
// Learn more about F# at http://fsharp.org
21+
// See the 'F# Tutorial' project for more help.
22+
23+
[<EntryPoint>]
24+
let main argv =
25+
printfn \"%A\" argv
26+
0 // return an integer exit code"
27+
28+
static let libraryProjectTemplate = "
29+
namespace ProjectNamespace
30+
31+
type Class1() =
32+
member this.X = \"F#\""
33+
34+
static let nestedTypesTemplate = "
35+
namespace testspace
36+
type testtype
37+
static member testmember = 1"
38+
39+
static member private testCases: Object[][] = [|
40+
[| None; 0; consoleProjectTemplate |]
41+
[| Some(0); 1; consoleProjectTemplate |]
42+
[| Some(0); 2; consoleProjectTemplate |]
43+
[| Some(0); 3; consoleProjectTemplate |]
44+
[| Some(0); 4; consoleProjectTemplate |]
45+
[| Some(0); 5; consoleProjectTemplate |]
46+
[| Some(0); 6; consoleProjectTemplate |]
47+
[| Some(4); 7; consoleProjectTemplate |]
48+
[| Some(4); 8; consoleProjectTemplate |]
49+
50+
[| None; 0; libraryProjectTemplate |]
51+
[| Some(0); 1; libraryProjectTemplate |]
52+
[| Some(0); 2; libraryProjectTemplate |]
53+
[| Some(0); 3; libraryProjectTemplate |]
54+
[| Some(0); 4; libraryProjectTemplate |]
55+
[| Some(4); 5; libraryProjectTemplate |]
56+
57+
[| None; 0; nestedTypesTemplate |]
58+
[| Some(0); 1; nestedTypesTemplate |]
59+
[| Some(0); 2; nestedTypesTemplate |]
60+
[| Some(4); 3; nestedTypesTemplate |]
61+
[| Some(8); 4; nestedTypesTemplate |]
62+
|]
63+
64+
[<TestCaseSource("testCases")>]
65+
member this.TestIndentation(expectedIndentation: Option<int>, lineNumber: int, template: string) =
66+
let actualIndentation = FSharpIndentationService.GetDesiredIndentation(SourceText.From(template), lineNumber, tabSize)
67+
match expectedIndentation with
68+
| None -> Assert.IsTrue(actualIndentation.IsNone, "No indentation was expected at line {0}", lineNumber)
69+
| Some(indentation) -> Assert.AreEqual(expectedIndentation.Value, actualIndentation.Value, "Indentation on line {0} doesn't match", lineNumber)
70+

vsintegration/tests/unittests/VisualFSharp.Unittests.fsproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
<Compile Include="TestLib.ProjectSystem.fs" />
3636
<Compile Include="Tests.Roslyn.ColorizationService.fs" />
3737
<Compile Include="Tests.Roslyn.BraceMatchingService.fs" />
38+
<Compile Include="Tests.Roslyn.IndentationService.fs" />
3839
<Compile Include="Tests.InternalCollections.fs" />
3940
<Compile Include="Tests.Build.fs" />
4041
<Compile Include="Tests.TaskReporter.fs" />

0 commit comments

Comments
 (0)