Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 10 additions & 7 deletions src/FsAutoComplete.Core/CompilerServiceInterface.fs
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,8 @@ type CompilerProjectOption =

member x.SourceFilesTagged =
match x with
| BackgroundCompiler(options) -> options.SourceFiles |> Array.toList
| TransparentCompiler(snapshot) -> snapshot.SourceFiles |> List.map (fun f -> f.FileName)
|> List.map Utils.normalizePath
| BackgroundCompiler(options) -> options.SourceFiles |> Array.map Utils.normalizePath |> Array.toList
| TransparentCompiler(snapshot) -> snapshot.SourceFiles |> List.map (fun f -> Utils.normalizePath f.FileName)

member x.ReferencedProjectsPath =
match x with
Expand Down Expand Up @@ -189,12 +188,16 @@ type FSharpCompilerServiceChecker
None

let processFSIArgs args =
(([||], [||]), args)
||> Array.fold (fun (args, files) arg ->
let argsOut = ResizeArray()
let filesOut = ResizeArray()

for arg in args do
match arg with
| StartsWith "--use:" file
| StartsWith "--load:" file -> args, Array.append files [| file |]
| arg -> Array.append args [| arg |], files)
| StartsWith "--load:" file -> filesOut.Add(file)
| arg -> argsOut.Add(arg)

argsOut.ToArray(), filesOut.ToArray()

let (|Reference|_|) (opt: string) =
if opt.StartsWith("-r:", StringComparison.Ordinal) then
Expand Down
25 changes: 22 additions & 3 deletions src/FsAutoComplete.Core/FileSystem.fs
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,9 @@ module RoslynSourceText =

type RoslynSourceTextFile(fileName: string<LocalPath>, sourceText: SourceText) =

let cachedLines =
lazy (sourceText.Lines |> Seq.toArray |> Array.map (fun l -> l.ToString()))

let walk
(
x: IFSACSourceText,
Expand Down Expand Up @@ -250,8 +253,7 @@ module RoslynSourceText =
member x.TotalRange: Range =
(Range.mkRange (UMX.untag fileName) Position.pos0 ((x :> IFSACSourceText).LastFilePosition))

member x.Lines: string array =
sourceText.Lines |> Seq.toArray |> Array.map (fun l -> l.ToString())
member x.Lines: string array = cachedLines.Value

member this.GetText(range: Range) : Result<string, string> =
range.ToRoslynTextSpan(sourceText) |> sourceText.GetSubText |> string |> Ok
Expand Down Expand Up @@ -488,7 +490,24 @@ type FileSystem(actualFs: IFileSystem, tryFindFile: string<LocalPath> -> Volatil
>> Log.addContext "hash" (file.Source.GetHashCode())
)

file.Source.ToString() |> System.Text.Encoding.UTF8.GetBytes)
// Write source text to bytes in chunks via CopyTo, avoiding a full intermediate string allocation
let source = file.Source :> FSharp.Compiler.Text.ISourceText
let length = source.Length
let ms = new MemoryStream()
let utf8NoBom = System.Text.UTF8Encoding(encoderShouldEmitUTF8Identifier = false)
use sw = new StreamWriter(ms, utf8NoBom, bufferSize = 4096, leaveOpen = true)
let chunkSize = 8192
let charBuffer = Array.zeroCreate (min length chunkSize)
let mutable offset = 0

while offset < length do
let count = min (length - offset) charBuffer.Length
source.CopyTo(offset, charBuffer, 0, count)
sw.Write(charBuffer, 0, count)
offset <- offset + count

sw.Flush()
ms.ToArray())

/// translation of the BCL's Windows logic for Path.IsPathRooted.
///
Expand Down
23 changes: 14 additions & 9 deletions src/FsAutoComplete.Core/Lexer.fs
Original file line number Diff line number Diff line change
Expand Up @@ -59,17 +59,22 @@ module Lexer =
/// Return all tokens of current line
let tokenizeLine (args: string[]) lineStr =
let defines, langVersion =
((ResizeArray(), None), args)
||> Array.fold (fun (defines, langVersion) arg ->
match arg with
| Define d ->
defines.Add(d)
defines, langVersion
| LangVersion v -> defines, Some(v)
| _ -> defines, langVersion)
if args.Length = 0 then
[], None
else
let defs = ResizeArray()
let mutable lang = None

for arg in args do
match arg with
| Define d -> defs.Add(d)
| LangVersion v -> lang <- Some(v)
| _ -> ()

Seq.toList defs, lang

let sourceTokenizer =
FSharpSourceTokenizer(Seq.toList defines, Some "/tmp.fsx", langVersion, None)
FSharpSourceTokenizer(defines, Some "/tmp.fsx", langVersion, None)

let lineTokenizer = sourceTokenizer.CreateLineTokenizer lineStr

Expand Down
18 changes: 13 additions & 5 deletions src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs
Original file line number Diff line number Diff line change
Expand Up @@ -763,10 +763,14 @@ type AdaptiveFSharpLspServer
| Error e -> return Error e
}

let getCompletions forceGetTypeCheckResultsStale =
let getCompletions forceGetTypeCheckResultsStale rereadFile =
asyncResult {

let! volatileFile = state.GetOpenFileOrRead filePath
let! volatileFile =
if rereadFile then
state.GetOpenFileOrRead filePath
else
async { return Ok volatileFile }

let! lineStr =
volatileFile.Source
Expand Down Expand Up @@ -815,8 +819,12 @@ type AdaptiveFSharpLspServer
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 state.GetOpenFileTypeCheckResults
| _ -> getCompletions state.GetOpenFileTypeCheckResultsCached
// No need to re-read the file — only the typecheck results matter
getCompletions state.GetOpenFileTypeCheckResults false
| "TextDocumentCompletion was sent before TextDocumentDidChange" ->
// File content is stale, re-read on next attempt
getCompletions state.GetOpenFileTypeCheckResultsCached true
| _ -> getCompletions state.GetOpenFileTypeCheckResultsCached true

let getCodeToInsert (d: DeclarationListItem) =
match d.NamespaceToOpen with
Expand Down Expand Up @@ -850,7 +858,7 @@ type AdaptiveFSharpLspServer
(TimeSpan.FromMilliseconds(15.))
100
handleError
(getCompletions state.GetOpenFileTypeCheckResultsCached)
(getCompletions state.GetOpenFileTypeCheckResultsCached false)
|> AsyncResult.ofStringErr
with
| None -> return! LspResult.success (None)
Expand Down
30 changes: 17 additions & 13 deletions src/FsAutoComplete/LspServers/AdaptiveServerState.fs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,8 @@ type AdaptiveWorkspaceChosen =
type LoadedProject =
{ ProjectOptions: Types.ProjectOptions
FSharpProjectCompilerOptions: aval<CompilerProjectOption>
LanguageVersion: LanguageVersionShim }
LanguageVersion: LanguageVersionShim
_sourceFilesTagged: Lazy<string<LocalPath> array> }

interface IEquatable<LoadedProject> with
member x.Equals(other) = x.ProjectOptions = other.ProjectOptions
Expand All @@ -133,11 +134,17 @@ type LoadedProject =
| :? LoadedProject as other -> (x :> IEquatable<_>).Equals other
| _ -> false

member x.SourceFilesTagged =
x.ProjectOptions.SourceFiles |> List.map Utils.normalizePath |> List.toArray
member x.SourceFilesTagged = x._sourceFilesTagged.Value

member x.ProjectFileName = x.ProjectOptions.ProjectFileName

static member Create(projectOptions, fsharpProjectCompilerOptions, languageVersion) =
{ ProjectOptions = projectOptions
FSharpProjectCompilerOptions = fsharpProjectCompilerOptions
LanguageVersion = languageVersion
_sourceFilesTagged =
lazy (projectOptions.SourceFiles |> List.map Utils.normalizePath |> List.toArray) }

/// The reality is a file can be in multiple projects
/// This is extracted to make it easier to do some type of customized select in the future
type IFindProject =
Expand Down Expand Up @@ -1211,9 +1218,10 @@ type AdaptiveState
let createSnapshots projectOptions =
Snapshots.createSnapshots openFilesWithChanges (AVal.constant sourceTextFactory) (AMap.ofHashMap projectOptions)
|> AMap.map (fun _ (proj, snap) ->
{ ProjectOptions = proj
FSharpProjectCompilerOptions = snap |> AVal.map CompilerProjectOption.TransparentCompiler
LanguageVersion = LanguageVersionShim.fromOtherOptions proj.OtherOptions })
LoadedProject.Create(
proj,
snap |> AVal.map CompilerProjectOption.TransparentCompiler,
LanguageVersionShim.fromOtherOptions proj.OtherOptions))

let createOptions projectOptions =
let projectOptions = HashMap.toValueList projectOptions
Expand All @@ -1233,9 +1241,7 @@ type AdaptiveState
|> CompilerProjectOption.BackgroundCompiler

Utils.normalizePath projectOption.ProjectFileName,
{ FSharpProjectCompilerOptions = AVal.constant fso
LanguageVersion = langversion
ProjectOptions = projectOption })
LoadedProject.Create(projectOption, AVal.constant fso, langversion))
|> AMap.ofList

let loadedProjects =
Expand Down Expand Up @@ -1589,9 +1595,7 @@ type AdaptiveState
}

return
{ FSharpProjectCompilerOptions = opts |> AVal.constant
LanguageVersion = LanguageVersionShim.fromOtherOptions opts.OtherOptions
ProjectOptions = projectOptions }
LoadedProject.Create(projectOptions, opts |> AVal.constant, LanguageVersionShim.fromOtherOptions opts.OtherOptions)
|> List.singleton

with e ->
Expand Down Expand Up @@ -1774,7 +1778,7 @@ type AdaptiveState
let tags =
[ SemanticConventions.fsac_sourceCodePath, box (UMX.untag file.Source.FileName)
SemanticConventions.projectFilePath, box (options.ProjectFileName)
"source.text", box (file.Source.String)
"source.length", box (file.Source.Length)
"source.version", box (file.Version)

]
Expand Down
10 changes: 9 additions & 1 deletion src/FsAutoComplete/LspServers/AdaptiveServerState.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,21 @@ type AdaptiveWorkspaceChosen =
type LoadedProject =
{ ProjectOptions: Types.ProjectOptions
FSharpProjectCompilerOptions: aval<CompilerProjectOption>
LanguageVersion: LanguageVersionShim }
LanguageVersion: LanguageVersionShim
_sourceFilesTagged: Lazy<string<LocalPath> array> }

interface IEquatable<LoadedProject>
override GetHashCode: unit -> int
override Equals: other: obj -> bool
member SourceFilesTagged: string<LocalPath> array
member ProjectFileName: string

static member Create:
projectOptions: Types.ProjectOptions *
fsharpProjectCompilerOptions: aval<CompilerProjectOption> *
languageVersion: LanguageVersionShim ->
LoadedProject

type AdaptiveState =
new:
lspClient: FSharpLspClient *
Expand Down
Loading