From e3bfd3a610158d8bf9031242bc2c2d0cd4277ac2 Mon Sep 17 00:00:00 2001 From: Joey Robichaud Date: Wed, 18 Mar 2020 15:06:50 -0700 Subject: [PATCH 1/4] Use workspace.findFiles instead of glob --- src/omnisharp/options.ts | 45 +++++++++++++++++++++++----------------- src/omnisharp/utils.ts | 28 ++++++++++++++++--------- 2 files changed, 44 insertions(+), 29 deletions(-) diff --git a/src/omnisharp/options.ts b/src/omnisharp/options.ts index 22d0c47d2..4fd2162d6 100644 --- a/src/omnisharp/options.ts +++ b/src/omnisharp/options.ts @@ -31,9 +31,8 @@ export class Options { public defaultLaunchSolution?: string, public monoPath?: string, public excludePaths?: string[], - public maxProjectFileCountForDiagnosticAnalysis?: number | null) - { - } + public maxProjectFileCountForDiagnosticAnalysis?: number | null) { + } public static Read(vscode: vscode): Options { // Extra effort is taken below to ensure that legacy versions of options @@ -65,7 +64,7 @@ export class Options { const maxProjectResults = omnisharpConfig.get('maxProjectResults', 250); const defaultLaunchSolution = omnisharpConfig.get('defaultLaunchSolution', undefined); const useEditorFormattingSettings = omnisharpConfig.get('useEditorFormattingSettings', true); - + const enableRoslynAnalyzers = omnisharpConfig.get('enableRoslynAnalyzers', false); const enableEditorConfigSupport = omnisharpConfig.get('enableEditorConfigSupport', false); @@ -89,21 +88,7 @@ export class Options { const maxProjectFileCountForDiagnosticAnalysis = csharpConfig.get('maxProjectFileCountForDiagnosticAnalysis', 1000); - let workspaceConfig = vscode.workspace.getConfiguration(); - let excludePaths = []; - if (workspaceConfig) - { - let excludeFilesOption = workspaceConfig.get<{ [i: string]: boolean }>('files.exclude'); - if (excludeFilesOption) - { - for (let field in excludeFilesOption) { - if (excludeFilesOption[field]) { - excludePaths.push(field); - } - } - } - } - + const excludePaths = this.getExcludedPaths(vscode); return new Options( path, @@ -134,6 +119,28 @@ export class Options { ); } + public static getExcludedPaths(vscode: vscode): string[] { + let excludePaths: string[] = []; + + let workspaceConfig = vscode.workspace.getConfiguration(); + if (!workspaceConfig) { + return excludePaths; + } + + let excludeFilesOption = workspaceConfig.get<{ [i: string]: boolean }>('files.exclude'); + if (!excludeFilesOption) { + return excludePaths; + } + + for (let field in excludeFilesOption) { + if (excludeFilesOption[field]) { + excludePaths.push(field); + } + } + + return excludePaths; + } + private static readPathOption(csharpConfig: WorkspaceConfiguration, omnisharpConfig: WorkspaceConfiguration): string | null { if (omnisharpConfig.has('path')) { // If 'omnisharp.path' setting was found, use it. diff --git a/src/omnisharp/utils.ts b/src/omnisharp/utils.ts index 4d60c3654..dfe8bd2f7 100644 --- a/src/omnisharp/utils.ts +++ b/src/omnisharp/utils.ts @@ -4,12 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import * as fs from 'fs-extra'; -import * as glob from 'glob'; import { OmniSharpServer } from './server'; import * as path from 'path'; import * as protocol from './protocol'; import * as vscode from 'vscode'; import { MSBuildProject } from './protocol'; +import { Options } from './options'; export async function autoComplete(server: OmniSharpServer, request: protocol.AutoCompleteRequest, token: vscode.CancellationToken) { return server.makeRequest(protocol.Requests.AutoComplete, request, token); @@ -71,14 +71,20 @@ export async function requestWorkspaceInformation(server: OmniSharpServer) { const response = await server.makeRequest(protocol.Requests.Projects); if (response.MsBuild && response.MsBuild.Projects) { const blazorDetectionEnabled = hasBlazorWebAssemblyDebugPrerequisites(); + let blazorWebAssemblyProjectFound = false; for (const project of response.MsBuild.Projects) { + const isProjectBlazorWebAssemblyProject = await isBlazorWebAssemblyProject(project); + const isProjectBlazorWebAssemblyHosted = isBlazorWebAssemblyHosted(project, isProjectBlazorWebAssemblyProject); + project.IsWebProject = isWebProject(project); - project.IsBlazorWebAssemblyHosted = blazorDetectionEnabled && isBlazorWebAssemblyHosted(project); - project.IsBlazorWebAssemblyStandalone = blazorDetectionEnabled && !project.IsBlazorWebAssemblyHosted && isBlazorWebAssemblyProject(project); + project.IsBlazorWebAssemblyHosted = blazorDetectionEnabled && isProjectBlazorWebAssemblyHosted; + project.IsBlazorWebAssemblyStandalone = blazorDetectionEnabled && isProjectBlazorWebAssemblyProject && !project.IsBlazorWebAssemblyHosted; + + blazorWebAssemblyProjectFound = blazorWebAssemblyProjectFound || isProjectBlazorWebAssemblyProject; } - if (!blazorDetectionEnabled && response.MsBuild.Projects.some(project => isBlazorWebAssemblyProject(project))) { + if (!blazorDetectionEnabled && blazorWebAssemblyProjectFound) { // There's a Blazor Web Assembly project but VSCode isn't configured to debug the WASM code, show a notification // to help the user configure their VSCode appropriately. vscode.window.showInformationMessage('Additional setup is required to debug Blazor WebAssembly applications.', 'Learn more', 'Close') @@ -150,8 +156,8 @@ export async function isNetCoreProject(project: protocol.MSBuildProject) { return project.TargetFrameworks.find(tf => tf.ShortName.startsWith('netcoreapp') || tf.ShortName.startsWith('netstandard')) !== undefined; } -function isBlazorWebAssemblyHosted(project: protocol.MSBuildProject): boolean { - if (!isBlazorWebAssemblyProject(project)) { +function isBlazorWebAssemblyHosted(project: protocol.MSBuildProject, isProjectBlazorWebAssemblyProject: boolean): boolean { + if (!isProjectBlazorWebAssemblyProject) { return false; } @@ -170,17 +176,19 @@ function isBlazorWebAssemblyHosted(project: protocol.MSBuildProject): boolean { return true; } -function isBlazorWebAssemblyProject(project: MSBuildProject): boolean { +async function isBlazorWebAssemblyProject(project: MSBuildProject): Promise { const projectDirectory = path.dirname(project.Path); - const launchSettings = glob.sync('**/launchSettings.json', { cwd: projectDirectory }); + const launchSettingsPattern = new vscode.RelativePattern(projectDirectory, '**/launchSettings.json'); + const excludedPathPattern = `{${Options.getExcludedPaths(vscode).join(',')}}`; + + const launchSettings = await vscode.workspace.findFiles(launchSettingsPattern, excludedPathPattern); if (!launchSettings) { return false; } for (const launchSetting of launchSettings) { try { - const absoluteLaunchSetting = path.join(projectDirectory, launchSetting); - const launchSettingContent = fs.readFileSync(absoluteLaunchSetting); + const launchSettingContent = fs.readFileSync(launchSetting.fsPath); if (!launchSettingContent) { continue; } From a13f2f3abebed436267b2a7946f37298e7160357 Mon Sep 17 00:00:00 2001 From: Joey Robichaud Date: Wed, 18 Mar 2020 15:55:55 -0700 Subject: [PATCH 2/4] Add tests for getExcludedPaths --- test/unitTests/options.test.ts | 47 +++++++++++++++++------------- test/unitTests/testAssets/Fakes.ts | 5 ++++ 2 files changed, 32 insertions(+), 20 deletions(-) diff --git a/test/unitTests/options.test.ts b/test/unitTests/options.test.ts index c8b1db537..8d0c44523 100644 --- a/test/unitTests/options.test.ts +++ b/test/unitTests/options.test.ts @@ -10,8 +10,7 @@ import { getVSCodeWithConfig, updateConfig } from './testAssets/Fakes'; suite("Options tests", () => { suiteSetup(() => should()); - test('Verify defaults', () => - { + test('Verify defaults', () => { const vscode = getVSCodeWithConfig(); const options = Options.Read(vscode); expect(options.path).to.be.null; @@ -36,8 +35,23 @@ suite("Options tests", () => { expect(options.defaultLaunchSolution).to.be.undefined; }); - test('BACK-COMPAT: "omnisharp.loggingLevel": "verbose" == "omnisharp.loggingLevel": "debug"', () => - { + test('Verify return no excluded paths when empty', () => { + const vscode = getVSCodeWithConfig(); + updateConfig(vscode, null, 'files.exclude', {}); + + const excludedPaths = Options.getExcludedPaths(vscode); + expect(excludedPaths).to.be.empty; + }); + + test('Verify return excluded paths', () => { + const vscode = getVSCodeWithConfig(); + updateConfig(vscode, null, 'files.exclude', { "**/node_modules": true, "**/assets": false }); + + const excludedPaths = Options.getExcludedPaths(vscode); + expect(excludedPaths).to.equalTo(["**/node_modules"]); + }); + + test('BACK-COMPAT: "omnisharp.loggingLevel": "verbose" == "omnisharp.loggingLevel": "debug"', () => { const vscode = getVSCodeWithConfig(); updateConfig(vscode, 'omnisharp', 'loggingLevel', "verbose"); @@ -46,28 +60,25 @@ suite("Options tests", () => { options.loggingLevel.should.equal("debug"); }); - test('BACK-COMPAT: "omnisharp.useMono": true == "omnisharp.useGlobalMono": "always"', () => - { + test('BACK-COMPAT: "omnisharp.useMono": true == "omnisharp.useGlobalMono": "always"', () => { const vscode = getVSCodeWithConfig(); updateConfig(vscode, 'omnisharp', 'useMono', true); - + const options = Options.Read(vscode); options.useGlobalMono.should.equal("always"); }); - test('BACK-COMPAT: "omnisharp.useMono": false == "omnisharp.useGlobalMono": "auto"', () => - { + test('BACK-COMPAT: "omnisharp.useMono": false == "omnisharp.useGlobalMono": "auto"', () => { const vscode = getVSCodeWithConfig(); updateConfig(vscode, 'omnisharp', 'useMono', false); - + const options = Options.Read(vscode); options.useGlobalMono.should.equal("auto"); }); - test('BACK-COMPAT: "csharp.omnisharpUsesMono": true == "omnisharp.useGlobalMono": "always"', () => - { + test('BACK-COMPAT: "csharp.omnisharpUsesMono": true == "omnisharp.useGlobalMono": "always"', () => { const vscode = getVSCodeWithConfig(); updateConfig(vscode, 'csharp', 'omnisharpUsesMono', true); @@ -76,8 +87,7 @@ suite("Options tests", () => { options.useGlobalMono.should.equal("always"); }); - test('BACK-COMPAT: "csharp.omnisharpUsesMono": false == "omnisharp.useGlobalMono": "auto"', () => - { + test('BACK-COMPAT: "csharp.omnisharpUsesMono": false == "omnisharp.useGlobalMono": "auto"', () => { const vscode = getVSCodeWithConfig(); updateConfig(vscode, 'csharp', 'omnisharpUsesMono', false); @@ -86,8 +96,7 @@ suite("Options tests", () => { options.useGlobalMono.should.equal("auto"); }); - test('BACK-COMPAT: "csharp.omnisharp" is used if it is set and "omnisharp.path" is not', () => - { + test('BACK-COMPAT: "csharp.omnisharp" is used if it is set and "omnisharp.path" is not', () => { const vscode = getVSCodeWithConfig(); updateConfig(vscode, 'csharp', 'omnisharp', 'OldPath'); @@ -96,8 +105,7 @@ suite("Options tests", () => { options.path.should.equal("OldPath"); }); - test('BACK-COMPAT: "csharp.omnisharp" is not used if "omnisharp.path" is set', () => - { + test('BACK-COMPAT: "csharp.omnisharp" is not used if "omnisharp.path" is set', () => { const vscode = getVSCodeWithConfig(); updateConfig(vscode, 'omnisharp', 'path', 'NewPath'); updateConfig(vscode, 'csharp', 'omnisharp', 'OldPath'); @@ -107,8 +115,7 @@ suite("Options tests", () => { options.path.should.equal("NewPath"); }); - test('"omnisharp.defaultLaunchSolution" is used if set', () => - { + test('"omnisharp.defaultLaunchSolution" is used if set', () => { const vscode = getVSCodeWithConfig(); updateConfig(vscode, 'omnisharp', 'defaultLaunchSolution', 'some_valid_solution.sln'); diff --git a/test/unitTests/testAssets/Fakes.ts b/test/unitTests/testAssets/Fakes.ts index 5edb50c61..4473aa9e3 100644 --- a/test/unitTests/testAssets/Fakes.ts +++ b/test/unitTests/testAssets/Fakes.ts @@ -185,10 +185,15 @@ export function getWorkspaceInformationUpdated(msbuild: protocol.MsBuildWorkspac export function getVSCodeWithConfig() { const vscode = getFakeVsCode(); + const _vscodeConfig = getWorkspaceConfiguration(); const _omnisharpConfig = getWorkspaceConfiguration(); const _csharpConfig = getWorkspaceConfiguration(); vscode.workspace.getConfiguration = (section?, resource?) => { + if (!section) { + return _vscodeConfig; + } + if (section === 'omnisharp') { return _omnisharpConfig; } From 4be30aafdc093ba0f53a8940afb3dc131fa2d280 Mon Sep 17 00:00:00 2001 From: Joey Robichaud Date: Wed, 18 Mar 2020 16:46:14 -0700 Subject: [PATCH 3/4] Optionally include search.exclude with excluded paths --- src/omnisharp/options.ts | 28 ++++++++++++++++------------ src/omnisharp/utils.ts | 2 +- test/unitTests/options.test.ts | 22 ++++++++++++++++++++-- 3 files changed, 37 insertions(+), 15 deletions(-) diff --git a/src/omnisharp/options.ts b/src/omnisharp/options.ts index 4fd2162d6..e8dbae148 100644 --- a/src/omnisharp/options.ts +++ b/src/omnisharp/options.ts @@ -119,26 +119,30 @@ export class Options { ); } - public static getExcludedPaths(vscode: vscode): string[] { - let excludePaths: string[] = []; - + public static getExcludedPaths(vscode: vscode, includeSearchExcludes: boolean = false): string[] { let workspaceConfig = vscode.workspace.getConfiguration(); if (!workspaceConfig) { - return excludePaths; + return []; } - let excludeFilesOption = workspaceConfig.get<{ [i: string]: boolean }>('files.exclude'); - if (!excludeFilesOption) { - return excludePaths; - } + let excludePaths = getExcludes(workspaceConfig, 'files.exclude'); - for (let field in excludeFilesOption) { - if (excludeFilesOption[field]) { - excludePaths.push(field); - } + if (includeSearchExcludes) { + excludePaths = excludePaths.concat(getExcludes(workspaceConfig, 'search.exclude')); } return excludePaths; + + function getExcludes(config: WorkspaceConfiguration, option: string): string[] { + let optionValue = config.get<{ [i: string]: boolean }>(option); + if (!optionValue) { + return []; + } + + return Object.entries(optionValue) + .filter(([key, value]) => value) + .map(([key, value]) => key); + } } private static readPathOption(csharpConfig: WorkspaceConfiguration, omnisharpConfig: WorkspaceConfiguration): string | null { diff --git a/src/omnisharp/utils.ts b/src/omnisharp/utils.ts index dfe8bd2f7..aada83c83 100644 --- a/src/omnisharp/utils.ts +++ b/src/omnisharp/utils.ts @@ -179,7 +179,7 @@ function isBlazorWebAssemblyHosted(project: protocol.MSBuildProject, isProjectBl async function isBlazorWebAssemblyProject(project: MSBuildProject): Promise { const projectDirectory = path.dirname(project.Path); const launchSettingsPattern = new vscode.RelativePattern(projectDirectory, '**/launchSettings.json'); - const excludedPathPattern = `{${Options.getExcludedPaths(vscode).join(',')}}`; + const excludedPathPattern = `{${Options.getExcludedPaths(vscode, true).join(',')}}`; const launchSettings = await vscode.workspace.findFiles(launchSettingsPattern, excludedPathPattern); if (!launchSettings) { diff --git a/test/unitTests/options.test.ts b/test/unitTests/options.test.ts index 8d0c44523..f25ac7182 100644 --- a/test/unitTests/options.test.ts +++ b/test/unitTests/options.test.ts @@ -35,7 +35,7 @@ suite("Options tests", () => { expect(options.defaultLaunchSolution).to.be.undefined; }); - test('Verify return no excluded paths when empty', () => { + test('Verify return no excluded paths when files.exclude empty', () => { const vscode = getVSCodeWithConfig(); updateConfig(vscode, null, 'files.exclude', {}); @@ -43,7 +43,7 @@ suite("Options tests", () => { expect(excludedPaths).to.be.empty; }); - test('Verify return excluded paths', () => { + test('Verify return excluded paths when files.exclude populated', () => { const vscode = getVSCodeWithConfig(); updateConfig(vscode, null, 'files.exclude', { "**/node_modules": true, "**/assets": false }); @@ -51,6 +51,24 @@ suite("Options tests", () => { expect(excludedPaths).to.equalTo(["**/node_modules"]); }); + test('Verify return no excluded paths when files.exclude and search.exclude empty', () => { + const vscode = getVSCodeWithConfig(); + updateConfig(vscode, null, 'files.exclude', {}); + updateConfig(vscode, null, 'search.exclude', {}); + + const excludedPaths = Options.getExcludedPaths(vscode, true); + expect(excludedPaths).to.be.empty; + }); + + test('Verify return excluded paths when files.exclude and search.exclude populated', () => { + const vscode = getVSCodeWithConfig(); + updateConfig(vscode, null, 'files.exclude', { "/Library": true }); + updateConfig(vscode, null, 'search.exclude', { "**/node_modules": true, "**/assets": false }); + + const excludedPaths = Options.getExcludedPaths(vscode, true); + expect(excludedPaths).to.be.equalTo(["/Library", "**/node_modules"]); + }); + test('BACK-COMPAT: "omnisharp.loggingLevel": "verbose" == "omnisharp.loggingLevel": "debug"', () => { const vscode = getVSCodeWithConfig(); updateConfig(vscode, 'omnisharp', 'loggingLevel', "verbose"); From bf462643d5af044433316d3ca6d4185451243b74 Mon Sep 17 00:00:00 2001 From: Joey Robichaud Date: Wed, 18 Mar 2020 16:54:03 -0700 Subject: [PATCH 4/4] Pass null URI when getting excluded paths --- src/omnisharp/options.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/omnisharp/options.ts b/src/omnisharp/options.ts index e8dbae148..fcdc9ebc8 100644 --- a/src/omnisharp/options.ts +++ b/src/omnisharp/options.ts @@ -120,7 +120,7 @@ export class Options { } public static getExcludedPaths(vscode: vscode, includeSearchExcludes: boolean = false): string[] { - let workspaceConfig = vscode.workspace.getConfiguration(); + let workspaceConfig = vscode.workspace.getConfiguration(undefined, null); if (!workspaceConfig) { return []; }