diff --git a/vsintegration/src/FSharp.Editor/Common/Constants.fs b/vsintegration/src/FSharp.Editor/Common/Constants.fs index 1e4e0edbfe..88238c467f 100644 --- a/vsintegration/src/FSharp.Editor/Common/Constants.fs +++ b/vsintegration/src/FSharp.Editor/Common/Constants.fs @@ -68,3 +68,7 @@ module internal Guids = [] /// "9A66EB6A-DE52-4169-BC26-36FBD4312FD7" let codeFixesOptionPageIdString = "9A66EB6A-DE52-4169-BC26-36FBD4312FD7" + + [] + /// "8FDA964A-263D-4B4E-9560-29897535217C" + let languageServicePerformanceOptionPageIdString = "8FDA964A-263D-4B4E-9560-29897535217C" diff --git a/vsintegration/src/FSharp.Editor/FSharp.Editor.resx b/vsintegration/src/FSharp.Editor/FSharp.Editor.resx index f2c15c4cea..1414d45144 100644 --- a/vsintegration/src/FSharp.Editor/FSharp.Editor.resx +++ b/vsintegration/src/FSharp.Editor/FSharp.Editor.resx @@ -174,6 +174,9 @@ Code Fixes + + Performance + The value is unused diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index dead969dd2..19f8b1cf60 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -22,7 +22,6 @@ open Microsoft.CodeAnalysis.Completion open Microsoft.CodeAnalysis.Options open Microsoft.VisualStudio open Microsoft.VisualStudio.Editor -open Microsoft.VisualStudio.Text open Microsoft.VisualStudio.TextManager.Interop open Microsoft.VisualStudio.LanguageServices.Implementation.LanguageService open Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem @@ -46,7 +45,12 @@ type internal FSharpCheckerProvider let checker = lazy - let checker = FSharpChecker.Create(projectCacheSize = 200, keepAllBackgroundResolutions = false (* , MaxMemory = 2300 *), legacyReferenceResolver=Microsoft.FSharp.Compiler.MSBuildReferenceResolver.Resolver) + let checker = + FSharpChecker.Create( + projectCacheSize = Settings.LanguageServicePerformance.ProjectCheckCacheSize, + keepAllBackgroundResolutions = false, + (* , MaxMemory = 2300 *) + legacyReferenceResolver=Microsoft.FSharp.Compiler.MSBuildReferenceResolver.Resolver) // 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, @@ -126,10 +130,10 @@ type internal ProjectInfoManager // compiled and #r will refer to files on disk let referencedProjectFileNames = [| |] let site = ProjectSitesAndFiles.CreateProjectSiteForScript(fileName, referencedProjectFileNames, options) - return ProjectSitesAndFiles.GetProjectOptionsForProjectSite(tryGetOptionsForReferencedProject,site,fileName,options.ExtraProjectInfo,serviceProvider, true) + return ProjectSitesAndFiles.GetProjectOptionsForProjectSite(Settings.LanguageServicePerformance.EnableInMemoryCrossProjectReferences, tryGetOptionsForReferencedProject,site,fileName,options.ExtraProjectInfo,serviceProvider, true) else let site = ProjectSitesAndFiles.ProjectSiteOfSingleFile(fileName) - return ProjectSitesAndFiles.GetProjectOptionsForProjectSite(tryGetOptionsForReferencedProject,site,fileName,extraProjectInfo,serviceProvider, true) + return ProjectSitesAndFiles.GetProjectOptionsForProjectSite(Settings.LanguageServicePerformance.EnableInMemoryCrossProjectReferences, tryGetOptionsForReferencedProject,site,fileName,extraProjectInfo,serviceProvider, true) } /// Update the info for a project in the project table @@ -137,7 +141,7 @@ type internal ProjectInfoManager this.AddOrUpdateProject(projectId, (fun isRefresh -> let extraProjectInfo = Some(box workspace) let tryGetOptionsForReferencedProject f = f |> tryGetOrCreateProjectId |> Option.bind this.TryGetOptionsForProject - let referencedProjects, options = ProjectSitesAndFiles.GetProjectOptionsForProjectSite(tryGetOptionsForReferencedProject, site, site.ProjectFileName(), extraProjectInfo, serviceProvider, true) + let referencedProjects, options = ProjectSitesAndFiles.GetProjectOptionsForProjectSite(Settings.LanguageServicePerformance.EnableInMemoryCrossProjectReferences, tryGetOptionsForReferencedProject, site, site.ProjectFileName(), extraProjectInfo, serviceProvider, true) let referencedProjectIds = referencedProjects |> Array.choose tryGetOrCreateProjectId checkerProvider.Checker.InvalidateConfiguration(options, startBackgroundCompileIfAlreadySeen = not isRefresh, userOpName= userOpName + ".UpdateProjectInfo") referencedProjectIds, options)) @@ -231,6 +235,7 @@ type [, "F#", null, "IntelliSense", "6008")>] [, "F#", null, "QuickInfo", "6009")>] [, "F#", null, "Code Fixes", "6010")>] + [, "F#", null, "Performance", "6011")>] [, strLanguageName = FSharpConstants.FSharpLanguageName, languageResourceID = 100, diff --git a/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs b/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs index 88270cc7df..faf5230a7c 100644 --- a/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs +++ b/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs @@ -36,6 +36,11 @@ type CodeFixesOptions = AlwaysPlaceOpensAtTopLevel: bool UnusedOpens: bool } +[] +type LanguageServicePerformanceOptions = + { EnableInMemoryCrossProjectReferences: bool + ProjectCheckCacheSize: int } + [)>] type internal Settings [](store: SettingsStore) = do // Initialize default settings @@ -56,11 +61,16 @@ type internal Settings [](store: SettingsStore) = AlwaysPlaceOpensAtTopLevel = false UnusedOpens = true } + store.RegisterDefault + { EnableInMemoryCrossProjectReferences = true + ProjectCheckCacheSize = 200 } + interface ISettings static member IntelliSense : IntelliSenseOptions = getSettings() static member QuickInfo : QuickInfoOptions = getSettings() static member CodeFixes : CodeFixesOptions = getSettings() + static member LanguageServicePerformance : LanguageServicePerformanceOptions = getSettings() module internal OptionsUI = @@ -89,3 +99,9 @@ module internal OptionsUI = inherit AbstractOptionPage() override this.CreateView() = upcast CodeFixesOptionControl() + + [] + type internal LanguageServicePerformanceOptionPage() = + inherit AbstractOptionPage() + override this.CreateView() = + upcast LanguageServicePerformanceOptionControl() \ No newline at end of file diff --git a/vsintegration/src/FSharp.LanguageService/BackgroundRequests.fs b/vsintegration/src/FSharp.LanguageService/BackgroundRequests.fs index dc85178fe4..8d2cfce4b9 100644 --- a/vsintegration/src/FSharp.LanguageService/BackgroundRequests.fs +++ b/vsintegration/src/FSharp.LanguageService/BackgroundRequests.fs @@ -83,7 +83,8 @@ type internal FSharpLanguageServiceBackgroundRequests // This portion is executed on the UI thread. let rdt = getServiceProvider().RunningDocumentTable let projectSite = getProjectSitesAndFiles().FindOwningProject(rdt,fileName) - let _, checkOptions = ProjectSitesAndFiles.GetProjectOptionsForProjectSite((fun _ -> None), projectSite, fileName, None, getServiceProvider(), false) + let enableInMemoryCrossProjectReferences = true + let _, checkOptions = ProjectSitesAndFiles.GetProjectOptionsForProjectSite(enableInMemoryCrossProjectReferences, (fun _ -> None), projectSite, fileName, None, getServiceProvider(), false) let projectFileName = projectSite.ProjectFileName() let data = { ProjectSite = projectSite diff --git a/vsintegration/src/FSharp.LanguageService/ProjectSitesAndFiles.fs b/vsintegration/src/FSharp.LanguageService/ProjectSitesAndFiles.fs index 370e577a5f..8b9c8ad77d 100644 --- a/vsintegration/src/FSharp.LanguageService/ProjectSitesAndFiles.fs +++ b/vsintegration/src/FSharp.LanguageService/ProjectSitesAndFiles.fs @@ -123,7 +123,7 @@ type internal ProjectSitesAndFiles() = | None -> () } - static let rec referencedProjectsOf (tryGetOptionsForReferencedProject, projectSite:IProjectSite, extraProjectInfo, serviceProvider:System.IServiceProvider, useUniqueStamp) = + static let rec referencedProjectsOf (enableInMemoryCrossProjectReferences, tryGetOptionsForReferencedProject, projectSite:IProjectSite, extraProjectInfo, serviceProvider:System.IServiceProvider, useUniqueStamp) = [| for (p,ps) in referencedProvideProjectSites (projectSite, serviceProvider) do match fullOutputAssemblyPath p with | None -> () @@ -132,14 +132,16 @@ type internal ProjectSitesAndFiles() = // Lookup may not succeed if the project has not been established yet // In this case we go and compute the options recursively. match tryGetOptionsForReferencedProject p.FileName with - | None -> getProjectOptionsForProjectSite (tryGetOptionsForReferencedProject, ps.GetProjectSite(), p.FileName, extraProjectInfo, serviceProvider, useUniqueStamp) |> snd + | None -> getProjectOptionsForProjectSite (enableInMemoryCrossProjectReferences, tryGetOptionsForReferencedProject, ps.GetProjectSite(), p.FileName, extraProjectInfo, serviceProvider, useUniqueStamp) |> snd | Some options -> options yield (p.FileName, (path, referencedProjectOptions)) |] - and getProjectOptionsForProjectSite(tryGetOptionsForReferencedProject, projectSite:IProjectSite, fileName, extraProjectInfo, serviceProvider, useUniqueStamp) = + and getProjectOptionsForProjectSite(enableInMemoryCrossProjectReferences, tryGetOptionsForReferencedProject, projectSite:IProjectSite, fileName, extraProjectInfo, serviceProvider, useUniqueStamp) = let referencedProjectFileNames, referencedProjectOptions = - referencedProjectsOf(tryGetOptionsForReferencedProject, projectSite, extraProjectInfo, serviceProvider, useUniqueStamp) - |> Array.unzip + if enableInMemoryCrossProjectReferences then + referencedProjectsOf(enableInMemoryCrossProjectReferences, tryGetOptionsForReferencedProject, projectSite, extraProjectInfo, serviceProvider, useUniqueStamp) + |> Array.unzip + else [| |], [| |] let options = {ProjectFileName = projectSite.ProjectFileName() @@ -238,11 +240,11 @@ type internal ProjectSitesAndFiles() = |> Seq.toArray /// Create project options for this project site. - static member GetProjectOptionsForProjectSite(tryGetOptionsForReferencedProject, projectSite:IProjectSite,filename,extraProjectInfo,serviceProvider:System.IServiceProvider, useUniqueStamp) = + static member GetProjectOptionsForProjectSite(enableInMemoryCrossProjectReferences, tryGetOptionsForReferencedProject, projectSite:IProjectSite,filename,extraProjectInfo,serviceProvider:System.IServiceProvider, useUniqueStamp) = match projectSite with | :? IHaveCheckOptions as hco -> hco.OriginalCheckOptions() | _ -> - getProjectOptionsForProjectSite(tryGetOptionsForReferencedProject, projectSite, filename, extraProjectInfo, serviceProvider, useUniqueStamp) + getProjectOptionsForProjectSite(enableInMemoryCrossProjectReferences, tryGetOptionsForReferencedProject, projectSite, filename, extraProjectInfo, serviceProvider, useUniqueStamp) /// Create project site for these project options static member CreateProjectSiteForScript (filename, referencedProjectFileNames, checkOptions) = diff --git a/vsintegration/src/FSharp.UIResources/FSharp.UIResources.csproj b/vsintegration/src/FSharp.UIResources/FSharp.UIResources.csproj index 2223c656c5..5d1b0c7a37 100644 --- a/vsintegration/src/FSharp.UIResources/FSharp.UIResources.csproj +++ b/vsintegration/src/FSharp.UIResources/FSharp.UIResources.csproj @@ -55,6 +55,10 @@ CodeFixesOptionControl.xaml + + + LanguageServicePerformanceOptionControl.xaml + IntelliSenseOptionControl.xaml @@ -78,6 +82,10 @@ MSBuild:Compile Designer + + MSBuild:Compile + Designer + Designer MSBuild:Compile @@ -95,6 +103,7 @@ PublicResXFileCodeGenerator Strings.Designer.cs + Designer diff --git a/vsintegration/src/FSharp.UIResources/IntelliSenseOptionControl.xaml b/vsintegration/src/FSharp.UIResources/IntelliSenseOptionControl.xaml index d6d1858b54..c214018dc0 100644 --- a/vsintegration/src/FSharp.UIResources/IntelliSenseOptionControl.xaml +++ b/vsintegration/src/FSharp.UIResources/IntelliSenseOptionControl.xaml @@ -2,34 +2,34 @@ x:ClassModifier="internal" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:Microsoft.VisualStudio.FSharp.UIResources" - mc:Ignorable="d" + mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400"> - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - + + + + + + diff --git a/vsintegration/src/FSharp.UIResources/IntergerCheckValidationRule.cs b/vsintegration/src/FSharp.UIResources/IntergerCheckValidationRule.cs new file mode 100644 index 0000000000..a4399e34a2 --- /dev/null +++ b/vsintegration/src/FSharp.UIResources/IntergerCheckValidationRule.cs @@ -0,0 +1,32 @@ +using System.Globalization; +using System.Windows.Controls; + +namespace Microsoft.VisualStudio.FSharp.UIResources +{ + + public class IntegerRangeValidationRule : ValidationRule + { + public IntegerRangeValidationRule() + : base(ValidationStep.RawProposedValue, true) + { + } + + public int Min { get; set; } + + public int Max { get; set; } + + public override ValidationResult Validate(object value, CultureInfo cultureInfo) + { + if (value is string text) + { + if (int.TryParse(text, out int i) && + i >= Min && i <= Max) + { + return ValidationResult.ValidResult; + } + } + + return new ValidationResult(false, $"Expected a number between {Min} and {Max}"); + } + } +} \ No newline at end of file diff --git a/vsintegration/src/FSharp.UIResources/LanguageServicePerformanceOptionControl.xaml b/vsintegration/src/FSharp.UIResources/LanguageServicePerformanceOptionControl.xaml new file mode 100644 index 0000000000..9a1b418b48 --- /dev/null +++ b/vsintegration/src/FSharp.UIResources/LanguageServicePerformanceOptionControl.xaml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vsintegration/src/FSharp.UIResources/LanguageServicePerformanceOptionControl.xaml.cs b/vsintegration/src/FSharp.UIResources/LanguageServicePerformanceOptionControl.xaml.cs new file mode 100644 index 0000000000..f5e2be4e18 --- /dev/null +++ b/vsintegration/src/FSharp.UIResources/LanguageServicePerformanceOptionControl.xaml.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace Microsoft.VisualStudio.FSharp.UIResources +{ + /// + /// Interaction logic for LanguageServicePerformanceOptionControl.xaml + /// + internal partial class LanguageServicePerformanceOptionControl : UserControl + { + public LanguageServicePerformanceOptionControl() + { + InitializeComponent(); + } + } +} diff --git a/vsintegration/src/FSharp.UIResources/Strings.Designer.cs b/vsintegration/src/FSharp.UIResources/Strings.Designer.cs index 1526ee21a0..ff02acbe6e 100644 --- a/vsintegration/src/FSharp.UIResources/Strings.Designer.cs +++ b/vsintegration/src/FSharp.UIResources/Strings.Designer.cs @@ -105,6 +105,24 @@ public static string Dot_underline { } } + /// + /// Looks up a localized string similar to _Enable in-memory cross project references. + /// + public static string Enable_in_memory_cross_project_references { + get { + return ResourceManager.GetString("Enable_in_memory_cross_project_references", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Performance. + /// + public static string Language_Service_Performance { + get { + return ResourceManager.GetString("Language_Service_Performance", resourceCulture); + } + } + /// /// Looks up a localized string similar to Navigation links. /// @@ -114,6 +132,15 @@ public static string Navigation_links { } } + /// + /// Looks up a localized string similar to Project check cache size. + /// + public static string Project_check_cache_size { + get { + return ResourceManager.GetString("Project_check_cache_size", resourceCulture); + } + } + /// /// Looks up a localized string similar to Show s_ymbols in unopened namespaces. /// diff --git a/vsintegration/src/FSharp.UIResources/Strings.resx b/vsintegration/src/FSharp.UIResources/Strings.resx index 94860c1bf7..4500bfcc71 100644 --- a/vsintegration/src/FSharp.UIResources/Strings.resx +++ b/vsintegration/src/FSharp.UIResources/Strings.resx @@ -126,6 +126,9 @@ Completion Lists + + Performance + D_ash underline @@ -144,6 +147,12 @@ _Show completion list after a character is typed + + _Enable in-memory cross project references + + + Project check cache size + S_how navigation links as diff --git a/vsintegration/tests/Salsa/FSharpLanguageServiceTestable.fs b/vsintegration/tests/Salsa/FSharpLanguageServiceTestable.fs index afafc5e835..f0d61ee20f 100644 --- a/vsintegration/tests/Salsa/FSharpLanguageServiceTestable.fs +++ b/vsintegration/tests/Salsa/FSharpLanguageServiceTestable.fs @@ -126,7 +126,8 @@ type internal FSharpLanguageServiceTestable() as this = /// Respond to project being cleaned/rebuilt (any live type providers in the project should be refreshed) member this.OnProjectCleaned(projectSite:IProjectSite) = - let _, checkOptions = ProjectSitesAndFiles.GetProjectOptionsForProjectSite((fun _ -> None), projectSite, "" ,None, serviceProvider.Value, false) + let enableInMemoryCrossProjectReferences = true + let _, checkOptions = ProjectSitesAndFiles.GetProjectOptionsForProjectSite(enableInMemoryCrossProjectReferences, (fun _ -> None), projectSite, "" ,None, serviceProvider.Value, false) this.FSharpChecker.NotifyProjectCleaned(checkOptions) |> Async.RunSynchronously member this.OnActiveViewChanged(textView) = @@ -167,8 +168,9 @@ type internal FSharpLanguageServiceTestable() as this = // For each change in dependency files, notify the language service of the change and propagate the update interface IDependencyFileChangeNotify with member this.DependencyFileCreated projectSite = + let enableInMemoryCrossProjectReferences = true // Invalidate the configuration if we notice any add for any DependencyFiles - let _, checkOptions = ProjectSitesAndFiles.GetProjectOptionsForProjectSite((fun _ -> None), projectSite, "", None, this.ServiceProvider, false) + let _, checkOptions = ProjectSitesAndFiles.GetProjectOptionsForProjectSite(enableInMemoryCrossProjectReferences, (fun _ -> None), projectSite, "", None, this.ServiceProvider, false) this.FSharpChecker.InvalidateConfiguration(checkOptions) member this.DependencyFileChanged (filename) =