From dbcaa1df21b3d55e816396f8252340d1c59356c6 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 27 Nov 2019 18:57:15 +0100 Subject: [PATCH] debug: fine tune start experience #84677 --- .../browser/debugConfigurationManager.ts | 18 ++++ .../debug/browser/media/debugViewlet.css | 4 + .../contrib/debug/browser/startView.ts | 95 ++++++++++++++++--- .../workbench/contrib/debug/common/debug.ts | 9 +- 4 files changed, 112 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts index eb1eb008da0..d13bc6c47f4 100644 --- a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts +++ b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts @@ -11,6 +11,7 @@ import * as objects from 'vs/base/common/objects'; import { URI as uri } from 'vs/base/common/uri'; import * as resources from 'vs/base/common/resources'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import * as editorCommon from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; import { IEditor } from 'vs/workbench/common/editor'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; @@ -56,6 +57,7 @@ export class ConfigurationManager implements IConfigurationManager { private adapterDescriptorFactories: IDebugAdapterDescriptorFactory[]; private debugAdapterFactories = new Map(); private debugConfigurationTypeContext: IContextKey; + private readonly _onDidRegisterDebugger = new Emitter(); constructor( @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @@ -166,6 +168,10 @@ export class ConfigurationManager implements IConfigurationManager { return Promise.resolve(undefined); } + get onDidRegisterDebugger(): Event { + return this._onDidRegisterDebugger.event; + } + // debug configurations registerDebugConfigurationProvider(debugConfigurationProvider: IDebugConfigurationProvider): IDisposable { @@ -266,6 +272,7 @@ export class ConfigurationManager implements IConfigurationManager { }); this.setCompoundSchemaValues(); + this._onDidRegisterDebugger.fire(); }); breakpointsExtPoint.setHandler((extensions, delta) => { @@ -388,6 +395,17 @@ export class ConfigurationManager implements IConfigurationManager { return this.debuggers.filter(dbg => strings.equalsIgnoreCase(dbg.type, type)).pop(); } + getDebuggerLabelsForEditor(editor: editorCommon.IEditor | undefined): string[] { + if (isCodeEditor(editor)) { + const model = editor.getModel(); + const language = model ? model.getLanguageIdentifier().language : undefined; + + return this.debuggers.filter(a => language && a.languages && a.languages.indexOf(language) >= 0).map(d => d.label); + } + + return []; + } + async guessDebugger(type?: string): Promise { if (type) { const adapter = this.getDebugger(type); diff --git a/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css b/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css index 87d736069ed..2a1341f1eb5 100644 --- a/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css +++ b/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css @@ -22,6 +22,10 @@ margin-top: 20px; } +.debug-viewlet .debug-start-view .top-section { + margin-top: 10px; +} + .debug-viewlet .debug-start-view .configure { cursor: pointer; color: #007ACC; diff --git a/src/vs/workbench/contrib/debug/browser/startView.ts b/src/vs/workbench/contrib/debug/browser/startView.ts index 50f927ff906..0ccdd2e6902 100644 --- a/src/vs/workbench/contrib/debug/browser/startView.ts +++ b/src/vs/workbench/contrib/debug/browser/startView.ts @@ -16,6 +16,11 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { localize } from 'vs/nls'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { StartAction, RunAction, ConfigureAction } from 'vs/workbench/contrib/debug/browser/debugActions'; +import { IDebugService } from 'vs/workbench/contrib/debug/common/debug'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; +import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { equals } from 'vs/base/common/arrays'; const $ = dom.$; @@ -26,6 +31,9 @@ export class StartView extends ViewletPane { private debugButton!: Button; private runButton!: Button; + private firstMessageContainer!: HTMLElement; + private secondMessageContainer!: HTMLElement; + private debuggerLabels: string[] | undefined = undefined; constructor( options: IViewletViewOptions, @@ -34,14 +42,84 @@ export class StartView extends ViewletPane { @IContextMenuService contextMenuService: IContextMenuService, @IConfigurationService configurationService: IConfigurationService, @IContextKeyService contextKeyService: IContextKeyService, - @ICommandService private readonly commandService: ICommandService + @ICommandService private readonly commandService: ICommandService, + @IDebugService private readonly debugService: IDebugService, + @IEditorService private readonly editorService: IEditorService, + @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, + @IFileDialogService private readonly dialogService: IFileDialogService ) { super({ ...(options as IViewletPaneOptions), ariaHeaderLabel: localize('debugStart', "Debug Start Section") }, keybindingService, contextMenuService, configurationService, contextKeyService); + this._register(editorService.onDidActiveEditorChange(() => this.updateView())); + this._register(this.debugService.getConfigurationManager().onDidRegisterDebugger(() => this.updateView())); + } + + private updateView(): void { + const activeEditor = this.editorService.activeTextEditorWidget; + const debuggerLabels = this.debugService.getConfigurationManager().getDebuggerLabelsForEditor(activeEditor); + if (!equals(this.debuggerLabels, debuggerLabels)) { + this.debuggerLabels = debuggerLabels; + const enabled = this.debuggerLabels.length > 0; + + this.debugButton.enabled = enabled; + this.runButton.enabled = enabled; + this.debugButton.label = this.debuggerLabels.length !== 1 ? localize('debug', "Debug") : localize('debugWith', "Debug with {0}", this.debuggerLabels[0]); + this.runButton.label = this.debuggerLabels.length !== 1 ? localize('run', "Run") : localize('runWith', "Run with {0}", this.debuggerLabels[0]); + + const emptyWorkbench = this.workspaceContextService.getWorkbenchState() === WorkbenchState.EMPTY; + this.firstMessageContainer.innerHTML = ''; + this.secondMessageContainer.innerHTML = ''; + const secondMessageElement = $('span'); + this.secondMessageContainer.appendChild(secondMessageElement); + + const setFirstMessage = () => { + const firstMessageElement = $('span'); + this.firstMessageContainer.appendChild(firstMessageElement); + firstMessageElement.textContent = localize('simplyDebugAndRun', "Open a file which can be debugged or run."); + }; + const setSecondMessage = () => { + secondMessageElement.textContent = localize('specifyHowToRun', "To futher configure the Debug and Run experience"); + const clickElement = $('span.configure'); + clickElement.textContent = localize('configure', " create a launch.json file."); + clickElement.onclick = () => this.commandService.executeCommand(ConfigureAction.ID); + this.secondMessageContainer.appendChild(clickElement); + }; + const setSecondMessageWithFolder = () => { + secondMessageElement.textContent = localize('noLaunchConfiguration', "To futher configure the Debug and Run experience, "); + const clickElement = $('span.configure'); + clickElement.textContent = localize('openFolder', " open a folder"); + clickElement.onclick = () => this.dialogService.pickFolderAndOpen({ forceNewWindow: false }); + this.secondMessageContainer.appendChild(clickElement); + + const moreText = $('span.moreText'); + moreText.textContent = localize('andconfigure', " and create a launch.json file."); + this.secondMessageContainer.appendChild(moreText); + }; + + if (enabled && !emptyWorkbench) { + setSecondMessage(); + } + + if (enabled && emptyWorkbench) { + setSecondMessageWithFolder(); + } + + if (!enabled && !emptyWorkbench) { + setFirstMessage(); + setSecondMessage(); + } + + if (!enabled && emptyWorkbench) { + setFirstMessage(); + setSecondMessageWithFolder(); + } + } } protected renderBody(container: HTMLElement): void { + this.firstMessageContainer = $('.top-section'); + container.appendChild(this.firstMessageContainer); + this.debugButton = new Button(container); - this.debugButton.label = localize('debug', "Debug"); this._register(this.debugButton.onDidClick(() => { this.commandService.executeCommand(StartAction.ID); })); @@ -56,15 +134,10 @@ export class StartView extends ViewletPane { })); attachButtonStyler(this.runButton, this.themeService); - const messageContainer = $('.section'); - container.appendChild(messageContainer); - const messageElement = $('span'); - messageContainer.appendChild(messageElement); - messageElement.textContent = localize('noLaunchConfiguration', "To specify how to run and debug your code, "); - const clickElement = $('span.configure'); - clickElement.textContent = localize('configure', " create a launch.json file."); - clickElement.onclick = () => this.commandService.executeCommand(ConfigureAction.ID); - messageContainer.appendChild(clickElement); + this.secondMessageContainer = $('.section'); + container.appendChild(this.secondMessageContainer); + + this.updateView(); } protected layoutBody(_: number, __: number): void { diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index 968e4e63042..4ba0c58f117 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -9,7 +9,7 @@ import severity from 'vs/base/common/severity'; import { Event } from 'vs/base/common/event'; import { IJSONSchemaSnippet } from 'vs/base/common/jsonSchema'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IEditorContribution } from 'vs/editor/common/editorCommon'; +import * as editorCommon from 'vs/editor/common/editorCommon'; import { ITextModel as EditorIModel } from 'vs/editor/common/model'; import { IEditor, ITextEditor } from 'vs/workbench/common/editor'; import { Position, IPosition } from 'vs/editor/common/core/position'; @@ -630,8 +630,11 @@ export interface IConfigurationManager { */ onDidSelectConfiguration: Event; + onDidRegisterDebugger: Event; + activateDebuggers(activationEvent: string, debugType?: string): Promise; + getDebuggerLabelsForEditor(editor: editorCommon.IEditor | undefined): string[]; hasDebugConfigurationProvider(debugType: string): boolean; registerDebugConfigurationProvider(debugConfigurationProvider: IDebugConfigurationProvider): IDisposable; @@ -863,12 +866,12 @@ export const enum BreakpointWidgetContext { LOG_MESSAGE = 2 } -export interface IDebugEditorContribution extends IEditorContribution { +export interface IDebugEditorContribution extends editorCommon.IEditorContribution { showHover(range: Range, focus: boolean): Promise; addLaunchConfiguration(): Promise; } -export interface IBreakpointEditorContribution extends IEditorContribution { +export interface IBreakpointEditorContribution extends editorCommon.IEditorContribution { showBreakpointWidget(lineNumber: number, column: number | undefined, context?: BreakpointWidgetContext): void; closeBreakpointWidget(): void; } -- GitLab