debugEditorActions.ts 11.8 KB
Newer Older
1 2 3 4 5 6 7 8
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

import * as nls from 'vs/nls';
import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes';
import { Range } from 'vs/editor/common/core/range';
9
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
10
import { ServicesAccessor, registerEditorAction, EditorAction, IActionOptions } from 'vs/editor/browser/editorExtensions';
11
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
12
import { IDebugService, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE, State, REPL_ID, VIEWLET_ID, IDebugEditorContribution, EDITOR_CONTRIBUTION_ID, BreakpointWidgetContext, IBreakpoint, BREAKPOINT_EDITOR_CONTRIBUTION_ID, IBreakpointEditorContribution } from 'vs/workbench/contrib/debug/common/debug';
13
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
B
Benjamin Pasero 已提交
14
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
15
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
B
Benjamin Pasero 已提交
16
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
17
import { openBreakpointSource } from 'vs/workbench/contrib/debug/browser/breakpointsView';
18
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
19
import { PanelFocusContext } from 'vs/workbench/common/panel';
20

21
export const TOGGLE_BREAKPOINT_ID = 'editor.debug.action.toggleBreakpoint';
22 23 24
class ToggleBreakpointAction extends EditorAction {
	constructor() {
		super({
25
			id: TOGGLE_BREAKPOINT_ID,
26 27
			label: nls.localize('toggleBreakpointAction', "Debug: Toggle Breakpoint"),
			alias: 'Debug: Toggle Breakpoint',
28
			precondition: undefined,
29
			kbOpts: {
30
				kbExpr: EditorContextKeys.editorTextFocus,
A
Alex Dima 已提交
31
				primary: KeyCode.F9,
32
				weight: KeybindingWeight.EditorContrib
33 34 35 36
			}
		});
	}

37
	public run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<any> {
I
isidor 已提交
38
		if (editor.hasModel()) {
39
			const debugService = accessor.get(IDebugService);
I
isidor 已提交
40
			const modelUri = editor.getModel().uri;
41 42 43
			const canSet = debugService.getConfigurationManager().canSetBreakpointsIn(editor.getModel());
			// Does not account for multi line selections, Set to remove multiple cursor on the same line
			const lineNumbers = [...new Set(editor.getSelections().map(s => s.getPosition().lineNumber))];
I
isidor 已提交
44

45 46
			return Promise.all(lineNumbers.map(line => {
				const bps = debugService.getModel().getBreakpoints({ lineNumber: line, uri: modelUri });
47
				if (bps.length) {
48 49 50
					return Promise.all(bps.map(bp => debugService.removeBreakpoints(bp.getId())));
				} else if (canSet) {
					return (debugService.addBreakpoints(modelUri, [{ lineNumber: line }], 'debugEditorActions.toggleBreakpointAction'));
I
isidor 已提交
51
				} else {
52
					return Promise.resolve([]);
53
				}
54
			}));
55
		}
I
isidor 已提交
56

57
		return Promise.resolve();
58 59 60
	}
}

61
export const TOGGLE_CONDITIONAL_BREAKPOINT_ID = 'editor.debug.action.conditionalBreakpoint';
62
class ConditionalBreakpointAction extends EditorAction {
63 64 65

	constructor() {
		super({
66
			id: TOGGLE_CONDITIONAL_BREAKPOINT_ID,
67 68
			label: nls.localize('conditionalBreakpointEditorAction', "Debug: Add Conditional Breakpoint..."),
			alias: 'Debug: Add Conditional Breakpoint...',
69
			precondition: undefined
70 71 72
		});
	}

73
	public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
74 75
		const debugService = accessor.get(IDebugService);

I
isidor 已提交
76 77
		const position = editor.getPosition();
		if (position && editor.hasModel() && debugService.getConfigurationManager().canSetBreakpointsIn(editor.getModel())) {
78
			editor.getContribution<IBreakpointEditorContribution>(BREAKPOINT_EDITOR_CONTRIBUTION_ID).showBreakpointWidget(position.lineNumber, undefined);
79 80 81 82
		}
	}
}

83
export const TOGGLE_LOG_POINT_ID = 'editor.debug.action.toggleLogPoint';
84 85 86 87
class LogPointAction extends EditorAction {

	constructor() {
		super({
88
			id: TOGGLE_LOG_POINT_ID,
I
isidor 已提交
89 90
			label: nls.localize('logPointEditorAction', "Debug: Add Logpoint..."),
			alias: 'Debug: Add Logpoint...',
91
			precondition: undefined
92 93 94 95 96 97
		});
	}

	public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
		const debugService = accessor.get(IDebugService);

I
isidor 已提交
98 99
		const position = editor.getPosition();
		if (position && editor.hasModel() && debugService.getConfigurationManager().canSetBreakpointsIn(editor.getModel())) {
100
			editor.getContribution<IBreakpointEditorContribution>(BREAKPOINT_EDITOR_CONTRIBUTION_ID).showBreakpointWidget(position.lineNumber, BreakpointWidgetContext.LOG_MESSAGE);
101 102 103 104
		}
	}
}

I
isidor 已提交
105
export class RunToCursorAction extends EditorAction {
106

107 108 109
	public static ID = 'editor.debug.action.runToCursor';
	public static LABEL = nls.localize('runToCursor', "Run to Cursor");

110 111
	constructor() {
		super({
112 113
			id: RunToCursorAction.ID,
			label: RunToCursorAction.LABEL,
114
			alias: 'Debug: Run to Cursor',
I
isidor 已提交
115
			precondition: ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, PanelFocusContext.toNegated(), CONTEXT_DEBUG_STATE.isEqualTo('stopped'), EditorContextKeys.editorTextFocus),
116 117 118 119 120 121 122
			menuOpts: {
				group: 'debug',
				order: 2
			}
		});
	}

123
	public run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
124
		const debugService = accessor.get(IDebugService);
I
isidor 已提交
125 126
		const focusedSession = debugService.getViewModel().focusedSession;
		if (debugService.state !== State.Stopped || !focusedSession) {
R
Rob Lourens 已提交
127
			return Promise.resolve(undefined);
128 129
		}

130
		let breakpointToRemove: IBreakpoint;
I
isidor 已提交
131 132
		const oneTimeListener = focusedSession.onDidChangeState(() => {
			const state = focusedSession.state;
133
			if (state === State.Stopped || state === State.Inactive) {
134 135
				if (breakpointToRemove) {
					debugService.removeBreakpoints(breakpointToRemove.getId());
136 137 138 139 140
				}
				oneTimeListener.dispose();
			}
		});

141
		const position = editor.getPosition();
I
isidor 已提交
142 143 144 145
		if (!editor.hasModel() || !position) {
			return Promise.resolve();
		}

146
		const uri = editor.getModel().uri;
I
isidor 已提交
147
		const bpExists = !!(debugService.getModel().getBreakpoints({ column: position.column, lineNumber: position.lineNumber, uri }).length);
K
Format  
Kenneth Auchenberg 已提交
148
		return (bpExists ? Promise.resolve(null) : <Promise<any>>debugService.addBreakpoints(uri, [{ lineNumber: position.lineNumber, column: position.column }], 'debugEditorActions.runToCursorAction')).then((breakpoints) => {
149 150 151
			if (breakpoints && breakpoints.length) {
				breakpointToRemove = breakpoints[0];
			}
I
isidor 已提交
152
			debugService.getViewModel().focusedThread!.continue();
153 154 155 156 157 158 159 160 161 162 163
		});
	}
}

class SelectionToReplAction extends EditorAction {

	constructor() {
		super({
			id: 'editor.debug.action.selectionToRepl',
			label: nls.localize('debugEvaluate', "Debug: Evaluate"),
			alias: 'Debug: Evaluate',
164
			precondition: ContextKeyExpr.and(EditorContextKeys.hasNonEmptySelection, CONTEXT_IN_DEBUG_MODE, EditorContextKeys.editorTextFocus),
165 166 167 168 169 170 171
			menuOpts: {
				group: 'debug',
				order: 0
			}
		});
	}

172
	public async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
173 174
		const debugService = accessor.get(IDebugService);
		const panelService = accessor.get(IPanelService);
I
isidor 已提交
175 176
		const viewModel = debugService.getViewModel();
		const session = viewModel.focusedSession;
I
isidor 已提交
177 178 179 180 181
		if (!editor.hasModel() || !session) {
			return Promise.resolve();
		}

		const text = editor.getModel().getValueInRange(editor.getSelection());
182 183
		await session.addReplExpression(viewModel.focusedStackFrame!, text);
		await panelService.openPanel(REPL_ID, true);
184 185 186 187 188 189 190 191 192 193
	}
}

class SelectionToWatchExpressionsAction extends EditorAction {

	constructor() {
		super({
			id: 'editor.debug.action.selectionToWatch',
			label: nls.localize('debugAddToWatch', "Debug: Add to Watch"),
			alias: 'Debug: Add to Watch',
194
			precondition: ContextKeyExpr.and(EditorContextKeys.hasNonEmptySelection, CONTEXT_IN_DEBUG_MODE, EditorContextKeys.editorTextFocus),
195 196 197 198 199 200 201
			menuOpts: {
				group: 'debug',
				order: 1
			}
		});
	}

J
Johannes Rieken 已提交
202
	public run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
203 204
		const debugService = accessor.get(IDebugService);
		const viewletService = accessor.get(IViewletService);
I
isidor 已提交
205 206 207
		if (!editor.hasModel()) {
			return Promise.resolve();
		}
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222

		const text = editor.getModel().getValueInRange(editor.getSelection());
		return viewletService.openViewlet(VIEWLET_ID).then(() => debugService.addWatchExpression(text));
	}
}

class ShowDebugHoverAction extends EditorAction {

	constructor() {
		super({
			id: 'editor.debug.action.showDebugHover',
			label: nls.localize('showDebugHover', "Debug: Show Hover"),
			alias: 'Debug: Show Hover',
			precondition: CONTEXT_IN_DEBUG_MODE,
			kbOpts: {
223
				kbExpr: EditorContextKeys.editorTextFocus,
A
Alex Dima 已提交
224
				primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_I),
225
				weight: KeybindingWeight.EditorContrib
226 227 228 229
			}
		});
	}

230
	public run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
231
		const position = editor.getPosition();
I
isidor 已提交
232 233 234
		if (!position || !editor.hasModel()) {
			return Promise.resolve();
		}
235 236
		const word = editor.getModel().getWordAtPosition(position);
		if (!word) {
237
			return Promise.resolve();
238 239 240
		}

		const range = new Range(position.lineNumber, position.column, position.lineNumber, word.endColumn);
I
isidor 已提交
241
		return editor.getContribution<IDebugEditorContribution>(EDITOR_CONTRIBUTION_ID).showHover(range, true);
242 243
	}
}
244

245
class GoToBreakpointAction extends EditorAction {
246
	constructor(private isNext: boolean, opts: IActionOptions) {
247 248 249
		super(opts);
	}

J
Johannes Rieken 已提交
250
	public run(accessor: ServicesAccessor, editor: ICodeEditor, args: any): Promise<any> {
251 252
		const debugService = accessor.get(IDebugService);
		const editorService = accessor.get(IEditorService);
I
isidor 已提交
253 254 255 256 257 258 259 260
		if (editor.hasModel()) {
			const currentUri = editor.getModel().uri;
			const currentLine = editor.getPosition().lineNumber;
			//Breakpoints returned from `getBreakpoints` are already sorted.
			const allEnabledBreakpoints = debugService.getModel().getBreakpoints({ enabledOnly: true });

			//Try to find breakpoint in current file
			let moveBreakpoint =
261
				this.isNext
I
isidor 已提交
262 263 264 265 266 267 268 269 270 271
					? allEnabledBreakpoints.filter(bp => bp.uri.toString() === currentUri.toString() && bp.lineNumber > currentLine).shift()
					: allEnabledBreakpoints.filter(bp => bp.uri.toString() === currentUri.toString() && bp.lineNumber < currentLine).pop();

			//Try to find breakpoints in following files
			if (!moveBreakpoint) {
				moveBreakpoint =
					this.isNext
						? allEnabledBreakpoints.filter(bp => bp.uri.toString() > currentUri.toString()).shift()
						: allEnabledBreakpoints.filter(bp => bp.uri.toString() < currentUri.toString()).pop();
			}
272

I
isidor 已提交
273 274 275 276
			//Move to first or last possible breakpoint
			if (!moveBreakpoint && allEnabledBreakpoints.length) {
				moveBreakpoint = this.isNext ? allEnabledBreakpoints[0] : allEnabledBreakpoints[allEnabledBreakpoints.length - 1];
			}
277

I
isidor 已提交
278 279 280
			if (moveBreakpoint) {
				return openBreakpointSource(moveBreakpoint, false, true, debugService, editorService);
			}
281
		}
282

I
isidor 已提交
283
		return Promise.resolve(null);
284 285 286 287 288 289 290 291 292
	}
}

class GoToNextBreakpointAction extends GoToBreakpointAction {
	constructor() {
		super(true, {
			id: 'editor.debug.action.goToNextBreakpoint',
			label: nls.localize('goToNextBreakpoint', "Debug: Go To Next Breakpoint"),
			alias: 'Debug: Go To Next Breakpoint',
293
			precondition: undefined
294 295 296 297 298 299 300 301 302 303
		});
	}
}

class GoToPreviousBreakpointAction extends GoToBreakpointAction {
	constructor() {
		super(false, {
			id: 'editor.debug.action.goToPreviousBreakpoint',
			label: nls.localize('goToPreviousBreakpoint', "Debug: Go To Previous Breakpoint"),
			alias: 'Debug: Go To Previous Breakpoint',
304
			precondition: undefined
305 306 307 308
		});
	}
}

309 310
registerEditorAction(ToggleBreakpointAction);
registerEditorAction(ConditionalBreakpointAction);
311
registerEditorAction(LogPointAction);
312 313 314 315
registerEditorAction(RunToCursorAction);
registerEditorAction(SelectionToReplAction);
registerEditorAction(SelectionToWatchExpressionsAction);
registerEditorAction(ShowDebugHoverAction);
316 317
registerEditorAction(GoToNextBreakpointAction);
registerEditorAction(GoToPreviousBreakpointAction);