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

Commit5083832

Browse files
vasily-kirichenkoKevinRansom
authored andcommitted
Disable completion inside comments and excluded code (dotnet#2505)
* disable completion inside comments and excluded coderefactoring* disable normal completion inside comments and excluded code* better include directive selection
1 parentdf1276e commit5083832

File tree

3 files changed

+135
-121
lines changed

3 files changed

+135
-121
lines changed

‎vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs‎

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -38,16 +38,28 @@ type internal FSharpCompletionProvider
3838
letxmlMemberIndexService= serviceProvider.GetService(typeof<IVsXMLMemberIndexService>):?> IVsXMLMemberIndexService
3939
letdocumentationBuilder= XmlDocumentation.CreateDocumentationBuilder(xmlMemberIndexService, serviceProvider.DTE)
4040
static letattributeSuffixLength="Attribute".Length
41+
42+
static letshouldProvideCompletion(documentId:DocumentId,filePath:string,defines:string list,text:SourceText,position:int):bool=
43+
lettextLines= text.Lines
44+
lettriggerLine= textLines.GetLineFromPosition position
45+
letcolorizationData= CommonHelpers.getColorizationData(documentId, text, triggerLine.Span, Some filePath, defines, CancellationToken.None)
46+
colorizationData.Count=0||// we should provide completion at the start of empty line, where there are no tokens at all
47+
colorizationData.Exists(fun classifiedSpan->
48+
classifiedSpan.TextSpan.IntersectsWith position&&
49+
(
50+
match classifiedSpan.ClassificationTypewith
51+
| ClassificationTypeNames.Comment
52+
| ClassificationTypeNames.StringLiteral
53+
| ClassificationTypeNames.ExcludedCode
54+
| ClassificationTypeNames.NumericLiteral->false
55+
|_->true// anything else is a valid classification type
56+
))
4157

4258
static memberShouldTriggerCompletionAux(sourceText:SourceText,caretPosition:int,trigger:CompletionTriggerKind,getInfo:(unit-> DocumentId* string* string list))=
4359
// Skip if we are at the start of a document
44-
if caretPosition=0then
45-
false
46-
60+
if caretPosition=0thenfalse
4761
// Skip if it was triggered by an operation other than insertion
48-
elifnot(trigger= CompletionTriggerKind.Insertion)then
49-
false
50-
62+
elifnot(trigger= CompletionTriggerKind.Insertion)thenfalse
5163
// Skip if we are not on a completion trigger
5264
else
5365
lettriggerPosition= caretPosition-1
@@ -63,22 +75,7 @@ type internal FSharpCompletionProvider
6375
// Trigger completion if we are on a valid classification type
6476
else
6577
letdocumentId,filePath,defines= getInfo()
66-
lettextLines= sourceText.Lines
67-
lettriggerLine= textLines.GetLineFromPosition(triggerPosition)
68-
69-
letclassifiedSpanOption=
70-
CommonHelpers.getColorizationData(documentId, sourceText, triggerLine.Span, Some(filePath), defines, CancellationToken.None)
71-
|> Seq.tryFind(fun classifiedSpan-> classifiedSpan.TextSpan.Contains(triggerPosition))
72-
73-
match classifiedSpanOptionwith
74-
| None->false
75-
| Some(classifiedSpan)->
76-
match classifiedSpan.ClassificationTypewith
77-
| ClassificationTypeNames.Comment
78-
| ClassificationTypeNames.StringLiteral
79-
| ClassificationTypeNames.ExcludedCode
80-
| ClassificationTypeNames.NumericLiteral->false
81-
|_->true// anything else is a valid classification type
78+
shouldProvideCompletion(documentId, filePath, defines, sourceText, triggerPosition)
8279

8380
static memberProvideCompletionsAsyncAux(checker:FSharpChecker,sourceText:SourceText,caretPosition:int,options:FSharpProjectOptions,filePath:string,textVersionHash:int)=
8481
asyncMaybe{
@@ -131,10 +128,13 @@ type internal FSharpCompletionProvider
131128

132129
overridethis.ProvideCompletionsAsync(context:Microsoft.CodeAnalysis.Completion.CompletionContext)=
133130
asyncMaybe{
134-
let!options=projectInfoManager.TryGetOptionsForEditingDocumentOrProject(context.Document)
131+
letdocument= context.Document
135132
let!sourceText= context.Document.GetTextAsync(context.CancellationToken)
133+
letdefines= projectInfoManager.GetCompilationDefinesForEditingDocument(document)
134+
do! Option.guard(shouldProvideCompletion(document.Id, document.FilePath, defines, sourceText, context.Position))
135+
let!options= projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document)
136136
let!textVersion= context.Document.GetTextVersionAsync(context.CancellationToken)
137-
let!results= FSharpCompletionProvider.ProvideCompletionsAsyncAux(checkerProvider.Checker, sourceText, context.Position, options,context.Document.FilePath, textVersion.GetHashCode())
137+
let!results= FSharpCompletionProvider.ProvideCompletionsAsyncAux(checkerProvider.Checker, sourceText, context.Position, options,document.FilePath, textVersion.GetHashCode())
138138
context.AddItems(results)
139139
}|> Async.Ignore|> CommonRoslynHelpers.StartAsyncUnitAsTask context.CancellationToken
140140

‎vsintegration/src/FSharp.Editor/Completion/CompletionService.fs‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,10 @@ type internal FSharpCompletionService
2424
letbuiltInProviders=
2525
ImmutableArray.Create<CompletionProvider>(
2626
FSharpCompletionProvider(workspace, serviceProvider, checkerProvider, projectInfoManager),
27-
ReferenceDirectiveCompletionProvider(),
28-
LoadDirectiveCompletionProvider(),
29-
IncludeDirectiveCompletionProvider()
27+
HashDirectiveCompletionProvider(workspace, projectInfoManager,
28+
[ Completion.Create("""\s*#load\s+(@?"*(?<literal>"[^"]*"?))""",[".fs";".fsx"], useIncludeDirectives=true)
29+
Completion.Create("""\s*#r\s+(@?"*(?<literal>"[^"]*"?))""",[".dll";".exe"], useIncludeDirectives=true)
30+
Completion.Create("""\s*#I\s+(@?"*(?<literal>"[^"]*"?))""",["\x00"], useIncludeDirectives=false)])
3031
// we've turned off keyword completion because it does not filter suggestion depending on context.
3132
// FSharpKeywordCompletionProvider(workspace, projectInfoManager)
3233
)

‎vsintegration/src/FSharp.Editor/Completion/FileSystemCompletion.fs‎

Lines changed: 107 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -3,151 +3,164 @@
33
namespaceMicrosoft.VisualStudio.FSharp.Editor
44

55
openSystem
6+
openSystem.Text.RegularExpressions
7+
openSystem.IO
68
openSystem.Collections.Immutable
79
openSystem.Threading
810
openSystem.Threading.Tasks
911
openMicrosoft.CodeAnalysis
1012
openMicrosoft.CodeAnalysis.Completion
1113
openMicrosoft.CodeAnalysis.Editor.Implementation.IntelliSense.Completion.FileSystem
1214
openMicrosoft.CodeAnalysis.Text
15+
openMicrosoft.CodeAnalysis.Classification
16+
17+
typeinternalCompletion=
18+
{ DirectiveRegex:Regex
19+
AllowableExtensions:string list
20+
UseIncludeDirectives:bool}
21+
static memberCreate(directiveRegex,allowableExtensions,useIncludeDirectives)=
22+
{ DirectiveRegex= Regex(directiveRegex, RegexOptions.Compiled||| RegexOptions.ExplicitCapture)
23+
AllowableExtensions= allowableExtensions
24+
UseIncludeDirectives= useIncludeDirectives}
25+
26+
typeinternalHashDirectiveCompletionProvider(workspace: Workspace,projectInfoManager: ProjectInfoManager,completions: Completion list)=
27+
inherit CommonCompletionProvider()
1328

14-
openSystem.Text.RegularExpressions
15-
openSystem.IO
16-
17-
moduleinternalFileSystemCompletion=
18-
let [<Literal>]privateNetworkPath="\\\\"
19-
letprivatecommitRules= ImmutableArray.Create(CharacterSetModificationRule.Create(CharacterSetModificationKind.Replace,'"','\\',',','/'))
20-
letprivaterules= CompletionItemRules.Create(commitCharacterRules= commitRules)
29+
let [<Literal>]NetworkPath="\\\\"
30+
letcommitRules= ImmutableArray.Create(CharacterSetModificationRule.Create(CharacterSetModificationKind.Replace,'"','\\',',','/'))
31+
letrules= CompletionItemRules.Create(commitCharacterRules= commitRules)
2132

22-
letprivategetQuotedPathStart(text:SourceText,position:int,quotedPathGroup:Group)=
33+
letgetQuotedPathStart(text:SourceText,position:int,quotedPathGroup:Group)=
2334
text.Lines.GetLineFromPosition(position).Start+ quotedPathGroup.Index
2435

25-
letprivategetPathThroughLastSlash(text:SourceText,position:int,quotedPathGroup:Group)=
36+
letgetPathThroughLastSlash(text:SourceText,position:int,quotedPathGroup:Group)=
2637
PathCompletionUtilities.GetPathThroughLastSlash(
2738
quotedPath= quotedPathGroup.Value,
2839
quotedPathStart= getQuotedPathStart(text, position, quotedPathGroup),
2940
position= position)
3041

31-
letprivategetTextChangeSpan(text:SourceText,position:int,quotedPathGroup:Group)=
42+
letgetTextChangeSpan(text:SourceText,position:int,quotedPathGroup:Group)=
3243
PathCompletionUtilities.GetTextChangeSpan(
3344
quotedPath= quotedPathGroup.Value,
3445
quotedPathStart= getQuotedPathStart(text, position, quotedPathGroup),
3546
position= position)
3647

37-
letprivategetFileGlyph(extention:string)=
48+
letgetFileGlyph(extention:string)=
3849
match extentionwith
3950
|".exe"|".dll"-> Some Glyph.Assembly
4051
|_-> None
4152

42-
letgetItems(provider:CompletionProvider,document:Document,position:int,allowableExtensions:string list,directiveRegex:Regex,searchPaths:string list)=
43-
asyncMaybe{
44-
do! Option.guard(Path.GetExtension document.FilePath=".fsx")
53+
letincludeDirectiveCleanRegex= Regex("""#I\s+(@?"*(?<literal>[^"]*)"?)""", RegexOptions.Compiled||| RegexOptions.ExplicitCapture)
54+
55+
letgetColorizationData(text:SourceText,position:int):ResizeArray<ClassifiedSpan>=
56+
letdocumentId= workspace.GetDocumentIdInCurrentContext(text.Container)
57+
letdocument= workspace.CurrentSolution.GetDocument(documentId)
58+
letdefines= projectInfoManager.GetCompilationDefinesForEditingDocument(document)
59+
lettextLines= text.Lines
60+
lettriggerLine= textLines.GetLineFromPosition(position)
61+
CommonHelpers.getColorizationData(documentId, text, triggerLine.Span, Some document.FilePath, defines, CancellationToken.None)
62+
63+
letisInStringLiteral(text:SourceText,position:int):bool=
64+
getColorizationData(text, position)
65+
|> Seq.exists(fun classifiedSpan->
66+
classifiedSpan.TextSpan.IntersectsWith position&&
67+
classifiedSpan.ClassificationType= ClassificationTypeNames.StringLiteral)
68+
69+
letgetIncludeDirectives(text:SourceText,position:int)=
70+
letlines= text.Lines
71+
letcaretLine= text.Lines.GetLinePosition(position).Line
72+
lines
73+
|> Seq.filter(fun x-> x.LineNumber< caretLine)
74+
|> Seq.choose(fun line->
75+
letlineStr= line.ToString().Trim()
76+
// optimization: fail fast if the line does not start with "(optional spaces) #I"
77+
ifnot(lineStr.StartsWith"#I")then None
78+
else
79+
match includeDirectiveCleanRegex.Match lineStrwith
80+
| mwhen m.Success->
81+
getColorizationData(text, line.Start)
82+
|> Seq.tryPick(fun span->
83+
if span.TextSpan.IntersectsWith line.Start&&
84+
(span.ClassificationType<> ClassificationTypeNames.Comment&&
85+
span.ClassificationType<> ClassificationTypeNames.ExcludedCode)then
86+
Some(m.Groups.["literal"].Value)
87+
else None)
88+
|_-> None
89+
)
90+
|> Seq.toList
91+
92+
overridethis.ProvideCompletionsAsync(context)=
93+
asyncMaybe{
94+
letdocument= context.Document
95+
letposition= context.Position
96+
do!letextension= Path.GetExtension document.FilePath
97+
Option.guard(extension=".fsx"|| extension=".fsscript")
98+
4599
let!ct= liftAsync Async.CancellationToken
46-
let!text= document.GetTextAsync ct
100+
let!text= document.GetTextAsync(ct)
101+
do! Option.guard(isInStringLiteral(text, position))
47102
letline= text.Lines.GetLineFromPosition(position)
48-
letlineText= text.ToString(TextSpan.FromBounds(line.Start, position));
49-
letm= directiveRegex.Match lineText
50-
51-
do! Option.guard m.Success
52-
letquotedPathGroup= m.Groups.["literal"]
53-
letquotedPath= quotedPathGroup.Value;
54-
letendsWithQuote= PathCompletionUtilities.EndsWithQuote(quotedPath)
103+
letlineText= text.ToString(TextSpan.FromBounds(line.Start, position))
55104

56-
do! Option.guard(not(endsWithQuote&&(position>= line.Start+ m.Length)))
105+
let!completion,quotedPathGroup=
106+
completions|> List.tryPick(fun completion->
107+
match completion.DirectiveRegex.Match lineTextwith
108+
| mwhen m.Success->
109+
letquotedPathGroup= m.Groups.["literal"]
110+
letendsWithQuote= PathCompletionUtilities.EndsWithQuote(quotedPathGroup.Value)
111+
if endsWithQuote&&(position>= line.Start+ m.Length)then
112+
None
113+
else
114+
Some(completion, quotedPathGroup)
115+
|_-> None)
116+
57117
letsnapshot= text.FindCorrespondingEditorTextSnapshot()
58118

59119
do! Option.guard(not(isNull snapshot))
60120
letfileSystem= CurrentWorkingDirectoryDiscoveryService.GetService(snapshot)
61121

122+
letextraSearchPaths=
123+
if completion.UseIncludeDirectivesthen
124+
getIncludeDirectives(text, position)
125+
else[]
126+
127+
letdefaultSearchPath= Path.GetDirectoryName document.FilePath
128+
letsearchPaths= defaultSearchPath:: extraSearchPaths
129+
62130
lethelper=
63131
FileSystemCompletionHelper(
64-
provider,
132+
this,
65133
getTextChangeSpan(text, position, quotedPathGroup),
66134
fileSystem,
67135
Glyph.OpenFolder,
68-
allowableExtensions|> List.tryPick getFileGlyph|> Option.defaultValue Glyph.None,
136+
completion.AllowableExtensions|> List.tryPick getFileGlyph|> Option.defaultValue Glyph.None,
69137
searchPaths= Seq.toImmutableArray searchPaths,
70-
allowableExtensions=allowableExtensions,
138+
allowableExtensions=completion.AllowableExtensions,
71139
itemRules= rules)
72140

73141
letpathThroughLastSlash= getPathThroughLastSlash(text, position, quotedPathGroup)
74142
letdocumentPath=if document.Project.IsSubmissionthennullelse document.FilePath
75-
return helper.GetItems(pathThroughLastSlash, documentPath)
76-
}|> Async.map(Option.defaultValue ImmutableArray.Empty)
77-
78-
letisInsertionTrigger(text:SourceText,position)=
143+
context.AddItems(helper.GetItems(pathThroughLastSlash, documentPath))
144+
}
145+
|> Async.Ignore
146+
|> CommonRoslynHelpers.StartAsyncUnitAsTask context.CancellationToken
147+
148+
override__.IsInsertionTrigger(text,position,_)=
79149
// Bring up completion when the user types a quote (i.e.: #r "), or if they type a slash
80150
// path separator character, or if they type a comma (#r "foo,version...").
81151
// Also, if they're starting a word. i.e. #r "c:\W
82152
letch= text.[position]
83-
ch='"'|| ch='\\'|| ch=','|| ch='/'||
84-
CommonCompletionUtilities.IsStartingNewWord(text, position,(fun x-> Char.IsLetter x),(fun x-> Char.IsLetterOrDigit x))
153+
letisTriggerChar=
154+
ch='"'|| ch='\\'|| ch=','|| ch='/'||
155+
CommonCompletionUtilities.IsStartingNewWord(text, position,(fun x-> Char.IsLetter x),(fun x-> Char.IsLetterOrDigit x))
156+
isTriggerChar&& isInStringLiteral(text, position)
85157

86-
letgetTextChange(selectedItem:CompletionItem,ch:Nullable<char>)=
158+
override__.GetTextChangeAsync(selectedItem,ch,cancellationToken)=
87159
// When we commit "\\" when the user types \ we have to adjust for the fact that the
88160
// controller will automatically append \ after we commit. Because of that, we don't
89161
// want to actually commit "\\" as we'll end up with "\\\". So instead we just commit
90162
// "\" and know that controller will append "\" and give us "\\".
91163
if selectedItem.DisplayText= NetworkPath&& ch= Nullable'\\'then
92-
Some(TextChange(selectedItem.Span,"\\"))
164+
Task.FromResult(Nullable(TextChange(selectedItem.Span,"\\")))
93165
else
94-
None
95-
96-
letprivateincludeDirectiveCleanRegex= Regex("""#I\s+(@?"*(?<literal>[^"]*)"?)""", RegexOptions.Compiled||| RegexOptions.ExplicitCapture)
97-
98-
letgetIncludeDirectives(document:Document,position:int)=
99-
async{
100-
let!ct= Async.CancellationToken
101-
let!text= document.GetTextAsync(ct)
102-
letlines= text.Lines
103-
letcaretLine= text.Lines.GetLinePosition(position).Line
104-
return
105-
lines
106-
|> Seq.filter(fun x-> x.LineNumber<= caretLine)
107-
|> Seq.choose(fun line->
108-
letlineStr= line.ToString().Trim()
109-
// optimization: fail fast if the line does not start with "(optional spaces) #I"
110-
ifnot(lineStr.StartsWith"#I")then None
111-
else
112-
match includeDirectiveCleanRegex.Match lineStrwith
113-
| mwhen m.Success-> Some(m.Groups.["literal"].Value)
114-
|_-> None
115-
)
116-
|> Seq.toList
117-
}
118-
119-
[<AbstractClass>]
120-
typeinternalHashDirectiveCompletionProvider(directiveRegex: string,allowableExtensions: string list,useIncludeDirectives: bool)=
121-
inherit CommonCompletionProvider()
122-
123-
letdirectiveRegex= Regex(directiveRegex, RegexOptions.Compiled||| RegexOptions.ExplicitCapture)
124-
125-
overridethis.ProvideCompletionsAsync(context)=
126-
async{
127-
letdefaultSearchPath= Path.GetDirectoryName context.Document.FilePath
128-
let!extraSearchPaths=
129-
if useIncludeDirectivesthen
130-
FileSystemCompletion.getIncludeDirectives(context.Document, context.Position)
131-
else async.Return[]
132-
letsearchPaths= defaultSearchPath:: extraSearchPaths
133-
let!items= FileSystemCompletion.getItems(this, context.Document, context.Position, allowableExtensions, directiveRegex, searchPaths)
134-
context.AddItems(items)
135-
}|> CommonRoslynHelpers.StartAsyncUnitAsTask context.CancellationToken
136-
137-
override__.IsInsertionTrigger(text,position,_)= FileSystemCompletion.isInsertionTrigger(text, position)
138-
139-
override__.GetTextChangeAsync(selectedItem,ch,cancellationToken)=
140-
match FileSystemCompletion.getTextChange(selectedItem, ch)with
141-
| Some x-> Task.FromResult(Nullable x)
142-
| None->base.GetTextChangeAsync(selectedItem, ch, cancellationToken)
143-
144-
145-
typeinternalLoadDirectiveCompletionProvider()=
146-
inherit HashDirectiveCompletionProvider("""\s*#load\s+(@?"*(?<literal>"[^"]*"?))""",[".fs";".fsx"], useIncludeDirectives=true)
147-
148-
typeinternalReferenceDirectiveCompletionProvider()=
149-
inherit HashDirectiveCompletionProvider("""\s*#r\s+(@?"*(?<literal>"[^"]*"?))""",[".dll";".exe"], useIncludeDirectives=true)
150-
151-
typeinternalIncludeDirectiveCompletionProvider()=
152-
// we have to pass an extension that's not met in real life because if we pass empty list, it does not filter at all.
153-
inherit HashDirectiveCompletionProvider("""\s*#I\s+(@?"*(?<literal>"[^"]*"?))""",[".impossible_extension"], useIncludeDirectives=false)
166+
base.GetTextChangeAsync(selectedItem, ch, cancellationToken)

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp