-
Notifications
You must be signed in to change notification settings - Fork 789
/
service.fs
3391 lines (2890 loc) · 196 KB
/
service.fs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
// Open up the compiler as an incremental service for parsing,
// type checking and intellisense-like environment-reporting.
namespace Microsoft.FSharp.Compiler.SourceCodeServices
open System
open System.Collections.Generic
open System.Collections.Concurrent
open System.Diagnostics
open System.IO
open System.Reflection
open System.Text
open Microsoft.FSharp.Core.Printf
open Microsoft.FSharp.Compiler
open Microsoft.FSharp.Compiler.AbstractIL
open Microsoft.FSharp.Compiler.AbstractIL.IL
open Microsoft.FSharp.Compiler.AbstractIL.ILBinaryReader
open Microsoft.FSharp.Compiler.AbstractIL.Diagnostics
open Microsoft.FSharp.Compiler.AbstractIL.Internal
open Microsoft.FSharp.Compiler.AbstractIL.Internal.Library
open Microsoft.FSharp.Compiler.AccessibilityLogic
open Microsoft.FSharp.Compiler.Ast
open Microsoft.FSharp.Compiler.CompileOps
open Microsoft.FSharp.Compiler.CompileOptions
open Microsoft.FSharp.Compiler.Driver
open Microsoft.FSharp.Compiler.ErrorLogger
open Microsoft.FSharp.Compiler.Lib
open Microsoft.FSharp.Compiler.PrettyNaming
open Microsoft.FSharp.Compiler.Parser
open Microsoft.FSharp.Compiler.Range
open Microsoft.FSharp.Compiler.Lexhelp
open Microsoft.FSharp.Compiler.Layout
open Microsoft.FSharp.Compiler.Tast
open Microsoft.FSharp.Compiler.Tastops
open Microsoft.FSharp.Compiler.TcGlobals
open Microsoft.FSharp.Compiler.Infos
open Microsoft.FSharp.Compiler.InfoReader
open Microsoft.FSharp.Compiler.NameResolution
open Microsoft.FSharp.Compiler.TypeChecker
open Microsoft.FSharp.Compiler.SourceCodeServices.SymbolHelpers
open Internal.Utilities
open Internal.Utilities.Collections
open Microsoft.FSharp.Compiler.Layout.TaggedTextOps
#if FX_RESHAPED_REFLECTION
open Microsoft.FSharp.Core.ReflectionAdapters
#endif
type internal Layout = StructuredFormat.Layout
[<AutoOpen>]
module EnvMisc =
let getToolTipTextSize = GetEnvInteger "FCS_GetToolTipTextCacheSize" 5
let maxTypeCheckErrorsOutOfProjectContext = GetEnvInteger "FCS_MaxErrorsOutOfProjectContext" 3
let braceMatchCacheSize = GetEnvInteger "FCS_BraceMatchCacheSize" 5
let parseFileCacheSize = GetEnvInteger "FCS_ParseFileCacheSize" 2
let checkFileInProjectCacheSize = GetEnvInteger "FCS_CheckFileInProjectCacheSize" 10
let projectCacheSizeDefault = GetEnvInteger "FCS_ProjectCacheSizeDefault" 3
let frameworkTcImportsCacheStrongSize = GetEnvInteger "FCS_frameworkTcImportsCacheStrongSizeDefault" 8
let maxMBDefault = GetEnvInteger "FCS_MaxMB" 1000000 // a million MB = 1TB = disabled
//let maxMBDefault = GetEnvInteger "FCS_maxMB" (if sizeof<int> = 4 then 1700 else 3400)
/// Maximum time share for a piece of background work before it should (cooperatively) yield
/// to enable other requests to be serviced. Yielding means returning a continuation function
/// (via an Eventually<_> value of case NotYetDone) that can be called as the next piece of work.
let maxTimeShareMilliseconds =
match System.Environment.GetEnvironmentVariable("FCS_MaxTimeShare") with
| null | "" -> 100L
| s -> int64 s
//----------------------------------------------------------------------------
// Scopes.
//--------------------------------------------------------------------------
[<RequireQualifiedAccess>]
type FSharpFindDeclFailureReason =
// generic reason: no particular information about error
| Unknown of message: string
// source code file is not available
| NoSourceCode
// trying to find declaration of ProvidedType without TypeProviderDefinitionLocationAttribute
| ProvidedType of string
// trying to find declaration of ProvidedMember without TypeProviderDefinitionLocationAttribute
| ProvidedMember of string
[<RequireQualifiedAccess>]
type FSharpFindDeclResult =
/// declaration not found + reason
| DeclNotFound of FSharpFindDeclFailureReason
/// found declaration
| DeclFound of range
/// Indicates an external declaration was found
| ExternalDecl of assembly : string * externalSym : ExternalSymbol
/// This type is used to describe what was found during the name resolution.
/// (Depending on the kind of the items, we may stop processing or continue to find better items)
[<RequireQualifiedAccess>]
[<NoEquality; NoComparison>]
type internal NameResResult =
| Members of (ItemWithInst list * DisplayEnv * range)
| Cancel of DisplayEnv * range
| Empty
| TypecheckStaleAndTextChanged
[<RequireQualifiedAccess>]
type ResolveOverloads =
| Yes
| No
[<RequireQualifiedAccess>]
type GetPreciseCompletionListFromExprTypingsResult =
| NoneBecauseTypecheckIsStaleAndTextChanged
| NoneBecauseThereWereTypeErrors
| None
| Some of (ItemWithInst list * DisplayEnv * range) * TType
type Names = string list
[<RequireQualifiedAccess>]
type SemanticClassificationType =
| ReferenceType
| ValueType
| UnionCase
| Function
| Property
| MutableVar
| Module
| Printf
| ComputationExpression
| IntrinsicFunction
| Enumeration
| Interface
| TypeArgument
| Operator
| Disposable
/// A TypeCheckInfo represents everything we get back from the typecheck of a file.
/// It acts like an in-memory database about the file.
/// It is effectively immutable and not updated: when we re-typecheck we just drop the previous
/// scope object on the floor and make a new one.
[<Sealed>]
type TypeCheckInfo
(// Information corresponding to miscellaneous command-line options (--define, etc).
_sTcConfig: TcConfig,
g: TcGlobals,
// The signature of the assembly being checked, up to and including the current file
ccuSigForFile: ModuleOrNamespaceType,
thisCcu: CcuThunk,
tcImports: TcImports,
tcAccessRights: AccessorDomain,
projectFileName: string,
mainInputFileName: string,
sResolutions: TcResolutions,
sSymbolUses: TcSymbolUses,
// This is a name resolution environment to use if no better match can be found.
sFallback: NameResolutionEnv,
loadClosure : LoadClosure option,
reactorOps : IReactorOperations,
checkAlive : (unit -> bool),
textSnapshotInfo:obj option,
implFileOpt: TypedImplFile option,
openDeclarations: OpenDeclaration[]) =
let textSnapshotInfo = defaultArg textSnapshotInfo null
let (|CNR|) (cnr:CapturedNameResolution) =
(cnr.Pos, cnr.Item, cnr.ItemOccurence, cnr.DisplayEnv, cnr.NameResolutionEnv, cnr.AccessorDomain, cnr.Range)
// These strings are potentially large and the editor may choose to hold them for a while.
// Use this cache to fold together data tip text results that are the same.
// Is not keyed on 'Names' collection because this is invariant for the current position in
// this unchanged file. Keyed on lineStr though to prevent a change to the currently line
// being available against a stale scope.
let getToolTipTextCache = AgedLookup<CompilationThreadToken, int*int*string, FSharpToolTipText<Layout>>(getToolTipTextSize,areSimilar=(fun (x,y) -> x = y))
let amap = tcImports.GetImportMap()
let infoReader = new InfoReader(g,amap)
let ncenv = new NameResolver(g,amap,infoReader,NameResolution.FakeInstantiationGenerator)
let cenv = SymbolEnv(g, thisCcu, Some ccuSigForFile, tcImports, amap, infoReader)
/// Find the most precise naming environment for the given line and column
let GetBestEnvForPos cursorPos =
let mutable bestSoFar = None
// Find the most deeply nested enclosing scope that contains given position
sResolutions.CapturedEnvs |> ResizeArray.iter (fun (possm,env,ad) ->
if rangeContainsPos possm cursorPos then
match bestSoFar with
| Some (bestm,_,_) ->
if rangeContainsRange bestm possm then
bestSoFar <- Some (possm,env,ad)
| None ->
bestSoFar <- Some (possm,env,ad))
let mostDeeplyNestedEnclosingScope = bestSoFar
// Look for better subtrees on the r.h.s. of the subtree to the left of where we are
// Should really go all the way down the r.h.s. of the subtree to the left of where we are
// This is all needed when the index is floating free in the area just after the environment we really want to capture
// We guarantee to only refine to a more nested environment. It may not be strictly
// the right environment, but will always be at least as rich
let bestAlmostIncludedSoFar = ref None
sResolutions.CapturedEnvs |> ResizeArray.iter (fun (possm,env,ad) ->
// take only ranges that strictly do not include cursorPos (all ranges that touch cursorPos were processed during 'Strict Inclusion' part)
if rangeBeforePos possm cursorPos && not (posEq possm.End cursorPos) then
let contained =
match mostDeeplyNestedEnclosingScope with
| Some (bestm,_,_) -> rangeContainsRange bestm possm
| None -> true
if contained then
match !bestAlmostIncludedSoFar with
| Some (rightm:range,_,_) ->
if posGt possm.End rightm.End ||
(posEq possm.End rightm.End && posGt possm.Start rightm.Start) then
bestAlmostIncludedSoFar := Some (possm,env,ad)
| _ -> bestAlmostIncludedSoFar := Some (possm,env,ad))
let resEnv =
match !bestAlmostIncludedSoFar, mostDeeplyNestedEnclosingScope with
| Some (_,env,ad), None -> env, ad
| Some (_,almostIncludedEnv,ad), Some (_,mostDeeplyNestedEnv,_)
when almostIncludedEnv.eFieldLabels.Count >= mostDeeplyNestedEnv.eFieldLabels.Count ->
almostIncludedEnv,ad
| _ ->
match mostDeeplyNestedEnclosingScope with
| Some (_,env,ad) ->
env,ad
| None ->
sFallback,AccessibleFromSomeFSharpCode
let pm = mkRange mainInputFileName cursorPos cursorPos
resEnv,pm
/// The items that come back from ResolveCompletionsInType are a bit
/// noisy. Filter a few things out.
///
/// e.g. prefer types to constructors for FSharpToolTipText
let FilterItemsForCtors filterCtors (items: ItemWithInst list) =
let items = items |> List.filter (fun item -> match item.Item with (Item.CtorGroup _) when filterCtors = ResolveTypeNamesToTypeRefs -> false | _ -> true)
items
// Filter items to show only valid & return Some if there are any
let ReturnItemsOfType (items: ItemWithInst list) g denv (m:range) filterCtors hasTextChangedSinceLastTypecheck =
let items =
items
|> RemoveDuplicateItems g
|> RemoveExplicitlySuppressed g
|> FilterItemsForCtors filterCtors
if not (isNil items) then
if hasTextChangedSinceLastTypecheck(textSnapshotInfo, m) then
NameResResult.TypecheckStaleAndTextChanged // typecheck is stale, wait for second-chance IntelliSense to bring up right result
else
NameResResult.Members (items, denv, m)
else NameResResult.Empty
let GetCapturedNameResolutions endOfNamesPos resolveOverloads =
let quals =
match resolveOverloads with
| ResolveOverloads.Yes -> sResolutions.CapturedNameResolutions
| ResolveOverloads.No -> sResolutions.CapturedMethodGroupResolutions
let quals = quals |> ResizeArray.filter (fun cnr -> posEq cnr.Pos endOfNamesPos)
quals
/// Looks at the exact name resolutions that occurred during type checking
/// If 'membersByResidue' is specified, we look for members of the item obtained
/// from the name resolution and filter them by the specified residue (?)
let GetPreciseItemsFromNameResolution(line, colAtEndOfNames, membersByResidue, filterCtors, resolveOverloads, hasTextChangedSinceLastTypecheck) =
let endOfNamesPos = mkPos line colAtEndOfNames
// Logic below expects the list to be in reverse order of resolution
let cnrs = GetCapturedNameResolutions endOfNamesPos resolveOverloads |> ResizeArray.toList |> List.rev
match cnrs, membersByResidue with
// If we're looking for members using a residue, we'd expect only
// a single item (pick the first one) and we need the residue (which may be "")
| CNR(_,Item.Types(_,(ty::_)), _, denv, nenv, ad, m)::_, Some _ ->
let items = ResolveCompletionsInType ncenv nenv (ResolveCompletionTargets.All(ConstraintSolver.IsApplicableMethApprox g amap m)) m ad true ty
let items = List.map ItemWithNoInst items
ReturnItemsOfType items g denv m filterCtors hasTextChangedSinceLastTypecheck
// Value reference from the name resolution. Primarily to disallow "let x.$ = 1"
// In most of the cases, value references can be obtained from expression typings or from environment,
// so we wouldn't have to handle values here. However, if we have something like:
// let varA = "string"
// let varA = if b then 0 else varA.
// then the expression typings get confused (thinking 'varA:int'), so we use name resolution even for usual values.
| CNR(_, Item.Value(vref), occurence, denv, nenv, ad, m)::_, Some _ ->
if (occurence = ItemOccurence.Binding || occurence = ItemOccurence.Pattern) then
// Return empty list to stop further lookup - for value declarations
NameResResult.Cancel(denv, m)
else
// If we have any valid items for the value, then return completions for its type now.
// Adjust the type in case this is the 'this' pointer stored in a reference cell.
let ty = StripSelfRefCell(g, vref.BaseOrThisInfo, vref.TauType)
// patch accessibility domain to remove protected members if accessing NormalVal
let ad =
match vref.BaseOrThisInfo, ad with
| ValBaseOrThisInfo.NormalVal, AccessibleFrom(paths, Some tcref) ->
let tcref = generalizedTyconRef tcref
// check that type of value is the same or subtype of tcref
// yes - allow access to protected members
// no - strip ability to access protected members
if Microsoft.FSharp.Compiler.TypeRelations.TypeFeasiblySubsumesType 0 g amap m tcref Microsoft.FSharp.Compiler.TypeRelations.CanCoerce ty then
ad
else
AccessibleFrom(paths, None)
| _ -> ad
let items = ResolveCompletionsInType ncenv nenv (ResolveCompletionTargets.All(ConstraintSolver.IsApplicableMethApprox g amap m)) m ad false ty
let items = List.map ItemWithNoInst items
ReturnItemsOfType items g denv m filterCtors hasTextChangedSinceLastTypecheck
// No residue, so the items are the full resolution of the name
| CNR(_, _, _, denv, _, _, m) :: _, None ->
let items =
cnrs
|> List.map (fun cnr -> cnr.ItemWithInst)
// "into" is special magic syntax, not an identifier or a library call. It is part of capturedNameResolutions as an
// implementation detail of syntax coloring, but we should not report name resolution results for it, to prevent spurious QuickInfo.
|> List.filter (fun item -> match item.Item with Item.CustomOperation(CustomOperations.Into,_,_) -> false | _ -> true)
ReturnItemsOfType items g denv m filterCtors hasTextChangedSinceLastTypecheck
| _, _ -> NameResResult.Empty
let TryGetTypeFromNameResolution(line, colAtEndOfNames, membersByResidue, resolveOverloads) =
let endOfNamesPos = mkPos line colAtEndOfNames
let items = GetCapturedNameResolutions endOfNamesPos resolveOverloads |> ResizeArray.toList |> List.rev
match items, membersByResidue with
| CNR(_,Item.Types(_,(ty::_)),_,_,_,_,_)::_, Some _ -> Some ty
| CNR(_, Item.Value(vref), occurence,_,_,_,_)::_, Some _ ->
if (occurence = ItemOccurence.Binding || occurence = ItemOccurence.Pattern) then None
else Some (StripSelfRefCell(g, vref.BaseOrThisInfo, vref.TauType))
| _, _ -> None
let CollectParameters (methods: MethInfo list) amap m: Item list =
methods
|> List.collect (fun meth ->
match meth.GetParamDatas(amap, m, meth.FormalMethodInst) with
| x::_ -> x |> List.choose(fun (ParamData(_isParamArray, _isInArg, _isOutArg, _optArgInfo, _callerInfo, name, _, ty)) ->
match name with
| Some n -> Some (Item.ArgName(n, ty, Some (ArgumentContainer.Method meth)))
| None -> None
)
| _ -> []
)
let GetNamedParametersAndSettableFields endOfExprPos hasTextChangedSinceLastTypecheck =
let cnrs = GetCapturedNameResolutions endOfExprPos ResolveOverloads.No |> ResizeArray.toList |> List.rev
let result =
match cnrs with
| CNR(_, Item.CtorGroup(_, ((ctor::_) as ctors)), _, denv, nenv, ad, m) ::_ ->
let props = ResolveCompletionsInType ncenv nenv ResolveCompletionTargets.SettablePropertiesAndFields m ad false ctor.ApparentEnclosingType
let parameters = CollectParameters ctors amap m
let items = props @ parameters
Some (denv, m, items)
| CNR(_, Item.MethodGroup(_, methods, _), _, denv, nenv, ad, m) ::_ ->
let props =
methods
|> List.collect (fun meth ->
let retTy = meth.GetFSharpReturnTy(amap, m, meth.FormalMethodInst)
ResolveCompletionsInType ncenv nenv ResolveCompletionTargets.SettablePropertiesAndFields m ad false retTy
)
let parameters = CollectParameters methods amap m
let items = props @ parameters
Some (denv, m, items)
| _ ->
None
match result with
| None ->
NameResResult.Empty
| Some (denv, m, items) ->
let items = List.map ItemWithNoInst items
ReturnItemsOfType items g denv m TypeNameResolutionFlag.ResolveTypeNamesToTypeRefs hasTextChangedSinceLastTypecheck
/// finds captured typing for the given position
let GetExprTypingForPosition(endOfExprPos) =
let quals =
sResolutions.CapturedExpressionTypings
|> Seq.filter (fun (pos,ty,denv,_,_,_) ->
// We only want expression types that end at the particular position in the file we are looking at.
let isLocationWeCareAbout = posEq pos endOfExprPos
// Get rid of function types. True, given a 2-arg curried function "f x y", it is legal to do "(f x).GetType()",
// but you almost never want to do this in practice, and we choose not to offer up any intellisense for
// F# function types.
let isFunction = isFunTy denv.g ty
isLocationWeCareAbout && not isFunction)
|> Seq.toArray
let thereWereSomeQuals = not (Array.isEmpty quals)
// filter out errors
let quals = quals
|> Array.filter (fun (_,ty,denv,_,_,_) -> not (isTyparTy denv.g ty && (destTyparTy denv.g ty).IsFromError))
thereWereSomeQuals, quals
/// obtains captured typing for the given position
/// if type of captured typing is record - returns list of record fields
let GetRecdFieldsForExpr(r : range) =
let _, quals = GetExprTypingForPosition(r.End)
let bestQual =
match quals with
| [||] -> None
| quals ->
quals |> Array.tryFind (fun (_,_,_,_,_,rq) ->
ignore(r) // for breakpoint
posEq r.Start rq.Start)
match bestQual with
| Some (_,ty,denv,_nenv,ad,m) when isRecdTy denv.g ty ->
let items = NameResolution.ResolveRecordOrClassFieldsOfType ncenv m ad ty false
Some (items, denv, m)
| _ -> None
/// Looks at the exact expression types at the position to the left of the
/// residue then the source when it was typechecked.
let GetPreciseCompletionListFromExprTypings(parseResults:FSharpParseFileResults, endOfExprPos, filterCtors, hasTextChangedSinceLastTypecheck: (obj * range -> bool)) =
let thereWereSomeQuals, quals = GetExprTypingForPosition(endOfExprPos)
match quals with
| [| |] ->
if thereWereSomeQuals then
GetPreciseCompletionListFromExprTypingsResult.NoneBecauseThereWereTypeErrors
else
GetPreciseCompletionListFromExprTypingsResult.None
| _ ->
let bestQual, textChanged =
match parseResults.ParseTree with
| Some(input) ->
match UntypedParseImpl.GetRangeOfExprLeftOfDot(endOfExprPos,Some(input)) with // TODO we say "colAtEndOfNames" everywhere, but that's not really a good name ("foo . $" hit Ctrl-Space at $)
| Some( exprRange) ->
if hasTextChangedSinceLastTypecheck(textSnapshotInfo, exprRange) then
None, true // typecheck is stale, wait for second-chance IntelliSense to bring up right result
else
// See bug 130733. We have an up-to-date sync parse, and know the exact range of the prior expression.
// The quals all already have the same ending position, so find one with a matching starting position, if it exists.
// If not, then the stale typecheck info does not have a capturedExpressionTyping for this exact expression, and the
// user can wait for typechecking to catch up and second-chance intellisense to give the right result.
let qual =
quals |> Array.tryFind (fun (_,_,_,_,_,r) ->
ignore(r) // for breakpoint
posEq exprRange.Start r.Start)
qual, false
| None ->
// TODO In theory I think we should never get to this code path; it would be nice to add an assert.
// In practice, we do get here in some weird cases like "2.0 .. 3.0" and hitting Ctrl-Space in between the two dots of the range operator.
// I wasn't able to track down what was happening in those weird cases, not worth worrying about, it doesn't manifest as a product bug or anything.
None, false
| _ -> None, false
match bestQual with
| Some bestQual ->
let (_,ty,denv,nenv,ad,m) = bestQual
let items = ResolveCompletionsInType ncenv nenv (ResolveCompletionTargets.All(ConstraintSolver.IsApplicableMethApprox g amap m)) m ad false ty
let items = items |> List.map ItemWithNoInst
let items = items |> RemoveDuplicateItems g
let items = items |> RemoveExplicitlySuppressed g
let items = items |> FilterItemsForCtors filterCtors
GetPreciseCompletionListFromExprTypingsResult.Some((items,denv,m), ty)
| None ->
if textChanged then GetPreciseCompletionListFromExprTypingsResult.NoneBecauseTypecheckIsStaleAndTextChanged
else GetPreciseCompletionListFromExprTypingsResult.None
/// Find items in the best naming environment.
let GetEnvironmentLookupResolutions(nenv, ad, m, plid, filterCtors, showObsolete) =
let items = NameResolution.ResolvePartialLongIdent ncenv nenv (ConstraintSolver.IsApplicableMethApprox g amap m) m ad plid showObsolete
let items = items |> List.map ItemWithNoInst
let items = items |> RemoveDuplicateItems g
let items = items |> RemoveExplicitlySuppressed g
let items = items |> FilterItemsForCtors filterCtors
(items, nenv.DisplayEnv, m)
/// Find items in the best naming environment.
let GetEnvironmentLookupResolutionsAtPosition(cursorPos, plid, filterCtors, showObsolete) =
let (nenv,ad),m = GetBestEnvForPos cursorPos
GetEnvironmentLookupResolutions(nenv, ad, m, plid, filterCtors, showObsolete)
/// Find record fields in the best naming environment.
let GetClassOrRecordFieldsEnvironmentLookupResolutions(cursorPos, plid) =
let (nenv, ad),m = GetBestEnvForPos cursorPos
let items = NameResolution.ResolvePartialLongIdentToClassOrRecdFields ncenv nenv m ad plid false
let items = items |> List.map ItemWithNoInst
let items = items |> RemoveDuplicateItems g
let items = items |> RemoveExplicitlySuppressed g
items, nenv.DisplayEnv, m
/// Resolve a location and/or text to items.
// Three techniques are used
// - look for an exact known name resolution from type checking
// - use the known type of an expression, e.g. (expr).Name, to generate an item list
// - lookup an entire name in the name resolution environment, e.g. A.B.Name, to generate an item list
//
// The overall aim is to resolve as accurately as possible based on what we know from type inference
let GetBaseClassCandidates = function
| Item.ModuleOrNamespaces _ -> true
| Item.Types(_, ty::_) when (isClassTy g ty) && not (isSealedTy g ty) -> true
| _ -> false
let GetInterfaceCandidates = function
| Item.ModuleOrNamespaces _ -> true
| Item.Types(_, ty::_) when (isInterfaceTy g ty) -> true
| _ -> false
// Return only items with the specified name
let FilterDeclItemsByResidue (getItem: 'a -> Item) residue (items: 'a list) =
let attributedResidue = residue + "Attribute"
let nameMatchesResidue name = (residue = name) || (attributedResidue = name)
items |> List.filter (fun x ->
let item = getItem x
let n1 = item.DisplayName
match item with
| Item.Types _ -> nameMatchesResidue n1
| Item.CtorGroup (_, meths) ->
nameMatchesResidue n1 ||
meths |> List.exists (fun meth ->
let tcref = meth.ApparentEnclosingTyconRef
tcref.IsProvided || nameMatchesResidue tcref.DisplayName)
| _ -> residue = n1)
/// Post-filter items to make sure they have precisely the right name
/// This also checks that there are some remaining results
/// exactMatchResidueOpt = Some _ -- means that we are looking for exact matches
let FilterRelevantItemsBy (getItem: 'a -> Item) (exactMatchResidueOpt : _ option) check (items: 'a list, denv, m) =
// can throw if type is in located in non-resolved CCU: i.e. bigint if reference to System.Numerics is absent
let safeCheck item = try check item with _ -> false
// Are we looking for items with precisely the given name?
if not (isNil items) && exactMatchResidueOpt.IsSome then
let items = items |> FilterDeclItemsByResidue getItem exactMatchResidueOpt.Value |> List.filter safeCheck
if not (isNil items) then Some(items, denv, m) else None
else
// When (items = []) we must returns Some([],..) and not None
// because this value is used if we want to stop further processing (e.g. let x.$ = ...)
let items = items |> List.filter safeCheck
Some(items, denv, m)
/// Post-filter items to make sure they have precisely the right name
/// This also checks that there are some remaining results
let (|FilterRelevantItems|_|) getItem exactMatchResidueOpt orig =
FilterRelevantItemsBy getItem exactMatchResidueOpt (fun _ -> true) orig
/// Find the first non-whitespace position in a line prior to the given character
let FindFirstNonWhitespacePosition (lineStr: string) i =
if i >= lineStr.Length then None
else
let mutable p = i
while p >= 0 && System.Char.IsWhiteSpace(lineStr.[p]) do
p <- p - 1
if p >= 0 then Some p else None
let CompletionItem (ty: ValueOption<TyconRef>) (assemblySymbol: ValueOption<AssemblySymbol>) (item: ItemWithInst) =
let kind =
match item.Item with
| Item.MethodGroup (_, minfo :: _, _) -> CompletionItemKind.Method minfo.IsExtensionMember
| Item.RecdField _
| Item.Property _ -> CompletionItemKind.Property
| Item.Event _ -> CompletionItemKind.Event
| Item.ILField _
| Item.Value _ -> CompletionItemKind.Field
| Item.CustomOperation _ -> CompletionItemKind.CustomOperation
| _ -> CompletionItemKind.Other
{ ItemWithInst = item
MinorPriority = 0
Kind = kind
IsOwnMember = false
Type = match ty with ValueSome x -> Some x | _ -> None
Unresolved = match assemblySymbol with ValueSome x -> Some x.UnresolvedSymbol | _ -> None }
let DefaultCompletionItem item = CompletionItem ValueNone ValueNone item
let getItem (x: ItemWithInst) = x.Item
let GetDeclaredItems (parseResultsOpt: FSharpParseFileResults option, lineStr: string, origLongIdentOpt, colAtEndOfNamesAndResidue, residueOpt, lastDotPos, line, loc,
filterCtors, resolveOverloads, hasTextChangedSinceLastTypecheck, isInRangeOperator, allSymbols: unit -> AssemblySymbol list) =
// Are the last two chars (except whitespaces) = ".."
let isLikeRangeOp =
match FindFirstNonWhitespacePosition lineStr (colAtEndOfNamesAndResidue - 1) with
| Some x when x >= 1 && lineStr.[x] = '.' && lineStr.[x - 1] = '.' -> true
| _ -> false
// if last two chars are .. and we are not in range operator context - no completion
if isLikeRangeOp && not isInRangeOperator then None else
// Try to use the exact results of name resolution during type checking to generate the results
// This is based on position (i.e. colAtEndOfNamesAndResidue). This is not used if a residueOpt is given.
let nameResItems =
match residueOpt with
| None -> GetPreciseItemsFromNameResolution(line, colAtEndOfNamesAndResidue, None, filterCtors,resolveOverloads, hasTextChangedSinceLastTypecheck)
| Some residue ->
// deals with cases when we have spaces between dot and\or identifier, like A . $
// if this is our case - then we need to locate end position of the name skipping whitespaces
// this allows us to handle cases like: let x . $ = 1
match lastDotPos |> Option.orElseWith (fun _ -> FindFirstNonWhitespacePosition lineStr (colAtEndOfNamesAndResidue - 1)) with
| Some p when lineStr.[p] = '.' ->
match FindFirstNonWhitespacePosition lineStr (p - 1) with
| Some colAtEndOfNames ->
let colAtEndOfNames = colAtEndOfNames + 1 // convert 0-based to 1-based
GetPreciseItemsFromNameResolution(line, colAtEndOfNames, Some(residue), filterCtors,resolveOverloads, hasTextChangedSinceLastTypecheck)
| None -> NameResResult.Empty
| _ -> NameResResult.Empty
// Normalize to form A.B.C.D where D is the residue. It may be empty for "A.B.C."
// residueOpt = Some when we are looking for the exact match
let plid, exactMatchResidueOpt =
match origLongIdentOpt, residueOpt with
| None, _ -> [], None
| Some(origLongIdent), Some _ -> origLongIdent, None
| Some(origLongIdent), None ->
System.Diagnostics.Debug.Assert(not (isNil origLongIdent), "origLongIdent is empty")
// note: as above, this happens when we are called for "precise" resolution - (F1 keyword, data tip etc..)
let plid, residue = List.frontAndBack origLongIdent
plid, Some residue
let pos = mkPos line loc
let (nenv, ad), m = GetBestEnvForPos pos
let getType() =
match NameResolution.TryToResolveLongIdentAsType ncenv nenv m plid with
| Some x -> tryDestAppTy g x
| None ->
match lastDotPos |> Option.orElseWith (fun _ -> FindFirstNonWhitespacePosition lineStr (colAtEndOfNamesAndResidue - 1)) with
| Some p when lineStr.[p] = '.' ->
match FindFirstNonWhitespacePosition lineStr (p - 1) with
| Some colAtEndOfNames ->
let colAtEndOfNames = colAtEndOfNames + 1 // convert 0-based to 1-based
match TryGetTypeFromNameResolution(line, colAtEndOfNames, residueOpt, resolveOverloads) with
| Some x -> tryDestAppTy g x
| _ -> ValueNone
| None -> ValueNone
| _ -> ValueNone
match nameResItems with
| NameResResult.TypecheckStaleAndTextChanged -> None // second-chance intellisense will try again
| NameResResult.Cancel(denv,m) -> Some([], denv, m)
| NameResResult.Members(FilterRelevantItems getItem exactMatchResidueOpt (items, denv, m)) ->
// lookup based on name resolution results successful
Some (items |> List.map (CompletionItem (getType()) ValueNone), denv, m)
| _ ->
match origLongIdentOpt with
| None -> None
| Some _ ->
// Try to use the type of the expression on the left to help generate a completion list
let qualItems, thereIsADotInvolved =
match parseResultsOpt with
| None ->
// Note, you will get here if the 'reason' is not CompleteWord/MemberSelect/DisplayMemberList, as those are currently the
// only reasons we do a sync parse to have the most precise and likely-to-be-correct-and-up-to-date info. So for example,
// if you do QuickInfo hovering over A in "f(x).A()", you will only get a tip if typechecking has a name-resolution recorded
// for A, not if merely we know the capturedExpressionTyping of f(x) and you very recently typed ".A()" - in that case,
// you won't won't get a tip until the typechecking catches back up.
GetPreciseCompletionListFromExprTypingsResult.None, false
| Some parseResults ->
match UntypedParseImpl.TryFindExpressionASTLeftOfDotLeftOfCursor(mkPos line colAtEndOfNamesAndResidue,parseResults.ParseTree) with
| Some(pos,_) ->
GetPreciseCompletionListFromExprTypings(parseResults, pos, filterCtors, hasTextChangedSinceLastTypecheck), true
| None ->
// Can get here in a case like: if "f xxx yyy" is legal, and we do "f xxx y"
// We have no interest in expression typings, those are only useful for dot-completion. We want to fallback
// to "Use an environment lookup as the last resort" below
GetPreciseCompletionListFromExprTypingsResult.None, false
match qualItems,thereIsADotInvolved with
| GetPreciseCompletionListFromExprTypingsResult.Some(FilterRelevantItems getItem exactMatchResidueOpt (items, denv, m), ty), _
// Initially we only use the expression typings when looking up, e.g. (expr).Nam or (expr).Name1.Nam
// These come through as an empty plid and residue "". Otherwise we try an environment lookup
// and then return to the qualItems. This is because the expression typings are a little inaccurate, primarily because
// it appears we're getting some typings recorded for non-atomic expressions like "f x"
when isNil plid ->
// lookup based on expression typings successful
Some (items |> List.map (CompletionItem (tryDestAppTy g ty) ValueNone), denv, m)
| GetPreciseCompletionListFromExprTypingsResult.NoneBecauseThereWereTypeErrors, _ ->
// There was an error, e.g. we have "<expr>." and there is an error determining the type of <expr>
// In this case, we don't want any of the fallback logic, rather, we want to produce zero results.
None
| GetPreciseCompletionListFromExprTypingsResult.NoneBecauseTypecheckIsStaleAndTextChanged, _ ->
// we want to report no result and let second-chance intellisense kick in
None
| _, true when isNil plid ->
// If the user just pressed '.' after an _expression_ (not a plid), it is never right to show environment-lookup top-level completions.
// The user might by typing quickly, and the LS didn't have an expression type right before the dot yet.
// Second-chance intellisense will bring up the correct list in a moment.
None
| _ ->
// Use an environment lookup as the last resort
let envItems, denv, m = GetEnvironmentLookupResolutions(nenv, ad, m, plid, filterCtors, residueOpt.IsSome)
let envResult =
match nameResItems, (envItems, denv, m), qualItems with
// First, use unfiltered name resolution items, if they're not empty
| NameResResult.Members(items, denv, m), _, _ when not (isNil items) ->
// lookup based on name resolution results successful
ValueSome(items |> List.map (CompletionItem (getType()) ValueNone), denv, m)
// If we have nonempty items from environment that were resolved from a type, then use them...
// (that's better than the next case - here we'd return 'int' as a type)
| _, FilterRelevantItems getItem exactMatchResidueOpt (items, denv, m), _ when not (isNil items) ->
// lookup based on name and environment successful
ValueSome(items |> List.map (CompletionItem (getType()) ValueNone), denv, m)
// Try again with the qualItems
| _, _, GetPreciseCompletionListFromExprTypingsResult.Some(FilterRelevantItems getItem exactMatchResidueOpt (items, denv, m), ty) ->
ValueSome(items |> List.map (CompletionItem (tryDestAppTy g ty) ValueNone), denv, m)
| _ -> ValueNone
let globalResult =
match origLongIdentOpt with
| None | Some [] ->
let globalItems =
allSymbols()
|> List.filter (fun x -> not x.Symbol.IsExplicitlySuppressed)
|> List.filter (fun x ->
match x.Symbol with
| :? FSharpMemberOrFunctionOrValue as m when m.IsConstructor && filterCtors = ResolveTypeNamesToTypeRefs -> false
| _ -> true)
let getItem (x: AssemblySymbol) = x.Symbol.Item
match globalItems, denv, m with
| FilterRelevantItems getItem exactMatchResidueOpt (globalItemsFiltered, denv, m) when not (isNil globalItemsFiltered) ->
globalItemsFiltered
|> List.map(fun globalItem -> CompletionItem (getType()) (ValueSome globalItem) (ItemWithNoInst globalItem.Symbol.Item))
|> fun r -> ValueSome(r, denv, m)
| _ -> ValueNone
| _ -> ValueNone // do not return unresolved items after dot
match envResult, globalResult with
| ValueSome (items, denv, m), ValueSome (gItems,_,_) -> Some (items @ gItems, denv, m)
| ValueSome x, ValueNone -> Some x
| ValueNone, ValueSome y -> Some y
| ValueNone, ValueNone -> None
let toCompletionItems (items: ItemWithInst list, denv: DisplayEnv, m: range ) =
items |> List.map DefaultCompletionItem, denv, m
/// Get the auto-complete items at a particular location.
let GetDeclItemsForNamesAtPosition(ctok: CompilationThreadToken, parseResultsOpt: FSharpParseFileResults option, origLongIdentOpt: string list option,
residueOpt:string option, lastDotPos: int option, line:int, lineStr:string, colAtEndOfNamesAndResidue, filterCtors, resolveOverloads,
getAllSymbols: unit -> AssemblySymbol list, hasTextChangedSinceLastTypecheck: (obj * range -> bool))
: (CompletionItem list * DisplayEnv * CompletionContext option * range) option =
RequireCompilationThread ctok // the operations in this method need the reactor thread
let loc =
match colAtEndOfNamesAndResidue with
| pastEndOfLine when pastEndOfLine >= lineStr.Length -> lineStr.Length
| atDot when lineStr.[atDot] = '.' -> atDot + 1
| atStart when atStart = 0 -> 0
| otherwise -> otherwise - 1
// Look for a "special" completion context
let completionContext =
parseResultsOpt
|> Option.bind (fun x -> x.ParseTree)
|> Option.bind (fun parseTree -> UntypedParseImpl.TryGetCompletionContext(mkPos line colAtEndOfNamesAndResidue, parseTree, lineStr))
let res =
match completionContext with
// Invalid completion locations
| Some CompletionContext.Invalid -> None
// Completion at 'inherit C(...)"
| Some (CompletionContext.Inherit(InheritanceContext.Class, (plid, _))) ->
GetEnvironmentLookupResolutionsAtPosition(mkPos line loc, plid, filterCtors, false)
|> FilterRelevantItemsBy getItem None (getItem >> GetBaseClassCandidates)
|> Option.map toCompletionItems
// Completion at 'interface ..."
| Some (CompletionContext.Inherit(InheritanceContext.Interface, (plid, _))) ->
GetEnvironmentLookupResolutionsAtPosition(mkPos line loc, plid, filterCtors, false)
|> FilterRelevantItemsBy getItem None (getItem >> GetInterfaceCandidates)
|> Option.map toCompletionItems
// Completion at 'implement ..."
| Some (CompletionContext.Inherit(InheritanceContext.Unknown, (plid, _))) ->
GetEnvironmentLookupResolutionsAtPosition(mkPos line loc, plid, filterCtors, false)
|> FilterRelevantItemsBy getItem None (getItem >> (fun t -> GetBaseClassCandidates t || GetInterfaceCandidates t))
|> Option.map toCompletionItems
// Completion at ' { XXX = ... } "
| Some(CompletionContext.RecordField(RecordContext.New(plid, _))) ->
// { x. } can be either record construction or computation expression. Try to get all visible record fields first
match GetClassOrRecordFieldsEnvironmentLookupResolutions(mkPos line loc, plid) |> toCompletionItems with
| [],_,_ ->
// no record fields found, return completion list as if we were outside any computation expression
GetDeclaredItems (parseResultsOpt, lineStr, origLongIdentOpt, colAtEndOfNamesAndResidue, residueOpt, lastDotPos, line, loc, filterCtors,resolveOverloads, hasTextChangedSinceLastTypecheck, false, fun() -> [])
| result -> Some(result)
// Completion at ' { XXX = ... with ... } "
| Some(CompletionContext.RecordField(RecordContext.CopyOnUpdate(r, (plid, _)))) ->
match GetRecdFieldsForExpr(r) with
| None ->
Some (GetClassOrRecordFieldsEnvironmentLookupResolutions(mkPos line loc, plid))
|> Option.map toCompletionItems
| Some (items, denv, m) ->
Some (List.map ItemWithNoInst items, denv, m)
|> Option.map toCompletionItems
// Completion at ' { XXX = ... with ... } "
| Some(CompletionContext.RecordField(RecordContext.Constructor(typeName))) ->
Some(GetClassOrRecordFieldsEnvironmentLookupResolutions(mkPos line loc, [typeName]))
|> Option.map toCompletionItems
// Completion at ' SomeMethod( ... ) ' with named arguments
| Some(CompletionContext.ParameterList (endPos, fields)) ->
let results = GetNamedParametersAndSettableFields endPos hasTextChangedSinceLastTypecheck
let declaredItems =
GetDeclaredItems (parseResultsOpt, lineStr, origLongIdentOpt, colAtEndOfNamesAndResidue, residueOpt, lastDotPos, line, loc, filterCtors, resolveOverloads,
hasTextChangedSinceLastTypecheck, false, getAllSymbols)
match results with
| NameResResult.Members(items, denv, m) ->
let filtered =
items
|> RemoveDuplicateItems g
|> RemoveExplicitlySuppressed g
|> List.filter (fun item -> not (fields.Contains item.Item.DisplayName))
|> List.map (fun item ->
{ ItemWithInst = item
Kind = CompletionItemKind.Argument
MinorPriority = 0
IsOwnMember = false
Type = None
Unresolved = None })
match declaredItems with
| None -> Some (toCompletionItems (items, denv, m))
| Some (declItems, declaredDisplayEnv, declaredRange) -> Some (filtered @ declItems, declaredDisplayEnv, declaredRange)
| _ -> declaredItems
| Some(CompletionContext.AttributeApplication) ->
GetDeclaredItems (parseResultsOpt, lineStr, origLongIdentOpt, colAtEndOfNamesAndResidue, residueOpt, lastDotPos, line, loc, filterCtors, resolveOverloads, hasTextChangedSinceLastTypecheck, false, getAllSymbols)
|> Option.map (fun (items, denv, m) ->
items
|> List.filter (fun cItem ->
match cItem.Item with
| Item.ModuleOrNamespaces _ -> true
| _ when IsAttribute infoReader cItem.Item -> true
| _ -> false), denv, m)
| Some(CompletionContext.OpenDeclaration) ->
GetDeclaredItems (parseResultsOpt, lineStr, origLongIdentOpt, colAtEndOfNamesAndResidue, residueOpt, lastDotPos, line, loc, filterCtors, resolveOverloads, hasTextChangedSinceLastTypecheck, false, getAllSymbols)
|> Option.map (fun (items, denv, m) ->
items |> List.filter (fun x -> match x.Item with Item.ModuleOrNamespaces _ -> true | _ -> false), denv, m)
// Completion at '(x: ...)"
| Some (CompletionContext.PatternType) ->
GetDeclaredItems (parseResultsOpt, lineStr, origLongIdentOpt, colAtEndOfNamesAndResidue, residueOpt, lastDotPos, line, loc, filterCtors, resolveOverloads, hasTextChangedSinceLastTypecheck, false, getAllSymbols)
|> Option.map (fun (items, denv, m) ->
items
|> List.filter (fun cItem ->
match cItem.Item with
| Item.ModuleOrNamespaces _
| Item.Types _
| Item.UnqualifiedType _
| Item.ExnCase _ -> true
| _ -> false), denv, m)
// Other completions
| cc ->
match residueOpt |> Option.bind Seq.tryHead with
| Some ''' ->
// The last token in
// let x = 'E
// is Ident with text "'E", however it's either unfinished char literal or generic parameter.
// We should not provide any completion in the former case, and we don't provide it for the latter one for now
// because providing generic parameters list is context aware, which we don't have here (yet).
None
| _ ->
let isInRangeOperator = (match cc with Some (CompletionContext.RangeOperator) -> true | _ -> false)
GetDeclaredItems (parseResultsOpt, lineStr, origLongIdentOpt, colAtEndOfNamesAndResidue, residueOpt, lastDotPos, line, loc, filterCtors,resolveOverloads, hasTextChangedSinceLastTypecheck, isInRangeOperator, getAllSymbols)
res |> Option.map (fun (items, denv, m) -> items, denv, completionContext, m)
/// Return 'false' if this is not a completion item valid in an interface file.
let IsValidSignatureFileItem item =
match item with
| Item.Types _ | Item.ModuleOrNamespaces _ -> true
| _ -> false
/// Find the most precise display context for the given line and column.
member __.GetBestDisplayEnvForPos cursorPos = GetBestEnvForPos cursorPos
member __.GetVisibleNamespacesAndModulesAtPosition(cursorPos: pos) : ModuleOrNamespaceRef list =
let (nenv, ad), m = GetBestEnvForPos cursorPos
NameResolution.GetVisibleNamespacesAndModulesAtPoint ncenv nenv m ad
/// Determines if a long ident is resolvable at a specific point.
member __.IsRelativeNameResolvable(cursorPos: pos, plid: string list, item: Item) : bool =
ErrorScope.Protect
Range.range0
(fun () ->
/// Find items in the best naming environment.
let (nenv, ad), m = GetBestEnvForPos cursorPos
NameResolution.IsItemResolvable ncenv nenv m ad plid item)
(fun msg ->
Trace.TraceInformation(sprintf "FCS: recovering from error in IsRelativeNameResolvable: '%s'" msg)
false)
/// Determines if a long ident is resolvable at a specific point.
member scope.IsRelativeNameResolvableFromSymbol(cursorPos: pos, plid: string list, symbol: FSharpSymbol) : bool =
scope.IsRelativeNameResolvable(cursorPos, plid, symbol.Item)
/// Get the auto-complete items at a location
member __.GetDeclarations (ctok, parseResultsOpt, line, lineStr, partialName, getAllEntities, hasTextChangedSinceLastTypecheck) =
let isInterfaceFile = SourceFileImpl.IsInterfaceFile mainInputFileName
ErrorScope.Protect Range.range0
(fun () ->
match GetDeclItemsForNamesAtPosition(ctok, parseResultsOpt, Some partialName.QualifyingIdents, Some partialName.PartialIdent, partialName.LastDotPos, line, lineStr, partialName.EndColumn + 1, ResolveTypeNamesToCtors, ResolveOverloads.Yes, getAllEntities, hasTextChangedSinceLastTypecheck) with
| None -> FSharpDeclarationListInfo.Empty
| Some (items, denv, ctx, m) ->
let items = if isInterfaceFile then items |> List.filter (fun x -> IsValidSignatureFileItem x.Item) else items
let getAccessibility item = FSharpSymbol.GetAccessibility (FSharpSymbol.Create(cenv, item))
let currentNamespaceOrModule =
parseResultsOpt
|> Option.bind (fun x -> x.ParseTree)
|> Option.map (fun parsedInput -> UntypedParseImpl.GetFullNameOfSmallestModuleOrNamespaceAtPoint(parsedInput, mkPos line 0))
let isAttributeApplication = ctx = Some CompletionContext.AttributeApplication
FSharpDeclarationListInfo.Create(infoReader,m,denv,getAccessibility,items,reactorOps,currentNamespaceOrModule,isAttributeApplication,checkAlive))
(fun msg ->
Trace.TraceInformation(sprintf "FCS: recovering from error in GetDeclarations: '%s'" msg)
FSharpDeclarationListInfo.Error msg)
/// Get the symbols for auto-complete items at a location
member __.GetDeclarationListSymbols (ctok, parseResultsOpt, line, lineStr, partialName, getAllEntities, hasTextChangedSinceLastTypecheck) =
let isInterfaceFile = SourceFileImpl.IsInterfaceFile mainInputFileName
ErrorScope.Protect Range.range0
(fun () ->
match GetDeclItemsForNamesAtPosition(ctok, parseResultsOpt, Some partialName.QualifyingIdents, Some partialName.PartialIdent, partialName.LastDotPos, line, lineStr, partialName.EndColumn + 1, ResolveTypeNamesToCtors, ResolveOverloads.Yes, getAllEntities, hasTextChangedSinceLastTypecheck) with
| None -> List.Empty
| Some (items, denv, _, m) ->
let items = if isInterfaceFile then items |> List.filter (fun x -> IsValidSignatureFileItem x.Item) else items
//do filtering like Declarationset
let items = items |> RemoveExplicitlySuppressedCompletionItems g
// Sort by name. For things with the same name,
// - show types with fewer generic parameters first
// - show types before over other related items - they usually have very useful XmlDocs
let items =
items |> List.sortBy (fun d ->
let n =
match d.Item with
| Item.Types (_,(TType_app(tcref,_) :: _)) -> 1 + tcref.TyparsNoRange.Length
// Put delegate ctors after types, sorted by #typars. RemoveDuplicateItems will remove FakeInterfaceCtor and DelegateCtor if an earlier type is also reported with this name
| Item.FakeInterfaceCtor (TType_app(tcref,_))
| Item.DelegateCtor (TType_app(tcref,_)) -> 1000 + tcref.TyparsNoRange.Length
// Put type ctors after types, sorted by #typars. RemoveDuplicateItems will remove DefaultStructCtors if a type is also reported with this name
| Item.CtorGroup (_, (cinfo :: _)) -> 1000 + 10 * cinfo.DeclaringTyconRef.TyparsNoRange.Length
| _ -> 0
(d.Item.DisplayName,n))
// Remove all duplicates. We've put the types first, so this removes the DelegateCtor and DefaultStructCtor's.
let items = items |> RemoveDuplicateCompletionItems g
// Group by compiled name for types, display name for functions
// (We don't want types with the same display name to be grouped as overloads)
let items =
items |> List.groupBy (fun d ->
match d.Item with
| Item.Types (_,(TType_app(tcref,_) :: _))
| Item.ExnCase tcref -> tcref.LogicalName
| Item.UnqualifiedType(tcref :: _)
| Item.FakeInterfaceCtor (TType_app(tcref,_))
| Item.DelegateCtor (TType_app(tcref,_)) -> tcref.CompiledName
| Item.CtorGroup (_, (cinfo :: _)) ->
cinfo.ApparentEnclosingTyconRef.CompiledName
| _ -> d.Item.DisplayName)
// Filter out operators (and list)
let items =
// Check whether this item looks like an operator.
let isOpItem(nm, item: CompletionItem list) =
match item |> List.map (fun x -> x.Item) with
| [Item.Value _]
| [Item.MethodGroup(_,[_],_)] -> IsOperatorName nm