Show local project for C#/Java (#1883)

This commit is contained in:
Eric Jizba 2020-02-14 13:21:34 -08:00 коммит произвёл GitHub
Родитель afd650bc89
Коммит 270d46c4f3
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
12 изменённых файлов: 172 добавлений и 58 удалений

4
resources/dark/info.svg Normal file
Просмотреть файл

@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none"
xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.56838 1.03128C10.1595 1.19039 11.6436 1.90518 12.76 3.04996C13.9763 4.28555 14.6955 5.92552 14.7803 7.65726C14.8651 9.38899 14.3098 11.0913 13.2201 12.4398C12.2178 13.6857 10.8113 14.5416 9.24429 14.8594C7.67727 15.1772 6.04844 14.9369 4.64004 14.18C3.22861 13.4066 2.12677 12.1706 1.52004 10.68C0.910598 9.18166 0.829469 7.52043 1.29003 5.96988C1.7496 4.42537 2.72797 3.0868 4.06002 2.17996C5.38101 1.27892 6.97729 0.87217 8.56838 1.03128ZM9.04006 13.8799C10.3829 13.6075 11.5887 12.8756 12.45 11.81C13.3826 10.6509 13.8571 9.18961 13.7834 7.70377C13.7097 6.21792 13.0928 4.81093 12.05 3.74991C11.0949 2.77492 9.82747 2.16667 8.46932 2.0314C7.11117 1.89613 5.74869 2.24247 4.62002 3.00992C3.77045 3.59531 3.08444 4.38792 2.62691 5.31265C2.16939 6.23738 1.95553 7.26359 2.00561 8.2941C2.05568 9.3246 2.36803 10.3253 2.91305 11.2013C3.45807 12.0773 4.2177 12.7997 5.12002 13.3C6.31834 13.9467 7.7058 14.1519 9.04006 13.8799ZM7.375 6L8.625 6L8.625 5L7.375 5L7.375 6ZM8.625 7L8.625 11L7.375 11L7.375 7L8.625 7Z" fill="#75BEFF"/>
</svg>

После

Ширина:  |  Высота:  |  Размер: 1.2 KiB

4
resources/light/info.svg Normal file
Просмотреть файл

@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none"
xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.5682 1.03128C10.1593 1.19039 11.6434 1.90518 12.7598 3.04996C13.9762 4.28555 14.6953 5.92552 14.7801 7.65726C14.8649 9.38899 14.3096 11.0913 13.2199 12.4398C12.2176 13.6857 10.8111 14.5416 9.2441 14.8594C7.67708 15.1772 6.04826 14.9369 4.63985 14.18C3.22843 13.4066 2.12659 12.1706 1.51986 10.68C0.910415 9.18166 0.829286 7.52043 1.28985 5.96988C1.74942 4.42537 2.72779 3.0868 4.05984 2.17996C5.38083 1.27892 6.9771 0.87217 8.5682 1.03128ZM9.03988 13.8799C10.3827 13.6075 11.5886 12.8756 12.4499 11.81C13.3825 10.6509 13.857 9.18961 13.7832 7.70377C13.7095 6.21792 13.0926 4.81093 12.0498 3.74991C11.0947 2.77492 9.82729 2.16667 8.46914 2.0314C7.11099 1.89613 5.7485 2.24247 4.61983 3.00992C3.77027 3.59531 3.08425 4.38792 2.62673 5.31265C2.16921 6.23738 1.95535 7.26359 2.00542 8.2941C2.0555 9.3246 2.36784 10.3253 2.91286 11.2013C3.45789 12.0773 4.21752 12.7997 5.11983 13.3C6.31816 13.9467 7.70562 14.1519 9.03988 13.8799ZM7.37482 6L8.62482 6L8.62482 5L7.37482 5L7.37482 6ZM8.62482 7L8.62482 11L7.37482 11L7.37482 7L8.62482 7Z" fill="#007ACC"/>
</svg>

После

Ширина:  |  Высота:  |  Размер: 1.2 KiB

Просмотреть файл

@ -37,7 +37,7 @@ export async function addBinding(context: IActionContext, data: Uri | LocalFunct
functionJsonPath = data.functionJsonPath;
workspaceFolder = data.parent.parent.workspaceFolder;
workspacePath = data.parent.parent.workspacePath;
projectPath = data.parent.parent.projectPath;
projectPath = data.parent.parent.effectiveProjectPath;
}
const [language, version]: [ProjectLanguage, FuncVersion] = await verifyInitForVSCode(context, projectPath);

Просмотреть файл

@ -83,7 +83,7 @@ export class DotnetInitVSCodeStep extends InitVSCodeStepBase {
const targetFramework: string = await getTargetFramework(projFilePath);
this.setDeploySubpath(context, `bin/Release/${targetFramework}/publish`);
this._debugSubpath = `bin/Debug/${targetFramework}`;
this._debugSubpath = getDotnetDebugSubpath(targetFramework);
}
protected getTasks(): TaskDefinition[] {
@ -153,6 +153,10 @@ export class DotnetInitVSCodeStep extends InitVSCodeStepBase {
}
}
export function getDotnetDebugSubpath(targetFramework: string): string {
return path.posix.join('bin', 'Debug', targetFramework);
}
export async function tryGetCsprojFile(projectPath: string): Promise<string | undefined> {
return await tryGetProjFile(projectPath, /\.csproj$/i);
}

Просмотреть файл

@ -28,7 +28,7 @@ export class JavaInitVSCodeStep extends InitVSCodeStepBase {
this._debugSubpath = '<function_build_path>';
window.showWarningMessage(localize('functionAppNameNotFound', 'Cannot parse the Azure Functions name from pom file, you may need to specify it in the tasks.json.'));
} else {
this._debugSubpath = `target/azure-functions/${functionAppName}/`;
this._debugSubpath = getJavaDebugSubpath(functionAppName);
}
this.setDeploySubpath(context, this._debugSubpath);
@ -49,7 +49,11 @@ export class JavaInitVSCodeStep extends InitVSCodeStepBase {
{
label: javaPackageTaskLabel,
command: 'mvn clean package',
type: 'shell'
type: 'shell',
group: {
kind: 'build',
isDefault: true
}
}
];
}
@ -62,3 +66,7 @@ export class JavaInitVSCodeStep extends InitVSCodeStepBase {
return ['vscjava.vscode-java-debug'];
}
}
export function getJavaDebugSubpath(functionAppName: string): string {
return path.posix.join('target', 'azure-functions', functionAppName);
}

Просмотреть файл

@ -12,14 +12,13 @@ import { AzureWebJobsStorageExecuteStep } from "../commands/appSettings/AzureWeb
import { AzureWebJobsStoragePromptStep } from "../commands/appSettings/AzureWebJobsStoragePromptStep";
import { IAzureWebJobsStorageWizardContext } from "../commands/appSettings/IAzureWebJobsStorageWizardContext";
import { tryGetFunctionProjectRoot } from '../commands/createNewProject/verifyIsProject';
import { functionJsonFileName, localEmulatorConnectionString, localSettingsFileName, projectLanguageSetting, workerRuntimeKey } from "../constants";
import { functionJsonFileName, localEmulatorConnectionString, localSettingsFileName, ProjectLanguage, projectLanguageSetting, workerRuntimeKey } from "../constants";
import { ext } from "../extensionVariables";
import { ParsedFunctionJson } from "../funcConfig/function";
import { azureWebJobsStorageKey, getAzureWebJobsStorage, MismatchBehavior, setLocalAppSetting } from "../funcConfig/local.settings";
import { validateFuncCoreToolsInstalled } from '../funcCoreTools/validateFuncCoreToolsInstalled';
import { localize } from '../localize';
import { getFunctionFolders } from "../tree/localProject/LocalFunctionsTreeItem";
import { supportsLocalProjectTree } from "../tree/localProject/supportsLocalProjectTree";
import { getDebugConfigs, isDebugConfigEqual } from '../vsCodeConfig/launch';
import { getFunctionsWorkerRuntime, getWorkspaceSetting } from "../vsCodeConfig/settings";
@ -70,8 +69,15 @@ export async function preDebugValidate(context: IActionContext, debugConfig: vsc
}
export function canValidateAzureWebJobStorageOnDebug(projectLanguage: string | undefined): boolean {
// For now this is the same as `langSupportsLocalProjectTree`, but that's more of an implementation detail and may change in the future
return supportsLocalProjectTree(projectLanguage);
switch (projectLanguage) {
case ProjectLanguage.CSharp:
case ProjectLanguage.FSharp:
case ProjectLanguage.Java:
// We know if we need `AzureWebJobStorage` based on the function.json files, but those files don't exist until after a build for languages that need to be compiled
return false;
default:
return true;
}
}
function getMatchingWorkspace(debugConfig: vscode.DebugConfiguration): vscode.WorkspaceFolder {

Просмотреть файл

@ -7,14 +7,17 @@ import * as path from 'path';
import { Disposable, workspace, WorkspaceFolder } from 'vscode';
import { AzExtTreeItem, AzureAccountTreeItemBase, GenericTreeItem, IActionContext, ISubscriptionContext } from 'vscode-azureextensionui';
import { tryGetFunctionProjectRoot } from '../commands/createNewProject/verifyIsProject';
import { funcVersionSetting, hostFileName, projectLanguageSetting } from '../constants';
import { getDotnetDebugSubpath, getTargetFramework, tryGetCsprojFile, tryGetFsprojFile } from '../commands/initProjectForVSCode/InitVSCodeStep/DotnetInitVSCodeStep';
import { getJavaDebugSubpath } from '../commands/initProjectForVSCode/InitVSCodeStep/JavaInitVSCodeStep';
import { funcVersionSetting, hostFileName, ProjectLanguage, projectLanguageSetting } from '../constants';
import { ext } from '../extensionVariables';
import { localize } from '../localize';
import { mavenUtils } from '../utils/mavenUtils';
import { treeUtils } from '../utils/treeUtils';
import { getWorkspaceSetting } from '../vsCodeConfig/settings';
import { createRefreshFileWatcher } from './localProject/createRefreshFileWatcher';
import { InvalidLocalProjectTreeItem } from './localProject/InvalidLocalProjectTreeItem';
import { LocalProjectTreeItem } from './localProject/LocalProjectTreeItem';
import { supportsLocalProjectTree } from './localProject/supportsLocalProjectTree';
import { isLocalProjectCV, isProjectCV, isRemoteProjectCV } from './projectContextValues';
import { SubscriptionTreeItem } from './SubscriptionTreeItem';
@ -53,13 +56,24 @@ export class AzureAccountTreeItemWithProjects extends AzureAccountTreeItemBase {
for (const folder of folders) {
const projectPath: string | undefined = await tryGetFunctionProjectRoot(folder.uri.fsPath, true /* suppressPrompt */);
if (projectPath) {
hasLocalProject = true;
try {
hasLocalProject = true;
const projectLanguage: string | undefined = getWorkspaceSetting(projectLanguageSetting, projectPath);
if (supportsLocalProjectTree(projectLanguage)) {
const treeItem: LocalProjectTreeItem = new LocalProjectTreeItem(this, projectPath, folder.uri.fsPath, folder);
let preCompiledProjectPath: string | undefined;
let effectiveProjectPath: string;
const compiledProjectPath: string | undefined = await getCompiledProjectPath(projectPath);
if (compiledProjectPath) {
preCompiledProjectPath = projectPath;
effectiveProjectPath = compiledProjectPath;
} else {
effectiveProjectPath = projectPath;
}
const treeItem: LocalProjectTreeItem = new LocalProjectTreeItem(this, { effectiveProjectPath, folder, preCompiledProjectPath });
this._projectDisposables.push(treeItem);
children.push(treeItem);
} catch (error) {
children.push(new InvalidLocalProjectTreeItem(this, projectPath, error));
}
}
@ -109,3 +123,25 @@ export class AzureAccountTreeItemWithProjects extends AzureAccountTreeItemBase {
return super.pickTreeItemImpl(expectedContextValues);
}
}
async function getCompiledProjectPath(projectPath: string): Promise<string | undefined> {
const projectLanguage: string | undefined = getWorkspaceSetting(projectLanguageSetting, projectPath);
if (projectLanguage === ProjectLanguage.CSharp || projectLanguage === ProjectLanguage.FSharp) {
const projFileName: string | undefined = projectLanguage === ProjectLanguage.CSharp ? await tryGetCsprojFile(projectPath) : await tryGetFsprojFile(projectPath);
if (projFileName) {
const targetFramework: string = await getTargetFramework(path.join(projectPath, projFileName));
return path.join(projectPath, getDotnetDebugSubpath(targetFramework));
} else {
throw new Error(localize('unableToFindProj', 'Unable to detect project file.'));
}
} else if (projectLanguage === ProjectLanguage.Java) {
const functionAppName: string | undefined = await mavenUtils.getFunctionAppNameInPom(path.join(projectPath, 'pom.xml'));
if (!functionAppName) {
throw new Error(localize('unableToGetFunctionAppName', 'Unable to detect property "functionAppName" in pom.xml.'));
} else {
return path.join(projectPath, getJavaDebugSubpath(functionAppName));
}
} else {
return undefined;
}
}

Просмотреть файл

@ -0,0 +1,43 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as path from 'path';
import { AzExtParentTreeItem, AzExtTreeItem, TreeItemIconPath } from 'vscode-azureextensionui';
import { localize } from '../../localize';
import { treeUtils } from '../../utils/treeUtils';
export class InvalidLocalProjectTreeItem extends AzExtParentTreeItem {
public contextValue: string = 'invalidAzFuncLocalProject';
public readonly label: string = localize('localProject', 'Local Project');
private readonly _projectName: string;
private readonly _projectError: unknown | undefined;
public constructor(parent: AzExtParentTreeItem, projectPath: string, projectError: unknown) {
super(parent);
this._projectName = path.basename(projectPath);
this._projectError = projectError;
}
public get iconPath(): TreeItemIconPath {
return treeUtils.getThemedIconPath('CreateNewProject');
}
public get id(): string {
return 'localProject' + this._projectName;
}
public get description(): string {
return this._projectName;
}
public hasMoreChildrenImpl(): boolean {
return false;
}
public async loadMoreChildrenImpl(_clearCache: boolean): Promise<AzExtTreeItem[]> {
throw this._projectError;
}
}

Просмотреть файл

@ -5,19 +5,22 @@
import * as fse from 'fs-extra';
import * as path from 'path';
import { AzExtTreeItem } from 'vscode-azureextensionui';
import { AzExtTreeItem, GenericTreeItem } from 'vscode-azureextensionui';
import { functionJsonFileName } from '../../constants';
import { ParsedFunctionJson } from '../../funcConfig/function';
import { localize } from '../../localize';
import { treeUtils } from '../../utils/treeUtils';
import { FunctionsTreeItemBase } from '../FunctionsTreeItemBase';
import { LocalFunctionTreeItem } from './LocalFunctionTreeItem';
import { LocalProjectTreeItem } from './LocalProjectTreeItem';
export class LocalFunctionsTreeItem extends FunctionsTreeItemBase {
public readonly parent: LocalProjectTreeItem;
public isReadOnly: boolean = false;
public isReadOnly: boolean;
public constructor(parent: LocalProjectTreeItem) {
super(parent);
this.isReadOnly = !!this.parent.preCompiledProjectPath;
}
public hasMoreChildrenImpl(): boolean {
@ -25,27 +28,53 @@ export class LocalFunctionsTreeItem extends FunctionsTreeItemBase {
}
public async loadMoreChildrenImpl(_clearCache: boolean): Promise<AzExtTreeItem[]> {
const functions: string[] = await getFunctionFolders(this.parent.projectPath);
return await this.createTreeItemsWithErrorHandling(
const functions: string[] = await getFunctionFolders(this.parent.effectiveProjectPath);
const children: AzExtTreeItem[] = await this.createTreeItemsWithErrorHandling(
functions,
'azFuncInvalidLocalFunction',
async func => {
const functionJsonPath: string = path.join(this.parent.projectPath, func, functionJsonFileName);
const functionJsonPath: string = path.join(this.parent.effectiveProjectPath, func, functionJsonFileName);
const config: ParsedFunctionJson = new ParsedFunctionJson(await fse.readJSON(functionJsonPath));
return LocalFunctionTreeItem.create(this, func, config, functionJsonPath);
},
(func: string) => func
);
if (this.parent.preCompiledProjectPath) {
const ti: GenericTreeItem = new GenericTreeItem(this, {
label: localize('runBuildTask', 'Run build task to update this list...'),
iconPath: treeUtils.getThemedIconPath('info'),
commandId: 'workbench.action.tasks.build',
contextValue: 'runBuildTask'
});
// By default `GenericTreeItem` will pass itself as the args, but VS Code doesn't seem to like that so pass empty array
ti.commandArgs = [];
children.push(ti);
}
return children;
}
public compareChildrenImpl(item1: AzExtTreeItem, item2: AzExtTreeItem): number {
if (item1 instanceof GenericTreeItem && !(item2 instanceof GenericTreeItem)) {
return -1;
} else if (!(item1 instanceof GenericTreeItem) && item2 instanceof GenericTreeItem) {
return 1;
} else {
return super.compareChildrenImpl(item1, item2);
}
}
}
export async function getFunctionFolders(projectPath: string): Promise<string[]> {
const subpaths: string[] = await fse.readdir(projectPath);
const result: string[] = [];
await Promise.all(subpaths.map(async s => {
if (await fse.pathExists(path.join(projectPath, s, functionJsonFileName))) {
result.push(s);
}
}));
if (await fse.pathExists(projectPath)) {
const subpaths: string[] = await fse.readdir(projectPath);
await Promise.all(subpaths.map(async s => {
if (await fse.pathExists(path.join(projectPath, s, functionJsonFileName))) {
result.push(s);
}
}));
}
return result;
}

Просмотреть файл

@ -26,22 +26,24 @@ export class LocalProjectTreeItem extends AzExtParentTreeItem implements Disposa
public readonly label: string = localize('localProject', 'Local Project');
public readonly source: ProjectSource = ProjectSource.Local;
public readonly projectName: string;
public readonly projectPath: string;
public readonly effectiveProjectPath: string;
public readonly preCompiledProjectPath: string | undefined;
public readonly workspacePath: string;
public readonly workspaceFolder: WorkspaceFolder;
private readonly _disposables: Disposable[] = [];
private readonly _localFunctionsTreeItem: LocalFunctionsTreeItem;
public constructor(parent: AzExtParentTreeItem, projectPath: string, workspacePath: string, workspaceFolder: WorkspaceFolder) {
public constructor(parent: AzExtParentTreeItem, options: { effectiveProjectPath: string; folder: WorkspaceFolder; preCompiledProjectPath?: string }) {
super(parent);
this.projectName = path.basename(projectPath);
this.projectPath = projectPath;
this.workspacePath = workspacePath;
this.workspaceFolder = workspaceFolder;
this.projectName = path.basename(options.preCompiledProjectPath || options.effectiveProjectPath);
this.effectiveProjectPath = options.effectiveProjectPath;
this.workspacePath = options.folder.uri.fsPath;
this.workspaceFolder = options.folder;
this.preCompiledProjectPath = options.preCompiledProjectPath;
this._disposables.push(createRefreshFileWatcher(this, path.join(projectPath, '*', functionJsonFileName)));
this._disposables.push(createRefreshFileWatcher(this, path.join(projectPath, localSettingsFileName)));
this._disposables.push(createRefreshFileWatcher(this, path.join(this.effectiveProjectPath, '*', functionJsonFileName)));
this._disposables.push(createRefreshFileWatcher(this, path.join(this.effectiveProjectPath, localSettingsFileName)));
this._localFunctionsTreeItem = new LocalFunctionsTreeItem(this);
}
@ -91,7 +93,7 @@ export class LocalProjectTreeItem extends AzExtParentTreeItem implements Disposa
public async getHostJson(): Promise<IParsedHostJson> {
const version: FuncVersion = await this.getVersion();
// tslint:disable-next-line: no-any
const data: any = await fse.readJSON(path.join(this.projectPath, hostFileName));
const data: any = await fse.readJSON(path.join(this.effectiveProjectPath, hostFileName));
return parseHostJson(data, version);
}
@ -101,7 +103,7 @@ export class LocalProjectTreeItem extends AzExtParentTreeItem implements Disposa
}
public async getApplicationSettings(): Promise<ApplicationSettings> {
const localSettings: ILocalSettingsJson = await getLocalSettingsJson(path.join(this.projectPath, localSettingsFileName));
const localSettings: ILocalSettingsJson = await getLocalSettingsJson(path.join(this.effectiveProjectPath, localSettingsFileName));
// tslint:disable-next-line: strict-boolean-expressions
return localSettings.Values || {};
}

Просмотреть файл

@ -1,22 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ProjectLanguage } from "../../constants";
export function supportsLocalProjectTree(projectLanguage: string | undefined): boolean {
// The project tree relies heavily on a standard file structure, including "host.json" and "function.json" files
// Languages not listed here (notably C# and Java) require a build step to generate files like "function.json" and are not yet supported
// https://github.com/microsoft/vscode-azurefunctions/issues/1165
const supportedLanguages: ProjectLanguage[] = [
ProjectLanguage.CSharpScript,
ProjectLanguage.FSharpScript,
ProjectLanguage.JavaScript,
ProjectLanguage.PowerShell,
ProjectLanguage.Python,
ProjectLanguage.TypeScript
];
return supportedLanguages.some(l => l === projectLanguage);
}

Просмотреть файл

@ -175,7 +175,7 @@ export function getJavaValidateOptions(appName: string, version: FuncVersion = d
'azureFunctions.projectLanguage': ProjectLanguage.Java,
'azureFunctions.projectRuntime': version,
'azureFunctions.preDeployTask': 'package',
'azureFunctions.deploySubpath': `target/azure-functions/${appName}/`,
'azureFunctions.deploySubpath': `target/azure-functions/${appName}`,
'debug.internalConsoleOptions': 'neverOpen',
},
expectedPaths: [