@@ -4,17 +4,20 @@ open System
44open  FSharp.Compiler .CodeAnalysis  
55open  FSharp.Compiler .EditorServices  
66open  FSharp.Compiler .Text  
7+ open  FSharp.Compiler .Text .Range  
78open  FSharp.Compiler .Tokenization  
89
910type  SourceContext  = 
1011    {  Source:  string 
1112      LineText:  string 
12-       CaretPos:  pos  } 
13+       CaretPos:  pos 
14+       SelectedRange:  range option  } 
1315
1416type  ResolveContext  = 
1517    {  SourceContext:  SourceContext 
1618      Pos:  pos 
17-       Names:  string list  } 
19+       Names:  string list 
20+       SelectedRange:  range option  } 
1821
1922    member  this.Source  =  this.SourceContext.Source
2023    member  this.LineText  =  this.SourceContext.LineText
@@ -29,16 +32,82 @@ type CodeCompletionContext =
2932
3033[<RequireQualifiedAccess>] 
3134module  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+ 
3270    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 ) 
37109
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 } 
42111
43112
44113[<AutoOpen>] 
@@ -70,7 +139,7 @@ module Checker =
70139
71140        let  plid  =  QuickParse.GetPartialLongNameEx( context.LineText,  pos.Column -  1 ) 
72141        let  names  =  plid.QualifyingIdents @  [ plid.PartialIdent] 
73-         {  SourceContext =  context;  Pos =  pos;  Names =  names } 
142+         {  SourceContext =  context;  Pos =  pos;  Names =  names;  SelectedRange  =  context.SelectedRange  } 
74143
75144    let  getCompletionContext   ( markedSource :  string )  = 
76145        let  context  =  SourceContext.fromMarkedSource markedSource
0 commit comments