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

Commit90474e8

Browse files
vasily-kirichenkoTIHan
authored andcommitted
Detect AttributeApplication completion context for unfinished attributes (#4126)
* detect AttributeApplication completion context for unfinished attributes* fix whitespace sensitiveness, add more tests* more tests* support ":" in attribute namesfix tests* fix tests* pass ParsedInput instead of ParsedInput option to TryGetCompletionContext* refactor tests
1 parent66df730 commit90474e8

File tree

7 files changed

+183
-33
lines changed

7 files changed

+183
-33
lines changed

‎src/fsharp/service/ServiceUntypedParse.fs‎

Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,8 @@ type EntityKind =
411411
overridex.ToString()= sprintf"%A" x
412412

413413
moduleUntypedParseImpl=
414+
openSystem.Text.RegularExpressions
415+
openMicrosoft.FSharp.Compiler.PrettyNaming
414416

415417
letemptyStringSet= HashSet<string>()
416418

@@ -932,20 +934,13 @@ module UntypedParseImpl =
932934
| ParsedInput.ImplFile input-> walkImplFileInput input
933935

934936
typeinternalTS= AstTraversal.TraverseStep
937+
/// Matches the most nested [< and >] pair.
938+
letinsideAttributeApplicationRegex= Regex(@"(?<=\[\<)(?<attribute>(.*?))(?=\>\])", RegexOptions.Compiled||| RegexOptions.ExplicitCapture)
935939

936940
/// Try to determine completion context for the given pair (row, columns)
937-
letTryGetCompletionContext(pos,untypedParseOpt:FSharpParseFileResults option,lineStr:string):CompletionContext option=
938-
letparsedInputOpt=
939-
match untypedParseOptwith
940-
| Some upi-> upi.ParseTree
941-
| None-> None
942-
943-
match parsedInputOptwith
944-
| None-> None
945-
| Some pt->
941+
letTryGetCompletionContext(pos,parsedInput:ParsedInput,lineStr:string):CompletionContext option=
946942

947-
948-
match GetEntityKind(pos, pt)with
943+
match GetEntityKind(pos, parsedInput)with
949944
| Some EntityKind.Attribute-> Some CompletionContext.AttributeApplication
950945
|_->
951946

@@ -1282,7 +1277,48 @@ module UntypedParseImpl =
12821277
|_-> defaultTraverse ty
12831278
}
12841279

1285-
AstTraversal.Traverse(pos, pt, walker)
1280+
AstTraversal.Traverse(pos, parsedInput, walker)
1281+
// Uncompleted attribute applications are not presented in the AST in any way. So, we have to parse source string.
1282+
|> Option.orElseWith(fun _->
1283+
letcutLeadingAttributes(str:string)=
1284+
// cut off leading attributes, i.e. we cut "[<A1; A2; >]" to " >]"
1285+
match str.LastIndexOf';'with
1286+
|-1-> str
1287+
| idxwhen idx< str.Length-> str.[idx+1..].TrimStart()
1288+
|_->""
1289+
1290+
letisLongIdent= Seq.forall(fun c-> IsIdentifierPartCharacter c|| c='.'|| c=':')// ':' may occur in "[<type:AnAttribute>]"
1291+
1292+
// match the most nested paired [< and >] first
1293+
letmatches=
1294+
insideAttributeApplicationRegex.Matches(lineStr)
1295+
|> Seq.cast<Match>
1296+
|> Seq.filter(fun m-> m.Index<= pos.Column&& m.Index+ m.Length>= pos.Column)
1297+
|> Seq.toArray
1298+
1299+
ifnot(Array.isEmpty matches)then
1300+
matches
1301+
|> Seq.tryPick(fun m->
1302+
letg= m.Groups.["attribute"]
1303+
letcol= pos.Column- g.Index
1304+
if col>=0&& col< g.Lengththen
1305+
letstr= g.Value.Substring(0, col).TrimStart()// cut other rhs attributes
1306+
letstr= cutLeadingAttributes str
1307+
if isLongIdent strthen
1308+
Some CompletionContext.AttributeApplication
1309+
else None
1310+
else None)
1311+
else
1312+
// Paired [< and >] were not found, try to determine that we are after [< without closing >]
1313+
match lineStr.LastIndexOf"[<"with
1314+
|-1-> None
1315+
| openParenIndexwhen pos.Column>= openParenIndex+2->
1316+
letstr= lineStr.[openParenIndex+2..pos.Column-1].TrimStart()
1317+
letstr= cutLeadingAttributes str
1318+
if isLongIdent strthen
1319+
Some CompletionContext.AttributeApplication
1320+
else None
1321+
|_-> None)
12861322

12871323
/// Check if we are at an "open" declaration
12881324
letGetFullNameOfSmallestModuleOrNamespaceAtPoint(parsedInput:ParsedInput,pos:pos)=

‎src/fsharp/service/ServiceUntypedParse.fsi‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ module public UntypedParseImpl =
105105
valTryFindExpressionASTLeftOfDotLeftOfCursor:pos* ParsedInput option->(pos* bool) option
106106
val GetRangeOfExprLeftOfDot: pos* ParsedInput option-> range option
107107
val TryFindExpressionIslandInPosition: pos* ParsedInput option-> string option
108-
val TryGetCompletionContext: pos*FSharpParseFileResults option* lineStr: string-> CompletionContext option
108+
val TryGetCompletionContext: pos*ParsedInput* lineStr: string-> CompletionContext option
109109
val GetEntityKind: pos* ParsedInput-> EntityKind option
110110
val GetFullNameOfSmallestModuleOrNamespaceAtPoint: ParsedInput* pos-> string[]
111111

‎src/fsharp/service/service.fs‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -780,7 +780,11 @@ type TypeCheckInfo
780780
| otherwise-> otherwise-1
781781

782782
// Look for a "special" completion context
783-
letcompletionContext= UntypedParseImpl.TryGetCompletionContext(mkPos line colAtEndOfNamesAndResidue, parseResultsOpt, lineStr)
783+
letcompletionContext=
784+
parseResultsOpt
785+
|> Option.bind(fun x-> x.ParseTree)
786+
|> Option.bind(fun parseTree-> UntypedParseImpl.TryGetCompletionContext(mkPos line colAtEndOfNamesAndResidue, parseTree, lineStr))
787+
784788
letres=
785789
match completionContextwith
786790
// Invalid completion locations
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
#if INTERACTIVE
2+
#r"../../Debug/fcs/net45/FSharp.Compiler.Service.dll"// note, run 'build fcs debug' to generate this, this DLL has a public API so can be used from F# Interactive
3+
#r"../../packages/NUnit.3.5.0/lib/net45/nunit.framework.dll"
4+
#load"FsUnit.fs"
5+
#load"Common.fs"
6+
#else
7+
moduleTests.Service.ServiceUntypedParseTests
8+
#endif
9+
10+
openSystem
11+
openSystem.IO
12+
openSystem.Text
13+
openNUnit.Framework
14+
openMicrosoft.FSharp.Compiler.Range
15+
openMicrosoft.FSharp.Compiler.SourceCodeServices
16+
openFSharp.Compiler.Service.Tests.Common
17+
openTests.Service
18+
19+
let [<Literal>]privateMarker="(* marker *)"
20+
21+
letprivate(=>)(source: string)(expected: CompletionContext option)=
22+
23+
letlines=
24+
use reader=new StringReader(source)
25+
[|letline= ref(reader.ReadLine())
26+
whilenot(isNull!line)do
27+
yield!line
28+
line:= reader.ReadLine()
29+
if source.EndsWith"\n"then
30+
yield""|]
31+
32+
letmarkerPos=
33+
lines
34+
|> Array.mapi(fun i x-> i, x)
35+
|> Array.tryPick(fun(lineIdx,line)->
36+
match line.IndexOf Markerwith
37+
|-1-> None
38+
| idx-> Some(mkPos(Line.fromZ lineIdx) idx))
39+
40+
match markerPoswith
41+
| None-> failwithf"Marker '%s' was not found in the source code" Marker
42+
| Some markerPos->
43+
match parseSourceCode("C:\\test.fs", source)with
44+
| None-> failwith"No parse tree"
45+
| Some parseTree->
46+
letactual= UntypedParseImpl.TryGetCompletionContext(markerPos, parseTree, lines.[Line.toZ markerPos.Line])
47+
try Assert.AreEqual(expected, actual)
48+
with e->
49+
printfn"ParseTree:%A" parseTree
50+
reraise()
51+
52+
moduleAttributeCompletion=
53+
[<Test>]
54+
let``at[<|, appliedto nothing``()=
55+
"""
56+
[<(* marker *)
57+
"""
58+
=> Some CompletionContext.AttributeApplication
59+
60+
[<TestCase("[<(* marker *)",true)>]
61+
[<TestCase("[<AnAttr(* marker *)",true)>]
62+
[<TestCase("[<type:(* marker *)",true)>]
63+
[<TestCase("[<type:AnAttr(* marker *)",true)>]
64+
[<TestCase("[< (* marker *)",true)>]
65+
[<TestCase("[<AnAttribute;(* marker *)",true)>]
66+
[<TestCase("[<AnAttribute; (* marker *)",true)>]
67+
[<TestCase("[<AnAttribute>][<(* marker *)",true)>]
68+
[<TestCase("[<AnAttribute>][< (* marker *)",true)>]
69+
[<TestCase("[<AnAttribute((* marker *)",false)>]
70+
[<TestCase("[<AnAttribute( (* marker *)",false)>]
71+
[<TestCase("[<AnAttribute (* marker *)",false)>]
72+
[<TestCase("[<AnAttribute>][<AnAttribute((* marker *)",false)>]
73+
[<TestCase("[<AnAttribute; AnAttribute((* marker *)",false)>]
74+
let``incomplete``(lineStr:string,expectAttributeApplicationContext:bool)=
75+
(sprintf"""
76+
%s
77+
type T =
78+
{ F: int }
79+
""" lineStr)=>(if expectAttributeApplicationContextthen Some CompletionContext.AttributeApplicationelse None)
80+
81+
[<TestCase("[<(* marker *)>]",true)>]
82+
[<TestCase("[<AnAttr(* marker *)>]",true)>]
83+
[<TestCase("[<type:(* marker *)>]",true)>]
84+
[<TestCase("[<type:AnAttr(* marker *)>]",true)>]
85+
[<TestCase("[< (* marker *)>]",true)>]
86+
[<TestCase("[<AnAttribute>][<(* marker *)>]",true)>]
87+
[<TestCase("[<AnAttribute>][< (* marker *)>]",true)>]
88+
[<TestCase("[<AnAttribute;(* marker *)>]",true)>]
89+
[<TestCase("[<AnAttribute; (* marker *) >]",true)>]
90+
[<TestCase("[<AnAttribute>][<AnAttribute;(* marker *)>]",true)>]
91+
[<TestCase("[<AnAttribute((* marker *)>]",false)>]
92+
[<TestCase("[<AnAttribute (* marker *) >]",false)>]
93+
[<TestCase("[<AnAttribute>][<AnAttribute((* marker *)>]",false)>]
94+
[<TestCase("[<AnAttribute; AnAttribute((* marker *)>]",false)>]
95+
[<TestCase("[<AnAttribute; AnAttribute( (* marker *)>]",false)>]
96+
[<TestCase("[<AnAttribute>][<AnAttribute; AnAttribute((* marker *)>]",false)>]
97+
let``complete``(lineStr:string,expectAttributeApplicationContext:bool)=
98+
(sprintf"""
99+
%s
100+
type T =
101+
{ F: int }
102+
""" lineStr)=>(if expectAttributeApplicationContextthen Some CompletionContext.AttributeApplicationelse None)

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,13 @@ type internal FSharpCompletionProvider
194194

195195
if results.Count>0&&not declarations.IsForType&&not declarations.IsError&& List.isEmpty partialName.QualifyingIdentsthen
196196
letlineStr= textLines.[caretLinePos.Line].ToString()
197-
match UntypedParseImpl.TryGetCompletionContext(Pos.fromZ caretLinePos.Line caretLinePos.Character, Some parseResults, lineStr)with
197+
198+
letcompletionContext=
199+
parseResults.ParseTree
200+
|> Option.bind(fun parseTree->
201+
UntypedParseImpl.TryGetCompletionContext(Pos.fromZ caretLinePos.Line caretLinePos.Character, parseTree, lineStr))
202+
203+
match completionContextwith
198204
| None-> results.AddRange(keywordCompletionItems)
199205
|_->()
200206

‎vsintegration/tests/unittests/Tests.LanguageService.Completion.fs‎

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -182,9 +182,8 @@ type UsingMSBuild() as this =
182182
shouldContain// should contain
183183
shouldNotContain
184184

185-
memberpublicthis.AutoCompleteBug70080Helper(programText:string,?withSuffix:bool)=
186-
letexpected=if defaultArg withSuffixfalsethen"AttributeUsageAttribute"else"AttributeUsage"
187-
this.AutoCompleteBug70080HelperHelper(programText,[expected],[])
185+
memberpublicthis.AutoCompleteBug70080Helper(programText:string)=
186+
this.AutoCompleteBug70080HelperHelper(programText,["AttributeUsage"],[])
188187

189188
memberprivatethis.testAutoCompleteAdjacentToDot op=
190189
lettext= sprintf"System.Console%s" op
@@ -3546,56 +3545,56 @@ let x = query { for bbbb in abbbbc(*D0*) do
35463545
memberpublicthis.``Attribute.WhenAttachedToType.Bug70080``()=
35473546
this.AutoCompleteBug70080Helper(@"
35483547
open System
3549-
[<Attr // expectAttributeUsageAttribute from System namespace
3550-
type MyAttr() = inherit Attribute()",true)
3548+
[<Attr // expectAttributeUsage from System namespace
3549+
type MyAttr() = inherit Attribute()")
35513550

35523551
[<Test>]
35533552
memberpublicthis.``Attribute.WhenAttachedToNothing.Bug70080``()=
35543553
this.AutoCompleteBug70080Helper(@"
35553554
open System
3556-
[<Attr // expectAttributeUsageAttribute from System namespace
3557-
// nothing here",true)
3555+
[<Attr // expectAttributeUsage
3556+
// nothing here")
35583557

35593558
[<Test>]
35603559
memberpublicthis.``Attribute.WhenAttachedToLetInNamespace.Bug70080``()=
35613560
this.AutoCompleteBug70080Helper@"
35623561
namespace Foo
35633562
open System
3564-
[<Attr // expectAttributeUsageAttribute from System namespace
3563+
[<Attr // expectAttributeUsage from System namespace
35653564
let f() = 4"
35663565

35673566
[<Test>]
35683567
memberpublicthis.``Attribute.WhenAttachedToTypeInNamespace.Bug70080``()=
35693568
this.AutoCompleteBug70080Helper(@"
35703569
namespace Foo
35713570
open System
3572-
[<Attr // expectAttributeUsageAttribute from System namespace
3573-
type MyAttr() = inherit Attribute()",true)
3571+
[<Attr // expectAttributeUsage from System namespace
3572+
type MyAttr() = inherit Attribute()")
35743573

35753574
[<Test>]
35763575
memberpublicthis.``Attribute.WhenAttachedToNothingInNamespace.Bug70080``()=
35773576
this.AutoCompleteBug70080Helper(@"
35783577
namespace Foo
35793578
open System
3580-
[<Attr // expectAttributeUsageAttribute from System namespace
3581-
// nothing here",true)
3579+
[<Attr // expectAttributeUsage from System namespace
3580+
// nothing here")
35823581

35833582
[<Test>]
35843583
memberpublicthis.``Attribute.WhenAttachedToModuleInNamespace.Bug70080``()=
35853584
this.AutoCompleteBug70080Helper(@"
35863585
namespace Foo
35873586
open System
3588-
[<Attr // expectAttributeUsageAttribute from System namespace
3587+
[<Attr // expectAttributeUsage from System namespace
35893588
module Foo =
3590-
let x = 42",true)
3589+
let x = 42")
35913590

35923591
[<Test>]
35933592
memberpublicthis.``Attribute.WhenAttachedToModule.Bug70080``()=
35943593
this.AutoCompleteBug70080Helper(@"
35953594
open System
3596-
[<Attr // expectAttributeUsageAttribute from System namespace
3595+
[<Attr // expectAttributeUsage from System namespace
35973596
module Foo =
3598-
let x = 42",true)
3597+
let x = 42")
35993598

36003599
[<Test>]
36013600
memberpublicthis.``Identifer.InMatchStatemente.Bug72595``()=
@@ -5052,7 +5051,7 @@ let x = query { for bbbb in abbbbc(*D0*) do
50525051
[<
50535052
"""]
50545053
"[<"
5055-
["AttributeUsageAttribute"]
5054+
["AttributeUsage"]
50565055
[]
50575056

50585057
[<Test>]
@@ -5063,7 +5062,7 @@ let x = query { for bbbb in abbbbc(*D0*) do
50635062
[<
50645063
"""]
50655064
"[<"
5066-
["AttributeUsageAttribute"]
5065+
["AttributeUsage"]
50675066
[]
50685067

50695068
[<Test>]

‎vsintegration/tests/unittests/VisualFSharp.UnitTests.fsproj‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@
9797
<CompileInclude="..\..\..\tests\service\AssemblyContentProviderTests.fs">
9898
<Link>AssemblyContentProviderTests.fs</Link>
9999
</Compile>
100+
<CompileInclude="..\..\..\tests\service\ServiceUntypedParseTests.fs">
101+
<Link>ServiceUntypedParseTests.fs</Link>
102+
</Compile>
100103
<CompileInclude="UnusedOpensTests.fs">
101104
<Link>ServiceAnalysis\UnusedOpensTests.fs</Link>
102105
</Compile>

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp