diff --git a/CHANGELOG.md b/CHANGELOG.md index 60b28c208..ad041a9aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ This changelog records changes to stable releases since 1.50.2. "TBA" changes he ## Nightly (only) +- feat: simplify pretty print to align with devtools ([vscode#151410](https://github.com/microsoft/vscode/issues/151410)) - fix: debugged child processes in ext host causing teardown ([#1289](https://github.com/microsoft/vscode-js-debug/issues/1289)) - fix: errors thrown in process tree lookup not being visible ([vscode#150754](https://github.com/microsoft/vscode/issues/150754)) - chore: adopt new restartFrame semantics from Chrome 104 ([#1283](https://github.com/microsoft/vscode-js-debug/issues/1283)) diff --git a/src/adapter/debugAdapter.ts b/src/adapter/debugAdapter.ts index f49673295..7bf99937c 100644 --- a/src/adapter/debugAdapter.ts +++ b/src/adapter/debugAdapter.ts @@ -102,7 +102,6 @@ export class DebugAdapter implements IDisposable { this.dap.on('enableCustomBreakpoints', params => this.enableCustomBreakpoints(params)); this.dap.on('toggleSkipFileStatus', params => this._toggleSkipFileStatus(params)); this.dap.on('disableCustomBreakpoints', params => this._disableCustomBreakpoints(params)); - this.dap.on('canPrettyPrintSource', params => this._canPrettyPrintSource(params)); this.dap.on('prettyPrintSource', params => this._prettyPrintSource(params)); this.dap.on('revealPage', () => this._withThread(thread => thread.revealPage())); this.dap.on('getPerformance', () => @@ -438,20 +437,6 @@ export class DebugAdapter implements IDisposable { return {}; } - async _canPrettyPrintSource( - params: Dap.CanPrettyPrintSourceParams, - ): Promise { - if (!params.source) { - return { canPrettyPrint: false }; - } - - params.source.path = urlUtils.platformPathToPreferredCase(params.source.path); - const source = this.sourceContainer.source(params.source); - if (!source) - return errors.createSilentError(localize('error.sourceNotFound', 'Source not found')); - return { canPrettyPrint: source.canPrettyPrint() }; - } - async _prettyPrintSource( params: Dap.PrettyPrintSourceParams, ): Promise { diff --git a/src/adapter/sources.ts b/src/adapter/sources.ts index 808775f7e..4f57b2973 100644 --- a/src/adapter/sources.ts +++ b/src/adapter/sources.ts @@ -223,20 +223,13 @@ export class Source { return 'text/javascript'; } - /** - * Gets whether this source is able to be pretty-printed. - */ - public canPrettyPrint(): boolean { - return this._container && !this._name.endsWith('-pretty.js'); - } - /** * Pretty-prints the source. Generates a beauitified source map if possible * and it hasn't already been done, and returns the created map and created * ephemeral source. Returns undefined if the source can't be beautified. */ public async prettyPrint(): Promise<{ map: SourceMap; source: Source } | undefined> { - if (!this._container || !this.canPrettyPrint()) { + if (!this._container) { return undefined; } diff --git a/src/build/dapCustom.ts b/src/build/dapCustom.ts index ba4bf865e..de532552c 100644 --- a/src/build/dapCustom.ts +++ b/src/build/dapCustom.ts @@ -107,29 +107,6 @@ const dapCustom: JSONSchema4 = { required: ['ids'], }), - ...makeRequest( - 'canPrettyPrintSource', - 'Returns whether particular source can be pretty-printed.', - { - properties: { - source: { - $ref: '#/definitions/Source', - description: 'Source to be pretty printed.', - }, - }, - required: ['source'], - }, - { - required: ['canPrettyPrint'], - properties: { - canPrettyPrint: { - type: 'boolean', - description: 'Whether source can be pretty printed.', - }, - }, - }, - ), - ...makeRequest('prettyPrintSource', 'Pretty prints source for debugging.', { properties: { source: { diff --git a/src/build/generate-contributions.ts b/src/build/generate-contributions.ts index 17f239c0d..a86736958 100644 --- a/src/build/generate-contributions.ts +++ b/src/build/generate-contributions.ts @@ -8,6 +8,7 @@ import { AutoAttachMode, Commands, Configuration, + ContextKey, Contributions, CustomViews, DebugType, @@ -1110,11 +1111,6 @@ const configurationSchema: ConfigurationAttributes = { default: {}, properties: nodeTerminalConfiguration.configurationAttributes as { [key: string]: JSONSchema6 }, }, - [Configuration.SuggestPrettyPrinting]: { - type: 'boolean', - description: refString('configuration.suggestPrettyPrinting'), - default: true, - }, [Configuration.AutoServerTunnelOpen]: { type: 'boolean', description: refString('configuration.automaticallyTunnelRemoteServer'), @@ -1209,6 +1205,7 @@ const commands: ReadonlyArray<{ command: Commands.PrettyPrint, title: refString('pretty.print.script'), category: 'Debug', + icon: '$(json)', }, { command: Commands.ToggleSkipping, @@ -1452,6 +1449,13 @@ const menus: Menus = { when: `view == ${CustomViews.ExcludedCallers}`, }, ], + 'editor/title': [ + { + command: Commands.PrettyPrint, + group: 'navigation', + when: `resource in ${ContextKey.CanPrettyPrint}`, + }, + ], }; const keybindings = [ diff --git a/src/build/strings.ts b/src/build/strings.ts index ca78b8eba..0691792f4 100644 --- a/src/build/strings.ts +++ b/src/build/strings.ts @@ -280,8 +280,6 @@ const strings = { 'Where a "Run" and "Debug" code lens should be shown in your npm scripts. It may be on "all", scripts, on "top" of the script section, or "never".', 'configuration.terminalOptions': 'Default launch options for the JavaScript debug terminal and npm scripts.', - 'configuration.suggestPrettyPrinting': - 'Whether to suggest pretty printing JavaScript code that looks minified when you step into it.', 'configuration.automaticallyTunnelRemoteServer': 'When debugging a remote web app, configures whether to automatically tunnel the remote server to your local machine.', 'configuration.debugByLinkOptions': diff --git a/src/common/contributionUtils.ts b/src/common/contributionUtils.ts index 12d303e12..088807086 100644 --- a/src/common/contributionUtils.ts +++ b/src/common/contributionUtils.ts @@ -134,7 +134,6 @@ export const enum Configuration { TerminalDebugConfig = 'debug.javascript.terminalOptions', PickAndAttachDebugOptions = 'debug.javascript.pickAndAttachOptions', DebugByLinkOptions = 'debug.javascript.debugByLinkOptions', - SuggestPrettyPrinting = 'debug.javascript.suggestPrettyPrinting', AutoServerTunnelOpen = 'debug.javascript.automaticallyTunnelRemoteServer', AutoAttachMode = 'debug.javascript.autoAttachFilter', AutoAttachSmartPatterns = 'debug.javascript.autoAttachSmartPattern', @@ -153,7 +152,6 @@ export interface IConfigurationTypes { [Configuration.NpmScriptLens]: 'all' | 'top' | 'never'; [Configuration.TerminalDebugConfig]: Partial; [Configuration.PickAndAttachDebugOptions]: Partial; - [Configuration.SuggestPrettyPrinting]: boolean; [Configuration.AutoServerTunnelOpen]: boolean; [Configuration.DebugByLinkOptions]: | DebugByLinkState @@ -256,3 +254,21 @@ export const writeConfig = ( value: IConfigurationTypes[K], target?: ConfigurationTarget, ) => wsp.getConfiguration().update(key, value, target); + +export const enum ContextKey { + HasExcludedCallers = 'jsDebugHasExcludedCallers', + CanPrettyPrint = 'jsDebugCanPrettyPrint', + IsProfiling = 'jsDebugIsProfiling', +} + +export interface IContextKeyTypes { + [ContextKey.HasExcludedCallers]: boolean; + [ContextKey.CanPrettyPrint]: string[]; + [ContextKey.IsProfiling]: boolean; +} + +export const setContextKey = async ( + ns: typeof commands, + key: K, + value: IContextKeyTypes[K] | null, +) => await ns.executeCommand('setContext', key, value); diff --git a/src/dap/api.d.ts b/src/dap/api.d.ts index 7872301d5..0912f3d4f 100644 --- a/src/dap/api.d.ts +++ b/src/dap/api.d.ts @@ -842,20 +842,6 @@ export namespace Dap { params: DisableCustomBreakpointsParams, ): Promise; - /** - * Returns whether particular source can be pretty-printed. - */ - on( - request: 'canPrettyPrintSource', - handler: (params: CanPrettyPrintSourceParams) => Promise, - ): () => void; - /** - * Returns whether particular source can be pretty-printed. - */ - canPrettyPrintSourceRequest( - params: CanPrettyPrintSourceParams, - ): Promise; - /** * Pretty prints source for debugging. */ @@ -1601,11 +1587,6 @@ export namespace Dap { params: DisableCustomBreakpointsParams, ): Promise; - /** - * Returns whether particular source can be pretty-printed. - */ - canPrettyPrintSource(params: CanPrettyPrintSourceParams): Promise; - /** * Pretty prints source for debugging. */ @@ -1881,20 +1862,6 @@ export namespace Dap { breakpoints: BreakpointLocation[]; } - export interface CanPrettyPrintSourceParams { - /** - * Source to be pretty printed. - */ - source: Source; - } - - export interface CanPrettyPrintSourceResult { - /** - * Whether source can be pretty printed. - */ - canPrettyPrint: boolean; - } - export interface CancelParams { /** * The ID (attribute 'seq') of the request to cancel. If missing no request is cancelled. diff --git a/src/dap/telemetryClassification.d.ts b/src/dap/telemetryClassification.d.ts index 315ccd895..aaf222573 100644 --- a/src/dap/telemetryClassification.d.ts +++ b/src/dap/telemetryClassification.d.ts @@ -96,8 +96,6 @@ interface IDAPOperationClassification { '!enablecustombreakpoints.errors': { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth' }; disablecustombreakpoints: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth' }; '!disablecustombreakpoints.errors': { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth' }; - canprettyprintsource: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth' }; - '!canprettyprintsource.errors': { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth' }; prettyprintsource: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth' }; '!prettyprintsource.errors': { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth' }; toggleskipfilestatus: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth' }; diff --git a/src/extension.ts b/src/extension.ts index f2ece2c66..9abc57ca8 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -22,7 +22,6 @@ import { registerCustomBreakpointsUI } from './ui/customBreakpointsUI'; import { debugNpmScript } from './ui/debugNpmScript'; import { DebugSessionTracker } from './ui/debugSessionTracker'; import { registerDebugTerminalUI } from './ui/debugTerminalUI'; -import { PrettyPrintTrackerFactory } from './ui/prettyPrint'; import { attachProcess, pickProcess } from './ui/processPicker'; import { registerProfilingCommand } from './ui/profiling'; import { registerRequestCDPProxy } from './ui/requestCDPProxy'; @@ -87,7 +86,6 @@ export function activate(context: vscode.ExtensionContext) { const debugSessionTracker = services.get(DebugSessionTracker); debugSessionTracker.attach(); - context.subscriptions.push(PrettyPrintTrackerFactory.register(debugSessionTracker)); registerCompanionBrowserLaunch(context); registerCustomBreakpointsUI(context, debugSessionTracker); registerDebugTerminalUI( diff --git a/src/ui/debugSessionTracker.ts b/src/ui/debugSessionTracker.ts index 4997f6a86..84f76203e 100644 --- a/src/ui/debugSessionTracker.ts +++ b/src/ui/debugSessionTracker.ts @@ -56,6 +56,13 @@ export class DebugSessionTracker implements vscode.Disposable { */ public onSessionEnded = this._onSessionEndedEmitter.event; + /** + * Gets whether there's any active JS debug session. + */ + public get isDebugging() { + return this.sessions.size > 0; + } + /** * Returns the session with the given ID. */ diff --git a/src/ui/excludedCallersUI.ts b/src/ui/excludedCallersUI.ts index d6f7692c4..5f46b366b 100644 --- a/src/ui/excludedCallersUI.ts +++ b/src/ui/excludedCallersUI.ts @@ -6,7 +6,13 @@ import { createHash } from 'crypto'; import { inject, injectable } from 'inversify'; import { basename } from 'path'; import * as vscode from 'vscode'; -import { Commands, CustomViews, registerCommand } from '../common/contributionUtils'; +import { + Commands, + ContextKey, + CustomViews, + registerCommand, + setContextKey, +} from '../common/contributionUtils'; import type Dap from '../dap/api'; import { IExtensionContribution } from '../ioc-extras'; import { DebugSessionTracker } from './debugSessionTracker'; @@ -155,7 +161,7 @@ export class ExcludedCallersUI const hasCallers = this.allCallers.size > 0; if (hasCallers !== this.lastHadCallers) { - vscode.commands.executeCommand('setContext', 'jsDebugHasExcludedCallers', hasCallers); + setContextKey(vscode.commands, ContextKey.HasExcludedCallers, hasCallers); this.lastHadCallers = hasCallers; } } diff --git a/src/ui/manageContextKey.ts b/src/ui/manageContextKey.ts new file mode 100644 index 000000000..60b234fa5 --- /dev/null +++ b/src/ui/manageContextKey.ts @@ -0,0 +1,23 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import { ContextKey, IContextKeyTypes } from '../common/contributionUtils'; + +export class ManagedContextKey { + private _value: IContextKeyTypes[T] | undefined; + + public set value(value: IContextKeyTypes[T] | undefined) { + if (value !== this._value) { + this._value = value; + vscode.commands.executeCommand('setContext', this.key, value); + } + } + + public get value() { + return this._value; + } + + constructor(private readonly key: T) {} +} diff --git a/src/ui/prettyPrint.ts b/src/ui/prettyPrint.ts index 511190be1..784572faa 100644 --- a/src/ui/prettyPrint.ts +++ b/src/ui/prettyPrint.ts @@ -2,63 +2,29 @@ * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ +import { inject, injectable } from 'inversify'; import * as qs from 'querystring'; import * as vscode from 'vscode'; -import { ConfigurationTarget } from 'vscode'; -import * as nls from 'vscode-nls'; -import { - allDebugTypes, - Commands, - Configuration, - readConfig, - registerCommand, - writeConfig, -} from '../common/contributionUtils'; -import { DisposableList, IDisposable } from '../common/disposable'; +import { Commands, ContextKey, registerCommand } from '../common/contributionUtils'; import Dap from '../dap/api'; -import { Message as DapMessage } from '../dap/transport'; +import { IExtensionContribution } from '../ioc-extras'; import { DebugSessionTracker } from './debugSessionTracker'; +import { ManagedContextKey } from './manageContextKey'; -const localize = nls.loadMessageBundle(); +@injectable() +export class PrettyPrintUI implements IExtensionContribution { + private readonly canPrettyPrintKey = new ManagedContextKey(ContextKey.CanPrettyPrint); -export class PrettyPrintTrackerFactory implements vscode.DebugAdapterTrackerFactory, IDisposable { - private readonly sessions = new DisposableList(); + constructor(@inject(DebugSessionTracker) private readonly tracker: DebugSessionTracker) {} - /** - * Attaches the tracker to the VS Code workspace. - */ - public static register(tracker: DebugSessionTracker): PrettyPrintTrackerFactory { - const factory = new PrettyPrintTrackerFactory(tracker); - for (const debugType of allDebugTypes) { - vscode.debug.registerDebugAdapterTrackerFactory(debugType, factory); - } - - registerCommand(vscode.commands, Commands.PrettyPrint, () => factory.prettifyActive()); - - return factory; - } - - constructor(private readonly tracker: DebugSessionTracker) {} - - /** - * @inheritdoc - */ - public createDebugAdapterTracker( - session: vscode.DebugSession, - ): vscode.ProviderResult { - if (!readConfig(vscode.workspace, Configuration.SuggestPrettyPrinting)) { - return; - } - - const tracker = new PrettyPrintSession(session); - this.sessions.push(tracker); - vscode.debug.onDidTerminateDebugSession(s => { - if (s === session) { - this.sessions.disposeObject(tracker); - } - }); - - return tracker; + /** @inheritdoc */ + public register(context: vscode.ExtensionContext): void { + context.subscriptions.push( + registerCommand(vscode.commands, Commands.PrettyPrint, () => this.prettifyActive()), + vscode.window.onDidChangeActiveTextEditor(editor => this.updateEditorState(editor)), + this.tracker.onSessionAdded(() => this.updateEditorState(vscode.window.activeTextEditor)), + this.tracker.onSessionEnded(() => this.updateEditorState(vscode.window.activeTextEditor)), + ); } /** @@ -66,7 +32,7 @@ export class PrettyPrintTrackerFactory implements vscode.DebugAdapterTrackerFact */ public async prettifyActive() { const editor = vscode.window.activeTextEditor; - if (editor?.document.languageId !== 'javascript') { + if (!editor || !this.canPrettyPrint(editor)) { return; } @@ -85,119 +51,26 @@ export class PrettyPrintTrackerFactory implements vscode.DebugAdapterTrackerFact } } - /** - * @inheritdoc - */ - public dispose() { - this.sessions.dispose(); - } -} - -/** - * Session tracker for pretty printing. It monitors open files in the editor, - * and suggests formatting ones that look minified. - * - * It can suggest printing ephemeral files that have a source reference set. - * It will also suggest printing - */ -class PrettyPrintSession implements IDisposable, vscode.DebugAdapterTracker { - private readonly candidatePaths = new Set(); - private readonly disposable = new DisposableList(); - private readonly suggested = new Set(); - - constructor(private readonly session: vscode.DebugSession) { - this.disposable.push( - vscode.window.onDidChangeActiveTextEditor(editor => this.onEditorChange(editor)), + private canPrettyPrint(editor: vscode.TextEditor) { + return ( + this.tracker.isDebugging && + editor.document.languageId === 'javascript' && + !editor.document.fileName.endsWith('-pretty.js') ); } - /** - * @inheritdoc - */ - public onDidSendMessage(message: DapMessage) { - if (message.type !== 'response' || message.command !== 'stackTrace' || !message.body) { - return; - } - - const frames = (message.body as Dap.StackTraceResult).stackFrames; - if (!frames) { + private updateEditorState(editor: vscode.TextEditor | undefined) { + if (!this.tracker.isDebugging) { + this.canPrettyPrintKey.value = undefined; return; } - for (const frame of frames) { - const path = frame.source?.path; - if (path) { - this.candidatePaths.add(path); + if (editor && this.canPrettyPrint(editor)) { + const value = editor.document.uri.toString(); + if (value !== this.canPrettyPrintKey.value?.[0]) { + this.canPrettyPrintKey.value = [editor.document.uri.toString()]; } } - - // If the file that's currently opened is the top of the stacktrace, - // indicating we're probably about to break on it, then prompt immediately. - const first = frames[0]?.source?.path; - if (first && vscode.window.activeTextEditor?.document.uri.path === first) { - this.onEditorChange(vscode.window.activeTextEditor); - } - } - - /** - * @inheritdoc - */ - public dispose() { - this.disposable.dispose(); - } - - private onEditorChange(editor: vscode.TextEditor | undefined) { - if (editor?.document.languageId !== 'javascript') { - return; - } - - const { source } = sourceForUri(editor.document.uri); - if (!this.candidatePaths.has(source.path) && source.sourceReference === 0) { - return; - } - - const key = source.sourceReference || source.path; - if (this.suggested.has(key)) { - return; - } - - this.suggested.add(key); - if (!isMinified(editor.document)) { - return; - } - - return this.trySuggestPrinting(source, editor.selection.start); - } - - private async trySuggestPrinting(source: Dap.Source, cursor: vscode.Position) { - const canPrettyPrint = await this.session.customRequest('canPrettyPrintSource', { - source, - }); - - if (!canPrettyPrint?.canPrettyPrint) { - return; - } - - const yes = localize('yes', 'Yes'); - const no = localize('no', 'No'); - const never = localize('never', 'Never'); - const response = await vscode.window.showInformationMessage( - 'This JavaScript file seems to be minified.\nWould you like to pretty print it?', - yes, - no, - never, - ); - - if (response === yes) { - sendPrintCommand(this.session, source, cursor); - } else if (response === never) { - writeConfig( - vscode.workspace, - Configuration.SuggestPrettyPrinting, - false, - ConfigurationTarget.Global, - ); - } } } @@ -225,19 +98,3 @@ const sourceForUri = (uri: vscode.Uri) => { return { sessionId, source }; }; - -/** - * Heuristic check to see if a document is minified. - */ -function isMinified(document: vscode.TextDocument): boolean { - const maxNonMinifiedLength = 500; - const linesToCheck = 10; - for (let i = 0; i < linesToCheck && i < document.lineCount; ++i) { - const line = document.lineAt(i).text; - if (line.length > maxNonMinifiedLength && !line.startsWith('//#')) { - return true; - } - } - - return false; -} diff --git a/src/ui/profiling/uiProfileManager.ts b/src/ui/profiling/uiProfileManager.ts index 15668bc42..e22cc4890 100644 --- a/src/ui/profiling/uiProfileManager.ts +++ b/src/ui/profiling/uiProfileManager.ts @@ -8,7 +8,7 @@ import { basename, join } from 'path'; import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; import { ProfilerFactory } from '../../adapter/profiling'; -import { Commands } from '../../common/contributionUtils'; +import { Commands, ContextKey, setContextKey } from '../../common/contributionUtils'; import { DisposableList, IDisposable } from '../../common/disposable'; import { moveFile } from '../../common/fsUtils'; import { AnyLaunchConfiguration } from '../../configuration'; @@ -264,7 +264,7 @@ export class UiProfileManager implements IDisposable { private updateStatusBar() { if (this.activeSessions.size === 0) { this.statusBarItem?.hide(); - vscode.commands.executeCommand('setContext', 'jsDebugIsProfiling', false); + setContextKey(vscode.commands, ContextKey.IsProfiling, false); return; } @@ -273,7 +273,7 @@ export class UiProfileManager implements IDisposable { this.statusBarItem.command = Commands.StopProfile; } - vscode.commands.executeCommand('setContext', 'jsDebugIsProfiling', true); + setContextKey(vscode.commands, ContextKey.IsProfiling, true); if (this.activeSessions.size === 1) { const session: UiProfileSession = this.activeSessions.values().next().value; diff --git a/src/ui/ui-ioc.ts b/src/ui/ui-ioc.ts index 80a949182..5b37c77ea 100644 --- a/src/ui/ui-ioc.ts +++ b/src/ui/ui-ioc.ts @@ -21,6 +21,7 @@ import { ILinkedBreakpointLocation } from './linkedBreakpointLocation'; import { LinkedBreakpointLocationUI } from './linkedBreakpointLocationUI'; import { LongPredictionUI } from './longPredictionUI'; import { JsDebugPortAttributesProvider } from './portAttributesProvider'; +import { PrettyPrintUI } from './prettyPrint'; import { BreakpointTerminationConditionFactory } from './profiling/breakpointTerminationCondition'; import { DurationTerminationConditionFactory } from './profiling/durationTerminationCondition'; import { ManualTerminationConditionFactory } from './profiling/manualTerminationCondition'; @@ -51,6 +52,7 @@ export const registerUiComponents = (container: Container) => { container.bind(IExtensionContribution).to(JsDebugPortAttributesProvider).inSingletonScope(); container.bind(IExtensionContribution).to(EdgeDevToolOpener).inSingletonScope(); container.bind(IExtensionContribution).to(ExcludedCallersUI).inSingletonScope(); + container.bind(IExtensionContribution).to(PrettyPrintUI).inSingletonScope(); container.bind(ILinkedBreakpointLocation).to(LinkedBreakpointLocationUI).inSingletonScope(); container.bind(DebugSessionTracker).toSelf().inSingletonScope().onActivation(trackDispose); container.bind(UiProfileManager).toSelf().inSingletonScope().onActivation(trackDispose);