Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit4ed4eb0

Browse files
xuanduc987KevinRansom
authored andcommitted
Increase indent level after line end with certain keywords (#3196)
* Increase indent level after line end with certain keywords* Fix test case* Use Tokenizer* Refactoring and fix test* Saul review* Add more test* Fix tests* Rename tryFindLastNoneEmptyToken -> tryFindLastNoneWhitespaceToken* Trailing comment is ok* Move auto indent to a separate test* Use same indentation as the previous comment line
1 parent53f99c2 commit4ed4eb0

File tree

4 files changed

+214
-28
lines changed

4 files changed

+214
-28
lines changed

‎src/fsharp/vs/ServiceLexing.fs‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,17 @@ module FSharpTokenTag =
7070
letRARROW= tagOfToken RARROW
7171
letLARROW= tagOfToken LARROW
7272
letQUOTE= tagOfToken QUOTE
73+
letWHITESPACE= tagOfToken(WHITESPACE Unchecked.defaultof<_>)
74+
letCOMMENT= tagOfToken(COMMENT Unchecked.defaultof<_>)
75+
letLINE_COMMENT= tagOfToken(LINE_COMMENT Unchecked.defaultof<_>)
76+
letBEGIN= tagOfToken BEGIN
77+
letDO= tagOfToken DO
78+
letFUNCTION= tagOfToken FUNCTION
79+
letTHEN= tagOfToken THEN
80+
letELSE= tagOfToken ELSE
81+
letSTRUCT= tagOfToken STRUCT
82+
letCLASS= tagOfToken CLASS
83+
letTRY= tagOfToken TRY
7384

7485

7586
/// This corresponds to a token categorization originally used in Visual Studio 2003.

‎src/fsharp/vs/ServiceLexing.fsi‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,28 @@ module FSharpTokenTag =
171171
valLARROW:int
172172
/// Indicates the token is a `"`
173173
valQUOTE:int
174+
/// Indicates the token is a whitespace
175+
valWHITESPACE:int
176+
/// Indicates the token is a comment
177+
valCOMMENT:int
178+
/// Indicates the token is a line comment
179+
valLINE_COMMENT:int
180+
/// Indicates the token is keyword `begin`
181+
valBEGIN:int
182+
/// Indicates the token is keyword `do`
183+
valDO:int
184+
/// Indicates the token is keyword `function`
185+
valFUNCTION:int
186+
/// Indicates the token is keyword `then`
187+
valTHEN:int
188+
/// Indicates the token is keyword `else`
189+
valELSE:int
190+
/// Indicates the token is keyword `struct`
191+
valSTRUCT:int
192+
/// Indicates the token is keyword `class`
193+
valCLASS:int
194+
/// Indicates the token is keyword `try`
195+
valTRY:int
174196

175197
/// Information about a particular token from the tokenizer
176198
typeFSharpTokenInfo=

‎vsintegration/src/FSharp.Editor/Formatting/IndentationService.fs‎

Lines changed: 68 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,35 +12,81 @@ open Microsoft.CodeAnalysis.Formatting
1212
openMicrosoft.CodeAnalysis.Host.Mef
1313
openMicrosoft.CodeAnalysis.Text
1414

15+
openMicrosoft.FSharp.Compiler.SourceCodeServices
16+
1517
[<Shared>]
1618
[<ExportLanguageService(typeof<ISynchronousIndentationService>, FSharpConstants.FSharpLanguageName)>]
17-
typeinternalFSharpIndentationService()=
19+
typeinternalFSharpIndentationService
20+
[<ImportingConstructor>]
21+
(projectInfoManager: ProjectInfoManager)=
1822

19-
static memberGetDesiredIndentation(sourceText:SourceText,lineNumber:int,tabSize:int):Option<int>=
23+
static memberGetDesiredIndentation(documentId:DocumentId,sourceText:SourceText,filePath:string,lineNumber:int,tabSize:int,optionsOpt:FSharpProjectOptions option):Option<int>=
2024
// Match indentation with previous line
2125
let rectryFindPreviousNonEmptyLine l=
2226
if l<=0then None
2327
else
2428
letpreviousLine= sourceText.Lines.[l-1]
25-
ifnot(String.IsNullOrEmpty(previousLine.ToString()))then
29+
ifnot(String.IsNullOrEmpty(previousLine.ToString()))then
2630
Some previousLine
2731
else
2832
tryFindPreviousNonEmptyLine(l-1)
29-
// No indentation on the first line of a document
30-
if lineNumber=0then None
31-
else
32-
match tryFindPreviousNonEmptyLine lineNumberwith
33-
| None-> Some0
34-
| Some previousLine->
35-
let recloop column spaces=
36-
if previousLine.Start+ column>= previousLine.Endthen
37-
spaces
38-
else
39-
match previousLine.Text.[previousLine.Start+ column]with
40-
|' '-> loop(column+1)(spaces+1)
41-
|'\t'-> loop(column+1)(((spaces/ tabSize)+1)* tabSize)
42-
|_-> spaces
43-
Some(loop00)
33+
34+
let rectryFindLastNoneWhitespaceOrCommentToken(line:TextLine)=maybe{
35+
let!options= optionsOpt
36+
letdefines= CompilerEnvironment.GetCompilationDefinesForEditing(filePath, options.OtherOptions|> Seq.toList)
37+
lettokens= Tokenizer.tokenizeLine(documentId, sourceText, line.Start, filePath, defines)
38+
39+
return!
40+
tokens
41+
|> List.rev
42+
|> List.tryFind(fun x->
43+
x.Tag<> FSharpTokenTag.WHITESPACE&& x.Tag<> FSharpTokenTag.COMMENT&& x.Tag<> FSharpTokenTag.LINE_COMMENT)
44+
}
45+
46+
let(|Eq|_|)y x=
47+
if x= ythen Some()
48+
else None
49+
50+
let(|NeedIndent|_|)(token:FSharpTokenInfo)=
51+
match token.Tagwith
52+
| Eq FSharpTokenTag.EQUALS
53+
| Eq FSharpTokenTag.LARROW
54+
| Eq FSharpTokenTag.RARROW
55+
| Eq FSharpTokenTag.LPAREN
56+
| Eq FSharpTokenTag.LBRACK
57+
| Eq FSharpTokenTag.LBRACK_BAR
58+
| Eq FSharpTokenTag.LBRACK_LESS
59+
| Eq FSharpTokenTag.LBRACE
60+
| Eq FSharpTokenTag.BEGIN
61+
| Eq FSharpTokenTag.DO
62+
| Eq FSharpTokenTag.FUNCTION
63+
| Eq FSharpTokenTag.THEN
64+
| Eq FSharpTokenTag.ELSE
65+
| Eq FSharpTokenTag.STRUCT
66+
| Eq FSharpTokenTag.CLASS
67+
| Eq FSharpTokenTag.TRY-> Some()
68+
|_-> None
69+
70+
maybe{
71+
let!previousLine= tryFindPreviousNonEmptyLine lineNumber
72+
73+
let recloop column spaces=
74+
if previousLine.Start+ column>= previousLine.Endthen
75+
spaces
76+
else
77+
match previousLine.Text.[previousLine.Start+ column]with
78+
|' '-> loop(column+1)(spaces+1)
79+
|'\t'-> loop(column+1)(((spaces/ tabSize)+1)* tabSize)
80+
|_-> spaces
81+
82+
letlastIndent= loop00
83+
84+
letlastToken= tryFindLastNoneWhitespaceOrCommentToken previousLine
85+
return
86+
match lastTokenwith
87+
| Some(NeedIndent)->(lastIndent/tabSize+1)* tabSize
88+
|_-> lastIndent
89+
}
4490

4591
interface ISynchronousIndentationServicewith
4692
memberthis.GetDesiredIndentation(document:Document,lineNumber:int,cancellationToken:CancellationToken):Nullable<IndentationResult>=
@@ -49,9 +95,10 @@ type internal FSharpIndentationService() =
4995
let!sourceText= document.GetTextAsync(cancellationToken)|> Async.AwaitTask
5096
let!options= document.GetOptionsAsync(cancellationToken)|> Async.AwaitTask
5197
lettabSize= options.GetOption(FormattingOptions.TabSize, FSharpConstants.FSharpLanguageName)
52-
53-
return
54-
match FSharpIndentationService.GetDesiredIndentation(sourceText, lineNumber, tabSize)with
98+
letprojectOptionsOpt= projectInfoManager.TryGetOptionsForEditingDocumentOrProject document
99+
letindent= FSharpIndentationService.GetDesiredIndentation(document.Id, sourceText, document.FilePath, lineNumber, tabSize, projectOptionsOpt)
100+
return
101+
match indentwith
55102
| None-> Nullable()
56103
| Some(indentation)-> Nullable<IndentationResult>(IndentationResult(sourceText.Lines.[lineNumber].Start, indentation))
57104
}|>(fun c-> Async.RunSynchronously(c,cancellationToken=cancellationToken))

‎vsintegration/tests/unittests/IndentationServiceTests.fs‎

Lines changed: 113 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,32 @@ open System.Threading
66

77
openNUnit.Framework
88

9+
openMicrosoft.CodeAnalysis
910
openMicrosoft.CodeAnalysis.Classification
1011
openMicrosoft.CodeAnalysis.Editor
1112
openMicrosoft.CodeAnalysis.Text
1213
openMicrosoft.VisualStudio.FSharp.Editor
14+
openMicrosoft.FSharp.Compiler.SourceCodeServices
1315

1416
[<TestFixture>][<Category"Roslyn Services">]
1517
typeIndentationServiceTests()=
18+
static letfilePath="C:\\test.fs"
19+
static letoptions:FSharpProjectOptions={
20+
ProjectFileName="C:\\test.fsproj"
21+
SourceFiles=[| filePath|]
22+
ReferencedProjects=[||]
23+
OtherOptions=[||]
24+
IsIncompleteTypeCheckEnvironment=true
25+
UseScriptResolutionRules=false
26+
LoadTime= DateTime.MaxValue
27+
OriginalLoadReferences=[]
28+
UnresolvedReferences= None
29+
ExtraProjectInfo= None
30+
Stamp= None
31+
}
32+
static letdocumentId= DocumentId.CreateNewId(ProjectId.CreateNewId())
33+
34+
static letindentComment= System.Text.RegularExpressions.Regex(@"\$\s*Indent:\s*(\d+)\s*\$")
1635

1736
static lettabSize=4
1837

@@ -36,38 +55,125 @@ namespace testspace
3655
type testtype
3756
static member testmember = 1
3857
58+
"
59+
60+
static letautoIndentTemplate="
61+
let plus x y =
62+
x + y // $Indent: 4$
63+
64+
let mutable x = 0
65+
x <-
66+
10 * 2 // $Indent: 4$
67+
68+
match some 10 with
69+
| None -> 0
70+
| Some x ->
71+
x + 1 // $Indent: 4$
72+
73+
try
74+
failwith\"fail\" // $Indent: 4$
75+
with
76+
| :? System.Exception ->\"error\"
77+
78+
if 10 > 0 then
79+
true // $Indent: 4$
80+
else
81+
false // $Indent: 4$
82+
83+
(
84+
1, // $Indent: 4$
85+
2
86+
)
87+
88+
[
89+
1 // $Indent: 4$
90+
2
91+
]
92+
93+
[|
94+
1 // $Indent: 4$
95+
2
96+
|]
97+
98+
[<
99+
Literal // $Indent: 4$
100+
>]
101+
let constx = 10
102+
103+
let t = seq { // $Indent: 0$
104+
yield 1 // $Indent: 4$
105+
}
106+
107+
let g = function
108+
| None -> 1 // $Indent: 4$
109+
| Some _ -> 0
110+
111+
module MyModule = begin
112+
end // $Indent: 4$
113+
114+
type MyType() = class
115+
end // $Indent: 4$
116+
117+
type MyStruct = struct
118+
end // $Indent: 4$
119+
120+
while true do
121+
printfn\"never end\" // $Indent: 4$
122+
123+
// After line has keyword in comment such as function
124+
// should not be indented $Indent: 0$
125+
126+
// Even if the line before only had comment like this
127+
// The follwing line should inherit that indentation too $Indent: 4$
39128
"
40129

41130
static memberprivatetestCases:Object[][]=[|
42131
[| None;0; consoleProjectTemplate|]
43-
[|Some(0);1; consoleProjectTemplate|]
132+
[|None;1; consoleProjectTemplate|]
44133
[| Some(0);2; consoleProjectTemplate|]
45134
[| Some(0);3; consoleProjectTemplate|]
46135
[| Some(0);4; consoleProjectTemplate|]
47136
[| Some(0);5; consoleProjectTemplate|]
48-
[| Some(0);6; consoleProjectTemplate|]
137+
[| Some(4);6; consoleProjectTemplate|]
49138
[| Some(4);7; consoleProjectTemplate|]
50139
[| Some(4);8; consoleProjectTemplate|]
51140

52141
[| None;0; libraryProjectTemplate|]
53-
[|Some(0);1; libraryProjectTemplate|]
142+
[|None;1; libraryProjectTemplate|]
54143
[| Some(0);2; libraryProjectTemplate|]
55144
[| Some(0);3; libraryProjectTemplate|]
56-
[| Some(0);4; libraryProjectTemplate|]
145+
[| Some(4);4; libraryProjectTemplate|]
57146
[| Some(4);5; libraryProjectTemplate|]
58147

59148
[| None;0; nestedTypesTemplate|]
60-
[|Some(0);1; nestedTypesTemplate|]
149+
[|None;1; nestedTypesTemplate|]
61150
[| Some(0);2; nestedTypesTemplate|]
62151
[| Some(4);3; nestedTypesTemplate|]
63152
[| Some(8);4; nestedTypesTemplate|]
64153
[| Some(8);5; nestedTypesTemplate|]
65154
|]
66155

156+
static memberprivateautoIndentTestCases=
157+
autoIndentTemplate.Split[|'\n'|]
158+
|> Array.map(fun s-> s.Trim())
159+
|> Array.indexed
160+
|> Array.choose(fun(line,text)->
161+
letm= indentComment.Match text
162+
if m.Successthen Some(line, System.Convert.ToInt32 m.Groups.[1].Value)
163+
else None)
164+
|> Array.map(fun(lineNumber,expectedIndentation)->
165+
[| Some(expectedIndentation); lineNumber; autoIndentTemplate|]: Object[])
166+
67167
[<TestCaseSource("testCases")>]
68168
memberthis.TestIndentation(expectedIndentation:Option<int>,lineNumber:int,template:string)=
69-
letactualIndentation= FSharpIndentationService.GetDesiredIndentation(SourceText.From(template), lineNumber, tabSize)
169+
letactualIndentation= FSharpIndentationService.GetDesiredIndentation(documentId, SourceText.From(template), filePath, lineNumber, tabSize,(Some options))
170+
match expectedIndentationwith
171+
| None-> Assert.IsTrue(actualIndentation.IsNone,"No indentation was expected at line {0}", lineNumber)
172+
| Some(indentation)-> Assert.AreEqual(expectedIndentation.Value, actualIndentation.Value,"Indentation on line {0} doesn't match", lineNumber)
173+
174+
[<TestCaseSource("autoIndentTestCases")>]
175+
memberthis.TestAutoIndentation(expectedIndentation:Option<int>,lineNumber:int,template:string)=
176+
letactualIndentation= FSharpIndentationService.GetDesiredIndentation(documentId, SourceText.From(template), filePath, lineNumber, tabSize,(Some options))
70177
match expectedIndentationwith
71178
| None-> Assert.IsTrue(actualIndentation.IsNone,"No indentation was expected at line {0}", lineNumber)
72179
| Some(indentation)-> Assert.AreEqual(expectedIndentation.Value, actualIndentation.Value,"Indentation on line {0} doesn't match", lineNumber)
73-

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp