Skip to content

Commit

Permalink
#1115 autocompletions (#1119)
Browse files Browse the repository at this point in the history
  • Loading branch information
TheAngryByrd authored Jul 1, 2023
1 parent 95fa929 commit c3ea94b
Show file tree
Hide file tree
Showing 6 changed files with 315 additions and 16 deletions.
2 changes: 1 addition & 1 deletion src/FsAutoComplete.Core/AdaptiveExtensions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ module AMap =
// else
AMap.ofReader (fun () -> BatchRecalculateDirty(map, mapping))

let mapWithAdditionalDependenies
let mapWithAdditionalDependencies
(mapping: HashMap<'K, 'T1> -> HashMap<'K, 'T2 * #seq<#IAdaptiveValue>>)
(map: amap<'K, 'T1>)
=
Expand Down
2 changes: 1 addition & 1 deletion src/FsAutoComplete.Core/ParseAndCheckResults.fs
Original file line number Diff line number Diff line change
Expand Up @@ -690,7 +690,7 @@ type ParseAndCheckResults
&& not results.IsForType
&& not results.IsError
&& List.isEmpty longName.QualifyingIdents
// Debug.waitForDebuggerAttachedAndBreak "--> TryGetCompletions"

return Some(sortedDecls, residue, shouldKeywords)
with :? TimeoutException ->
return None
Expand Down
4 changes: 4 additions & 0 deletions src/FsAutoComplete/LspHelpers.fs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ module FcsRange = FSharp.Compiler.Text.Range
type FcsPos = FSharp.Compiler.Text.Position
module FcsPos = FSharp.Compiler.Text.Position

module FcsPos =
let subtractColumn (pos: FcsPos) (column: int) =
FcsPos.mkPos pos.Line (pos.Column - column)

[<AutoOpen>]
module Conversions =
module Lsp = Ionide.LanguageServerProtocol.Types
Expand Down
52 changes: 38 additions & 14 deletions src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs
Original file line number Diff line number Diff line change
Expand Up @@ -700,7 +700,7 @@ type AdaptiveFSharpLspServer

let! projectOptions =
projects
|> AMap.mapWithAdditionalDependenies (fun projects ->
|> AMap.mapWithAdditionalDependencies (fun projects ->

projects
|> Seq.iter (fun (proj: string<LocalPath>, _) ->
Expand Down Expand Up @@ -2315,9 +2315,9 @@ type AdaptiveFSharpLspServer

let! volatileFile = forceFindOpenFileOrRead filePath |> AsyncResult.ofStringErr

let! lineStr2 = volatileFile.Source |> tryGetLineStr pos |> Result.ofStringErr
let! lineStr = volatileFile.Source |> tryGetLineStr pos |> Result.ofStringErr

if lineStr2.StartsWith "#" then
if lineStr.StartsWith "#" then
let completionList =
{ IsIncomplete = false
Items = KeywordList.hashSymbolCompletionItems }
Expand All @@ -2327,28 +2327,23 @@ type AdaptiveFSharpLspServer
else
let config = AVal.force config

let rec retryAsyncOption (delay: TimeSpan) timesLeft action =
let rec retryAsyncOption (delay: TimeSpan) timesLeft handleError action =
async {
match! action with
| Ok x -> return Ok x
| Error _ when timesLeft >= 0 ->
| Error e when timesLeft >= 0 ->
let nextAction = handleError e
do! Async.Sleep(delay)
return! retryAsyncOption delay (timesLeft - 1) action
return! retryAsyncOption delay (timesLeft - 1) handleError nextAction
| Error e -> return Error e
}

let getCompletions =
let getCompletions forceGetTypeCheckResultsStale =
asyncResult {

let! volatileFile = forceFindOpenFileOrRead filePath
let! lineStr = volatileFile.Source |> tryGetLineStr pos
and! typeCheckResults = forceGetTypeCheckResultsStale filePath

let getAllSymbols () =
if config.ExternalAutocomplete then
typeCheckResults.GetAllEntities true
else
[]
// TextDocumentCompletion will sometimes come in before TextDocumentDidChange
// This will require the trigger character to be at the place VSCode says it is
// Otherwise we'll fail here and our retry logic will come into place
Expand All @@ -2359,17 +2354,46 @@ type AdaptiveFSharpLspServer
| _ -> true
|> Result.requireTrue $"TextDocumentCompletion was sent before TextDocumentDidChange"

// Special characters like parentheses, brackets, etc. require a full type check
let isSpecialChar = Option.exists (Char.IsLetterOrDigit >> not)

let previousCharacter = volatileFile.Source.TryGetChar(FcsPos.subtractColumn pos 1)

let! typeCheckResults =
if isSpecialChar previousCharacter then
forceGetTypeCheckResults filePath
else
forceGetTypeCheckResultsStale filePath

let getAllSymbols () =
if config.ExternalAutocomplete then
typeCheckResults.GetAllEntities true
else
[]

let! (decls, residue, shouldKeywords) =
Debug.measure "TextDocumentCompletion.TryGetCompletions" (fun () ->
typeCheckResults.TryGetCompletions pos lineStr None getAllSymbols
|> AsyncResult.ofOption (fun () -> "No TryGetCompletions results"))

do! Result.requireNotEmpty "Should not have empty completions" decls

return Some(decls, residue, shouldKeywords, typeCheckResults, getAllSymbols, volatileFile)
}

let handleError e =
match e with
| "Should not have empty completions" ->
// If we don't get any completions, assume we need to wait for a full typecheck
getCompletions forceGetTypeCheckResults
| _ -> getCompletions forceGetTypeCheckResultsStale

match!
retryAsyncOption (TimeSpan.FromMilliseconds(10.)) 100 getCompletions
retryAsyncOption
(TimeSpan.FromMilliseconds(15.))
100
handleError
(getCompletions forceGetTypeCheckResultsStale)
|> AsyncResult.ofStringErr
with
| None -> return! success (None)
Expand Down
Loading

0 comments on commit c3ea94b

Please sign in to comment.