From 4f9bf977820074427f08d0457f9b0de46b3df6d4 Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Tue, 15 Aug 2023 08:32:43 -0700 Subject: [PATCH] Paths are normalized for Uri parsing --- vscode/src/common.ts | 12 +++-- vscode/src/debugger/activate.ts | 27 +++++++--- vscode/src/debugger/session.ts | 92 ++++++++++++++++++--------------- 3 files changed, 79 insertions(+), 52 deletions(-) diff --git a/vscode/src/common.ts b/vscode/src/common.ts index 38f6165fca..0b88910ec2 100644 --- a/vscode/src/common.ts +++ b/vscode/src/common.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -import { DocumentFilter } from "vscode"; +import { DocumentFilter, TextDocument, Uri } from "vscode"; export const qsharpLanguageId = "qsharp"; // Matches all Q# documents, including unsaved files, notebook cells, etc. @@ -16,7 +16,11 @@ export const qsharpNotebookCellDocumentFilter: DocumentFilter = { export const qsharpExtensionId = "qsharp-vscode"; export interface FileAccessor { - readFile(uri: string): Promise; - readFileAsString(uri: string): Promise; - writeFile(uri: string, contents: Uint8Array): Promise; + normalizePath(path: string): string; + resolvePathToUri(path: string): Uri; + openFile(path: string): Promise; + openUri(uri: Uri): Promise; + readFile(path: string): Promise; + readFileAsString(path: string): Promise; + writeFile(path: string, contents: Uint8Array): Promise; } diff --git a/vscode/src/debugger/activate.ts b/vscode/src/debugger/activate.ts index f692114b49..c04d2239c2 100644 --- a/vscode/src/debugger/activate.ts +++ b/vscode/src/debugger/activate.ts @@ -125,15 +125,30 @@ class QsDebugConfigProvider implements vscode.DebugConfigurationProvider { } export const workspaceFileAccessor: FileAccessor = { - async readFile(uri: string): Promise { - return await vscode.workspace.fs.readFile(vscode.Uri.parse(uri)); + normalizePath(path: string): string { + return path.replace(/\\/g, "/"); }, - async readFileAsString(uri: string): Promise { - const contents = await this.readFile(uri); + resolvePathToUri(path: string): vscode.Uri { + const normalizedPath = this.normalizePath(path); + return vscode.Uri.parse(normalizedPath, false); + }, + async openFile(path: string): Promise { + const uri: vscode.Uri = this.resolvePathToUri(path); + return await vscode.workspace.openTextDocument(uri); + }, + async openUri(uri: vscode.Uri): Promise { + return await vscode.workspace.openTextDocument(uri); + }, + async readFile(path: string): Promise { + let uri: vscode.Uri = this.resolvePathToUri(path); + return await vscode.workspace.fs.readFile(uri); + }, + async readFileAsString(path: string): Promise { + const contents = await this.readFile(path); return new TextDecoder().decode(contents); }, - async writeFile(uri: string, contents: Uint8Array) { - await vscode.workspace.fs.writeFile(vscode.Uri.parse(uri), contents); + async writeFile(path: string, contents: Uint8Array) { + await vscode.workspace.fs.writeFile(this.resolvePathToUri(path), contents); }, }; diff --git a/vscode/src/debugger/session.ts b/vscode/src/debugger/session.ts index 4993d64ed5..48efadd4b8 100644 --- a/vscode/src/debugger/session.ts +++ b/vscode/src/debugger/session.ts @@ -51,7 +51,7 @@ export class QscDebugSession extends LoggingDebugSession { private breakpoints: Map; private variableHandles = new Handles<"locals" | "quantum">(); private failed: boolean; - private program: string; + private program: vscode.Uri; private eventTarget: QscEventTarget; private supportsVariableType = false; @@ -62,7 +62,7 @@ export class QscDebugSession extends LoggingDebugSession { ) { super(); - this.program = vscode.Uri.parse(this.config.program).path; + this.program = fileAccessor.resolvePathToUri(this.config.program); this.failed = false; this.eventTarget = createDebugConsoleEventTarget((message) => { this.writeToStdOut(message); @@ -75,17 +75,20 @@ export class QscDebugSession extends LoggingDebugSession { } public async init(): Promise { - const programText = await this.fileAccessor.readFileAsString( - this.config.program - ); + const programText = ( + await this.fileAccessor.openUri(this.program) + ).getText(); + const loaded = await this.debugService.loadSource( - this.program, + this.program.toString(), programText ); if (loaded) { - const locations = await this.debugService.getBreakpoints(this.program); + const locations = await this.debugService.getBreakpoints( + this.program.toString() + ); log.trace(`init breakpointLocations: %O`, locations); - this.breakpointLocations.set(this.program, locations); + this.breakpointLocations.set(this.program.toString(), locations); } else { log.warn(`compilation failed.`); this.failed = true; @@ -299,7 +302,7 @@ export class QscDebugSession extends LoggingDebugSession { private getBreakpointIds(): number[] { const bps: number[] = []; - for (const bp of this.breakpoints.get(this.program) ?? []) { + for (const bp of this.breakpoints.get(this.program.toString()) ?? []) { if (bp && bp.id) { bps.push(bp.id); } @@ -364,19 +367,20 @@ export class QscDebugSession extends LoggingDebugSession { breakpoints: [], }; - const fileUri = vscode.Uri.file(args.source.path ?? ""); + const file = await this.fileAccessor + .openFile(args.source.path ?? "") + .catch((e) => { + log.error(`Failed to open file: ${e}`); + const fileUri = this.fileAccessor.resolvePathToUri( + args.source.path ?? "" + ); + log.trace( + "breakpointLocationsRequest, target file: " + fileUri.toString() + ); + }); - const file = vscode.workspace.textDocuments.find( - (td) => td.uri.path === fileUri.path - ); - if (!file) { - for (const td of vscode.workspace.textDocuments) { - log.trace("breakpointLocationsRequest: potential file" + td.uri.path); - } - log.trace("breakpointLocationsRequest: target file" + fileUri.path); - } const targetLineNumber = this.convertClientLineToDebugger(args.line); - if (fileUri && file && targetLineNumber < file.lineCount) { + if (file && targetLineNumber < file.lineCount) { // Map request start/end line/column to file offset for debugger const line = file.lineAt(targetLineNumber); const lineRange = line.range; @@ -406,7 +410,7 @@ export class QscDebugSession extends LoggingDebugSession { // where the rest of the statement is on the next line(s) const bps = this.breakpointLocations - .get(fileUri.path) + .get(file.uri.toString()) ?.filter((bp) => startOffset <= bp.lo && bp.hi <= endOffset) ?? []; log.trace(`breakpointLocationsRequest: candidates %O`, bps); @@ -439,26 +443,24 @@ export class QscDebugSession extends LoggingDebugSession { ): Promise { log.trace(`setBreakPointsRequest: %O`, args); - const fileUri = vscode.Uri.file(args.source.path ?? ""); - - const file = vscode.workspace.textDocuments.find( - (td) => td.uri.path === fileUri.path - ); - if (!file) { - for (const td of vscode.workspace.textDocuments) { - log.trace("setBreakPointsRequest: potential file" + td.uri.path); - } - log.trace("setBreakPointsRequest: target file" + fileUri.path); - } + const file = await this.fileAccessor + .openFile(args.source.path ?? "") + .catch((e) => { + log.error(`setBreakPointsRequest - Failed to open file: ${e}`); + const fileUri = this.fileAccessor.resolvePathToUri( + args.source.path ?? "" + ); + log.trace("setBreakPointsRequest, target file: " + fileUri.toString()); + }); - if (fileUri && file) { + if (file) { log.trace(`setBreakPointsRequest: looking`); - this.breakpoints.set(fileUri.path, []); + this.breakpoints.set(file.uri.toString(), []); log.trace( `setBreakPointsRequest: files in cache %O`, this.breakpointLocations.keys() ); - const locations = this.breakpointLocations.get(fileUri.path) ?? []; + const locations = this.breakpointLocations.get(file.uri.toString()) ?? []; log.trace(`setBreakPointsRequest: got locations %O`, locations); // convert the request line/column to file offset for debugger const bpOffsets: [lo: number, hi: number][] = (args.breakpoints ?? []) @@ -513,7 +515,7 @@ export class QscDebugSession extends LoggingDebugSession { } // Update our breakpoint list for the given file - this.breakpoints.set(fileUri.path, bps); + this.breakpoints.set(file.uri.toString(), bps); response.body = { breakpoints: bps, @@ -544,11 +546,17 @@ export class QscDebugSession extends LoggingDebugSession { const mappedStackFrames = await Promise.all( debuggerStackFrames .map(async (f, id) => { - const fileUri = vscode.Uri.file(f.path); - log.trace(`frames: fileUri %O`, fileUri); - const file = vscode.workspace.textDocuments.find( - (td) => td.uri.path === fileUri.path - ); + log.trace(`frames: path %O`, f.path); + + const file = await this.fileAccessor + .openFile(f.path ?? "") + .catch((e) => { + log.error(`stackTraceRequest - Failed to open file: ${e}`); + const fileUri = this.fileAccessor.resolvePathToUri(f.path ?? ""); + log.trace( + "stackTraceRequest, target file: " + fileUri.toString() + ); + }); if (file) { log.trace(`frames: file %O`, file); const start_pos = file.positionAt(f.lo); @@ -582,7 +590,7 @@ export class QscDebugSession extends LoggingDebugSession { scheme: qsharpLibraryUriScheme, path: f.path, }); - const file = await vscode.workspace.openTextDocument(uri); + const file = await this.fileAccessor.openUri(uri); const start_pos = file.positionAt(f.lo); const end_pos = file.positionAt(f.hi); const source = new Source(