@@ -4,6 +4,7 @@ open System
44open System.IO
55open Microsoft.FSharp .Compiler .SourceCodeServices
66open Utils
7+ open System.Collections .Concurrent
78
89type ParseAndCheckResults
910 (
@@ -51,7 +52,7 @@ type ParseAndCheckResults
5152 | None -> return Failure " Could not find ident at this location"
5253 | Some( col, identIsland) ->
5354
54- let! declarations = checkResults.GetDeclarationLocationAlternate( pos.Line, col + 1 , lineStr, identIsland, false )
55+ let! declarations = checkResults.GetDeclarationLocationAlternate( pos.Line, col, lineStr, identIsland, false )
5556
5657 match declarations with
5758 | FSharpFindDeclResult.DeclNotFound _ -> return Failure " Could not find declaration"
@@ -64,7 +65,7 @@ type ParseAndCheckResults
6465 | Some( col, identIsland) ->
6566
6667 // TODO: Display other tooltip types, for example for strings or comments where appropriate
67- let! tip = checkResults.GetToolTipTextAlternate( pos.Line, col + 1 , lineStr, identIsland, FSharpTokenTag.Identifier)
68+ let! tip = checkResults.GetToolTipTextAlternate( pos.Line, col, lineStr, identIsland, FSharpTokenTag.Identifier)
6869
6970 match tip with
7071 | FSharpToolTipText( elems) when elems |> List.forall ( function
@@ -79,7 +80,7 @@ type ParseAndCheckResults
7980 | None -> return ( Failure " No ident at this location" )
8081 | Some( colu, identIsland) ->
8182
82- let! symboluse = checkResults.GetSymbolUseAtLocation( pos.Line, colu + 1 , lineStr, identIsland)
83+ let! symboluse = checkResults.GetSymbolUseAtLocation( pos.Line, colu, lineStr, identIsland)
8384 match symboluse with
8485 | None -> return ( Failure " No symbol information found" )
8586 | Some symboluse ->
@@ -105,14 +106,50 @@ type ParseAndCheckResults
105106 member __.GetAST = parseResults.ParseTree
106107 member __.GetCheckResults = checkResults
107108
109+ type private FileState =
110+ | Checked
111+ | NeedChecking
112+ | BeingChecked
113+ | Cancelled
114+
115+ type Version = int
116+
108117type FSharpCompilerServiceChecker () =
109118 let checker =
110119 FSharpChecker.Create(
111120 projectCacheSize = 200 ,
112121 keepAllBackgroundResolutions = true ,
113122 keepAssemblyContents = true )
114123
115- do checker.BeforeBackgroundFileCheck.Add ( fun _ -> ())
124+ let files = ConcurrentDictionary< string, Version * FileState>()
125+ do checker.BeforeBackgroundFileCheck.Add ignore
126+
127+ let isResultObsolete fileName =
128+ match files.TryGetValue fileName with
129+ | true , (_, Cancelled) -> true
130+ | _ -> false
131+
132+ let fileChanged filePath version =
133+ files.AddOrUpdate ( filePath, ( version, NeedChecking), ( fun _ ( oldVersion , oldState ) ->
134+ if version <> oldVersion then
135+ ( version,
136+ match oldState with
137+ | BeingChecked -> Cancelled
138+ | Cancelled -> Cancelled
139+ | NeedChecking -> NeedChecking
140+ | Checked -> NeedChecking)
141+ else oldVersion, oldState))
142+ |> debug " [LanguageService] %s changed: set status to %A " filePath
143+
144+
145+ let fixFileName path =
146+ if ( try Path.GetFullPath path |> ignore; true with _ -> false ) then path
147+ else
148+ match Environment.OSVersion.Platform with
149+ | PlatformID.Unix
150+ | PlatformID.MacOSX -> Environment.GetEnvironmentVariable " HOME"
151+ | _ -> Environment.ExpandEnvironmentVariables " %HOMEDRIVE%%HOMEPATH%"
152+ </> Path.GetFileName path
116153
117154 let ensureCorrectFSharpCore ( options : string []) =
118155 Environment.fsharpCoreOpt
@@ -187,8 +224,42 @@ type FSharpCompilerServiceChecker() =
187224 return res |> Array.collect id
188225 }
189226
190- member __.ParseAndCheckFileInProject ( fileName , version , source , options ) =
191- checker.ParseAndCheckFileInProject( fileName, version, source, options)
227+ member __.ParseAndCheckFileInProject ( filePath , version , source , options ) =
228+ async {
229+ debug " [LanguageService] ParseAndCheckFileInProject - enter"
230+ fileChanged filePath version
231+ let fixedFilePath = fixFileName filePath
232+ let! res = Async.Catch ( async {
233+ try
234+ // wait until the previous checking completed
235+ while files.ContainsKey filePath &&
236+ ( match files.TryGetValue filePath with
237+ | true , ( v, Checked)
238+ | true , ( v, NeedChecking) ->
239+ files.[ filePath] <- ( v, BeingChecked)
240+ true
241+ | _ -> false ) do
242+ do ! Async.Sleep 20
243+
244+ debug " [LanguageService] Change state for %s to `BeingChecked`" filePath
245+ debug " [LanguageService] Parse and typecheck source..."
246+ return ! checker.ParseAndCheckFileInProject
247+ ( fixedFilePath, version, source, options,
248+ IsResultObsolete ( fun _ -> isResultObsolete filePath), null )
249+ finally
250+ match files.TryGetValue filePath with
251+ | true , ( v, BeingChecked)
252+ | true , ( v, Cancelled) -> files.[ filePath] <- ( v, Checked)
253+ | _ -> ()
254+ })
255+
256+ debug " [LanguageService]: Check completed"
257+ // Construct new typed parse result if the task succeeded
258+ return
259+ match res with
260+ | Choice1Of2 x -> Success x
261+ | Choice2Of2 e -> Failure e.Message
262+ }
192263
193264 member __.TryGetRecentCheckResultsForFile ( file , options , ? source ) =
194265 checker.TryGetRecentCheckResultsForFile( file, options, ?source= source)
@@ -207,8 +278,7 @@ type FSharpCompilerServiceChecker() =
207278 return !
208279 options
209280 |> Seq.filter ( fun ( _ , projectOpts ) -> projectOpts = opts)
210- |> Seq.map fst
211- |> Seq.map ( fun projectFile -> async {
281+ |> Seq.map ( fun ( projectFile , _ ) -> async {
212282 let! parseRes , _ = checker.GetBackgroundCheckResultsForFileInProject( projectFile, opts)
213283 return ( parseRes.GetNavigationItems() .Declarations |> Array.map ( fun decl -> decl, projectFile))
214284 })
@@ -231,11 +301,19 @@ type FSharpCompilerServiceChecker() =
231301 p.OtherOptions
232302 { p with OtherOptions = opts }, logMap
233303
234- let compileFiles = Seq.filter ( fun ( s : string ) -> s.EndsWith( " .fs" )) po.OtherOptions
304+ let po =
305+ match po.ProjectFileNames with
306+ | [||] ->
307+ let compileFiles , otherOptions =
308+ po.OtherOptions |> Array.partition ( fun ( s : string ) -> s.EndsWith( " .fs" ))
309+ { po with ProjectFileNames = compileFiles; OtherOptions = otherOptions }
310+ | _ -> po
311+
312+ let po = { po with ProjectFileNames = po.ProjectFileNames |> Array.map normalizeDirSeparators }
235313 let outputFile = Seq.tryPick ( chooseByPrefix " --out:" ) po.OtherOptions
236314 let references = Seq.choose ( chooseByPrefix " -r:" ) po.OtherOptions
237315
238- Success ( po, Seq .toList compileFiles , outputFile, Seq.toList references, logMap)
316+ Success ( po, Array .toList po.ProjectFileNames , outputFile, Seq.toList references, logMap)
239317 with e ->
240318 Failure e.Message
241319
0 commit comments