diff --git a/src/commands/pickFuncProcess.ts b/src/commands/pickFuncProcess.ts index b99a913a3..f0e6af7ab 100644 --- a/src/commands/pickFuncProcess.ts +++ b/src/commands/pickFuncProcess.ts @@ -10,7 +10,7 @@ import * as vscode from 'vscode'; import { hostStartTaskName } from '../constants'; import { preDebugValidate, type IPreDebugValidateResult } from '../debug/validatePreDebug'; import { ext } from '../extensionVariables'; -import { buildPathToWorkspaceFolderMap, getFuncPortFromTaskOrProject, isFuncHostTask, runningFuncTaskMap, stopFuncTaskIfRunning, type IRunningFuncTask } from '../funcCoreTools/funcHostTask'; +import { buildPathToWorkspaceFolderMap, getFuncPortFromTaskOrProject, isFuncHostTask, resolveAndNormalizeCwd, runningFuncTaskMap, stopFuncTaskIfRunning, type IRunningFuncTask } from '../funcCoreTools/funcHostTask'; import { localize } from '../localize'; import { delay } from '../utils/delay'; import { requestUtils } from '../utils/requestUtils'; @@ -134,10 +134,11 @@ export async function pickFuncProcess(context: IActionContext, debugConfig: vsco async function waitForPrevFuncTaskToStop(workspaceFolder: vscode.WorkspaceFolder, buildPath?: string): Promise { await stopFuncTaskIfRunning(workspaceFolder, buildPath); + const normalizedBuildPath = resolveAndNormalizeCwd(workspaceFolder, buildPath); const timeoutInSeconds: number = 30; const maxTime: number = Date.now() + timeoutInSeconds * 1000; while (Date.now() < maxTime) { - if (!runningFuncTaskMap.has(workspaceFolder)) { + if (!runningFuncTaskMap.has(workspaceFolder, normalizedBuildPath)) { return; } await delay(1000); @@ -178,7 +179,7 @@ async function startFuncTask(context: IActionContext, workspaceFolder: vscode.Wo throw taskError; } - const taskInfo: IRunningFuncTask | undefined = runningFuncTaskMap.get(workspaceFolder, buildPath); + const taskInfo: IRunningFuncTask | undefined = runningFuncTaskMap.get(workspaceFolder, resolveAndNormalizeCwd(workspaceFolder, buildPath)); if (taskInfo) { for (const scheme of ['http', 'https']) { const statusRequest: AzExtRequestPrepareOptions = { url: `${scheme}://localhost:${funcPort}/admin/host/status`, method: 'GET' }; diff --git a/src/debug/nodes/HostErrorNode.ts b/src/debug/nodes/HostErrorNode.ts index 9be506516..7e3141057 100644 --- a/src/debug/nodes/HostErrorNode.ts +++ b/src/debug/nodes/HostErrorNode.ts @@ -10,7 +10,7 @@ export class HostErrorNode { public readonly kind = 'hostError' as const; constructor( - public readonly workspaceFolder: vscode.WorkspaceFolder | vscode.TaskScope, + public readonly workspaceFolder: vscode.WorkspaceFolder, public readonly portNumber: string, public readonly message: string, public readonly cwd?: string, diff --git a/src/debug/nodes/HostTaskNode.ts b/src/debug/nodes/HostTaskNode.ts index 379853ed8..01b5cb001 100644 --- a/src/debug/nodes/HostTaskNode.ts +++ b/src/debug/nodes/HostTaskNode.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { runningFuncTaskMap } from '../../funcCoreTools/funcHostTask'; +import { resolveAndNormalizeCwd, runningFuncTaskMap } from '../../funcCoreTools/funcHostTask'; import { buildHostTooltip, formatTimestamp, getScopeLabel } from './funcHostDebugUtils'; import { HostErrorNode } from './HostErrorNode'; @@ -12,14 +12,14 @@ export class HostTaskNode { public readonly kind = 'hostTask' as const; constructor( - public readonly workspaceFolder: vscode.WorkspaceFolder | vscode.TaskScope, + public readonly workspaceFolder: vscode.WorkspaceFolder, public readonly portNumber: string, public readonly startTime: Date, public readonly cwd?: string, ) { } public getTreeItem(): vscode.TreeItem { - const task = runningFuncTaskMap.get(this.workspaceFolder, this.cwd); + const task = runningFuncTaskMap.get(this.workspaceFolder, resolveAndNormalizeCwd(this.workspaceFolder, this.cwd)); const scopeLabel = getScopeLabel(this.workspaceFolder); const label = `${scopeLabel} (${this.portNumber})`; @@ -34,7 +34,7 @@ export class HostTaskNode { } public getChildren(): HostErrorNode[] { - const task = runningFuncTaskMap.get(this.workspaceFolder, this.cwd); + const task = runningFuncTaskMap.get(this.workspaceFolder, resolveAndNormalizeCwd(this.workspaceFolder, this.cwd)); const errors = task?.errorLogs ?? []; return errors .slice() diff --git a/src/debug/nodes/funcHostDebugUtils.ts b/src/debug/nodes/funcHostDebugUtils.ts index afb7862ad..ef5ecddf6 100644 --- a/src/debug/nodes/funcHostDebugUtils.ts +++ b/src/debug/nodes/funcHostDebugUtils.ts @@ -10,10 +10,8 @@ export function formatTimestamp(date: Date): string { return date.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit', second: '2-digit' }); } -export function getScopeLabel(scope: vscode.WorkspaceFolder | vscode.TaskScope): string { - return typeof scope === 'object' - ? scope.name - : localize('funcHostDebug.globalScope', 'Global'); +export function getScopeLabel(scope: vscode.WorkspaceFolder): string { + return scope.name; } export function buildHostTooltip(opts: { label: string; scopeLabel: string; portNumber: string; startTime: Date; stopTime?: Date; cwd?: string; pid?: number }): vscode.MarkdownString { diff --git a/src/debug/registerFunctionHostDebugView.ts b/src/debug/registerFunctionHostDebugView.ts index 8bdfd9181..f334e8afd 100644 --- a/src/debug/registerFunctionHostDebugView.ts +++ b/src/debug/registerFunctionHostDebugView.ts @@ -6,7 +6,7 @@ import { registerCommand, type IActionContext } from '@microsoft/vscode-azext-utils'; import * as vscode from 'vscode'; import { getRecentLogsPlainText } from '../funcCoreTools/funcHostErrorUtils'; -import { clearStoppedSessions, onRunningFuncTasksChanged, runningFuncTaskMap, type IRunningFuncTask } from '../funcCoreTools/funcHostTask'; +import { clearStoppedSessions, onRunningFuncTasksChanged, resolveAndNormalizeCwd, runningFuncTaskMap, type IRunningFuncTask } from '../funcCoreTools/funcHostTask'; import { localize } from '../localize'; import { stripAnsiControlCharacters } from '../utils/ansiUtils'; import { FuncHostDebugViewProvider, HostErrorNode, HostTaskNode, StoppedHostNode } from './FunctionHostDebugView'; @@ -40,7 +40,7 @@ function getNodeContext(args: unknown): { scopeLabel: string; portNumber: string errorOutput: stripAnsiControlCharacters(args.message).trim() || args.message, }; } else if (isHostTaskNode(args)) { - const task = runningFuncTaskMap.get(args.workspaceFolder, args.cwd); + const task = runningFuncTaskMap.get(args.workspaceFolder, resolveAndNormalizeCwd(args.workspaceFolder, args.cwd)); return { scopeLabel: getScopeLabel(args.workspaceFolder), portNumber: args.portNumber, @@ -109,7 +109,7 @@ export function registerFunctionHostDebugView(context: vscode.ExtensionContext): registerCommand('azureFunctions.funcHostDebug.copyRecentLogs', async (actionContext: IActionContext, args: unknown) => { actionContext.telemetry.properties.source = 'funcHostDebugView'; if (isHostTaskNode(args)) { - const task = runningFuncTaskMap.get(args.workspaceFolder, args.cwd); + const task = runningFuncTaskMap.get(args.workspaceFolder, resolveAndNormalizeCwd(args.workspaceFolder, args.cwd)); const text = getRecentLogsPlainText(task); await vscode.env.clipboard.writeText(text); } else if (isStoppedHostNode(args)) { @@ -122,7 +122,7 @@ export function registerFunctionHostDebugView(context: vscode.ExtensionContext): actionContext.telemetry.properties.source = 'funcHostDebugView'; let text: string | undefined; if (isHostTaskNode(args)) { - const task = runningFuncTaskMap.get(args.workspaceFolder, args.cwd); + const task = runningFuncTaskMap.get(args.workspaceFolder, resolveAndNormalizeCwd(args.workspaceFolder, args.cwd)); text = getRecentLogsPlainText(task); } else if (isStoppedHostNode(args)) { text = getRecentLogsPlainText(args.stoppedTask); diff --git a/src/funcCoreTools/funcHostTask.ts b/src/funcCoreTools/funcHostTask.ts index a0d7b7984..d693c04d1 100644 --- a/src/funcCoreTools/funcHostTask.ts +++ b/src/funcCoreTools/funcHostTask.ts @@ -44,14 +44,14 @@ export interface IStoppedFuncTask { portNumber: string; startTime: Date; stopTime: Date; - workspaceFolder: vscode.WorkspaceFolder | vscode.TaskScope; + workspaceFolder: vscode.WorkspaceFolder; cwd?: string; logs: string[]; errorLogs: string[]; } export interface IRunningFuncTaskWithScope { - scope: vscode.WorkspaceFolder | vscode.TaskScope; + scope: vscode.WorkspaceFolder; task: IRunningFuncTask; } @@ -65,59 +65,64 @@ namespace DotnetDebugDebugConfiguration { } } -class RunningFunctionTaskMap { - private _map: Map = new Map(); - - public set(key: vscode.WorkspaceFolder | vscode.TaskScope, value: IRunningFuncTask): void { - const values = this._map.get(key) || []; - values.push(value); - this._map.set(key, values); +/** + * Resolves a raw cwd string by replacing `${workspaceFolder}` with the actual workspace folder path, + * then normalizes the result. Callers should use this before interacting with `runningFuncTaskMap`. + */ +export function resolveAndNormalizeCwd(folder: vscode.WorkspaceFolder, rawCwd?: string): string | undefined { + if (!rawCwd) { + return undefined; } + const resolved = rawCwd.replace('${workspaceFolder}', folder.uri.path); + return normalizePath(resolved); +} - public get(key: vscode.WorkspaceFolder | vscode.TaskScope, buildPath?: string): IRunningFuncTask | undefined { - const values = this._map.get(key) || []; - return values.find(t => { - const taskExecution = t.taskExecution.task.execution as vscode.ShellExecution; - // the cwd will include ${workspaceFolder} from our tasks.json so we need to replace it with the actual path - const workspacePath = typeof t.taskExecution.task?.scope === 'object' - ? (t.taskExecution.task.scope as vscode.WorkspaceFolder).uri?.path - : undefined; - const taskDirectory = workspacePath - ? taskExecution.options?.cwd?.replace('${workspaceFolder}', workspacePath) - : taskExecution.options?.cwd; - const resolvedBuildPath = workspacePath - ? buildPath?.replace('${workspaceFolder}', workspacePath) - : buildPath; - - // When neither cwd is set, both tasks use the default working directory — treat as a match - if (!taskDirectory && !resolvedBuildPath) { - return true; - } +class RunningFunctionTaskMap { + private _map: Map = new Map(); - return taskDirectory && resolvedBuildPath && normalizePath(taskDirectory) === normalizePath(resolvedBuildPath); - }); + private makeKey(folder: vscode.WorkspaceFolder, normalizedCwd?: string): string { + const folderKey = folder.uri.toString(); + return normalizedCwd ? `${folderKey}::${normalizedCwd}` : folderKey; } - public getAll(key: vscode.WorkspaceFolder | vscode.TaskScope): (IRunningFuncTask | undefined)[] { - return this._map.get(key) || []; + private folderPrefix(folder: vscode.WorkspaceFolder): string { + return folder.uri.toString(); } - public has(key: vscode.WorkspaceFolder | vscode.TaskScope, buildPath?: string): boolean { - return !!this.get(key, buildPath); + public set(folder: vscode.WorkspaceFolder, normalizedCwd: string | undefined, value: IRunningFuncTask): void { + this._map.set(this.makeKey(folder, normalizedCwd), value); } - public delete(key: vscode.WorkspaceFolder | vscode.TaskScope, buildPath?: string): void { - const value = this.get(key, buildPath); - const values = this._map.get(key) || []; + public get(folder: vscode.WorkspaceFolder, normalizedCwd?: string): IRunningFuncTask | undefined { + return this._map.get(this.makeKey(folder, normalizedCwd)); + } - if (value) { - // remove the individual entry from the array - values.splice(values.indexOf(value), 1); - this._map.set(key, values); + public getAll(folder: vscode.WorkspaceFolder): IRunningFuncTask[] { + const prefix = this.folderPrefix(folder); + const results: IRunningFuncTask[] = []; + for (const [key, value] of this._map) { + if (key === prefix || key.startsWith(`${prefix}::`)) { + results.push(value); + } } + return results; + } + + public has(folder: vscode.WorkspaceFolder, normalizedCwd?: string): boolean { + return !!this.get(folder, normalizedCwd); + } - if (values?.length === 0) { - this._map.delete(key); + public delete(folder: vscode.WorkspaceFolder, normalizedCwd?: string): void { + if (normalizedCwd !== undefined) { + this._map.delete(this.makeKey(folder, normalizedCwd)); + } else { + // Delete all entries for this folder + const prefix = this.folderPrefix(folder); + for (const key of [...this._map.keys()]) { + if (key === prefix || key.startsWith(`${prefix}::`)) { + this._map.delete(key); + } + } } } } @@ -135,7 +140,7 @@ export function clearStoppedSessions(): void { runningFuncTasksChangedEmitter.fire(); } -const funcTaskStartedEmitter = new vscode.EventEmitter<{ scope: vscode.WorkspaceFolder | vscode.TaskScope, execution?: vscode.ShellExecution }>(); +const funcTaskStartedEmitter = new vscode.EventEmitter<{ scope: vscode.WorkspaceFolder, execution?: vscode.ShellExecution }>(); export const onFuncTaskStarted = funcTaskStartedEmitter.event; const runningFuncTasksChangedEmitter = new vscode.EventEmitter(); @@ -194,8 +199,11 @@ export function registerFuncHostTaskEvents(): void { context.telemetry.suppressIfSuccessful = true; - if (e.execution.task.scope !== undefined && isFuncHostTask(e.execution.task)) { - const portNumber = await getFuncPortFromTaskOrProject(context, e.execution.task, e.execution.task.scope); + if (e.execution.task.scope !== undefined && typeof e.execution.task.scope === 'object' && isFuncHostTask(e.execution.task)) { + const scope = e.execution.task.scope as vscode.WorkspaceFolder; + const portNumber = await getFuncPortFromTaskOrProject(context, e.execution.task, scope); + const rawCwd = (e.execution.task.execution as vscode.ShellExecution).options?.cwd; + const normalizedCwd = resolveAndNormalizeCwd(scope, rawCwd); const logs: string[] = []; const runningFuncTask: IRunningFuncTask = { processId: e.processId, @@ -209,8 +217,8 @@ export function registerFuncHostTaskEvents(): void { streamAbortController: new AbortController(), }; - runningFuncTaskMap.set(e.execution.task.scope, runningFuncTask); - funcTaskStartedEmitter.fire({ scope: e.execution.task.scope, execution: e.execution.task.execution as vscode.ShellExecution }); + runningFuncTaskMap.set(scope, normalizedCwd, runningFuncTask); + funcTaskStartedEmitter.fire({ scope, execution: e.execution.task.execution as vscode.ShellExecution }); runningFuncTasksChangedEmitter.fire(); } @@ -219,9 +227,11 @@ export function registerFuncHostTaskEvents(): void { registerEvent('azureFunctions.onDidEndTask', vscode.tasks.onDidEndTaskProcess, async (context: IActionContext, e: vscode.TaskProcessEndEvent) => { context.errorHandling.suppressDisplay = true; context.telemetry.suppressIfSuccessful = true; - if (e.execution.task.scope !== undefined && isFuncHostTask(e.execution.task)) { - const cwd = (e.execution.task.execution as vscode.ShellExecution).options?.cwd; - const task = runningFuncTaskMap.get(e.execution.task.scope, cwd); + if (e.execution.task.scope !== undefined && typeof e.execution.task.scope === 'object' && isFuncHostTask(e.execution.task)) { + const scope = e.execution.task.scope as vscode.WorkspaceFolder; + const rawCwd = (e.execution.task.execution as vscode.ShellExecution).options?.cwd; + const normalizedCwd = resolveAndNormalizeCwd(scope, rawCwd); + const task = runningFuncTaskMap.get(scope, normalizedCwd); // Abort the stream iteration to prevent it from hanging indefinitely if (task?.streamAbortController) { @@ -234,14 +244,14 @@ export function registerFuncHostTaskEvents(): void { portNumber: task.portNumber, startTime: task.startTime, stopTime: new Date(), - workspaceFolder: e.execution.task.scope, - cwd, + workspaceFolder: scope, + cwd: rawCwd, logs: task.logs.slice(), errorLogs: (task.errorLogs ?? []).slice(), }); } - runningFuncTaskMap.delete(e.execution.task.scope, cwd); + runningFuncTaskMap.delete(scope, normalizedCwd); runningFuncTasksChangedEmitter.fire(); } @@ -249,14 +259,15 @@ export function registerFuncHostTaskEvents(): void { registerEvent('azureFunctions.onFuncTaskStarted', onFuncTaskStarted, async ( context: IActionContext, - event: { scope: vscode.WorkspaceFolder | vscode.TaskScope; execution?: vscode.ShellExecution } + event: { scope: vscode.WorkspaceFolder; execution?: vscode.ShellExecution } ) => { context.errorHandling.suppressDisplay = true; context.telemetry.suppressIfSuccessful = true; const { scope, execution } = event; - const task = runningFuncTaskMap.get(scope, execution?.options?.cwd); + const normalizedCwd = resolveAndNormalizeCwd(scope, execution?.options?.cwd); + const task = runningFuncTaskMap.get(scope, normalizedCwd); if (!task) { return; } @@ -320,16 +331,16 @@ export function registerFuncHostTaskEvents(): void { }); } -export async function stopFuncTaskIfRunning(workspaceFolder: vscode.WorkspaceFolder | vscode.TaskScope, buildPath?: string, killAll?: boolean, terminate?: boolean): Promise { - let runningFuncTask: (IRunningFuncTask | undefined)[] | undefined; +export async function stopFuncTaskIfRunning(workspaceFolder: vscode.WorkspaceFolder, buildPath?: string, killAll?: boolean, terminate?: boolean): Promise { + const normalizedBuildPath = resolveAndNormalizeCwd(workspaceFolder, buildPath); + let runningFuncTask: (IRunningFuncTask | undefined)[]; if (killAll) { - // get all is needed here runningFuncTask = runningFuncTaskMap.getAll(workspaceFolder); } else { - runningFuncTask = [runningFuncTaskMap.get(workspaceFolder, buildPath)]; + runningFuncTask = [runningFuncTaskMap.get(workspaceFolder, normalizedBuildPath)]; } - if (runningFuncTask !== undefined && runningFuncTask.length > 0) { + if (runningFuncTask.length > 0) { for (const runningFuncTaskItem of runningFuncTask) { if (!runningFuncTaskItem) { break; } if (terminate) { @@ -340,8 +351,8 @@ export async function stopFuncTaskIfRunning(workspaceFolder: vscode.WorkspaceFol } } - if (buildPath) { - runningFuncTaskMap.delete(workspaceFolder, buildPath); + if (normalizedBuildPath) { + runningFuncTaskMap.delete(workspaceFolder, normalizedBuildPath); } } @@ -356,7 +367,7 @@ export async function stopFuncTaskIfRunning(workspaceFolder: vscode.WorkspaceFol * Kills the func process by first trying to find it by port or throws an error if it couldn't find it * @param runningFuncTask The running func task information */ -async function killFuncProcessByPortOrPid(runningFuncTask: IRunningFuncTask, workspaceFolder: vscode.WorkspaceFolder | vscode.TaskScope): Promise { +async function killFuncProcessByPortOrPid(runningFuncTask: IRunningFuncTask, workspaceFolder: vscode.WorkspaceFolder): Promise { try { // First, try to find the real func process by looking for what's listening on the port const realFuncPid = await findPidByPort(runningFuncTask.portNumber); @@ -373,7 +384,7 @@ async function killFuncProcessByPortOrPid(runningFuncTask: IRunningFuncTask, wor } } -export async function getFuncPortFromTaskOrProject(context: IActionContext, funcTask: vscode.Task | undefined, projectPathOrTaskScope: string | vscode.WorkspaceFolder | vscode.TaskScope): Promise { +export async function getFuncPortFromTaskOrProject(context: IActionContext, funcTask: vscode.Task | undefined, projectPathOrWorkspaceFolder: string | vscode.WorkspaceFolder): Promise { try { // First, check the task itself if (funcTask && funcTask.execution instanceof vscode.ShellExecution) { @@ -385,10 +396,10 @@ export async function getFuncPortFromTaskOrProject(context: IActionContext, func // Second, check local.settings.json let projectPath: string | undefined; - if (typeof projectPathOrTaskScope === 'string') { - projectPath = projectPathOrTaskScope; - } else if (typeof projectPathOrTaskScope === 'object') { - projectPath = await tryGetFunctionProjectRoot(context, projectPathOrTaskScope); + if (typeof projectPathOrWorkspaceFolder === 'string') { + projectPath = projectPathOrWorkspaceFolder; + } else if (typeof projectPathOrWorkspaceFolder === 'object') { + projectPath = await tryGetFunctionProjectRoot(context, projectPathOrWorkspaceFolder); } if (projectPath) { diff --git a/src/tree/localProject/LocalProjectTreeItem.ts b/src/tree/localProject/LocalProjectTreeItem.ts index 81f36f64e..f0354edf9 100644 --- a/src/tree/localProject/LocalProjectTreeItem.ts +++ b/src/tree/localProject/LocalProjectTreeItem.ts @@ -6,7 +6,7 @@ import { AppSettingsTreeItem, isSettingConnectionString } from '@microsoft/vscode-azext-azureappsettings'; import { callWithTelemetryAndErrorHandling, type AzExtParentTreeItem, type AzExtTreeItem, type IActionContext } from '@microsoft/vscode-azext-utils'; import * as path from 'path'; -import { Disposable, type TaskScope, type WorkspaceFolder } from 'vscode'; +import { Disposable, type WorkspaceFolder } from 'vscode'; import { type FuncVersion } from '../../FuncVersion'; import { LocalSettingsClientProvider } from '../../commands/appSettings/localSettings/LocalSettingsClient'; import { tryGetLocalSettingsFileNoPrompt } from '../../commands/appSettings/localSettings/getLocalSettingsFile'; @@ -59,8 +59,6 @@ export class LocalProjectTreeItem extends LocalProjectTreeItemBase implements Di this._disposables.push(createRefreshFileWatcher(this, path.join(this.effectiveProjectPath, localSettingsFileName))); this._disposables.push(onFuncTaskStarted(async event => this.onFuncTaskChanged(event))); - // onDotnetFuncTaskReady currently emits a TaskScope directly (for back-compat), - // so adapt it to the event-object shape expected by onFuncTaskChanged. this._disposables.push(onDotnetFuncTaskReady(async scope => this.onFuncTaskChanged({ scope }))); this._localFunctionsTreeItem = new LocalFunctionsTreeItem(this); @@ -125,7 +123,7 @@ export class LocalProjectTreeItem extends LocalProjectTreeItemBase implements Di await this.project.setApplicationSetting(context, key, value); } - private async onFuncTaskChanged(event: { scope: WorkspaceFolder | TaskScope | undefined }): Promise { + private async onFuncTaskChanged(event: { scope: WorkspaceFolder | undefined }): Promise { await callWithTelemetryAndErrorHandling('onFuncTaskChanged', async (context: IActionContext) => { if (this.workspaceFolder === event.scope) { context.errorHandling.suppressDisplay = true; diff --git a/src/workspace/LocalProject.ts b/src/workspace/LocalProject.ts index e5530e339..20559c2f5 100644 --- a/src/workspace/LocalProject.ts +++ b/src/workspace/LocalProject.ts @@ -9,7 +9,7 @@ import { type FuncVersion } from "../FuncVersion"; import { hostFileName, localSettingsFileName } from "../constants"; import { parseHostJson, type IParsedHostJson } from "../funcConfig/host"; import { MismatchBehavior, getLocalSettingsJson, setLocalAppSetting, type ILocalSettingsJson } from "../funcConfig/local.settings"; -import { getFuncPortFromTaskOrProject, isFuncHostTask, runningFuncTaskMap } from "../funcCoreTools/funcHostTask"; +import { getFuncPortFromTaskOrProject, isFuncHostTask, resolveAndNormalizeCwd, runningFuncTaskMap } from "../funcCoreTools/funcHostTask"; import { type ApplicationSettings, type FuncHostRequest } from "../tree/IProjectTreeItem"; import { ProjectSource } from "../tree/projectContextValues"; import { requestUtils } from "../utils/requestUtils"; @@ -42,7 +42,7 @@ export class LocalProject implements LocalProjectInternal { } public async getHostRequest(context: IActionContext): Promise { - let port = runningFuncTaskMap.get(this.options.folder)?.portNumber; + let port = runningFuncTaskMap.get(this.options.folder, resolveAndNormalizeCwd(this.options.folder, this.options.effectiveProjectPath))?.portNumber; if (!port) { const funcTask: Task | undefined = (await tasks.fetchTasks()).find(t => t.scope === this.options.folder && isFuncHostTask(t)); port = await getFuncPortFromTaskOrProject(context, funcTask, this.options.effectiveProjectPath); diff --git a/src/workspace/listLocalFunctions.ts b/src/workspace/listLocalFunctions.ts index 229b4c11c..f211767cc 100644 --- a/src/workspace/listLocalFunctions.ts +++ b/src/workspace/listLocalFunctions.ts @@ -8,7 +8,7 @@ import { type AzExtPipelineResponse } from '@microsoft/vscode-azext-azureutils'; import { AzExtFsExtra, callWithTelemetryAndErrorHandling, nonNullProp, parseError, type IActionContext } from "@microsoft/vscode-azext-utils"; import { functionJsonFileName } from "../constants"; import { ParsedFunctionJson } from "../funcConfig/function"; -import { runningFuncTaskMap } from "../funcCoreTools/funcHostTask"; +import { resolveAndNormalizeCwd, runningFuncTaskMap } from "../funcCoreTools/funcHostTask"; import { ProjectNotRunningError, getFunctionFolders } from "../tree/localProject/LocalFunctionsTreeItem"; import { nonNullValue } from "../utils/nonNull"; import { isNodeV4Plus, isPythonV2Plus } from "../utils/programmingModelUtils"; @@ -76,7 +76,7 @@ function getHostStartTimeoutMS(): number { * Some projects (e.g. .NET Isolated and PyStein (i.e. Python model >=2)) don't have typical "function.json" files, so we'll have to ping localhost to get functions (only available if the project is running) */ async function getFunctionsForHostedProject(context: IActionContext, project: LocalProjectInternal): Promise { - if (runningFuncTaskMap.has(project.options.folder, project.options.effectiveProjectPath)) { + if (runningFuncTaskMap.has(project.options.folder, resolveAndNormalizeCwd(project.options.folder, project.options.effectiveProjectPath))) { const hostRequest = await project.getHostRequest(context); const timeout = getHostStartTimeoutMS(); const startTime = Date.now();