Skip to content

Commit

Permalink
Refresh the check of files when notified by F# analysis engine (dotne…
Browse files Browse the repository at this point in the history
…t#1906)

This fixes a large chunk of dotnet#1808. I've tested it by hand. The build still doesn't react to files coming/going in DependencyFiles.  I'll adjust the description in dotnet#1808 to make the cases distinct. Basically F# has an analysis engine which tells the UI when to do "foreground" rechecks of files because the checking state has changed. This reacts to those events. See https://fsharp.github.io/FSharp.Compiler.Service/queue.html for some info.

When the UI gets the focus on a document, it should also notify the F# engine to start checking that document


* fix tests

* Fix bridge between backgronud builders

* fix test
  • Loading branch information
dsyme authored and forki committed Dec 2, 2016
1 parent 5286f27 commit 0f9fd2c
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 10 deletions.
63 changes: 60 additions & 3 deletions vsintegration/src/FSharp.Editor/LanguageService.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@

namespace Microsoft.VisualStudio.FSharp.Editor

#nowarn "40"

open System
open System.Collections.Concurrent
open System.Collections.Generic
open System.ComponentModel.Composition
open System.Runtime.InteropServices
open System.Linq
open System.IO
Expand All @@ -13,9 +16,11 @@ open Microsoft.FSharp.Compiler.CompileOps
open Microsoft.FSharp.Compiler.SourceCodeServices

open Microsoft.CodeAnalysis
open Microsoft.CodeAnalysis.Diagnostics
open Microsoft.CodeAnalysis.Editor.Options
open Microsoft.VisualStudio
open Microsoft.VisualStudio.Editor
open Microsoft.VisualStudio.Text
open Microsoft.VisualStudio.TextManager.Interop
open Microsoft.VisualStudio.LanguageServices
open Microsoft.VisualStudio.LanguageServices.Implementation.LanguageService
Expand All @@ -27,6 +32,7 @@ open Microsoft.VisualStudio.LanguageServices.ProjectSystem
open Microsoft.VisualStudio.Shell
open Microsoft.VisualStudio.Shell.Interop
open Microsoft.VisualStudio.FSharp.LanguageService
open Microsoft.VisualStudio.ComponentModelHost

// Workaround to access non-public settings persistence type.
// GetService( ) with this will work as long as the GUID matches the real type.
Expand All @@ -49,14 +55,42 @@ type internal SVsSettingsPersistenceManager = class end
type internal FSharpLanguageService(package : FSharpPackage) =
inherit AbstractLanguageService<FSharpPackage, FSharpLanguageService>(package)

// The latest component model. This is used to retrieve IDiagnosticAnalyzerService.
// FSROSLYNTODO: Work out how to make this and the two tables below non-static.
static let mutable componentModelOpt : IComponentModel option = None

// A table of information about projects, excluding single-file projects.
static let projectTable = ConcurrentDictionary<ProjectId, FSharpProjectOptions>()

// A table of information about single-file projects. Currently we only need the load time of each such file, plus
// the original options for editing
static let singleFileProjectTable = ConcurrentDictionary<ProjectId, DateTime * FSharpProjectOptions>()

static let checker = lazy FSharpChecker.Create()
static let checker =
lazy

let checker = FSharpChecker.Create()

// This is one half of the bridge between the F# background builder and the Roslyn analysis engine.
// When the F# background builder refreshes the background semantic build context for a file,
// we request Roslyn to reanalyze that individual file.
checker.BeforeBackgroundFileCheck.Add(fun (fileName, extraProjectInfo) ->
async {
try
match extraProjectInfo, componentModelOpt with
| Some (:? Workspace as workspace), Some componentModel ->
let analyzerService = componentModel.GetService<IDiagnosticAnalyzerService>()
let solution = workspace.CurrentSolution
let documentIds = solution.GetDocumentIdsWithFilePath(fileName)
if not documentIds.IsEmpty then
analyzerService.Reanalyze(workspace,documentIds=documentIds)
| _ -> ()
with ex ->
Assert.Exception(ex)
} |> Async.StartImmediate
)

checker


/// Update the info for a proejct in the project table
Expand Down Expand Up @@ -211,10 +245,12 @@ type internal FSharpLanguageService(package : FSharpPackage) =
override this.SetupNewTextView(textView) =
base.SetupNewTextView(textView)
let workspace = this.Package.ComponentModel.GetService<VisualStudioWorkspaceImpl>()
componentModelOpt <- Some this.Package.ComponentModel

// FSROSLYNTODO: Hide navigation bars for now. Enable after adding tests
workspace.Options <- workspace.Options.WithChangedOption(NavigationBarOptions.ShowNavigationBar, FSharpCommonConstants.FSharpLanguageName, false)

let textViewAdapter = this.Package.ComponentModel.GetService<IVsEditorAdaptersFactoryService>()
match textView.GetBuffer() with
| (VSConstants.S_OK, textLines) ->
let filename = VsTextLines.GetFilename textLines
Expand All @@ -224,12 +260,32 @@ type internal FSharpLanguageService(package : FSharpPackage) =
| :? IProvideProjectSite as siteProvider when not (IsScript(filename)) ->
this.SetupProjectFile(siteProvider, workspace)
| _ ->
let editorAdapterFactoryService = this.Package.ComponentModel.GetService<IVsEditorAdaptersFactoryService>()
let fileContents = VsTextLines.GetFileContents(textLines, editorAdapterFactoryService)
let fileContents = VsTextLines.GetFileContents(textLines, textViewAdapter)
this.SetupStandAloneFile(filename, fileContents, workspace, hier)
| _ -> ()
| _ -> ()

// This is the second half of the bridge between the F# IncrementalBuild analysis engine and the Roslyn analysis
// engine. When a document gets the focus, we call FSharpLanguageService.Checker.StartBackgroundCompile for the
// project containing that file. This ensures the F# IncrementalBuild engine starts analyzing the project.
let wpfTextView = textViewAdapter.GetWpfTextView(textView)
match wpfTextView.TextBuffer.Properties.TryGetProperty<ITextDocument>(typeof<ITextDocument>) with
| true, textDocument ->
let filePath = textDocument.FilePath
let onGotFocus = new EventHandler(fun _ _ ->
for documentId in workspace.CurrentSolution.GetDocumentIdsWithFilePath(filePath) do
let document = workspace.CurrentSolution.GetDocument(documentId)
match FSharpLanguageService.TryGetOptionsForEditingDocumentOrProject(document) with
| Some options -> FSharpLanguageService.Checker.StartBackgroundCompile(options)
| None -> ()
)
let rec onViewClosed = new EventHandler(fun _ _ ->
wpfTextView.GotAggregateFocus.RemoveHandler(onGotFocus)
wpfTextView.Closed.RemoveHandler(onViewClosed)
)
wpfTextView.GotAggregateFocus.AddHandler(onGotFocus)
wpfTextView.Closed.AddHandler(onViewClosed)
| _ -> ()

and [<Guid(FSharpCommonConstants.packageGuidString)>]
[<ProvideLanguageService(languageService = typeof<FSharpLanguageService>,
Expand Down Expand Up @@ -259,3 +315,4 @@ and [<Guid(FSharpCommonConstants.packageGuidString)>]
override this.CreateEditorFactories() = Seq.empty<IVsEditorFactory>

override this.RegisterMiscellaneousFilesWorkspaceInformation(_) = ()

12 changes: 6 additions & 6 deletions vsintegration/src/FSharp.VS.FSI/fsiSessionToolWindow.fs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ type internal FsiToolWindow() as this =

let providerGlobal = Package.GetGlobalService(typeof<IOleServiceProvider>) :?> IOleServiceProvider
let provider = new ServiceProvider(providerGlobal) :> System.IServiceProvider
let editorAdaptersFactory =
let textViewAdapter =
// end of 623708 workaround.
let componentModel = provider.GetService(typeof<SComponentModel>) :?> IComponentModel
componentModel.GetService<IVsEditorAdaptersFactoryService>()
Expand Down Expand Up @@ -130,7 +130,7 @@ type internal FsiToolWindow() as this =
do codeWinMan.OnNewView(textView) |> throwOnFailure0

// Create the stream on top of the text buffer.
let textStream = new TextBufferStream(editorAdaptersFactory.GetDataBuffer(textLines))
let textStream = new TextBufferStream(textViewAdapter.GetDataBuffer(textLines))
let synchronizationContext = System.Threading.SynchronizationContext.Current
let win32win = { new System.Windows.Forms.IWin32Window with member this.Handle = textView.GetWindowHandle()}
let mutable textView = textView
Expand All @@ -152,7 +152,7 @@ type internal FsiToolWindow() as this =
//
// REVIEW: Next question, can WORD-WRAP be toggled on by default? Do we want that? Maybe not!
let setTextViewProperties() =
let wpfTextView = editorAdaptersFactory.GetWpfTextView(textView)
let wpfTextView = textViewAdapter.GetWpfTextView(textView)
// Enable find in the text view without implementing the IVsFindTarget interface (by allowing
// the active text view to directly respond to the find manager via the locate find target
// command)
Expand Down Expand Up @@ -185,11 +185,11 @@ type internal FsiToolWindow() as this =
let horizontalScrollbar = 0
let verticalScrollbar = 1
// Make sure that the last line of the buffer is visible. [ignore errors].
let buffer = editorAdaptersFactory.GetDataBuffer(textLines)
let buffer = textViewAdapter.GetDataBuffer(textLines)
let lastLine = buffer.CurrentSnapshot.LineCount - 1
if lastLine >= 0 then
let lineStart = buffer.CurrentSnapshot.GetLineFromLineNumber(lastLine).Start
let wpfTextView = editorAdaptersFactory.GetWpfTextView(textView)
let wpfTextView = textViewAdapter.GetWpfTextView(textView)
wpfTextView.DisplayTextLineContainingBufferPosition(lineStart, 0.0, ViewRelativePosition.Bottom)

let setScrollToStartOfLine() =
Expand Down Expand Up @@ -784,7 +784,7 @@ type internal FsiToolWindow() as this =
member this.QueryStatus (guid, cCmds, prgCmds, pCmdText)=

// Added to prevent command processing when the zoom control in the margin is focused
let wpfTextView = editorAdaptersFactory.GetWpfTextView(textView)
let wpfTextView = textViewAdapter.GetWpfTextView(textView)

// Can't search in the F# Interactive window
// InterceptsCommandRouting property denotes whether this element requires normal input as opposed to VS commands
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4443,7 +4443,7 @@ let x = query { for bbbb in abbbbc(*D0*) do
| "DiscriminatedUnion",_,_,DeclarationType.DiscriminatedUnion -> ()
| "AsmType",_,_,DeclarationType.RareType -> ()
| "FunctionType",_,_,DeclarationType.FunctionType -> ()
| "TupleType",_,_,DeclarationType.ValueType -> ()
| "TupleType",_,_,DeclarationType.Class -> ()
| "ValueType",_,_,DeclarationType.ValueType -> ()
| "Class",_,_,DeclarationType.Class -> ()
| "Int32",_,_,DeclarationType.Method -> ()
Expand Down

0 comments on commit 0f9fd2c

Please sign in to comment.