Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add watch without selection #171449

Merged
merged 6 commits into from Jan 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 1 addition & 2 deletions src/vs/workbench/contrib/debug/browser/debug.contribution.ts
Expand Up @@ -10,7 +10,6 @@ import { URI } from 'vs/base/common/uri';
import 'vs/css!./media/debug.contribution';
import 'vs/css!./media/debugHover';
import { EditorContributionInstantiation, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import * as nls from 'vs/nls';
import { ICommandActionTitle, Icon } from 'vs/platform/action/common/action';
import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions';
Expand Down Expand Up @@ -123,7 +122,7 @@ registerDebugCommandPaletteItem(JUMP_TO_CURSOR_ID, { value: nls.localize('jumpTo
registerDebugCommandPaletteItem(JUMP_TO_CURSOR_ID, { value: nls.localize('SetNextStatement', "Set Next Statement"), original: 'Set Next Statement' }, CONTEXT_JUMP_TO_CURSOR_SUPPORTED);
registerDebugCommandPaletteItem(RunToCursorAction.ID, { value: RunToCursorAction.LABEL, original: 'Run to Cursor' }, ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE.isEqualTo('stopped')));
registerDebugCommandPaletteItem(SelectionToReplAction.ID, { value: SelectionToReplAction.LABEL, original: 'Evaluate in Debug Console' }, CONTEXT_IN_DEBUG_MODE);
registerDebugCommandPaletteItem(SelectionToWatchExpressionsAction.ID, { value: SelectionToWatchExpressionsAction.LABEL, original: 'Add to Watch' }, ContextKeyExpr.and(EditorContextKeys.hasNonEmptySelection, CONTEXT_IN_DEBUG_MODE));
registerDebugCommandPaletteItem(SelectionToWatchExpressionsAction.ID, { value: SelectionToWatchExpressionsAction.LABEL, original: 'Add to Watch' }, ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE));
registerDebugCommandPaletteItem(TOGGLE_INLINE_BREAKPOINT_ID, { value: nls.localize('inlineBreakpoint', "Inline Breakpoint"), original: 'Inline Breakpoint' });
registerDebugCommandPaletteItem(DEBUG_START_COMMAND_ID, DEBUG_START_LABEL, ContextKeyExpr.and(CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_DEBUG_STATE.notEqualsTo(getStateLabel(State.Initializing))));
registerDebugCommandPaletteItem(DEBUG_RUN_COMMAND_ID, DEBUG_RUN_LABEL, ContextKeyExpr.and(CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_DEBUG_STATE.notEqualsTo(getStateLabel(State.Initializing))));
Expand Down
28 changes: 25 additions & 3 deletions src/vs/workbench/contrib/debug/browser/debugEditorActions.ts
Expand Up @@ -10,6 +10,7 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { EditorAction, EditorAction2, IActionOptions, registerEditorAction } from 'vs/editor/browser/editorExtensions';
import { Position } from 'vs/editor/common/core/position';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures';
import { MessageController } from 'vs/editor/contrib/message/browser/messageController';
import * as nls from 'vs/nls';
import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions';
Expand All @@ -23,6 +24,7 @@ import { PanelFocusContext } from 'vs/workbench/common/contextkeys';
import { IViewsService } from 'vs/workbench/common/views';
import { openBreakpointSource } from 'vs/workbench/contrib/debug/browser/breakpointsView';
import { BreakpointWidgetContext, BREAKPOINT_EDITOR_CONTRIBUTION_ID, CONTEXT_CALLSTACK_ITEM_TYPE, CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_DEBUG_STATE, CONTEXT_DISASSEMBLE_REQUEST_SUPPORTED, CONTEXT_EXCEPTION_WIDGET_VISIBLE, CONTEXT_FOCUSED_STACK_FRAME_HAS_INSTRUCTION_POINTER_REFERENCE, CONTEXT_IN_DEBUG_MODE, CONTEXT_LANGUAGE_SUPPORTS_DISASSEMBLE_REQUEST, CONTEXT_STEP_INTO_TARGETS_SUPPORTED, EDITOR_CONTRIBUTION_ID, IBreakpointEditorContribution, IDebugConfiguration, IDebugEditorContribution, IDebugService, REPL_VIEW_ID, WATCH_VIEW_ID } from 'vs/workbench/contrib/debug/common/debug';
import { getEvaluatableExpressionAtPosition } from 'vs/workbench/contrib/debug/common/debugUtils';
import { DisassemblyViewInput } from 'vs/workbench/contrib/debug/common/disassemblyViewInput';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';

Expand Down Expand Up @@ -330,7 +332,7 @@ export class SelectionToWatchExpressionsAction extends EditorAction {
id: SelectionToWatchExpressionsAction.ID,
label: SelectionToWatchExpressionsAction.LABEL,
alias: 'Debug: Add to Watch',
precondition: ContextKeyExpr.and(EditorContextKeys.hasNonEmptySelection, EditorContextKeys.editorTextFocus),
precondition: ContextKeyExpr.and(EditorContextKeys.editorTextFocus),
contextMenuOpts: {
group: 'debug',
order: 1
Expand All @@ -341,13 +343,33 @@ export class SelectionToWatchExpressionsAction extends EditorAction {
async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
const debugService = accessor.get(IDebugService);
const viewsService = accessor.get(IViewsService);
const languageFeaturesService = accessor.get(ILanguageFeaturesService);
if (!editor.hasModel()) {
return;
}

const text = editor.getModel().getValueInRange(editor.getSelection());
let expression: string | undefined = undefined;

const model = editor.getModel();
const selection = editor.getSelection();

if (!selection.isEmpty()) {
expression = model.getValueInRange(selection);
} else {
const position = editor.getPosition();
const evaluatableExpression = await getEvaluatableExpressionAtPosition(languageFeaturesService, model, position);
if (!evaluatableExpression) {
return;
}
expression = evaluatableExpression.matchingExpression;
}

if (!expression) {
return;
}

await viewsService.openView(WATCH_VIEW_ID);
debugService.addWatchExpression(text);
debugService.addWatchExpression(expression);
}
}

Expand Down
45 changes: 3 additions & 42 deletions src/vs/workbench/contrib/debug/browser/debugHover.ts
Expand Up @@ -19,8 +19,7 @@ import { ScrollbarVisibility } from 'vs/base/common/scrollable';
import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser';
import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config/editorOptions';
import { Position } from 'vs/editor/common/core/position';
import { IRange, Range } from 'vs/editor/common/core/range';
import { ITextModel } from 'vs/editor/common/model';
import { Range } from 'vs/editor/common/core/range';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures';
import * as nls from 'vs/nls';
Expand All @@ -35,7 +34,7 @@ import { LinkDetector } from 'vs/workbench/contrib/debug/browser/linkDetector';
import { VariablesRenderer } from 'vs/workbench/contrib/debug/browser/variablesView';
import { IDebugService, IDebugSession, IExpression, IExpressionContainer, IStackFrame } from 'vs/workbench/contrib/debug/common/debug';
import { Expression, Variable } from 'vs/workbench/contrib/debug/common/debugModel';
import { getExactExpressionStartAndEnd } from 'vs/workbench/contrib/debug/common/debugUtils';
import { getEvaluatableExpressionAtPosition } from 'vs/workbench/contrib/debug/common/debugUtils';

const $ = dom.$;

Expand Down Expand Up @@ -380,7 +379,7 @@ class DebugHoverComputer {
}

const model = this.editor.getModel();
const result = await this.doCompute(model, position, token);
const result = await getEvaluatableExpressionAtPosition(this.languageFeaturesService, model, position, token);
if (!result) {
return { rangeChanged: false };
}
Expand All @@ -394,44 +393,6 @@ class DebugHoverComputer {
return { rangeChanged, range: this._currentRange };
}

private async doCompute(model: ITextModel, position: Position, token: CancellationToken): Promise<{ range: IRange; matchingExpression: string } | null> {
if (this.languageFeaturesService.evaluatableExpressionProvider.has(model)) {
const supports = this.languageFeaturesService.evaluatableExpressionProvider.ordered(model);

const results = coalesce(await Promise.all(supports.map(async support => {
try {
return await support.provideEvaluatableExpression(model, position, token);
} catch (err) {
return undefined;
}
})));

if (results.length > 0) {
let matchingExpression = results[0].expression;
const range = results[0].range;

if (!matchingExpression) {
const lineContent = model.getLineContent(position.lineNumber);
matchingExpression = lineContent.substring(range.startColumn - 1, range.endColumn - 1);
}

return { range, matchingExpression };
}
} else { // old one-size-fits-all strategy
const lineContent = model.getLineContent(position.lineNumber);
const { start, end } = getExactExpressionStartAndEnd(lineContent, position.column, position.column);

// use regex to extract the sub-expression #9821
const matchingExpression = lineContent.substring(start - 1, end);
return {
matchingExpression,
range: new Range(position.lineNumber, start, position.lineNumber, start + matchingExpression.length)
};
}

return null;
}

async evaluate(session: IDebugSession): Promise<IExpression | undefined> {
if (!this._currentExpression) {
this.logService.error('No expression to evaluate');
Expand Down
44 changes: 44 additions & 0 deletions src/vs/workbench/contrib/debug/common/debugUtils.ts
Expand Up @@ -11,6 +11,12 @@ import { deepClone } from 'vs/base/common/objects';
import { Schemas } from 'vs/base/common/network';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ITextModel } from 'vs/editor/common/model';
import { Position } from 'vs/editor/common/core/position';
import { IRange, Range } from 'vs/editor/common/core/range';
import { CancellationToken } from 'vs/base/common/cancellation';
import { coalesce } from 'vs/base/common/arrays';
import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures';

const _formatPIIRegexp = /{([^}]+)}/g;

Expand Down Expand Up @@ -115,6 +121,44 @@ export function getExactExpressionStartAndEnd(lineContent: string, looseStart: n
{ start: 0, end: 0 };
}

export async function getEvaluatableExpressionAtPosition(languageFeaturesService: ILanguageFeaturesService, model: ITextModel, position: Position, token?: CancellationToken): Promise<{ range: IRange; matchingExpression: string } | null> {
if (languageFeaturesService.evaluatableExpressionProvider.has(model)) {
const supports = languageFeaturesService.evaluatableExpressionProvider.ordered(model);

const results = coalesce(await Promise.all(supports.map(async support => {
try {
return await support.provideEvaluatableExpression(model, position, token ?? CancellationToken.None);
} catch (err) {
return undefined;
}
})));

if (results.length > 0) {
let matchingExpression = results[0].expression;
const range = results[0].range;

if (!matchingExpression) {
const lineContent = model.getLineContent(position.lineNumber);
matchingExpression = lineContent.substring(range.startColumn - 1, range.endColumn - 1);
}

return { range, matchingExpression };
}
} else { // old one-size-fits-all strategy
const lineContent = model.getLineContent(position.lineNumber);
const { start, end } = getExactExpressionStartAndEnd(lineContent, position.column, position.column);

// use regex to extract the sub-expression #9821
const matchingExpression = lineContent.substring(start - 1, end);
return {
matchingExpression,
range: new Range(position.lineNumber, start, position.lineNumber, start + matchingExpression.length)
};
}

return null;
}

// RFC 2396, Appendix A: https://www.ietf.org/rfc/rfc2396.txt
const _schemePattern = /^[a-zA-Z][a-zA-Z0-9\+\-\.]+:/;

Expand Down