Skip to content

Commit e493678

Browse files
authored
Merge pull request #132 from ionide/master
Ionide/FsAutoComplete changes
2 parents bd75a8e + f9a979a commit e493678

File tree

23 files changed

+816
-492
lines changed

23 files changed

+816
-492
lines changed

paket.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ NUGET
88
System.Reflection.Metadata (>= 1.4.1-beta-24227-04)
99
FSharp.Compiler.Service.ProjectCracker (6.0.2)
1010
FSharp.Core (4.0.0.1) - redirects: on
11-
FSharpLint.Core (0.4.4)
11+
FSharpLint.Core (0.4.8)
1212
FParsec (>= 1.0.2)
1313
FSharp.Compiler.Service (>= 6.0)
1414
FSharp.Compiler.Service.ProjectCracker (>= 6.0)

src/FsAutoComplete.Core/CommandResponse.fs

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ module internal CompletionUtils =
2121
0x00011, ("Property", "P")
2222
0x0005, ("Event", "e")
2323
0x0007, ("Field", "F") (* fieldblue ? *)
24-
0x0020, ("Field", "F") (* fieldyellow ? *)
25-
0x0001, ("Field", "F") (* const *)
24+
0x0020, ("Field", "Fy") (* fieldyellow ? *)
25+
0x0001, ("Function", "Fc") (* const *)
2626
0x0004, ("Property", "P") (* enummember *)
2727
0x0006, ("Exception", "X") (* exception *)
2828
0x0009, ("Text File Icon", "t") (* TextLine *)
@@ -33,7 +33,7 @@ module internal CompletionUtils =
3333
0x00014, ("Class", "C") (* Typedef *)
3434
0x00015, ("Type", "T") (* Type *)
3535
0x00016, ("Type", "T") (* Union *)
36-
0x00017, ("Field", "F") (* Variable *)
36+
0x00017, ("Field", "V") (* Variable *)
3737
0x00019, ("Class", "C") (* Intrinsic *)
3838
0x0001f, ("Other", "o") (* error *)
3939
0x00021, ("Other", "o") (* Misc1 *)
@@ -56,7 +56,7 @@ module internal CompletionUtils =
5656
| FSharpEnclosingEntityKind.DU -> "D"
5757

5858
module CommandResponse =
59-
59+
6060
type ResponseMsg<'T> =
6161
{
6262
Kind: string
@@ -228,12 +228,24 @@ module CommandResponse =
228228
Logs = logMap }
229229
serialize { Kind = "project"; Data = projectData }
230230

231-
let completion (serialize : Serializer) (decls: FSharpDeclarationListItem[]) =
231+
let completion (serialize : Serializer) (decls: FSharpDeclarationListItem[]) includeKeywords =
232+
let keywords = ["abstract"; "and"; "as"; "assert"; "base"; "begin"; "class"; "default"; "delegate"; "do";
233+
"done"; "downcast"; "downto"; "elif"; "else"; "end"; "exception"; "extern"; "false"; "finally"; "for";
234+
"fun"; "function"; "global"; "if"; "in"; "inherit"; "inline"; "interface"; "internal"; "lazy"; "let";
235+
"match"; "member"; "module"; "mutable"; "namespace"; "new"; "null"; "of"; "open"; "or"; "override";
236+
"private"; "public"; "rec"; "return"; "sig"; "static"; "struct"; "then"; "to"; "true"; "try"; "type";
237+
"upcast"; "use"; "val"; "void"; "when"; "while"; "with"; "yield"
238+
]
239+
232240
serialize { Kind = "completion"
233241
Data = [ for d in decls do
234242
let code = Microsoft.FSharp.Compiler.SourceCodeServices.PrettyNaming.QuoteIdentifierIfNeeded d.Name
235243
let (glyph, glyphChar) = CompletionUtils.getIcon d.Glyph
236-
yield {CompletionResponse.Name = d.Name; ReplacementText = code; Glyph = glyph; GlyphChar = glyphChar } ] }
244+
yield {CompletionResponse.Name = d.Name; ReplacementText = code; Glyph = glyph; GlyphChar = glyphChar }
245+
if includeKeywords then
246+
for k in keywords do
247+
yield {CompletionResponse.Name = k; ReplacementText = k; Glyph = "Keyword"; GlyphChar = "K"}
248+
] }
237249

238250
let symbolUse (serialize : Serializer) (symbol: FSharpSymbolUse, uses: FSharpSymbolUse[]) =
239251
let su =

src/FsAutoComplete.Core/Commands.fs

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ module Response = CommandResponse
1010
type Commands (serialize : Serializer) =
1111
let checker = FSharpCompilerServiceChecker()
1212
let state = FsAutoComplete.State.Initial
13+
let fsharpLintConfig = ConfigurationManager.ConfigurationManager()
1314

1415
member private __.SerializeResultAsync (successToString: Serializer -> 'a -> Async<string>, ?failureToString: Serializer -> string -> string) =
1516
Async.bind <| function
@@ -26,19 +27,22 @@ type Commands (serialize : Serializer) =
2627
member __.TryGetFileCheckerOptionsWithLines = state.TryGetFileCheckerOptionsWithLines
2728
member __.Files = state.Files
2829

29-
member __.Parse file lines = async {
30+
member __.Parse file lines version = async {
3031
let colorizations = state.ColorizationOutput
3132
let parse' fileName text options =
3233
async {
33-
let! _parseResults, checkResults = checker.ParseAndCheckFileInProject(fileName, 0, text, options)
34+
let! result = checker.ParseAndCheckFileInProject(fileName, version, text, options)
3435
return
35-
match checkResults with
36-
| FSharpCheckFileAnswer.Aborted -> [Response.info serialize "Parse aborted"]
37-
| FSharpCheckFileAnswer.Succeeded results ->
38-
if colorizations then
39-
[ Response.errors serialize (results.Errors)
40-
Response.colorizations serialize (results.GetExtraColorizationsAlternate()) ]
41-
else [ Response.errors serialize (results.Errors) ]
36+
match result with
37+
| Failure e -> [Response.error serialize e]
38+
| Success (_, checkResults) ->
39+
match checkResults with
40+
| FSharpCheckFileAnswer.Aborted -> [Response.info serialize "Parse aborted"]
41+
| FSharpCheckFileAnswer.Succeeded results ->
42+
if colorizations then
43+
[ Response.errors serialize (results.Errors)
44+
Response.colorizations serialize (results.GetExtraColorizationsAlternate()) ]
45+
else [ Response.errors serialize (results.Errors) ]
4246
}
4347
let file = Path.GetFullPath file
4448
let text = String.concat "\n" lines
@@ -113,7 +117,7 @@ type Commands (serialize : Serializer) =
113117
member __.Colorization enabled = state.ColorizationOutput <- enabled
114118
member __.Error msg = [Response.error serialize msg]
115119

116-
member __.Completion (tyRes : ParseAndCheckResults) (pos: Pos) lineStr filter = async {
120+
member __.Completion (tyRes : ParseAndCheckResults) (pos: Pos) lineStr filter includeKeywords = async {
117121
let! res = tyRes.TryGetCompletions pos lineStr filter
118122
return match res with
119123
| Some (decls, residue) ->
@@ -125,10 +129,10 @@ type Commands (serialize : Serializer) =
125129
Array.sortBy declName decls
126130
|> Array.tryFind (fun d -> (declName d).StartsWith(residue, StringComparison.InvariantCultureIgnoreCase))
127131
let res = match firstMatchOpt with
128-
| None -> [Response.completion serialize decls]
132+
| None -> [Response.completion serialize decls includeKeywords]
129133
| Some d ->
130134
[Response.helpText serialize (d.Name, d.DescriptionText)
131-
Response.completion serialize decls]
135+
Response.completion serialize decls includeKeywords]
132136

133137
for decl in decls do
134138
state.HelpText.[declName decl] <- decl.DescriptionText
@@ -172,9 +176,11 @@ type Commands (serialize : Serializer) =
172176
match tyRes.GetAST with
173177
| None -> [ Response.info serialize "Something went wrong during parsing"]
174178
| Some tree ->
179+
fsharpLintConfig.LoadConfigurationForProject file
180+
let opts = fsharpLintConfig.GetConfigurationForProject (file)
175181
let res =
176182
Lint.lintParsedSource
177-
Lint.OptionalLintParameters.Default
183+
{ Lint.OptionalLintParameters.Default with Configuration = Some opts}
178184
{ Ast = tree
179185
Source = source
180186
TypeCheckResults = Some tyRes.GetCheckResults

src/FsAutoComplete.Core/CompilerServiceInterface.fs

Lines changed: 88 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ open System
44
open System.IO
55
open Microsoft.FSharp.Compiler.SourceCodeServices
66
open Utils
7+
open System.Collections.Concurrent
78

89
type 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+
108117
type 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

src/FsAutoComplete.Core/FsAutoComplete.Core.fsproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
-->
6767
<ItemGroup>
6868
<Compile Include="Utils.fs" />
69+
<Compile Include="Lexer.fs" />
6970
<Compile Include="Environment.fs" />
7071
<Compile Include="TipFormatter.fs" />
7172
<Compile Include="FileSystem.fs" />

0 commit comments

Comments
 (0)