@@ -4,17 +4,20 @@ open System
4
4
open FSharp.Compiler .CodeAnalysis
5
5
open FSharp.Compiler .EditorServices
6
6
open FSharp.Compiler .Text
7
+ open FSharp.Compiler .Text .Range
7
8
open FSharp.Compiler .Tokenization
8
9
9
10
type SourceContext =
10
11
{ Source: string
11
12
LineText: string
12
- CaretPos: pos }
13
+ CaretPos: pos
14
+ SelectedRange: range option }
13
15
14
16
type ResolveContext =
15
17
{ SourceContext: SourceContext
16
18
Pos: pos
17
- Names: string list }
19
+ Names: string list
20
+ SelectedRange: range option }
18
21
19
22
member this.Source = this.SourceContext.Source
20
23
member this.LineText = this.SourceContext.LineText
@@ -29,16 +32,82 @@ type CodeCompletionContext =
29
32
30
33
[<RequireQualifiedAccess>]
31
34
module SourceContext =
35
+ let private markers = [ " {caret}" ; " {selstart}" ; " {selend}" ]
36
+
37
+ let getLines ( source : string ) =
38
+ source.Split([| " \r\n " ; " \n " |], StringSplitOptions.None)
39
+
40
+ let rec private extractMarkersOnLine markersAcc ( line , lineText : string ) =
41
+ let markersOnLine =
42
+ markers
43
+ |> List.choose ( fun ( marker : string ) ->
44
+ match lineText.IndexOf( marker) with
45
+ | - 1 -> None
46
+ | column -> Some( marker, column)
47
+ )
48
+
49
+ if markersOnLine.IsEmpty then
50
+ markersAcc
51
+ else
52
+ let marker , column = List.minBy snd markersOnLine
53
+
54
+ let markerPos =
55
+ let column =
56
+ match marker with
57
+ | " {caret}" -> column - 1
58
+ | _ -> column
59
+
60
+ Position.mkPos ( line + 1 ) column
61
+
62
+ if markersAcc |> List.map fst |> List.contains marker then
63
+ failwith $" Duplicate marker: {marker}"
64
+
65
+ let markersAcc = ( marker, markerPos) :: markersAcc
66
+ let lineText = lineText.Replace( marker, " " )
67
+
68
+ extractMarkersOnLine markersAcc ( line, lineText)
69
+
32
70
let fromMarkedSource ( markedSource : string ) : SourceContext =
33
- let lines = markedSource.Split([| " \r\n " ; " \n " |], StringSplitOptions.None)
34
- let line = lines |> Seq.findIndex _. Contains( " {caret}" )
35
- let lineText = lines[ line]
36
- let column = lineText.IndexOf( " {caret}" )
71
+ let markerPositions =
72
+ getLines markedSource
73
+ |> Seq.indexed
74
+ |> Seq.fold extractMarkersOnLine []
75
+
76
+ let source =
77
+ markerPositions
78
+ |> List.map fst
79
+ |> List.fold ( fun ( source : string ) marker -> source.Replace( marker, " " )) markedSource
80
+
81
+ let markerPositions = markerPositions |> dict
82
+
83
+ let tryGetPos marker =
84
+ match markerPositions.TryGetValue( marker) with
85
+ | true , pos -> Some pos
86
+ | _ -> None
87
+
88
+ let caretPos , selectedRange =
89
+ match tryGetPos " {caret}" , tryGetPos " {selstart}" , tryGetPos " {selend}" with
90
+ | Some caretPos, None, None ->
91
+ caretPos, None
92
+
93
+ | Some caretPos, Some startPos, Some endPos ->
94
+ let selectedRange = mkRange " Test.fsx" startPos endPos
95
+ caretPos, Some selectedRange
96
+
97
+ | None, Some startPos, Some endPos ->
98
+ let selectedRange = mkRange " Test.fsx" startPos endPos
99
+ let caretPos = Position.mkPos endPos.Line ( endPos.Column - 1 )
100
+ caretPos, Some selectedRange
101
+
102
+ | _, None, Some _ -> failwith " Missing selected range start"
103
+ | _, Some _, None -> failwith " Missing selected range end"
104
+
105
+ | None, None, None -> failwith " Missing caret marker"
106
+
107
+ let lines = getLines source
108
+ let lineText = Array.get lines ( caretPos.Line - 1 )
37
109
38
- let source = markedSource.Replace( " {caret}" , " " )
39
- let pos = Position.mkPos ( line + 1 ) ( column - 1 )
40
- let lineText = lineText.Replace( " {caret}" , " " )
41
- { Source = source; CaretPos = pos; LineText = lineText }
110
+ { Source = source; CaretPos = caretPos; LineText = lineText; SelectedRange = selectedRange }
42
111
43
112
44
113
[<AutoOpen>]
@@ -70,7 +139,7 @@ module Checker =
70
139
71
140
let plid = QuickParse.GetPartialLongNameEx( context.LineText, pos.Column - 1 )
72
141
let names = plid.QualifyingIdents @ [ plid.PartialIdent]
73
- { SourceContext = context; Pos = pos; Names = names }
142
+ { SourceContext = context; Pos = pos; Names = names; SelectedRange = context.SelectedRange }
74
143
75
144
let getCompletionContext ( markedSource : string ) =
76
145
let context = SourceContext.fromMarkedSource markedSource
0 commit comments