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

Commita58d9e6

Browse files
saulKevinRansom
authored andcommitted
Reformat indentation on paste (dotnet#4702)
* Format indentation on paste* Fix pasting when next line should be indented* Initial pass at 'Formatting' page* Add some tests* Fix tests
1 parent39fd7b8 commita58d9e6

File tree

43 files changed

+705
-223
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+705
-223
lines changed

‎build.cmd‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -633,8 +633,7 @@ goto :eof
633633
:havemsbuild
634634
set_nrswitch=/nr:false
635635

636-
setmsbuildflags=%_nrswitch% /nologo
637-
REM set msbuildflags=%_nrswitch% /nologo
636+
setmsbuildflags=%_nrswitch% /nologo /clp:Summary /v:minimal
638637
set_ngenexe="%SystemRoot%\Microsoft.NET\Framework\v4.0.30319\ngen.exe"
639638
ifnotexist%_ngenexe%echo Error: Could not find ngen.exe.&&goto :failure
640639

‎src/buildfromsource/FSharp.Compiler.Private/FSComp.fs‎

Lines changed: 153 additions & 150 deletions
Large diffs are not rendered by default.

‎src/buildfromsource/FSharp.Compiler.Private/FSComp.resx‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3082,7 +3082,7 @@
30823082
<value>This number is outside the allowable range for 32-bit floats</value>
30833083
</data>
30843084
<dataname="lexInvalidNumericLiteral"xml:space="preserve">
3085-
<value>This is not a valid numeric literal. Valid numeric literals include 1, 0x1, 0b0001 (int), 1u (uint32), 1L (int64), 1UL (uint64), 1s (int16),1us (uint16),1y (sbyte), 1uy (byte), 1.0 (float), 1.0f (float32), 1.0m (decimal), 1I (BigInteger).</value>
3085+
<value>This is not a valid numeric literal. Valid numeric literals include 1, 0x1, 0b0001 (int), 1u (uint32), 1L (int64), 1UL (uint64), 1s (int16), 1y (sbyte), 1uy (byte), 1.0 (float), 1.0f (float32), 1.0m (decimal), 1I (BigInteger).</value>
30863086
</data>
30873087
<dataname="lexInvalidByteLiteral"xml:space="preserve">
30883088
<value>This is not a valid byte literal</value>
@@ -3193,10 +3193,10 @@
31933193
<value>This operation accesses a mutable top-level value defined in another assembly in an unsupported way. The value cannot be accessed through its address. Consider copying the expression to a mutable local, e.g. 'let mutable x = ...', and if necessary assigning the value back after the completion of the operation</value>
31943194
</data>
31953195
<dataname="parsNonAdjacentTypars"xml:space="preserve">
3196-
<value>Type parameters must be placed directly adjacent to thetypename, e.g. \"type C&lt;'T&gt;\", nottype \"C&lt;'T&gt;\"</value>
3196+
<value>Remove spaces between the type name andtypeparameter, e.g. \"type C&lt;'T&gt;\", not type \"C&lt;'T&gt;\". Type parameters must be placed directly adjacent to the type name.</value>
31973197
</data>
31983198
<dataname="parsNonAdjacentTyargs"xml:space="preserve">
3199-
<value>Type arguments must be placed directly adjacent to thetypename, e.g. \"C&lt;'T&gt;\", not \"C&lt;'T&gt;\"</value>
3199+
<value>Remove spaces between the type name andtypeparameter, e.g. \"C&lt;'T&gt;\", not \"C&lt;'T&gt;\". Type parameters must be placed directly adjacent to the type name.</value>
32003200
</data>
32013201
<dataname="parsNonAtomicType"xml:space="preserve">
32023202
<value>The use of the type syntax 'int C' and 'C&lt;int&gt;' is not permitted here. Consider adjusting this type to be written in the form 'C&lt;int&gt;'</value>
@@ -4369,4 +4369,4 @@
43694369
<dataname="tcTypeDoesNotInheritAttribute"xml:space="preserve">
43704370
<value>This type does not inherit Attribute, it will not work correctly with other .NET languages.</value>
43714371
</data>
4372-
</root>
4372+
</root>

‎vsintegration/src/FSharp.Editor/Common/Constants.fs‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,11 @@ module internal Guids =
7878
letlanguageServicePerformanceOptionPageIdString="8FDA964A-263D-4B4E-9560-29897535217C"
7979

8080
[<Literal>]
81+
/// "9007718C-357A-4327-A193-AB3EC38D7EE8"
8182
letadvancedSettingsPageIdSring="9007718C-357A-4327-A193-AB3EC38D7EE8"
8283

84+
[<Literal>]
85+
/// "9EBEBCE8-A79B-46B0-A8C5-A9818AEED17D"
86+
letformattingOptionPageIdString="9EBEBCE8-A79B-46B0-A8C5-A9818AEED17D"
87+
8388
letblueHighContrastThemeId= Guid"{ce94d289-8481-498b-8ca9-9b6191a315b9}"

‎vsintegration/src/FSharp.Editor/FSharp.Editor.resx‎

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,15 @@
177177
<dataname="6011"xml:space="preserve">
178178
<value>Performance</value>
179179
</data>
180+
<dataname="6012"xml:space="preserve">
181+
<value>Advanced</value>
182+
</data>
183+
<dataname="6013"xml:space="preserve">
184+
<value>CodeLens</value>
185+
</data>
186+
<dataname="6014"xml:space="preserve">
187+
<value>Formatting</value>
188+
</data>
180189
<dataname="TheValueIsUnused"xml:space="preserve">
181190
<value>The value is unused</value>
182191
</data>
@@ -204,10 +213,4 @@
204213
<dataname="RenameValueToDoubleUnderscore"xml:space="preserve">
205214
<value>Rename '{0}' to '__'</value>
206215
</data>
207-
<dataname="6012"xml:space="preserve">
208-
<value>Advanced</value>
209-
</data>
210-
<dataname="6013"xml:space="preserve">
211-
<value>CodeLens</value>
212-
</data>
213216
</root>

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

Lines changed: 98 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,21 @@ open Microsoft.CodeAnalysis.Text
1313

1414
openMicrosoft.FSharp.Compiler.SourceCodeServices
1515
openSystem.Threading
16+
openSystem.Windows.Forms
1617

1718
[<Shared>]
1819
[<ExportLanguageService(typeof<IEditorFormattingService>, FSharpConstants.FSharpLanguageName)>]
1920
typeinternalFSharpEditorFormattingService
2021
[<ImportingConstructor>]
2122
(
2223
checkerProvider: FSharpCheckerProvider,
23-
projectInfoManager: FSharpProjectOptionsManager
24+
projectInfoManager: FSharpProjectOptionsManager,
25+
settings: EditorOptions
2426
)=
27+
28+
static lettoIList(xs:'a seq)= ResizeArray(xs):> IList<'a>
29+
30+
static letgetIndentation(line:string)= line|> Seq.takeWhile((=)' ')|> Seq.length
2531

2632
static memberGetFormattingChanges(documentId:DocumentId,sourceText:SourceText,filePath:string,checker:FSharpChecker,indentStyle:FormattingOptions.IndentStyle,options:(FSharpParsingOptions* FSharpProjectOptions)option,position:int)=
2733
// Logic for determining formatting changes:
@@ -68,6 +74,77 @@ type internal FSharpEditorFormattingService
6874
else
6975
return! None
7076
}
77+
78+
static memberGetPasteChanges(documentId:DocumentId,sourceText:SourceText,filePath:string,formattingOptions:Microsoft.VisualStudio.FSharp.Editor.FormattingOptions,tabSize:int,parsingOptions:FSharpParsingOptions,currentClipboard:string,span:TextSpan)=
79+
asyncMaybe{
80+
81+
do! Option.guard formattingOptions.FormatOnPaste
82+
83+
letstartLineIdx= sourceText.Lines.IndexOf span.Start
84+
85+
// If we're starting and ending on the same line, we've got nothing to format
86+
do! Option.guard(startLineIdx<> sourceText.Lines.IndexOf span.End)
87+
88+
letstartLine= sourceText.Lines.[startLineIdx]
89+
90+
// VS quirk: if we're pasting on an empty line which has automatically been
91+
// indented (i.e. by ISynchronousIndentationService), then the pasted span
92+
// includes this automatic indentation. When pasting, we only care about what
93+
// was actually in the clipboard.
94+
letfixedSpan=
95+
letpasteText= sourceText.GetSubText(span)
96+
letpasteTextString= pasteText.ToString()
97+
98+
if currentClipboard.Length>0&& pasteTextString.EndsWith currentClipboardthen
99+
letprepended= pasteTextString.[0..pasteTextString.Length-currentClipboard.Length-1]
100+
101+
// Only strip off leading indentation if the pasted span is otherwise
102+
// identical to the clipboard (ignoring leading spaces).
103+
if prepended|> Seq.forall((=)' ')then
104+
TextSpan(span.Start+ prepended.Length, span.Length- prepended.Length)
105+
else
106+
span
107+
else
108+
span
109+
110+
// Calculate the indentation of the line we pasted onto
111+
letcurrentIndent=
112+
letpriorStartSpan= TextSpan(startLine.Span.Start, startLine.Span.Length-(startLine.Span.End- fixedSpan.Start))
113+
114+
sourceText.GetSubText(priorStartSpan).ToString()
115+
|> Seq.takeWhile((=)' ')
116+
|> Seq.length
117+
118+
letfixedPasteText= sourceText.GetSubText(fixedSpan)
119+
letleadingIndentation= fixedPasteText.ToString()|> getIndentation
120+
121+
letstripIndentation charsToRemove=
122+
letsearchIndent= String.replicate charsToRemove""
123+
letnewText= String.replicate currentIndent""
124+
125+
fixedPasteText.Lines
126+
|> Seq.indexed
127+
|> Seq.choose(fun(i,line)->
128+
if line.ToString().StartsWith searchIndentthen
129+
TextChange(TextSpan(line.Start+ fixedSpan.Start, charsToRemove),if i=0then""else newText)
130+
|> Some
131+
else
132+
None
133+
)
134+
135+
if leadingIndentation>0then
136+
return stripIndentation leadingIndentation
137+
else
138+
letnextLineShouldBeIndented= FSharpIndentationService.IndentShouldFollow(documentId, sourceText, filePath, span.Start, parsingOptions)
139+
140+
letremoveIndentation=
141+
letnextLineIndent= fixedPasteText.Lines.[1].ToString()|> getIndentation
142+
143+
if nextLineShouldBeIndentedthen nextLineIndent- tabSize
144+
else nextLineIndent
145+
146+
return stripIndentation removeIndentation
147+
}
71148

72149
member__.GetFormattingChangesAsync(document:Document,position:int,cancellationToken:CancellationToken)=
73150
async{
@@ -76,20 +153,27 @@ type internal FSharpEditorFormattingService
76153
letindentStyle= options.GetOption(FormattingOptions.SmartIndent, FSharpConstants.FSharpLanguageName)
77154
letprojectOptionsOpt= projectInfoManager.TryGetOptionsForEditingDocumentOrProject document
78155
let!textChange= FSharpEditorFormattingService.GetFormattingChanges(document.Id, sourceText, document.FilePath, checkerProvider.Checker, indentStyle, projectOptionsOpt, position)
79-
80-
return
81-
match textChangewith
82-
| Some change->
83-
ResizeArray([change]):> IList<_>
84-
85-
| None->
86-
ResizeArray():> IList<_>
156+
return textChange|> Option.toList|> toIList
157+
}
158+
159+
member__.OnPasteAsync(document:Document,span:TextSpan,currentClipboard:string,cancellationToken:CancellationToken)=
160+
async{
161+
let!sourceText= document.GetTextAsync(cancellationToken)|> Async.AwaitTask
162+
let!options= document.GetOptionsAsync(cancellationToken)|> Async.AwaitTask
163+
lettabSize= options.GetOption<int>(FormattingOptions.TabSize, FSharpConstants.FSharpLanguageName)
164+
165+
match projectInfoManager.TryGetOptionsForEditingDocumentOrProject documentwith
166+
| Some(parsingOptions,_)->
167+
let!textChanges= FSharpEditorFormattingService.GetPasteChanges(document.Id, sourceText, document.FilePath, settings.Formatting, tabSize, parsingOptions, currentClipboard, span)
168+
return textChanges|> Option.defaultValue Seq.empty|> toIList
169+
| None->
170+
return toIList Seq.empty
87171
}
88172

89173
interface IEditorFormattingServicewith
90174
member valSupportsFormatDocument=false
91175
member valSupportsFormatSelection=false
92-
member valSupportsFormatOnPaste=false
176+
member valSupportsFormatOnPaste=true
93177
member valSupportsFormatOnReturn=true
94178

95179
override__.SupportsFormattingOnTypedCharacter(document,ch)=
@@ -104,8 +188,10 @@ type internal FSharpEditorFormattingService
104188
async{return ResizeArray():> IList<_>}
105189
|> RoslynHelpers.StartAsyncAsTask cancellationToken
106190

107-
override__.GetFormattingChangesOnPasteAsync(_document,_span,cancellationToken)=
108-
async{return ResizeArray():> IList<_>}
191+
overridethis.GetFormattingChangesOnPasteAsync(document,span,cancellationToken)=
192+
letcurrentClipboard= Clipboard.GetText()
193+
194+
this.OnPasteAsync(document, span, currentClipboard, cancellationToken)
109195
|> RoslynHelpers.StartAsyncAsTask cancellationToken
110196

111197
overridethis.GetFormattingChangesAsync(document,_typedChar,position,cancellationToken)=

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

Lines changed: 34 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -23,38 +23,26 @@ type internal FSharpIndentationService
2323
static memberIsSmartIndentEnabled(options:Microsoft.CodeAnalysis.Options.OptionSet)=
2424
options.GetOption(FormattingOptions.SmartIndent, FSharpConstants.FSharpLanguageName)= FormattingOptions.IndentStyle.Smart
2525

26-
static memberGetDesiredIndentation(documentId:DocumentId,sourceText:SourceText,filePath:string,lineNumber:int,tabSize:int,indentStyle:FormattingOptions.IndentStyle,options:(FSharpParsingOptions* FSharpProjectOptions)option):Option<int>=
27-
28-
// Match indentation with previous line
29-
let rectryFindPreviousNonEmptyLine l=
30-
if l<=0then None
31-
else
32-
letpreviousLine= sourceText.Lines.[l-1]
33-
ifnot(String.IsNullOrEmpty(previousLine.ToString()))then
34-
Some previousLine
35-
else
36-
tryFindPreviousNonEmptyLine(l-1)
37-
38-
let rectryFindLastNonWhitespaceOrCommentToken(line:TextLine)=maybe{
39-
let!parsingOptions,_projectOptions= options
26+
static memberIndentShouldFollow(documentId:DocumentId,sourceText:SourceText,filePath:string,position:int,parsingOptions:FSharpParsingOptions)=
27+
letlastTokenOpt=
4028
letdefines= CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions
41-
lettokens= Tokenizer.tokenizeLine(documentId, sourceText, line.Start, filePath, defines)
42-
43-
return!
44-
tokens
45-
|> Array.rev
46-
|> Array.tryFind(fun x->
47-
x.Tag<> FSharpTokenTag.WHITESPACE&&
48-
x.Tag<> FSharpTokenTag.COMMENT&&
49-
x.Tag<> FSharpTokenTag.LINE_COMMENT)
50-
}
29+
lettokens= Tokenizer.tokenizeLine(documentId, sourceText, position, filePath, defines)
30+
31+
tokens
32+
|> Array.rev
33+
|> Array.tryFind(fun x->
34+
x.Tag<> FSharpTokenTag.WHITESPACE&&
35+
x.Tag<> FSharpTokenTag.COMMENT&&
36+
x.Tag<> FSharpTokenTag.LINE_COMMENT)
5137

5238
let(|Eq|_|)y x=
5339
if x= ythen Some()
5440
else None
5541

56-
let(|NeedIndent|_|)(token:Tokenizer.SavedTokenInfo)=
57-
match token.Tagwith
42+
match lastTokenOptwith
43+
| None->false
44+
| Some lastToken->
45+
match lastToken.Tagwith
5846
| Eq FSharpTokenTag.EQUALS// =
5947
| Eq FSharpTokenTag.LARROW// <-
6048
| Eq FSharpTokenTag.RARROW// ->
@@ -70,8 +58,20 @@ type internal FSharpIndentationService
7058
| Eq FSharpTokenTag.STRUCT// struct
7159
| Eq FSharpTokenTag.CLASS// class
7260
| Eq FSharpTokenTag.TRY->// try
73-
Some()
74-
|_-> None
61+
true
62+
|_->false
63+
64+
static memberGetDesiredIndentation(documentId:DocumentId,sourceText:SourceText,filePath:string,lineNumber:int,tabSize:int,indentStyle:FormattingOptions.IndentStyle,options:(FSharpParsingOptions* FSharpProjectOptions)option):Option<int>=
65+
66+
// Match indentation with previous line
67+
let rectryFindPreviousNonEmptyLine l=
68+
if l<=0then None
69+
else
70+
letpreviousLine= sourceText.Lines.[l-1]
71+
ifnot(String.IsNullOrEmpty(previousLine.ToString()))then
72+
Some previousLine
73+
else
74+
tryFindPreviousNonEmptyLine(l-1)
7575

7676
maybe{
7777
let!previousLine= tryFindPreviousNonEmptyLine lineNumber
@@ -81,18 +81,15 @@ type internal FSharpIndentationService
8181
|> Seq.takeWhile((=)' ')
8282
|> Seq.length
8383

84+
let!parsingOptions,_= options
85+
8486
// Only use smart indentation after tokens that need indentation
8587
// if the option is enabled
86-
letlastToken=
87-
if indentStyle= FormattingOptions.IndentStyle.Smartthen
88-
tryFindLastNonWhitespaceOrCommentToken previousLine
89-
else
90-
None
91-
9288
return
93-
match lastTokenwith
94-
| Some NeedIndent->(lastIndent/tabSize+1)* tabSize
95-
|_-> lastIndent
89+
if indentStyle= FormattingOptions.IndentStyle.Smart&& FSharpIndentationService.IndentShouldFollow(documentId, sourceText, filePath, previousLine.Start, parsingOptions)then
90+
(lastIndent/tabSize+1)* tabSize
91+
else
92+
lastIndent
9693
}
9794

9895
interface ISynchronousIndentationServicewith

‎vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ type internal FSharpSettingsFactory
101101
[<ProvideLanguageEditorOptionPage(typeof<OptionsUI.LanguageServicePerformanceOptionPage>,"F#",null,"Performance","6011")>]
102102
[<ProvideLanguageEditorOptionPage(typeof<OptionsUI.AdvancedSettingsOptionPage>,"F#",null,"Advanced","6012")>]
103103
[<ProvideLanguageEditorOptionPage(typeof<OptionsUI.CodeLensOptionPage>,"F#",null,"CodeLens","6013")>]
104+
[<ProvideLanguageEditorOptionPage(typeof<OptionsUI.FormattingOptionPage>,"F#",null,"Formatting","6014")>]
104105
[<ProvideFSharpVersionRegistration(FSharpConstants.projectPackageGuidString,"Microsoft Visual F#")>]
105106
// 64 represents a hex number. It needs to be greater than 37 so the TextMate editor will not be chosen as higher priority.
106107
[<ProvideEditorExtension(typeof<FSharpEditorFactory>,".fs",64)>]

‎vsintegration/src/FSharp.Editor/Options/EditorOptions.fs‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,12 @@ type AdvancedOptions =
9494
{ IsBlockStructureEnabled=true
9595
IsOutliningEnabled=true}
9696

97+
[<CLIMutable>]
98+
typeFormattingOptions=
99+
{ FormatOnPaste:bool}
100+
static memberDefault=
101+
{ FormatOnPaste=true}
102+
97103
[<Export>]
98104
[<Export(typeof<IPersistSettings>)>]
99105
typeEditorOptions
@@ -111,13 +117,15 @@ type EditorOptions
111117
store.Register AdvancedOptions.Default
112118
store.Register IntelliSenseOptions.Default
113119
store.Register CodeLensOptions.Default
120+
store.Register FormattingOptions.Default
114121

115122
member__.IntelliSense:IntelliSenseOptions= store.Read()
116123
member__.QuickInfo:QuickInfoOptions= store.Read()
117124
member__.CodeFixes:CodeFixesOptions= store.Read()
118125
member__.LanguageServicePerformance:LanguageServicePerformanceOptions= store.Read()
119126
member__.Advanced:AdvancedOptions= store.Read()
120127
member__.CodeLens:CodeLensOptions= store.Read()
128+
member__.Formatting:FormattingOptions= store.Read()
121129

122130
interface Microsoft.CodeAnalysis.Host.IWorkspaceService
123131

@@ -183,3 +191,9 @@ module internal OptionsUI =
183191
inherit AbstractOptionPage<AdvancedOptions>()
184192
override__.CreateView()=
185193
upcast AdvancedOptionsControl()
194+
195+
[<Guid(Guids.formattingOptionPageIdString)>]
196+
typeinternalFormattingOptionPage()=
197+
inherit AbstractOptionPage<FormattingOptions>()
198+
override__.CreateView()=
199+
upcast FormattingOptionsControl()

‎vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.cs.xlf‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,11 @@
157157
<targetstate="translated">CodeLens</target>
158158
<note />
159159
</trans-unit>
160+
<trans-unitid="6014">
161+
<source>Formatting</source>
162+
<targetstate="new">Formatting</target>
163+
<note />
164+
</trans-unit>
160165
</body>
161166
</file>
162167
</xliff>

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp