@@ -83,185 +83,174 @@ module internal Main =
8383
8484 let commandQueue = new FSharpx.Control.BlockingQueueAgent< Command>( 10 )
8585
86- let rec main ( state : State ) : int =
87- currentFiles <- state.Files
88-
89- try
90- match commandQueue.Get() with
91- | Parse( file, kind, lines) ->
92- let parse fileName text options =
93- let task =
94- async {
95- let! _parseResults , checkResults = checker.ParseAndCheckFileInProject( fileName, 0 , text, options)
96- match checkResults with
97- | FSharpCheckFileAnswer.Aborted -> ()
98- | FSharpCheckFileAnswer.Succeeded results ->
99- Response.errors( results.Errors)
100- if state.ColorizationOutput then
101- Response.colorizations( results.GetExtraColorizationsAlternate())
102- }
103- match kind with
104- | Synchronous -> Response.info " Synchronous parsing started"
105- Async.RunSynchronously task
106- | Normal -> Response.info " Background parsing started"
107- Async.StartImmediate task
108-
109- let file = Path.GetFullPath file
110- let text = String.concat " \n " lines
111-
112- if Utils.isAScript file then
113- let checkOptions = checker.GetProjectOptionsFromScript( file, text)
114- let state = state.WithFileTextAndCheckerOptions( file, lines, checkOptions)
115- parse file text checkOptions
116- main state
117- else
118- let state , checkOptions = state.WithFileTextGetCheckerOptions( file, lines)
119- parse file text checkOptions
120- main state
121-
122- | Project ( file, time) ->
123- let file = Path.GetFullPath file
124-
125- // The FileSystemWatcher often triggers multiple times for
126- // each event, as editors often modify files in several steps.
127- // This 'debounces' the events, by only reloading a max of once
128- // per second.
129- match state.ProjectLoadTimes.TryFind file with
130- | Some oldtime when time - oldtime < TimeSpan.FromSeconds( 1.0 ) -> main state
131- | _ ->
132-
133- match checker.TryGetProjectOptions( file) with
134- | Result.Failure s ->
135- Response.error( s)
136- main state
137-
138- | Result.Success( po, projectFiles, outFileOpt, references, frameworkOpt) ->
139- Response.project( file, projectFiles, outFileOpt, references, frameworkOpt)
140-
141- let fsw = new FileSystemWatcher()
142- fsw.Path <- Path.GetDirectoryName file
143- fsw.Filter <- Path.GetFileName file
144- fsw.Changed.Add( fun _ -> commandQueue.Add( Project ( file, DateTime.Now)))
145- fsw.EnableRaisingEvents <- true
146-
147- let checkOptions =
148- projectFiles
149- |> List.fold ( fun s f -> Map.add f po s) state.FileCheckOptions
150- let loadTimes = Map.add file time state.ProjectLoadTimes
151- main { state with FileCheckOptions = checkOptions
152- ProjectLoadTimes = loadTimes }
153-
154- | Declarations file ->
155- let file = Path.GetFullPath file
156- match state.TryGetFileCheckerOptionsWithSource( file) with
157- | Failure s -> Response.error( s)
158- | Success ( checkOptions, source) ->
159- let decls = checker.GetDeclarations( file, source, checkOptions)
160- Response.declarations( decls)
161-
162- main state
163-
164- | HelpText sym ->
165- match Map.tryFind sym state.HelpText with
166- | None -> Response.error ( sprintf " No help text available for symbol '%s '" sym)
167- | Some tip -> Response.helpText( sym, tip)
168-
169- main state
170-
171- | PosCommand( cmd, file, line, col, timeout, filter) ->
172- let file = Path.GetFullPath file
173- match state.TryGetFileCheckerOptionsWithLinesAndLineStr( file, line, col) with
174- | Failure s -> Response.error( s)
175- main state
176- | Success ( options, lines, lineStr) ->
177- // TODO: Should sometimes pass options.Source in here to force a reparse
178- // for completions e.g. `(some typed expr).$`
179- let tyResOpt = checker.TryGetRecentTypeCheckResultsForFile( file, options)
180- match tyResOpt with
181- | None -> Response.info " Cached typecheck results not yet available" ; main state
182- | Some tyRes ->
183-
184- match cmd with
185- | Completion ->
186- match tyRes.TryGetCompletions line col lineStr timeout filter with
187- | Some ( decls, residue) ->
188- let declName ( d : FSharpDeclarationListItem ) = d.Name
189-
190- // Send the first helptext without being requested.
191- // This allows it to be displayed immediately in the editor.
192- let firstMatchOpt =
193- Array.sortBy declName decls
194- |> Array.tryFind ( fun d -> ( declName d) .StartsWith residue)
195- match firstMatchOpt with
196- | None -> ()
197- | Some d -> Response.helpText( d.Name, d.DescriptionText)
198-
199- Response.completion( decls)
200-
201- let helptext =
202- Seq.fold ( fun m d -> Map.add ( declName d) d.DescriptionText m) Map.empty decls
203- main { state with HelpText = helptext }
204-
205- | None ->
206- Response.error " Timed out while fetching completions"
207- main state
208-
209- | ToolTip ->
210- // A failure is only info here, as this command is expected to be
211- // used 'on idle', and frequent errors are expected.
212- match tyRes.TryGetToolTip line col lineStr with
213- | Result.Failure s -> Response.info( s)
214- | Result.Success tip -> Response.toolTip( tip)
215-
216- main state
217-
218- | SymbolUse ->
219- // A failure is only info here, as this command is expected to be
220- // used 'on idle', and frequent errors are expected.
221- match tyRes.TryGetSymbolUse line col lineStr with
222- | Result.Failure s -> Response.info( s)
223- | Result.Success ( sym, usages) -> Response.symbolUse( sym, usages)
224-
225- main state
226-
227- | FindDeclaration ->
228- match tyRes.TryFindDeclaration line col lineStr with
229- | Result.Failure s -> Response.error s
230- | Result.Success range -> Response.findDeclaration( range)
231-
232- main state
233-
234- | Methods ->
235- match tyRes.TryGetMethodOverrides lines line col with
236- | Result.Failure s -> Response.error s
237- | Result.Success ( meth, commas) -> Response.methods( meth, commas)
238-
239- main state
240-
241- | CompilerLocation ->
242- Response.compilerLocation Environment.fsc Environment.fsi Environment.msbuild
243- main state
244-
245- | Colorization enabled ->
246- main { state with ColorizationOutput = enabled }
247-
248- | Error( msg) ->
249- Response.error msg
250- main state
251-
252- | Quit ->
253- 0
254-
255- with e ->
256- let msg = " Unexpected internal error. Please report at \
257- https://github.com/fsharp/FsAutoComplete/issues, \
258- attaching the exception information:\n "
259- + e.ToString()
260- Response.error msg
261- main state
86+ let main ( state : State ) : int =
87+ let mutable state = state
88+ let mutable quit = false
89+
90+ while not quit do
91+ currentFiles <- state.Files
92+ try
93+ match commandQueue.Get() with
94+ | Parse( file, kind, lines) ->
95+ let colorizations = state.ColorizationOutput
96+ let parse fileName text options =
97+ let task =
98+ async {
99+ let! _parseResults , checkResults = checker.ParseAndCheckFileInProject( fileName, 0 , text, options)
100+ match checkResults with
101+ | FSharpCheckFileAnswer.Aborted -> ()
102+ | FSharpCheckFileAnswer.Succeeded results ->
103+ Response.errors( results.Errors)
104+ if colorizations then
105+ Response.colorizations( results.GetExtraColorizationsAlternate())
106+ }
107+ match kind with
108+ | Synchronous -> Response.info " Synchronous parsing started"
109+ Async.RunSynchronously task
110+ | Normal -> Response.info " Background parsing started"
111+ Async.StartImmediate task
112+
113+ let file = Path.GetFullPath file
114+ let text = String.concat " \n " lines
115+
116+ if Utils.isAScript file then
117+ let checkOptions = checker.GetProjectOptionsFromScript( file, text)
118+ parse file text checkOptions
119+ state <- state.WithFileTextAndCheckerOptions( file, lines, checkOptions)
120+ else
121+ let state ' , checkOptions = state.WithFileTextGetCheckerOptions( file, lines)
122+ parse file text checkOptions
123+ state <- state'
124+
125+ | Project ( file, time) ->
126+ let file = Path.GetFullPath file
127+
128+ // The FileSystemWatcher often triggers multiple times for
129+ // each event, as editors often modify files in several steps.
130+ // This 'debounces' the events, by only reloading a max of once
131+ // per second.
132+ match state.ProjectLoadTimes.TryFind file with
133+ | Some oldtime when time - oldtime < TimeSpan.FromSeconds( 1.0 ) -> ()
134+ | _ ->
135+
136+ match checker.TryGetProjectOptions( file) with
137+ | Result.Failure s ->
138+ Response.error( s)
139+
140+ | Result.Success( po, projectFiles, outFileOpt, references, frameworkOpt) ->
141+ Response.project( file, projectFiles, outFileOpt, references, frameworkOpt)
142+
143+ let fsw = new FileSystemWatcher()
144+ fsw.Path <- Path.GetDirectoryName file
145+ fsw.Filter <- Path.GetFileName file
146+ fsw.Changed.Add( fun _ -> commandQueue.Add( Project ( file, DateTime.Now)))
147+ fsw.EnableRaisingEvents <- true
148+
149+ let checkOptions =
150+ projectFiles
151+ |> List.fold ( fun s f -> Map.add f po s) state.FileCheckOptions
152+ let loadTimes = Map.add file time state.ProjectLoadTimes
153+ state <- { state with FileCheckOptions = checkOptions
154+ ProjectLoadTimes = loadTimes }
155+
156+ | Declarations file ->
157+ let file = Path.GetFullPath file
158+ match state.TryGetFileCheckerOptionsWithSource( file) with
159+ | Failure s -> Response.error( s)
160+ | Success ( checkOptions, source) ->
161+ let decls = checker.GetDeclarations( file, source, checkOptions)
162+ Response.declarations( decls)
163+
164+ | HelpText sym ->
165+ match Map.tryFind sym state.HelpText with
166+ | None -> Response.error ( sprintf " No help text available for symbol '%s '" sym)
167+ | Some tip -> Response.helpText( sym, tip)
168+
169+ | PosCommand( cmd, file, line, col, timeout, filter) ->
170+ let file = Path.GetFullPath file
171+ match state.TryGetFileCheckerOptionsWithLinesAndLineStr( file, line, col) with
172+ | Failure s -> Response.error( s)
173+ | Success ( options, lines, lineStr) ->
174+ // TODO: Should sometimes pass options.Source in here to force a reparse
175+ // for completions e.g. `(some typed expr).$`
176+ let tyResOpt = checker.TryGetRecentTypeCheckResultsForFile( file, options)
177+ match tyResOpt with
178+ | None -> Response.info " Cached typecheck results not yet available"
179+ | Some tyRes ->
180+
181+ match cmd with
182+ | Completion ->
183+ match tyRes.TryGetCompletions line col lineStr timeout filter with
184+ | Some ( decls, residue) ->
185+ let declName ( d : FSharpDeclarationListItem ) = d.Name
186+
187+ // Send the first helptext without being requested.
188+ // This allows it to be displayed immediately in the editor.
189+ let firstMatchOpt =
190+ Array.sortBy declName decls
191+ |> Array.tryFind ( fun d -> ( declName d) .StartsWith residue)
192+ match firstMatchOpt with
193+ | None -> ()
194+ | Some d -> Response.helpText( d.Name, d.DescriptionText)
195+
196+ Response.completion( decls)
197+
198+ let helptext =
199+ Seq.fold ( fun m d -> Map.add ( declName d) d.DescriptionText m) Map.empty decls
200+ state <- { state with HelpText = helptext }
201+
202+ | None ->
203+ Response.error " Timed out while fetching completions"
204+
205+ | ToolTip ->
206+ // A failure is only info here, as this command is expected to be
207+ // used 'on idle', and frequent errors are expected.
208+ match tyRes.TryGetToolTip line col lineStr with
209+ | Result.Failure s -> Response.info( s)
210+ | Result.Success tip -> Response.toolTip( tip)
211+
212+ | SymbolUse ->
213+ // A failure is only info here, as this command is expected to be
214+ // used 'on idle', and frequent errors are expected.
215+ match tyRes.TryGetSymbolUse line col lineStr with
216+ | Result.Failure s -> Response.info( s)
217+ | Result.Success ( sym, usages) -> Response.symbolUse( sym, usages)
218+
219+ | FindDeclaration ->
220+ match tyRes.TryFindDeclaration line col lineStr with
221+ | Result.Failure s -> Response.error s
222+ | Result.Success range -> Response.findDeclaration( range)
223+
224+ | Methods ->
225+ match tyRes.TryGetMethodOverrides lines line col with
226+ | Result.Failure s -> Response.error s
227+ | Result.Success ( meth, commas) -> Response.methods( meth, commas)
228+
229+ | CompilerLocation ->
230+ Response.compilerLocation Environment.fsc Environment.fsi Environment.msbuild
231+
232+ | Colorization enabled ->
233+ state <- { state with ColorizationOutput = enabled }
234+
235+ | Error( msg) ->
236+ Response.error msg
237+
238+ | Quit ->
239+ quit <- true
240+
241+ with e ->
242+ let msg = " Unexpected internal error. Please report at \
243+ https://github.com/fsharp/FsAutoComplete/issues, \
244+ attaching the exception information:\n "
245+ + e.ToString()
246+ Response.error msg
247+
248+ 0
262249
263250 [<EntryPoint>]
264251 let entry args =
252+ Console.InputEncoding <- Text.Encoding.UTF8
253+ Console.OutputEncoding <- Text.Encoding.UTF8
265254 let extra = Options.p.Parse args
266255 if extra.Count <> 0 then
267256 printfn " Unrecognised arguments: %s " ( String.concat " ," extra)
0 commit comments