VSCode merge (#4610)
* Merge from vscode e388c734f30757875976c7e326d6cfeee77710de * fix yarn lcoks * remove small issue
@@ -0,0 +1,160 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { CallHierarchyProviderRegistry, CallHierarchyDirection } from 'vs/workbench/contrib/callHierarchy/common/callHierarchy';
|
||||
import { CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { CallHierarchyTreePeekWidget } from 'vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { registerEditorContribution, registerEditorAction, EditorAction, registerEditorCommand, EditorCommand } from 'vs/editor/browser/editorExtensions';
|
||||
import { IEditorContribution } from 'vs/editor/common/editorCommon';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { IContextKeyService, RawContextKey, IContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||
|
||||
|
||||
const _ctxHasCompletionItemProvider = new RawContextKey<boolean>('editorHasCallHierarchyProvider', false);
|
||||
const _ctxCallHierarchyVisible = new RawContextKey<boolean>('callHierarchyVisible', false);
|
||||
|
||||
class CallHierarchyController extends Disposable implements IEditorContribution {
|
||||
|
||||
static Id = 'callHierarchy';
|
||||
|
||||
static get(editor: ICodeEditor): CallHierarchyController {
|
||||
return editor.getContribution<CallHierarchyController>(CallHierarchyController.Id);
|
||||
}
|
||||
|
||||
private readonly _ctxHasProvider: IContextKey<boolean>;
|
||||
private readonly _ctxIsVisible: IContextKey<boolean>;
|
||||
|
||||
private _sessionDispose: IDisposable[] = [];
|
||||
|
||||
constructor(
|
||||
private readonly _editor: ICodeEditor,
|
||||
@IContextKeyService private readonly _contextKeyService: IContextKeyService,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
) {
|
||||
super();
|
||||
|
||||
this._ctxIsVisible = _ctxCallHierarchyVisible.bindTo(this._contextKeyService);
|
||||
this._ctxHasProvider = _ctxHasCompletionItemProvider.bindTo(this._contextKeyService);
|
||||
this._register(Event.any<any>(_editor.onDidChangeModel, _editor.onDidChangeModelLanguage, CallHierarchyProviderRegistry.onDidChange)(() => {
|
||||
this._ctxHasProvider.set(_editor.hasModel() && CallHierarchyProviderRegistry.has(_editor.getModel()));
|
||||
}));
|
||||
|
||||
this._register({ dispose: () => dispose(this._sessionDispose) });
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._ctxHasProvider.reset();
|
||||
this._ctxIsVisible.reset();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
getId(): string {
|
||||
return CallHierarchyController.Id;
|
||||
}
|
||||
|
||||
async startCallHierarchy(): Promise<void> {
|
||||
this._sessionDispose = dispose(this._sessionDispose);
|
||||
|
||||
if (!this._editor.hasModel()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const model = this._editor.getModel();
|
||||
const position = this._editor.getPosition();
|
||||
const [provider] = CallHierarchyProviderRegistry.ordered(model);
|
||||
if (!provider) {
|
||||
return;
|
||||
}
|
||||
|
||||
Event.any<any>(this._editor.onDidChangeModel, this._editor.onDidChangeModelLanguage)(this.endCallHierarchy, this, this._sessionDispose);
|
||||
const widget = this._instantiationService.createInstance(
|
||||
CallHierarchyTreePeekWidget,
|
||||
this._editor,
|
||||
position,
|
||||
provider,
|
||||
CallHierarchyDirection.CallsTo
|
||||
);
|
||||
|
||||
widget.showLoading();
|
||||
this._ctxIsVisible.set(true);
|
||||
|
||||
const cancel = new CancellationTokenSource();
|
||||
|
||||
this._sessionDispose.push(widget.onDidClose(() => this.endCallHierarchy()));
|
||||
this._sessionDispose.push({ dispose() { cancel.cancel(); } });
|
||||
this._sessionDispose.push(widget);
|
||||
|
||||
Promise.resolve(provider.provideCallHierarchyItem(model, position, cancel.token)).then(item => {
|
||||
if (cancel.token.isCancellationRequested) {
|
||||
return;
|
||||
}
|
||||
if (!item) {
|
||||
widget.showMessage(localize('no.item', "No results"));
|
||||
return;
|
||||
}
|
||||
|
||||
widget.showItem(item);
|
||||
});
|
||||
}
|
||||
|
||||
endCallHierarchy(): void {
|
||||
this._sessionDispose = dispose(this._sessionDispose);
|
||||
this._ctxIsVisible.set(false);
|
||||
this._editor.focus();
|
||||
}
|
||||
}
|
||||
|
||||
registerEditorContribution(CallHierarchyController);
|
||||
|
||||
registerEditorAction(class extends EditorAction {
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: 'editor.showCallHierarchy',
|
||||
label: localize('title', "Call Hierarchy"),
|
||||
alias: 'Call Hierarchy',
|
||||
menuOpts: {
|
||||
group: 'navigation',
|
||||
order: 111
|
||||
},
|
||||
kbOpts: {
|
||||
kbExpr: EditorContextKeys.editorTextFocus,
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
primary: KeyMod.Shift + KeyMod.Alt + KeyCode.KEY_H
|
||||
},
|
||||
precondition: _ctxHasCompletionItemProvider
|
||||
});
|
||||
}
|
||||
|
||||
async run(_accessor: ServicesAccessor, editor: ICodeEditor, args: any): Promise<void> {
|
||||
return CallHierarchyController.get(editor).startCallHierarchy();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
registerEditorCommand(new class extends EditorCommand {
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: 'editor.closeCallHierarchy',
|
||||
kbOpts: {
|
||||
weight: KeybindingWeight.WorkbenchContrib + 10,
|
||||
primary: KeyCode.Escape
|
||||
},
|
||||
precondition: ContextKeyExpr.and(_ctxCallHierarchyVisible, ContextKeyExpr.not('config.editor.stablePeek'))
|
||||
});
|
||||
}
|
||||
|
||||
runEditorCommand(_accessor: ServicesAccessor, editor: ICodeEditor): void {
|
||||
return CallHierarchyController.get(editor).endCallHierarchy();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,425 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./media/callHierarchy';
|
||||
import { PeekViewWidget } from 'vs/editor/contrib/referenceSearch/peekViewWidget';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { CallHierarchyItem, CallHierarchyProvider, CallHierarchyDirection } from 'vs/workbench/contrib/callHierarchy/common/callHierarchy';
|
||||
import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService';
|
||||
import { FuzzyScore } from 'vs/base/common/filters';
|
||||
import * as callHTree from 'vs/workbench/contrib/callHierarchy/browser/callHierarchyTree';
|
||||
import { IAsyncDataTreeOptions } from 'vs/base/browser/ui/tree/asyncDataTree';
|
||||
import { localize } from 'vs/nls';
|
||||
import { ScrollType } from 'vs/editor/common/editorCommon';
|
||||
import { IRange, Range } from 'vs/editor/common/core/range';
|
||||
import { SplitView, Orientation, Sizing } from 'vs/base/browser/ui/splitview/splitview';
|
||||
import { Dimension, addClass } from 'vs/base/browser/dom';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget';
|
||||
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
|
||||
import { ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { TrackedRangeStickiness, IModelDeltaDecoration, IModelDecorationOptions, OverviewRulerLane } from 'vs/editor/common/model';
|
||||
import { registerThemingParticipant, themeColorFromId, IThemeService, ITheme } from 'vs/platform/theme/common/themeService';
|
||||
import * as referencesWidget from 'vs/editor/contrib/referenceSearch/referencesWidget';
|
||||
import { isNonEmptyArray } from 'vs/base/common/arrays';
|
||||
import { IPosition } from 'vs/editor/common/core/position';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { IActionBarOptions, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
|
||||
const enum State {
|
||||
Loading = 'loading',
|
||||
Message = 'message',
|
||||
Data = 'data'
|
||||
}
|
||||
|
||||
class ToggleHierarchyDirectionAction extends Action {
|
||||
|
||||
constructor(public direction: () => CallHierarchyDirection, callback: () => void) {
|
||||
super('toggle.dir', undefined, 'call-hierarchy-toggle', true, () => {
|
||||
callback();
|
||||
this._update();
|
||||
return Promise.resolve();
|
||||
});
|
||||
this._update();
|
||||
}
|
||||
|
||||
private _update() {
|
||||
if (this.direction() === CallHierarchyDirection.CallsFrom) {
|
||||
this.label = localize('toggle.from', "Calls From...");
|
||||
this.checked = true;
|
||||
} else {
|
||||
this.label = localize('toggle.to', "Calls To...");
|
||||
this.checked = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class LayoutInfo {
|
||||
|
||||
static store(info: LayoutInfo, storageService: IStorageService): void {
|
||||
storageService.store('callHierarchyPeekLayout', JSON.stringify(info), StorageScope.GLOBAL);
|
||||
}
|
||||
|
||||
static retrieve(storageService: IStorageService): LayoutInfo {
|
||||
const value = storageService.get('callHierarchyPeekLayout', StorageScope.GLOBAL, '{}');
|
||||
const defaultInfo: LayoutInfo = { ratio: 0.7, height: 17 };
|
||||
try {
|
||||
return { ...defaultInfo, ...JSON.parse(value) };
|
||||
} catch {
|
||||
return defaultInfo;
|
||||
}
|
||||
}
|
||||
|
||||
constructor(
|
||||
public ratio: number,
|
||||
public height: number
|
||||
) { }
|
||||
}
|
||||
|
||||
export class CallHierarchyTreePeekWidget extends PeekViewWidget {
|
||||
|
||||
private _toggleDirection: ToggleHierarchyDirectionAction;
|
||||
private _parent: HTMLElement;
|
||||
private _message: HTMLElement;
|
||||
private _splitView: SplitView;
|
||||
private _tree: WorkbenchAsyncDataTree<CallHierarchyItem, callHTree.Call, FuzzyScore>;
|
||||
private _editor: EmbeddedCodeEditorWidget;
|
||||
private _dim: Dimension;
|
||||
private _layoutInfo: LayoutInfo;
|
||||
|
||||
constructor(
|
||||
editor: ICodeEditor,
|
||||
private readonly _where: IPosition,
|
||||
private readonly _provider: CallHierarchyProvider,
|
||||
private _direction: CallHierarchyDirection,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IEditorService private readonly _editorService: IEditorService,
|
||||
@ITextModelService private readonly _textModelService: ITextModelService,
|
||||
@ILabelService private readonly _labelService: ILabelService,
|
||||
@IStorageService private readonly _storageService: IStorageService,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
) {
|
||||
super(editor, { showFrame: true, showArrow: true, isResizeable: true, isAccessible: true });
|
||||
this.create();
|
||||
this._applyTheme(themeService.getTheme());
|
||||
themeService.onThemeChange(this._applyTheme, this, this._disposables);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
LayoutInfo.store(this._layoutInfo, this._storageService);
|
||||
this._splitView.dispose();
|
||||
this._tree.dispose();
|
||||
this._editor.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
private _applyTheme(theme: ITheme) {
|
||||
const borderColor = theme.getColor(referencesWidget.peekViewBorder) || Color.transparent;
|
||||
this.style({
|
||||
arrowColor: borderColor,
|
||||
frameColor: borderColor,
|
||||
headerBackgroundColor: theme.getColor(referencesWidget.peekViewTitleBackground) || Color.transparent,
|
||||
primaryHeadingColor: theme.getColor(referencesWidget.peekViewTitleForeground),
|
||||
secondaryHeadingColor: theme.getColor(referencesWidget.peekViewTitleInfoForeground)
|
||||
});
|
||||
}
|
||||
|
||||
protected _getActionBarOptions(): IActionBarOptions {
|
||||
return {
|
||||
orientation: ActionsOrientation.HORIZONTAL_REVERSE
|
||||
};
|
||||
}
|
||||
|
||||
protected _fillBody(parent: HTMLElement): void {
|
||||
|
||||
this._layoutInfo = LayoutInfo.retrieve(this._storageService);
|
||||
this._dim = { height: 0, width: 0 };
|
||||
|
||||
this._parent = parent;
|
||||
addClass(parent, 'call-hierarchy');
|
||||
|
||||
const message = document.createElement('div');
|
||||
addClass(message, 'message');
|
||||
parent.appendChild(message);
|
||||
this._message = message;
|
||||
|
||||
const container = document.createElement('div');
|
||||
addClass(container, 'results');
|
||||
parent.appendChild(container);
|
||||
|
||||
this._splitView = new SplitView(container, { orientation: Orientation.HORIZONTAL });
|
||||
|
||||
// editor stuff
|
||||
const editorContainer = document.createElement('div');
|
||||
addClass(editorContainer, 'editor');
|
||||
container.appendChild(editorContainer);
|
||||
let editorOptions: IEditorOptions = {
|
||||
scrollBeyondLastLine: false,
|
||||
scrollbar: {
|
||||
verticalScrollbarSize: 14,
|
||||
horizontal: 'auto',
|
||||
useShadows: true,
|
||||
verticalHasArrows: false,
|
||||
horizontalHasArrows: false
|
||||
},
|
||||
overviewRulerLanes: 2,
|
||||
fixedOverflowWidgets: true,
|
||||
minimap: {
|
||||
enabled: false
|
||||
}
|
||||
};
|
||||
this._editor = this._instantiationService.createInstance(
|
||||
EmbeddedCodeEditorWidget,
|
||||
editorContainer,
|
||||
editorOptions,
|
||||
this.editor
|
||||
);
|
||||
|
||||
// tree stuff
|
||||
const treeContainer = document.createElement('div');
|
||||
addClass(treeContainer, 'tree');
|
||||
container.appendChild(treeContainer);
|
||||
const options: IAsyncDataTreeOptions<callHTree.Call, FuzzyScore> = {
|
||||
identityProvider: new callHTree.IdentityProvider(),
|
||||
ariaLabel: localize('tree.aria', "Call Hierarchy"),
|
||||
expandOnlyOnTwistieClick: true,
|
||||
};
|
||||
this._tree = <any>this._instantiationService.createInstance(
|
||||
WorkbenchAsyncDataTree,
|
||||
treeContainer,
|
||||
new callHTree.VirtualDelegate(),
|
||||
[this._instantiationService.createInstance(callHTree.CallRenderer)],
|
||||
new callHTree.SingleDirectionDataSource(this._provider, () => this._direction),
|
||||
options
|
||||
);
|
||||
|
||||
// split stuff
|
||||
this._splitView.addView({
|
||||
onDidChange: Event.None,
|
||||
element: editorContainer,
|
||||
minimumSize: 200,
|
||||
maximumSize: Number.MAX_VALUE,
|
||||
layout: (width) => {
|
||||
this._editor.layout({ height: this._dim.height, width });
|
||||
}
|
||||
}, Sizing.Distribute);
|
||||
|
||||
this._splitView.addView({
|
||||
onDidChange: Event.None,
|
||||
element: treeContainer,
|
||||
minimumSize: 100,
|
||||
maximumSize: Number.MAX_VALUE,
|
||||
layout: (width) => {
|
||||
this._tree.layout(this._dim.height, width);
|
||||
}
|
||||
}, Sizing.Distribute);
|
||||
|
||||
this._splitView.onDidSashChange(() => {
|
||||
if (this._dim.width) {
|
||||
this._layoutInfo.ratio = this._splitView.getViewSize(0) / this._dim.width;
|
||||
}
|
||||
}, undefined, this._disposables);
|
||||
|
||||
// session state
|
||||
let localDispose: IDisposable[] = [];
|
||||
this._disposables.push({ dispose() { dispose(localDispose); } });
|
||||
|
||||
// update editor
|
||||
this._tree.onDidChangeFocus(e => {
|
||||
const [element] = e.elements;
|
||||
if (element && isNonEmptyArray(element.locations)) {
|
||||
|
||||
localDispose = dispose(localDispose);
|
||||
|
||||
const options: IModelDecorationOptions = {
|
||||
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
|
||||
className: 'call-decoration',
|
||||
overviewRuler: {
|
||||
color: themeColorFromId(referencesWidget.peekViewEditorMatchHighlight),
|
||||
position: OverviewRulerLane.Center
|
||||
},
|
||||
};
|
||||
let decorations: IModelDeltaDecoration[] = [];
|
||||
let fullRange: IRange | undefined;
|
||||
for (const { range } of element.locations) {
|
||||
decorations.push({ range, options });
|
||||
fullRange = !fullRange ? range : Range.plusRange(range, fullRange);
|
||||
}
|
||||
|
||||
this._textModelService.createModelReference(element.item.uri).then(value => {
|
||||
this._editor.setModel(value.object.textEditorModel);
|
||||
this._editor.revealRangeInCenter(fullRange!, ScrollType.Smooth);
|
||||
this._editor.revealLine(element.item.range.startLineNumber, ScrollType.Smooth);
|
||||
const ids = this._editor.deltaDecorations([], decorations);
|
||||
localDispose.push({ dispose: () => this._editor.deltaDecorations(ids, []) });
|
||||
localDispose.push(value);
|
||||
});
|
||||
}
|
||||
}, undefined, this._disposables);
|
||||
|
||||
this._editor.onMouseDown(e => {
|
||||
const { event, target } = e;
|
||||
if (event.detail !== 2) {
|
||||
return;
|
||||
}
|
||||
const [focus] = this._tree.getFocus();
|
||||
if (!focus) {
|
||||
return;
|
||||
}
|
||||
this.dispose();
|
||||
this._editorService.openEditor({
|
||||
resource: focus.item.uri,
|
||||
options: { selection: target.range! }
|
||||
});
|
||||
|
||||
}, undefined, this._disposables);
|
||||
|
||||
this._tree.onMouseDblClick(e => {
|
||||
if (e.element && isNonEmptyArray(e.element.locations)) {
|
||||
this.dispose();
|
||||
this._editorService.openEditor({
|
||||
resource: e.element.item.uri,
|
||||
options: { selection: e.element.locations[0].range }
|
||||
});
|
||||
}
|
||||
}, undefined, this._disposables);
|
||||
|
||||
this._tree.onDidChangeSelection(e => {
|
||||
const [element] = e.elements;
|
||||
// don't close on click
|
||||
if (element && !(e.browserEvent instanceof MouseEvent)) {
|
||||
this.dispose();
|
||||
this._editorService.openEditor({
|
||||
resource: element.item.uri,
|
||||
options: { selection: element.locations[0].range }
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
showLoading(): void {
|
||||
this._parent.dataset['state'] = State.Loading;
|
||||
this.setTitle(localize('title.loading', "Loading..."));
|
||||
this._show();
|
||||
}
|
||||
|
||||
showMessage(message: string): void {
|
||||
this._parent.dataset['state'] = State.Message;
|
||||
this.setTitle('');
|
||||
this.setMetaTitle('');
|
||||
this._message.innerText = message;
|
||||
this._show();
|
||||
}
|
||||
|
||||
showItem(item: CallHierarchyItem) {
|
||||
this._parent.dataset['state'] = State.Data;
|
||||
|
||||
this._show();
|
||||
this._tree.setInput(item).then(() => {
|
||||
|
||||
if (!this._tree.getFirstElementChild(item)) {
|
||||
//
|
||||
this.showMessage(this._direction === CallHierarchyDirection.CallsFrom
|
||||
? localize('empt.callsFrom', "No calls from '{0}'", item.name)
|
||||
: localize('empt.callsTo', "No calls to '{0}'", item.name));
|
||||
|
||||
} else {
|
||||
this._tree.domFocus();
|
||||
this._tree.focusFirst();
|
||||
this.setTitle(
|
||||
item.name,
|
||||
item.detail || this._labelService.getUriLabel(item.uri, { relative: true }),
|
||||
);
|
||||
this.setMetaTitle(this._direction === CallHierarchyDirection.CallsFrom
|
||||
? localize('title.from', " – calls from '{0}'", item.name)
|
||||
: localize('title.to', " – calls to '{0}'", item.name));
|
||||
}
|
||||
});
|
||||
|
||||
if (!this._toggleDirection) {
|
||||
this._toggleDirection = new ToggleHierarchyDirectionAction(
|
||||
() => this._direction,
|
||||
() => {
|
||||
let newDirection = this._direction === CallHierarchyDirection.CallsFrom ? CallHierarchyDirection.CallsTo : CallHierarchyDirection.CallsFrom;
|
||||
this._direction = newDirection;
|
||||
this.showItem(item);
|
||||
}
|
||||
);
|
||||
this._actionbarWidget.push(this._toggleDirection, { label: false, icon: true });
|
||||
this._disposables.push(this._toggleDirection);
|
||||
}
|
||||
}
|
||||
|
||||
private _show() {
|
||||
if (!this._isShowing) {
|
||||
this.editor.revealLineInCenterIfOutsideViewport(this._where.lineNumber, ScrollType.Smooth);
|
||||
super.show(Range.fromPositions(this._where), this._layoutInfo.height);
|
||||
}
|
||||
}
|
||||
|
||||
protected _onWidth(width: number) {
|
||||
if (this._dim) {
|
||||
this._doLayoutBody(this._dim.height, width);
|
||||
}
|
||||
}
|
||||
|
||||
protected _doLayoutBody(height: number, width: number): void {
|
||||
super._doLayoutBody(height, width);
|
||||
this._dim = { height, width };
|
||||
this._layoutInfo.height = this._viewZone ? this._viewZone.heightInLines : this._layoutInfo.height;
|
||||
this._splitView.layout(width);
|
||||
this._splitView.resizeView(0, width * this._layoutInfo.ratio);
|
||||
}
|
||||
}
|
||||
|
||||
registerThemingParticipant((theme, collector) => {
|
||||
const referenceHighlightColor = theme.getColor(referencesWidget.peekViewEditorMatchHighlight);
|
||||
if (referenceHighlightColor) {
|
||||
collector.addRule(`.monaco-editor .call-hierarchy .call-decoration { background-color: ${referenceHighlightColor}; }`);
|
||||
}
|
||||
const referenceHighlightBorder = theme.getColor(referencesWidget.peekViewEditorMatchHighlightBorder);
|
||||
if (referenceHighlightBorder) {
|
||||
collector.addRule(`.monaco-editor .call-hierarchy .call-decoration { border: 2px solid ${referenceHighlightBorder}; box-sizing: border-box; }`);
|
||||
}
|
||||
const resultsBackground = theme.getColor(referencesWidget.peekViewResultsBackground);
|
||||
if (resultsBackground) {
|
||||
collector.addRule(`.monaco-editor .call-hierarchy .tree { background-color: ${resultsBackground}; }`);
|
||||
}
|
||||
const resultsMatchForeground = theme.getColor(referencesWidget.peekViewResultsFileForeground);
|
||||
if (resultsMatchForeground) {
|
||||
collector.addRule(`.monaco-editor .call-hierarchy .tree { color: ${resultsMatchForeground}; }`);
|
||||
}
|
||||
const resultsSelectedBackground = theme.getColor(referencesWidget.peekViewResultsSelectionBackground);
|
||||
if (resultsSelectedBackground) {
|
||||
collector.addRule(`.monaco-editor .call-hierarchy .tree .monaco-list:focus .monaco-list-rows > .monaco-list-row.selected:not(.highlighted) { background-color: ${resultsSelectedBackground}; }`);
|
||||
}
|
||||
const resultsSelectedForeground = theme.getColor(referencesWidget.peekViewResultsSelectionForeground);
|
||||
if (resultsSelectedForeground) {
|
||||
collector.addRule(`.monaco-editor .call-hierarchy .tree .monaco-list:focus .monaco-list-rows > .monaco-list-row.selected:not(.highlighted) { color: ${resultsSelectedForeground} !important; }`);
|
||||
}
|
||||
const editorBackground = theme.getColor(referencesWidget.peekViewEditorBackground);
|
||||
if (editorBackground) {
|
||||
collector.addRule(
|
||||
`.monaco-editor .call-hierarchy .editor .monaco-editor .monaco-editor-background,` +
|
||||
`.monaco-editor .call-hierarchy .editor .monaco-editor .inputarea.ime-input {` +
|
||||
` background-color: ${editorBackground};` +
|
||||
`}`
|
||||
);
|
||||
}
|
||||
const editorGutterBackground = theme.getColor(referencesWidget.peekViewEditorGutterBackground);
|
||||
if (editorGutterBackground) {
|
||||
collector.addRule(
|
||||
`.monaco-editor .call-hierarchy .editor .monaco-editor .margin {` +
|
||||
` background-color: ${editorGutterBackground};` +
|
||||
`}`
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,96 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IAsyncDataSource, ITreeRenderer, ITreeNode } from 'vs/base/browser/ui/tree/tree';
|
||||
import { CallHierarchyItem, CallHierarchyDirection, CallHierarchyProvider } from 'vs/workbench/contrib/callHierarchy/common/callHierarchy';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { IIdentityProvider, IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
|
||||
import { FuzzyScore, createMatches } from 'vs/base/common/filters';
|
||||
import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel';
|
||||
import { symbolKindToCssClass, Location } from 'vs/editor/common/modes';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
|
||||
export class Call {
|
||||
constructor(
|
||||
readonly direction: CallHierarchyDirection,
|
||||
readonly item: CallHierarchyItem,
|
||||
readonly locations: Location[]
|
||||
) { }
|
||||
}
|
||||
|
||||
export class SingleDirectionDataSource implements IAsyncDataSource<CallHierarchyItem, Call> {
|
||||
|
||||
constructor(
|
||||
public provider: CallHierarchyProvider,
|
||||
public direction: () => CallHierarchyDirection
|
||||
) { }
|
||||
|
||||
hasChildren(_element: CallHierarchyItem): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
async getChildren(element: CallHierarchyItem | Call): Promise<Call[]> {
|
||||
if (element instanceof Call) {
|
||||
element = element.item;
|
||||
}
|
||||
const direction = this.direction();
|
||||
const calls = await this.provider.resolveCallHierarchyItem(element, direction, CancellationToken.None);
|
||||
return calls
|
||||
? calls.map(([item, locations]) => new Call(direction, item, locations))
|
||||
: [];
|
||||
}
|
||||
}
|
||||
|
||||
export class IdentityProvider implements IIdentityProvider<Call> {
|
||||
getId(element: Call): { toString(): string; } {
|
||||
return element.item._id;
|
||||
}
|
||||
}
|
||||
|
||||
class CallRenderingTemplate {
|
||||
iconLabel: IconLabel;
|
||||
}
|
||||
|
||||
export class CallRenderer implements ITreeRenderer<Call, FuzzyScore, CallRenderingTemplate> {
|
||||
|
||||
static id = 'CallRenderer';
|
||||
|
||||
templateId: string = CallRenderer.id;
|
||||
|
||||
constructor(@ILabelService private readonly _labelService: ILabelService) { }
|
||||
|
||||
renderTemplate(container: HTMLElement): CallRenderingTemplate {
|
||||
const iconLabel = new IconLabel(container, { supportHighlights: true });
|
||||
return { iconLabel };
|
||||
}
|
||||
renderElement(node: ITreeNode<Call, FuzzyScore>, _index: number, template: CallRenderingTemplate): void {
|
||||
const { element, filterData } = node;
|
||||
const detail = element.item.detail || this._labelService.getUriLabel(element.item.uri, { relative: true });
|
||||
|
||||
template.iconLabel.setLabel(
|
||||
element.item.name,
|
||||
detail,
|
||||
{
|
||||
labelEscapeNewLines: true,
|
||||
matches: createMatches(filterData),
|
||||
extraClasses: [symbolKindToCssClass(element.item.kind, true)]
|
||||
}
|
||||
);
|
||||
}
|
||||
disposeTemplate(template: CallRenderingTemplate): void {
|
||||
template.iconLabel.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
export class VirtualDelegate implements IListVirtualDelegate<Call> {
|
||||
|
||||
getHeight(_element: Call): number {
|
||||
return 22;
|
||||
}
|
||||
|
||||
getTemplateId(_element: Call): string {
|
||||
return CallRenderer.id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><style type="text/css">.icon-canvas-transparent{opacity:0;fill:#F6F6F6;} .icon-vs-out{fill:#F6F6F6;} .icon-vs-bg{fill:#424242;} .icon-vs-action-blue{fill:#00539C;}</style><path class="icon-canvas-transparent" d="M16 16h-16v-16h16v16z" id="canvas"/><path class="icon-vs-out" d="M15.989 8l-2 2h2v4h-2l2 2h-6l-3.989-3.989v2.287l-.497.289c-.464.27-.983.413-1.503.413-1.654 0-3-1.346-3-3v-6c0-1.654 1.346-3 3-3 .52 0 1.039.143 1.503.413l.497.289v4.298h-2v2h2v1.989l3.989-3.989h-3l2-2h-2v-4h2l-2-2h6l3 3 .011 1.989-3.011 3.011h3z" id="outline"/><path class="icon-vs-bg" d="M4 4c.366 0 .705.105 1 .277v2.723h-2v4h2v2.723c-.295.171-.634.277-1 .277-1.104 0-2-.896-2-2v-6c0-1.104.896-2 2-2z" id="iconBg"/><path class="icon-vs-action-blue" d="M11.989 1h-2l2 2h-4v2h4l-2 2h2l3-3-3-3zm-4 11l3 3h2l-2-2h4v-2h-4l2-2h-2l-3 3z" id="colorAction"/></svg>
|
||||
|
After Width: | Height: | Size: 898 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><style>.icon-canvas-transparent{opacity:0;fill:#f6f6f6}.icon-vs-out{fill:#f6f6f6}.icon-vs-bg{fill:#424242}.icon-vs-action-blue{fill:#00539c}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M15.989 8l-2 2h2v4h-2l2 2h-6L6 12.011v2.287l-.497.289C5.039 14.857 4.52 15 4 15c-1.654 0-3-1.346-3-3V6c0-1.654 1.346-3 3-3 .52 0 1.039.143 1.503.413L6 3.702V8H4v2h2v1.989L9.989 8h-3l2-2h-2V2h2l-2-2h6l3 3L16 4.989 12.989 8h3z" id="outline"/><path class="icon-vs-bg" d="M4 4c.366 0 .705.105 1 .277V7H3v4h2v2.723A1.987 1.987 0 0 1 4 14a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2z" id="iconBg"/><path class="icon-vs-action-blue" d="M11.989 1h-2l2 2h-4v2h4l-2 2h2l3-3-3-3zm-4 11l3 3h2l-2-2h4v-2h-4l2-2h-2l-3 3z" id="colorAction"/></svg>
|
||||
|
After Width: | Height: | Size: 831 B |
@@ -0,0 +1,39 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-workbench .call-hierarchy .results,
|
||||
.monaco-workbench .call-hierarchy .message {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.monaco-workbench .call-hierarchy[data-state="data"] .results {
|
||||
display: inherit;
|
||||
}
|
||||
|
||||
.monaco-workbench .call-hierarchy[data-state="message"] .message {
|
||||
display: inherit;
|
||||
text-align: center;
|
||||
padding-top: 3em;
|
||||
}
|
||||
|
||||
.monaco-workbench .call-hierarchy-toggle {
|
||||
background-image: url(CallerOrCalleeView_16x.svg);
|
||||
background-size: 14px 14px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: left center;
|
||||
}
|
||||
|
||||
.vs-dark .monaco-workbench .call-hierarchy-toggle,
|
||||
.hc-dark .monaco-workbench .call-hierarchy-toggle {
|
||||
background-image: url(CallerOrCalleeView_16x_dark.svg);
|
||||
}
|
||||
|
||||
.monaco-workbench .call-hierarchy .monaco-split-view2.horizontal > .split-view-container > .split-view-view{
|
||||
/* this is a little bizare.. */
|
||||
height: unset;
|
||||
}
|
||||
.monaco-workbench .call-hierarchy .tree{
|
||||
height: 100%;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IPosition } from 'vs/editor/common/core/position';
|
||||
import { IRange } from 'vs/editor/common/core/range';
|
||||
import { SymbolKind, ProviderResult, Location } from 'vs/editor/common/modes';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { LanguageFeatureRegistry } from 'vs/editor/common/modes/languageFeatureRegistry';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
export const enum CallHierarchyDirection {
|
||||
CallsFrom = 1,
|
||||
CallsTo = 2
|
||||
}
|
||||
|
||||
export interface CallHierarchyItem {
|
||||
_id: number;
|
||||
kind: SymbolKind;
|
||||
name: string;
|
||||
detail?: string;
|
||||
uri: URI;
|
||||
range: IRange;
|
||||
selectionRange: IRange;
|
||||
}
|
||||
|
||||
export interface CallHierarchyProvider {
|
||||
|
||||
provideCallHierarchyItem(
|
||||
document: ITextModel,
|
||||
postion: IPosition,
|
||||
token: CancellationToken
|
||||
): ProviderResult<CallHierarchyItem>;
|
||||
|
||||
resolveCallHierarchyItem(
|
||||
item: CallHierarchyItem,
|
||||
direction: CallHierarchyDirection,
|
||||
token: CancellationToken
|
||||
): ProviderResult<[CallHierarchyItem, Location[]][]>;
|
||||
}
|
||||
|
||||
export const CallHierarchyProviderRegistry = new LanguageFeatureRegistry<CallHierarchyProvider>();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import './menuPreventer';
|
||||
|
||||
@@ -245,7 +245,7 @@ export class CommentNode extends Disposable {
|
||||
}
|
||||
return this.actionItemProvider(action as Action);
|
||||
},
|
||||
this.actionRunner,
|
||||
this.actionRunner!,
|
||||
undefined,
|
||||
'toolbar-toggle-pickReactions',
|
||||
() => { return AnchorAlignment.RIGHT; }
|
||||
@@ -625,4 +625,4 @@ export class CommentNode extends Disposable {
|
||||
dispose() {
|
||||
this._toDispose.forEach(disposeable => disposeable.dispose());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,9 +200,16 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
|
||||
this._disposables.push(this._actionbarWidget);
|
||||
|
||||
this._collapseAction = new Action('review.expand', nls.localize('label.collapse', "Collapse"), COLLAPSE_ACTION_CLASS, true, () => {
|
||||
if (this._commentThread.comments.length === 0 && (this._commentThread as modes.CommentThread2).commentThreadHandle === undefined) {
|
||||
this.dispose();
|
||||
return Promise.resolve();
|
||||
if (this._commentThread.comments.length === 0) {
|
||||
if ((this._commentThread as modes.CommentThread2).commentThreadHandle === undefined) {
|
||||
this.dispose();
|
||||
return Promise.resolve();
|
||||
} else {
|
||||
const deleteCommand = (this._commentThread as modes.CommentThread2).deleteCommand;
|
||||
if (deleteCommand) {
|
||||
return this.commandService.executeCommand(deleteCommand.id, ...(deleteCommand.arguments || []));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._isCollapsed = true;
|
||||
@@ -1009,4 +1016,4 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
|
||||
this._submitActionsDisposables.forEach(local => local.dispose());
|
||||
this._onDidClose.fire(undefined);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,6 +168,8 @@ export class ReviewController implements IEditorContribution {
|
||||
private mouseDownInfo: { lineNumber: number } | null = null;
|
||||
private _commentingRangeSpaceReserved = false;
|
||||
private _computePromise: CancelablePromise<Array<ICommentInfo | null>> | null;
|
||||
private _addInProgress: boolean;
|
||||
private _emptyThreadsToAddQueue: number[] = [];
|
||||
private _computeCommentingRangePromise: CancelablePromise<ICommentInfo[]> | null;
|
||||
private _computeCommentingRangeScheduler: Delayer<Array<ICommentInfo | null>> | null;
|
||||
private _pendingCommentCache: { [key: number]: { [key: string]: string } };
|
||||
@@ -434,7 +436,8 @@ export class ReviewController implements IEditorContribution {
|
||||
}
|
||||
});
|
||||
added.forEach(thread => {
|
||||
this.displayCommentThread(e.owner, thread, null, draftMode);
|
||||
const pendingCommentText = this._pendingCommentCache[e.owner] && this._pendingCommentCache[e.owner][thread.threadId];
|
||||
this.displayCommentThread(e.owner, thread, pendingCommentText, draftMode);
|
||||
this._commentInfos.filter(info => info.owner === e.owner)[0].threads.push(thread);
|
||||
});
|
||||
|
||||
@@ -546,25 +549,41 @@ export class ReviewController implements IEditorContribution {
|
||||
|
||||
if (e.target.element.className.indexOf('comment-diff-added') >= 0) {
|
||||
const lineNumber = e.target.position!.lineNumber;
|
||||
this.addCommentAtLine(lineNumber);
|
||||
this.addOrToggleCommentAtLine(lineNumber);
|
||||
}
|
||||
}
|
||||
|
||||
public addOrToggleCommentAtLine(lineNumber: number): void {
|
||||
// The widget's position is undefined until the widget has been displayed, so rely on the glyph position instead
|
||||
const existingCommentsAtLine = this._commentWidgets.filter(widget => widget.getGlyphPosition() === lineNumber);
|
||||
if (existingCommentsAtLine.length) {
|
||||
existingCommentsAtLine.forEach(widget => widget.toggleExpand(lineNumber));
|
||||
return;
|
||||
public async addOrToggleCommentAtLine(lineNumber: number): Promise<void> {
|
||||
// If an add is already in progress, queue the next add and process it after the current one finishes to
|
||||
// prevent empty comment threads from being added to the same line.
|
||||
if (!this._addInProgress) {
|
||||
this._addInProgress = true;
|
||||
// The widget's position is undefined until the widget has been displayed, so rely on the glyph position instead
|
||||
const existingCommentsAtLine = this._commentWidgets.filter(widget => widget.getGlyphPosition() === lineNumber);
|
||||
if (existingCommentsAtLine.length) {
|
||||
existingCommentsAtLine.forEach(widget => widget.toggleExpand(lineNumber));
|
||||
this.processNextThreadToAdd();
|
||||
return;
|
||||
} else {
|
||||
this.addCommentAtLine(lineNumber);
|
||||
}
|
||||
} else {
|
||||
this.addCommentAtLine(lineNumber);
|
||||
this._emptyThreadsToAddQueue.push(lineNumber);
|
||||
}
|
||||
}
|
||||
|
||||
public addCommentAtLine(lineNumber: number): void {
|
||||
private processNextThreadToAdd(): void {
|
||||
this._addInProgress = false;
|
||||
const lineNumber = this._emptyThreadsToAddQueue.shift();
|
||||
if (lineNumber) {
|
||||
this.addOrToggleCommentAtLine(lineNumber);
|
||||
}
|
||||
}
|
||||
|
||||
public addCommentAtLine(lineNumber: number): Promise<void> {
|
||||
const newCommentInfo = this._commentingRangeDecorator.getMatchedCommentAction(lineNumber);
|
||||
if (!newCommentInfo || !this.editor.hasModel()) {
|
||||
return;
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const { replyCommand, ownerId, extensionId, commentingRangesInfo } = newCommentInfo;
|
||||
@@ -579,17 +598,26 @@ export class ReviewController implements IEditorContribution {
|
||||
this._commandService.executeCommand(commandId, ...args);
|
||||
}
|
||||
} else if (commentingRangesInfo.newCommentThreadCallback) {
|
||||
commentingRangesInfo.newCommentThreadCallback(this.editor.getModel().uri, range);
|
||||
return commentingRangesInfo.newCommentThreadCallback(this.editor.getModel().uri, range)
|
||||
.then(_ => {
|
||||
this.processNextThreadToAdd();
|
||||
})
|
||||
.catch(e => {
|
||||
this.notificationService.error(nls.localize('commentThreadAddFailure', "Adding a new comment thread failed: {0}.", e.message));
|
||||
this.processNextThreadToAdd();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
const commentInfo = this._commentInfos.filter(info => info.owner === ownerId);
|
||||
if (!commentInfo || !commentInfo.length) {
|
||||
return;
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const draftMode = commentInfo[0].draftMode;
|
||||
this.addComment(lineNumber, replyCommand, ownerId, extensionId, draftMode, null);
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
|
||||
@@ -735,8 +763,7 @@ CommandsRegistry.registerCommand({
|
||||
}
|
||||
|
||||
const position = activeEditor.getPosition();
|
||||
controller.addOrToggleCommentAtLine(position.lineNumber);
|
||||
return Promise.resolve();
|
||||
return controller.addOrToggleCommentAtLine(position.lineNumber);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -15,10 +15,8 @@ import { MenuId, IMenu, IMenuService } from 'vs/platform/actions/common/actions'
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { renderViewTree } from 'vs/workbench/contrib/debug/browser/baseDebugView';
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
import { RestartAction, StopAction, ContinueAction, StepOverAction, StepIntoAction, StepOutAction, PauseAction, RestartFrameAction, TerminateThreadAction, CopyStackTraceAction } from 'vs/workbench/contrib/debug/browser/debugActions';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IViewletPanelOptions, ViewletPanel } from 'vs/workbench/browser/parts/views/panelViewlet';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
@@ -264,32 +262,13 @@ export class CallStackView extends ViewletPanel {
|
||||
}
|
||||
|
||||
private onContextMenu(e: ITreeContextMenuEvent<CallStackItem>): void {
|
||||
const actions: IAction[] = [];
|
||||
const element = e.element;
|
||||
if (isDebugSession(element)) {
|
||||
this.callStackItemType.set('session');
|
||||
actions.push(this.instantiationService.createInstance(RestartAction, RestartAction.ID, RestartAction.LABEL));
|
||||
actions.push(new StopAction(StopAction.ID, StopAction.LABEL, this.debugService, this.keybindingService));
|
||||
} else if (element instanceof Thread) {
|
||||
this.callStackItemType.set('thread');
|
||||
const thread = <Thread>element;
|
||||
if (thread.stopped) {
|
||||
actions.push(new ContinueAction(ContinueAction.ID, ContinueAction.LABEL, this.debugService, this.keybindingService));
|
||||
actions.push(new StepOverAction(StepOverAction.ID, StepOverAction.LABEL, this.debugService, this.keybindingService));
|
||||
actions.push(new StepIntoAction(StepIntoAction.ID, StepIntoAction.LABEL, this.debugService, this.keybindingService));
|
||||
actions.push(new StepOutAction(StepOutAction.ID, StepOutAction.LABEL, this.debugService, this.keybindingService));
|
||||
} else {
|
||||
actions.push(new PauseAction(PauseAction.ID, PauseAction.LABEL, this.debugService, this.keybindingService));
|
||||
}
|
||||
|
||||
actions.push(new Separator());
|
||||
actions.push(new TerminateThreadAction(TerminateThreadAction.ID, TerminateThreadAction.LABEL, this.debugService, this.keybindingService));
|
||||
} else if (element instanceof StackFrame) {
|
||||
this.callStackItemType.set('stackFrame');
|
||||
if (element.thread.session.capabilities.supportsRestartFrame) {
|
||||
actions.push(new RestartFrameAction(RestartFrameAction.ID, RestartFrameAction.LABEL, this.debugService, this.keybindingService));
|
||||
}
|
||||
actions.push(this.instantiationService.createInstance(CopyStackTraceAction, CopyStackTraceAction.ID, CopyStackTraceAction.LABEL));
|
||||
} else {
|
||||
this.callStackItemType.reset();
|
||||
}
|
||||
@@ -297,7 +276,8 @@ export class CallStackView extends ViewletPanel {
|
||||
this.contextMenuService.showContextMenu({
|
||||
getAnchor: () => e.anchor,
|
||||
getActions: () => {
|
||||
fillInContextMenuActions(this.contributedContextMenu, { arg: this.getContextForContributedActions(element) }, actions, this.contextMenuService);
|
||||
const actions: IAction[] = [];
|
||||
fillInContextMenuActions(this.contributedContextMenu, { arg: this.getContextForContributedActions(element), shouldForwardArgs: true }, actions, this.contextMenuService);
|
||||
return actions;
|
||||
},
|
||||
getActionsContext: () => element
|
||||
@@ -315,6 +295,9 @@ export class CallStackView extends ViewletPanel {
|
||||
if (element instanceof Thread) {
|
||||
return element.threadId;
|
||||
}
|
||||
if (isDebugSession(element)) {
|
||||
return element.getId();
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@ import { List } from 'vs/base/browser/ui/list/listWidget';
|
||||
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { IListService } from 'vs/platform/list/browser/listService';
|
||||
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { IDebugService, IEnablement, CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_VARIABLES_FOCUSED, EDITOR_CONTRIBUTION_ID, IDebugEditorContribution, CONTEXT_IN_DEBUG_MODE, CONTEXT_EXPRESSION_SELECTED, CONTEXT_BREAKPOINT_SELECTED, IConfig } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { Expression, Variable, Breakpoint, FunctionBreakpoint } from 'vs/workbench/contrib/debug/common/debugModel';
|
||||
import { IDebugService, IEnablement, CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_VARIABLES_FOCUSED, EDITOR_CONTRIBUTION_ID, IDebugEditorContribution, CONTEXT_IN_DEBUG_MODE, CONTEXT_EXPRESSION_SELECTED, CONTEXT_BREAKPOINT_SELECTED, IConfig, IStackFrame, IThread, IDebugSession, CONTEXT_DEBUG_STATE, REPL_ID } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { Expression, Variable, Breakpoint, FunctionBreakpoint, Thread } from 'vs/workbench/contrib/debug/common/debugModel';
|
||||
import { IExtensionsViewlet, VIEWLET_ID as EXTENSIONS_VIEWLET_ID } from 'vs/workbench/contrib/extensions/common/extensions';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
@@ -25,12 +25,188 @@ import { ServicesAccessor } from 'vs/editor/browser/editorExtensions';
|
||||
import { PanelFocusContext } from 'vs/workbench/common/panel';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration';
|
||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||
import { IHistoryService } from 'vs/workbench/services/history/common/history';
|
||||
import { startDebugging } from 'vs/workbench/contrib/debug/common/debugUtils';
|
||||
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
|
||||
|
||||
export const ADD_CONFIGURATION_ID = 'debug.addConfiguration';
|
||||
export const TOGGLE_INLINE_BREAKPOINT_ID = 'editor.debug.action.toggleInlineBreakpoint';
|
||||
export const COPY_STACK_TRACE_ID = 'debug.copyStackTrace';
|
||||
export const REVERSE_CONTINUE_ID = 'workbench.action.debug.reverseContinue';
|
||||
export const STEP_BACK_ID = 'workbench.action.debug.stepBack';
|
||||
export const RESTART_SESSION_ID = 'workbench.action.debug.restart';
|
||||
export const TERMINATE_THREAD_ID = 'workbench.action.debug.terminateThread';
|
||||
export const STEP_OVER_ID = 'workbench.action.debug.stepOver';
|
||||
export const STEP_INTO_ID = 'workbench.action.debug.stepInto';
|
||||
export const STEP_OUT_ID = 'workbench.action.debug.stepOut';
|
||||
export const PAUSE_ID = 'workbench.action.debug.pause';
|
||||
export const DISCONNECT_ID = 'workbench.action.debug.disconnect';
|
||||
export const STOP_ID = 'workbench.action.debug.stop';
|
||||
export const RESTART_FRAME_ID = 'workbench.action.debug.restartFrame';
|
||||
export const CONTINUE_ID = 'workbench.action.debug.continue';
|
||||
export const FOCUS_REPL_ID = 'workbench.debug.action.focusRepl';
|
||||
|
||||
function getThreadAndRun(accessor: ServicesAccessor, thread: IThread | undefined, run: (thread: IThread) => Promise<void>, ): void {
|
||||
const debugService = accessor.get(IDebugService);
|
||||
if (!(thread instanceof Thread)) {
|
||||
thread = debugService.getViewModel().focusedThread;
|
||||
}
|
||||
|
||||
if (thread) {
|
||||
run(thread).then(undefined, onUnexpectedError);
|
||||
}
|
||||
}
|
||||
|
||||
export function registerCommands(): void {
|
||||
|
||||
CommandsRegistry.registerCommand({
|
||||
id: COPY_STACK_TRACE_ID,
|
||||
handler: (accessor: ServicesAccessor, _: string, frame: IStackFrame) => {
|
||||
const textResourcePropertiesService = accessor.get(ITextResourcePropertiesService);
|
||||
const clipboardService = accessor.get(IClipboardService);
|
||||
const eol = textResourcePropertiesService.getEOL(frame.source.uri);
|
||||
clipboardService.writeText(frame.thread.getCallStack().map(sf => sf.toString()).join(eol));
|
||||
}
|
||||
});
|
||||
|
||||
CommandsRegistry.registerCommand({
|
||||
id: REVERSE_CONTINUE_ID,
|
||||
handler: (accessor: ServicesAccessor, _: string, thread: IThread | undefined) => {
|
||||
getThreadAndRun(accessor, thread, thread => thread.reverseContinue());
|
||||
}
|
||||
});
|
||||
|
||||
CommandsRegistry.registerCommand({
|
||||
id: STEP_BACK_ID,
|
||||
handler: (accessor: ServicesAccessor, _: string, thread: IThread | undefined) => {
|
||||
getThreadAndRun(accessor, thread, thread => thread.stepBack());
|
||||
}
|
||||
});
|
||||
|
||||
CommandsRegistry.registerCommand({
|
||||
id: TERMINATE_THREAD_ID,
|
||||
handler: (accessor: ServicesAccessor, _: string, thread: IThread | undefined) => {
|
||||
getThreadAndRun(accessor, thread, thread => thread.terminate());
|
||||
}
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: RESTART_SESSION_ID,
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
primary: KeyMod.Shift | KeyMod.CtrlCmd | KeyCode.F5,
|
||||
when: CONTEXT_IN_DEBUG_MODE,
|
||||
handler: (accessor: ServicesAccessor, _: string, session: IDebugSession | undefined) => {
|
||||
const debugService = accessor.get(IDebugService);
|
||||
if (!session || !session.getId) {
|
||||
session = debugService.getViewModel().focusedSession;
|
||||
}
|
||||
|
||||
if (!session) {
|
||||
const historyService = accessor.get(IHistoryService);
|
||||
startDebugging(debugService, historyService, false);
|
||||
} else {
|
||||
session.removeReplExpressions();
|
||||
debugService.restartSession(session).then(undefined, onUnexpectedError);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: STEP_OVER_ID,
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
primary: KeyCode.F10,
|
||||
when: CONTEXT_DEBUG_STATE.isEqualTo('stopped'),
|
||||
handler: (accessor: ServicesAccessor, _: string, thread: IThread | undefined) => {
|
||||
getThreadAndRun(accessor, thread, thread => thread.next());
|
||||
}
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: STEP_INTO_ID,
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
primary: KeyCode.F11,
|
||||
when: CONTEXT_DEBUG_STATE.isEqualTo('stopped'),
|
||||
handler: (accessor: ServicesAccessor, _: string, thread: IThread | undefined) => {
|
||||
getThreadAndRun(accessor, thread, thread => thread.stepIn());
|
||||
}
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: STEP_OUT_ID,
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
primary: KeyMod.Shift | KeyCode.F11,
|
||||
when: CONTEXT_DEBUG_STATE.isEqualTo('stopped'),
|
||||
handler: (accessor: ServicesAccessor, _: string, thread: IThread | undefined) => {
|
||||
getThreadAndRun(accessor, thread, thread => thread.stepOut());
|
||||
}
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: PAUSE_ID,
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
primary: KeyCode.F6,
|
||||
when: CONTEXT_DEBUG_STATE.isEqualTo('running'),
|
||||
handler: (accessor: ServicesAccessor, _: string, thread: IThread | undefined) => {
|
||||
getThreadAndRun(accessor, thread, thread => thread.pause());
|
||||
}
|
||||
});
|
||||
|
||||
CommandsRegistry.registerCommand({
|
||||
id: DISCONNECT_ID,
|
||||
handler: (accessor: ServicesAccessor) => {
|
||||
const debugService = accessor.get(IDebugService);
|
||||
const session = debugService.getViewModel().focusedSession;
|
||||
debugService.stopSession(session).then(undefined, onUnexpectedError);
|
||||
}
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: STOP_ID,
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
primary: KeyMod.Shift | KeyCode.F5,
|
||||
when: CONTEXT_IN_DEBUG_MODE,
|
||||
handler: (accessor: ServicesAccessor, _: string, session: IDebugSession | undefined) => {
|
||||
const debugService = accessor.get(IDebugService);
|
||||
if (!session || !session.getId) {
|
||||
session = debugService.getViewModel().focusedSession;
|
||||
}
|
||||
|
||||
debugService.stopSession(session).then(undefined, onUnexpectedError);
|
||||
}
|
||||
});
|
||||
|
||||
CommandsRegistry.registerCommand({
|
||||
id: RESTART_FRAME_ID,
|
||||
handler: (accessor: ServicesAccessor, _: string, frame: IStackFrame | undefined) => {
|
||||
const debugService = accessor.get(IDebugService);
|
||||
if (!frame) {
|
||||
frame = debugService.getViewModel().focusedStackFrame;
|
||||
}
|
||||
|
||||
return frame!.restart();
|
||||
}
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: CONTINUE_ID,
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
primary: KeyCode.F5,
|
||||
when: CONTEXT_IN_DEBUG_MODE,
|
||||
handler: (accessor: ServicesAccessor, _: string, thread: IThread | undefined) => {
|
||||
getThreadAndRun(accessor, thread, thread => thread.continue());
|
||||
}
|
||||
});
|
||||
|
||||
CommandsRegistry.registerCommand({
|
||||
id: FOCUS_REPL_ID,
|
||||
handler: (accessor) => {
|
||||
const panelService = accessor.get(IPanelService);
|
||||
panelService.openPanel(REPL_ID, true);
|
||||
}
|
||||
});
|
||||
|
||||
CommandsRegistry.registerCommand({
|
||||
id: 'debug.startFromConfig',
|
||||
handler: (accessor, config: IConfig) => {
|
||||
|
||||
@@ -9,8 +9,8 @@ import { IAction } from 'vs/base/common/actions';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { IActionItem } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { ViewContainerViewlet } from 'vs/workbench/browser/parts/views/viewsViewlet';
|
||||
import { IDebugService, VIEWLET_ID, State, BREAKPOINTS_VIEW_ID, IDebugConfiguration } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { StartAction, ToggleReplAction, ConfigureAction, SelectAndStartAction, FocusSessionAction } from 'vs/workbench/contrib/debug/browser/debugActions';
|
||||
import { IDebugService, VIEWLET_ID, State, BREAKPOINTS_VIEW_ID, IDebugConfiguration, REPL_ID } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { StartAction, ConfigureAction, SelectAndStartAction, FocusSessionAction } from 'vs/workbench/contrib/debug/browser/debugActions';
|
||||
import { StartDebugActionItem, FocusSessionActionItem } from 'vs/workbench/contrib/debug/browser/debugActionItems';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
@@ -31,6 +31,8 @@ import { IMenu, MenuId, IMenuService, MenuItemAction } from 'vs/platform/actions
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { MenuItemActionItem } from 'vs/platform/actions/browser/menuItemActionItem';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { TogglePanelAction } from 'vs/workbench/browser/panel';
|
||||
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
|
||||
|
||||
export class DebugViewlet extends ViewContainerViewlet {
|
||||
|
||||
@@ -193,3 +195,15 @@ export class DebugViewlet extends ViewContainerViewlet {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ToggleReplAction extends TogglePanelAction {
|
||||
static readonly ID = 'debug.toggleRepl';
|
||||
static LABEL = nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugConsoleAction' }, 'Debug Console');
|
||||
|
||||
constructor(id: string, label: string,
|
||||
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
|
||||
@IPanelService panelService: IPanelService
|
||||
) {
|
||||
super(id, label, REPL_ID, panelService, layoutService, 'debug-action toggle-repl');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,6 @@ import { renderExpressionValue } from 'vs/workbench/contrib/debug/browser/baseDe
|
||||
import { handleANSIOutput } from 'vs/workbench/contrib/debug/browser/debugANSIHandling';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { LinkDetector } from 'vs/workbench/contrib/debug/browser/linkDetector';
|
||||
import { ReplCollapseAllAction, CopyAction } from 'vs/workbench/contrib/debug/browser/debugActions';
|
||||
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { removeAnsiEscapeCodes } from 'vs/base/common/strings';
|
||||
@@ -452,12 +451,19 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati
|
||||
|
||||
private onContextMenu(e: ITreeContextMenuEvent<IReplElement>): void {
|
||||
const actions: IAction[] = [];
|
||||
actions.push(new CopyAction(CopyAction.ID, CopyAction.LABEL, this.clipboardService));
|
||||
actions.push(new Action('debug.replCopy', nls.localize('copy', "Copy"), undefined, true, () => {
|
||||
this.clipboardService.writeText(window.getSelection().toString());
|
||||
return Promise.resolve();
|
||||
}));
|
||||
actions.push(new Action('workbench.debug.action.copyAll', nls.localize('copyAll', "Copy All"), undefined, true, () => {
|
||||
this.clipboardService.writeText(this.getVisibleContent());
|
||||
return Promise.resolve(undefined);
|
||||
}));
|
||||
actions.push(new ReplCollapseAllAction(this.tree, this.replInput));
|
||||
actions.push(new Action('debug.collapseRepl', nls.localize('collapse', "Collapse All"), undefined, true, () => {
|
||||
this.tree.collapseAll();
|
||||
this.replInput.focus();
|
||||
return Promise.resolve();
|
||||
}));
|
||||
actions.push(new Separator());
|
||||
actions.push(this.clearReplAction);
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@ import { Variable, Scope } from 'vs/workbench/contrib/debug/common/debugModel';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { renderViewTree, renderVariable, IInputBoxOptions, AbstractExpressionsRenderer, IExpressionTemplateData } from 'vs/workbench/contrib/debug/browser/baseDebugView';
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
import { SetValueAction, AddToWatchExpressionsAction, CopyValueAction, CopyEvaluatePathAction } from 'vs/workbench/contrib/debug/browser/debugActions';
|
||||
import { IAction, Action } from 'vs/base/common/actions';
|
||||
import { CopyValueAction } from 'vs/workbench/contrib/debug/browser/debugActions';
|
||||
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IViewletPanelOptions, ViewletPanel } from 'vs/workbench/browser/parts/views/panelViewlet';
|
||||
@@ -27,6 +27,7 @@ import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { FuzzyScore, createMatches } from 'vs/base/common/filters';
|
||||
import { HighlightedLabel, IHighlight } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel';
|
||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||
|
||||
const $ = dom.$;
|
||||
|
||||
@@ -45,6 +46,7 @@ export class VariablesView extends ViewletPanel {
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IClipboardService private readonly clipboardService: IClipboardService
|
||||
) {
|
||||
super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: nls.localize('variablesSection', "Variables Section") }, keybindingService, contextMenuService, configurationService);
|
||||
|
||||
@@ -129,20 +131,33 @@ export class VariablesView extends ViewletPanel {
|
||||
}
|
||||
|
||||
private onContextMenu(e: ITreeContextMenuEvent<IExpression | IScope>): void {
|
||||
const element = e.element;
|
||||
if (element instanceof Variable && !!element.value) {
|
||||
const variable = e.element;
|
||||
if (variable instanceof Variable && !!variable.value) {
|
||||
const actions: IAction[] = [];
|
||||
const variable = element as Variable;
|
||||
actions.push(new SetValueAction(SetValueAction.ID, SetValueAction.LABEL, variable, this.debugService, this.keybindingService));
|
||||
const session = this.debugService.getViewModel().focusedSession;
|
||||
if (session && session.capabilities.supportsSetVariable) {
|
||||
actions.push(new Action('workbench.setValue', nls.localize('setValue', "Set Value"), undefined, true, () => {
|
||||
this.debugService.getViewModel().setSelectedExpression(variable);
|
||||
return Promise.resolve();
|
||||
}));
|
||||
}
|
||||
actions.push(this.instantiationService.createInstance(CopyValueAction, CopyValueAction.ID, CopyValueAction.LABEL, variable, 'variables'));
|
||||
actions.push(this.instantiationService.createInstance(CopyEvaluatePathAction, CopyEvaluatePathAction.ID, CopyEvaluatePathAction.LABEL, variable));
|
||||
actions.push(new Separator());
|
||||
actions.push(new AddToWatchExpressionsAction(AddToWatchExpressionsAction.ID, AddToWatchExpressionsAction.LABEL, variable, this.debugService, this.keybindingService));
|
||||
if (variable.evaluateName) {
|
||||
actions.push(new Action('debug.copyEvaluatePath', nls.localize('copyAsExpression', "Copy as Expression"), undefined, true, () => {
|
||||
this.clipboardService.writeText(variable.evaluateName!);
|
||||
return Promise.resolve();
|
||||
}));
|
||||
actions.push(new Separator());
|
||||
actions.push(new Action('debug.addToWatchExpressions', nls.localize('addToWatchExpressions', "Add to Watch"), undefined, true, () => {
|
||||
this.debugService.addWatchExpression(variable.evaluateName);
|
||||
return Promise.resolve(undefined);
|
||||
}));
|
||||
}
|
||||
|
||||
this.contextMenuService.showContextMenu({
|
||||
getAnchor: () => e.anchor,
|
||||
getActions: () => actions,
|
||||
getActionsContext: () => element
|
||||
getActionsContext: () => variable
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,11 +10,11 @@ import { CollapseAction } from 'vs/workbench/browser/viewlet';
|
||||
import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
|
||||
import { IDebugService, IExpression, CONTEXT_WATCH_EXPRESSIONS_FOCUSED } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { Expression, Variable } from 'vs/workbench/contrib/debug/common/debugModel';
|
||||
import { AddWatchExpressionAction, RemoveAllWatchExpressionsAction, EditWatchExpressionAction, RemoveWatchExpressionAction, CopyValueAction } from 'vs/workbench/contrib/debug/browser/debugActions';
|
||||
import { AddWatchExpressionAction, RemoveAllWatchExpressionsAction, CopyValueAction } from 'vs/workbench/contrib/debug/browser/debugActions';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
import { IAction, Action } from 'vs/base/common/actions';
|
||||
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { renderExpressionValue, renderViewTree, IInputBoxOptions, AbstractExpressionsRenderer, IExpressionTemplateData } from 'vs/workbench/contrib/debug/browser/baseDebugView';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
@@ -144,13 +144,19 @@ export class WatchExpressionsView extends ViewletPanel {
|
||||
if (element instanceof Expression) {
|
||||
const expression = <Expression>element;
|
||||
actions.push(new AddWatchExpressionAction(AddWatchExpressionAction.ID, AddWatchExpressionAction.LABEL, this.debugService, this.keybindingService));
|
||||
actions.push(new EditWatchExpressionAction(EditWatchExpressionAction.ID, EditWatchExpressionAction.LABEL, this.debugService, this.keybindingService));
|
||||
actions.push(new Action('debug.editWatchExpression', nls.localize('editWatchExpression', "Edit Expression"), undefined, true, () => {
|
||||
this.debugService.getViewModel().setSelectedExpression(expression);
|
||||
return Promise.resolve();
|
||||
}));
|
||||
if (!expression.hasChildren) {
|
||||
actions.push(this.instantiationService.createInstance(CopyValueAction, CopyValueAction.ID, CopyValueAction.LABEL, expression.value, 'watch', this.debugService));
|
||||
}
|
||||
actions.push(new Separator());
|
||||
|
||||
actions.push(new RemoveWatchExpressionAction(RemoveWatchExpressionAction.ID, RemoveWatchExpressionAction.LABEL, this.debugService, this.keybindingService));
|
||||
actions.push(new Action('debug.removeWatchExpression', nls.localize('removeWatchExpression', "Remove Expression"), undefined, true, () => {
|
||||
this.debugService.removeWatchExpressions(expression.getId());
|
||||
return Promise.resolve();
|
||||
}));
|
||||
actions.push(new RemoveAllWatchExpressionsAction(RemoveAllWatchExpressionsAction.ID, RemoveAllWatchExpressionsAction.LABEL, this.debugService, this.keybindingService));
|
||||
} else {
|
||||
actions.push(new AddWatchExpressionAction(AddWatchExpressionAction.ID, AddWatchExpressionAction.LABEL, this.debugService, this.keybindingService));
|
||||
|
||||
161
src/vs/workbench/contrib/debug/common/abstractDebugAdapter.ts
Normal file
@@ -0,0 +1,161 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { IDebugAdapter } from 'vs/workbench/contrib/debug/common/debug';
|
||||
|
||||
/**
|
||||
* Abstract implementation of the low level API for a debug adapter.
|
||||
* Missing is how this API communicates with the debug adapter.
|
||||
*/
|
||||
export abstract class AbstractDebugAdapter implements IDebugAdapter {
|
||||
|
||||
private sequence: number;
|
||||
private pendingRequests: Map<number, (e: DebugProtocol.Response) => void>;
|
||||
private requestCallback: (request: DebugProtocol.Request) => void;
|
||||
private eventCallback: (request: DebugProtocol.Event) => void;
|
||||
private messageCallback: (message: DebugProtocol.ProtocolMessage) => void;
|
||||
protected readonly _onError: Emitter<Error>;
|
||||
protected readonly _onExit: Emitter<number | null>;
|
||||
|
||||
constructor() {
|
||||
this.sequence = 1;
|
||||
this.pendingRequests = new Map();
|
||||
this._onError = new Emitter<Error>();
|
||||
this._onExit = new Emitter<number>();
|
||||
}
|
||||
|
||||
abstract startSession(): Promise<void>;
|
||||
|
||||
abstract stopSession(): Promise<void>;
|
||||
|
||||
abstract sendMessage(message: DebugProtocol.ProtocolMessage): void;
|
||||
|
||||
get onError(): Event<Error> {
|
||||
return this._onError.event;
|
||||
}
|
||||
|
||||
get onExit(): Event<number | null> {
|
||||
return this._onExit.event;
|
||||
}
|
||||
|
||||
onMessage(callback: (message: DebugProtocol.ProtocolMessage) => void): void {
|
||||
if (this.eventCallback) {
|
||||
this._onError.fire(new Error(`attempt to set more than one 'Message' callback`));
|
||||
}
|
||||
this.messageCallback = callback;
|
||||
}
|
||||
|
||||
onEvent(callback: (event: DebugProtocol.Event) => void): void {
|
||||
if (this.eventCallback) {
|
||||
this._onError.fire(new Error(`attempt to set more than one 'Event' callback`));
|
||||
}
|
||||
this.eventCallback = callback;
|
||||
}
|
||||
|
||||
onRequest(callback: (request: DebugProtocol.Request) => void): void {
|
||||
if (this.requestCallback) {
|
||||
this._onError.fire(new Error(`attempt to set more than one 'Request' callback`));
|
||||
}
|
||||
this.requestCallback = callback;
|
||||
}
|
||||
|
||||
sendResponse(response: DebugProtocol.Response): void {
|
||||
if (response.seq > 0) {
|
||||
this._onError.fire(new Error(`attempt to send more than one response for command ${response.command}`));
|
||||
}
|
||||
else {
|
||||
this.internalSend('response', response);
|
||||
}
|
||||
}
|
||||
|
||||
sendRequest(command: string, args: any, clb: (result: DebugProtocol.Response) => void, timeout?: number): void {
|
||||
const request: any = {
|
||||
command: command
|
||||
};
|
||||
if (args && Object.keys(args).length > 0) {
|
||||
request.arguments = args;
|
||||
}
|
||||
this.internalSend('request', request);
|
||||
if (typeof timeout === 'number') {
|
||||
const timer = setTimeout(() => {
|
||||
clearTimeout(timer);
|
||||
const clb = this.pendingRequests.get(request.seq);
|
||||
if (clb) {
|
||||
this.pendingRequests.delete(request.seq);
|
||||
const err: DebugProtocol.Response = {
|
||||
type: 'response',
|
||||
seq: 0,
|
||||
request_seq: request.seq,
|
||||
success: false,
|
||||
command,
|
||||
message: `timeout after ${timeout} ms`
|
||||
};
|
||||
clb(err);
|
||||
}
|
||||
}, timeout);
|
||||
}
|
||||
if (clb) {
|
||||
// store callback for this request
|
||||
this.pendingRequests.set(request.seq, clb);
|
||||
}
|
||||
}
|
||||
|
||||
acceptMessage(message: DebugProtocol.ProtocolMessage): void {
|
||||
if (this.messageCallback) {
|
||||
this.messageCallback(message);
|
||||
}
|
||||
else {
|
||||
switch (message.type) {
|
||||
case 'event':
|
||||
if (this.eventCallback) {
|
||||
this.eventCallback(<DebugProtocol.Event>message);
|
||||
}
|
||||
break;
|
||||
case 'request':
|
||||
if (this.requestCallback) {
|
||||
this.requestCallback(<DebugProtocol.Request>message);
|
||||
}
|
||||
break;
|
||||
case 'response':
|
||||
const response = <DebugProtocol.Response>message;
|
||||
const clb = this.pendingRequests.get(response.request_seq);
|
||||
if (clb) {
|
||||
this.pendingRequests.delete(response.request_seq);
|
||||
clb(response);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private internalSend(typ: 'request' | 'response' | 'event', message: DebugProtocol.ProtocolMessage): void {
|
||||
message.type = typ;
|
||||
message.seq = this.sequence++;
|
||||
this.sendMessage(message);
|
||||
}
|
||||
|
||||
protected cancelPending() {
|
||||
const pending = this.pendingRequests;
|
||||
this.pendingRequests = new Map();
|
||||
setTimeout(_ => {
|
||||
pending.forEach((callback, request_seq) => {
|
||||
const err: DebugProtocol.Response = {
|
||||
type: 'response',
|
||||
seq: 0,
|
||||
request_seq,
|
||||
success: false,
|
||||
command: 'canceled',
|
||||
message: 'canceled'
|
||||
};
|
||||
callback(err);
|
||||
});
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.cancelPending();
|
||||
}
|
||||
}
|
||||
@@ -53,6 +53,7 @@ export const CONTEXT_LOADED_SCRIPTS_SUPPORTED = new RawContextKey<boolean>('load
|
||||
export const CONTEXT_LOADED_SCRIPTS_ITEM_TYPE = new RawContextKey<string>('loadedScriptsItemType', undefined);
|
||||
export const CONTEXT_FOCUSED_SESSION_IS_ATTACH = new RawContextKey<boolean>('focusedSessionIsAttach', false);
|
||||
export const CONTEXT_STEP_BACK_SUPPORTED = new RawContextKey<boolean>('stepBackSupported', false);
|
||||
export const CONTEXT_RESTART_FRAME_SUPPORTED = new RawContextKey<boolean>('restartFrameSupported', false);
|
||||
|
||||
export const EDITOR_CONTRIBUTION_ID = 'editor.contrib.debug';
|
||||
export const DEBUG_SCHEME = 'debug';
|
||||
|
||||
@@ -4,13 +4,32 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { equalsIgnoreCase } from 'vs/base/common/strings';
|
||||
import { IConfig, IDebuggerContribution } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IConfig, IDebuggerContribution, IDebugService } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { URI as uri } from 'vs/base/common/uri';
|
||||
import { isAbsolute } from 'vs/base/common/path';
|
||||
import { deepClone } from 'vs/base/common/objects';
|
||||
import { IHistoryService } from 'vs/workbench/services/history/common/history';
|
||||
import { first } from 'vs/base/common/arrays';
|
||||
|
||||
const _formatPIIRegexp = /{([^}]+)}/g;
|
||||
|
||||
export function startDebugging(debugService: IDebugService, historyService: IHistoryService, noDebug: boolean, ): Promise<boolean> {
|
||||
const configurationManager = debugService.getConfigurationManager();
|
||||
let launch = configurationManager.selectedConfiguration.launch;
|
||||
if (!launch || launch.getConfigurationNames().length === 0) {
|
||||
const rootUri = historyService.getLastActiveWorkspaceRoot();
|
||||
launch = configurationManager.getLaunch(rootUri);
|
||||
if (!launch || launch.getConfigurationNames().length === 0) {
|
||||
const launches = configurationManager.getLaunches();
|
||||
launch = first(launches, l => !!(l && l.getConfigurationNames().length), launch);
|
||||
}
|
||||
|
||||
configurationManager.selectConfiguration(launch);
|
||||
}
|
||||
|
||||
return debugService.startDebugging(launch, undefined, noDebug);
|
||||
}
|
||||
|
||||
export function formatPII(value: string, excludePII: boolean, args: { [key: string]: string }): string {
|
||||
return value.replace(_formatPIIRegexp, function (match, group) {
|
||||
if (excludePII && group.length > 0 && group[0] !== '_') {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { CONTEXT_EXPRESSION_SELECTED, IViewModel, IStackFrame, IDebugSession, IThread, IExpression, IFunctionBreakpoint, CONTEXT_BREAKPOINT_SELECTED, CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_FOCUSED_SESSION_IS_ATTACH } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { CONTEXT_EXPRESSION_SELECTED, IViewModel, IStackFrame, IDebugSession, IThread, IExpression, IFunctionBreakpoint, CONTEXT_BREAKPOINT_SELECTED, CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_RESTART_FRAME_SUPPORTED } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { isExtensionHostDebugging } from 'vs/workbench/contrib/debug/common/debugUtils';
|
||||
|
||||
@@ -26,6 +26,7 @@ export class ViewModel implements IViewModel {
|
||||
private loadedScriptsSupportedContextKey: IContextKey<boolean>;
|
||||
private stepBackSupportedContextKey: IContextKey<boolean>;
|
||||
private focusedSessionIsAttach: IContextKey<boolean>;
|
||||
private restartFrameSupportedContextKey: IContextKey<boolean>;
|
||||
|
||||
constructor(contextKeyService: IContextKeyService) {
|
||||
this._onDidFocusSession = new Emitter<IDebugSession | undefined>();
|
||||
@@ -37,6 +38,7 @@ export class ViewModel implements IViewModel {
|
||||
this.loadedScriptsSupportedContextKey = CONTEXT_LOADED_SCRIPTS_SUPPORTED.bindTo(contextKeyService);
|
||||
this.stepBackSupportedContextKey = CONTEXT_STEP_BACK_SUPPORTED.bindTo(contextKeyService);
|
||||
this.focusedSessionIsAttach = CONTEXT_FOCUSED_SESSION_IS_ATTACH.bindTo(contextKeyService);
|
||||
this.restartFrameSupportedContextKey = CONTEXT_RESTART_FRAME_SUPPORTED.bindTo(contextKeyService);
|
||||
}
|
||||
|
||||
getId(): string {
|
||||
@@ -65,6 +67,7 @@ export class ViewModel implements IViewModel {
|
||||
|
||||
this.loadedScriptsSupportedContextKey.set(session ? !!session.capabilities.supportsLoadedSourcesRequest : false);
|
||||
this.stepBackSupportedContextKey.set(session ? !!session.capabilities.supportsStepBack : false);
|
||||
this.restartFrameSupportedContextKey.set(session ? !!session.capabilities.supportsRestartFrame : false);
|
||||
const attach = !!session && session.configuration.request === 'attach' && !isExtensionHostDebugging(session.configuration);
|
||||
this.focusedSessionIsAttach.set(attach);
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { KeybindingWeight, IKeybindings } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { IKeybindings } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { IWorkbenchActionRegistry, Extensions as WorkbenchActionRegistryExtensions } from 'vs/workbench/common/actions';
|
||||
import { ShowViewletAction, Extensions as ViewletExtensions, ViewletRegistry, ViewletDescriptor } from 'vs/workbench/browser/viewlet';
|
||||
@@ -22,19 +22,16 @@ import { CallStackView } from 'vs/workbench/contrib/debug/browser/callStackView'
|
||||
import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
|
||||
import {
|
||||
IDebugService, VIEWLET_ID, REPL_ID, CONTEXT_IN_DEBUG_MODE, INTERNAL_CONSOLE_OPTIONS_SCHEMA,
|
||||
CONTEXT_DEBUG_STATE, VARIABLES_VIEW_ID, CALLSTACK_VIEW_ID, WATCH_VIEW_ID, BREAKPOINTS_VIEW_ID, VIEW_CONTAINER, LOADED_SCRIPTS_VIEW_ID, CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_STEP_BACK_SUPPORTED,
|
||||
CONTEXT_DEBUG_STATE, VARIABLES_VIEW_ID, CALLSTACK_VIEW_ID, WATCH_VIEW_ID, BREAKPOINTS_VIEW_ID, VIEW_CONTAINER, LOADED_SCRIPTS_VIEW_ID, CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_CALLSTACK_ITEM_TYPE, CONTEXT_RESTART_FRAME_SUPPORTED,
|
||||
} from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
|
||||
import { DebugEditorModelManager } from 'vs/workbench/contrib/debug/browser/debugEditorModelManager';
|
||||
import {
|
||||
StepOverAction, FocusReplAction, StepIntoAction, StepOutAction, StartAction, RestartAction, ContinueAction, StopAction, DisconnectAction, PauseAction, AddFunctionBreakpointAction,
|
||||
ConfigureAction, DisableAllBreakpointsAction, EnableAllBreakpointsAction, RemoveAllBreakpointsAction, RunAction, ReapplyBreakpointsAction, SelectAndStartAction, TerminateThreadAction, StepBackAction, ReverseContinueAction,
|
||||
} from 'vs/workbench/contrib/debug/browser/debugActions';
|
||||
import { StartAction, AddFunctionBreakpointAction, ConfigureAction, DisableAllBreakpointsAction, EnableAllBreakpointsAction, RemoveAllBreakpointsAction, RunAction, ReapplyBreakpointsAction, SelectAndStartAction } from 'vs/workbench/contrib/debug/browser/debugActions';
|
||||
import { DebugToolbar } from 'vs/workbench/contrib/debug/browser/debugToolbar';
|
||||
import * as service from 'vs/workbench/contrib/debug/electron-browser/debugService';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { registerCommands, ADD_CONFIGURATION_ID, TOGGLE_INLINE_BREAKPOINT_ID } from 'vs/workbench/contrib/debug/browser/debugCommands';
|
||||
import { registerCommands, ADD_CONFIGURATION_ID, TOGGLE_INLINE_BREAKPOINT_ID, COPY_STACK_TRACE_ID, REVERSE_CONTINUE_ID, STEP_BACK_ID, RESTART_SESSION_ID, TERMINATE_THREAD_ID, STEP_OVER_ID, STEP_INTO_ID, STEP_OUT_ID, PAUSE_ID, DISCONNECT_ID, STOP_ID, RESTART_FRAME_ID, CONTINUE_ID, FOCUS_REPL_ID } from 'vs/workbench/contrib/debug/browser/debugCommands';
|
||||
import { IQuickOpenRegistry, Extensions as QuickOpenExtensions, QuickOpenHandlerDescriptor } from 'vs/workbench/browser/quickopen';
|
||||
import { StatusBarColorProvider } from 'vs/workbench/contrib/debug/browser/statusbarColorProvider';
|
||||
import { IViewsRegistry, Extensions as ViewExtensions } from 'vs/workbench/common/views';
|
||||
@@ -119,6 +116,8 @@ viewsRegistry.registerViews([{ id: CALLSTACK_VIEW_ID, name: nls.localize('callSt
|
||||
viewsRegistry.registerViews([{ id: BREAKPOINTS_VIEW_ID, name: nls.localize('breakpoints', "Breakpoints"), ctorDescriptor: { ctor: BreakpointsView }, order: 40, weight: 20, canToggleVisibility: true, focusCommand: { id: 'workbench.debug.action.focusBreakpointsView' } }], VIEW_CONTAINER);
|
||||
viewsRegistry.registerViews([{ id: LOADED_SCRIPTS_VIEW_ID, name: nls.localize('loadedScripts', "Loaded Scripts"), ctorDescriptor: { ctor: LoadedScriptsView }, order: 35, weight: 5, canToggleVisibility: true, collapsed: true, when: CONTEXT_LOADED_SCRIPTS_SUPPORTED }], VIEW_CONTAINER);
|
||||
|
||||
registerCommands();
|
||||
|
||||
// register action to open viewlet
|
||||
const registry = Registry.as<IWorkbenchActionRegistry>(WorkbenchActionRegistryExtensions.WorkbenchActions);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenDebugPanelAction, OpenDebugPanelAction.ID, OpenDebugPanelAction.LABEL, openPanelKb), 'View: Debug Console', nls.localize('view', "View"));
|
||||
@@ -132,15 +131,6 @@ Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).regi
|
||||
const debugCategory = nls.localize('debugCategory', "Debug");
|
||||
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(StartAction, StartAction.ID, StartAction.LABEL, { primary: KeyCode.F5 }, CONTEXT_IN_DEBUG_MODE.toNegated()), 'Debug: Start Debugging', debugCategory);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(StepOverAction, StepOverAction.ID, StepOverAction.LABEL, { primary: KeyCode.F10 }, CONTEXT_IN_DEBUG_MODE), 'Debug: Step Over', debugCategory);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(StepIntoAction, StepIntoAction.ID, StepIntoAction.LABEL, { primary: KeyCode.F11 }, CONTEXT_IN_DEBUG_MODE, KeybindingWeight.WorkbenchContrib + 1), 'Debug: Step Into', debugCategory);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(StepOutAction, StepOutAction.ID, StepOutAction.LABEL, { primary: KeyMod.Shift | KeyCode.F11 }, CONTEXT_IN_DEBUG_MODE), 'Debug: Step Out', debugCategory);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(RestartAction, RestartAction.ID, RestartAction.LABEL, { primary: KeyMod.Shift | KeyMod.CtrlCmd | KeyCode.F5 }, CONTEXT_IN_DEBUG_MODE), 'Debug: Restart', debugCategory);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(StopAction, StopAction.ID, StopAction.LABEL, { primary: KeyMod.Shift | KeyCode.F5 }, CONTEXT_IN_DEBUG_MODE), 'Debug: Stop', debugCategory);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(DisconnectAction, DisconnectAction.ID, DisconnectAction.LABEL), 'Debug: Disconnect', debugCategory);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(ContinueAction, ContinueAction.ID, ContinueAction.LABEL, { primary: KeyCode.F5 }, CONTEXT_IN_DEBUG_MODE), 'Debug: Continue', debugCategory);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(PauseAction, PauseAction.ID, PauseAction.LABEL, { primary: KeyCode.F6 }, CONTEXT_IN_DEBUG_MODE), 'Debug: Pause', debugCategory);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(TerminateThreadAction, TerminateThreadAction.ID, TerminateThreadAction.LABEL, undefined, CONTEXT_IN_DEBUG_MODE), 'Debug: Terminate Thread', debugCategory);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(ConfigureAction, ConfigureAction.ID, ConfigureAction.LABEL), 'Debug: Open launch.json', debugCategory);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(AddFunctionBreakpointAction, AddFunctionBreakpointAction.ID, AddFunctionBreakpointAction.LABEL), 'Debug: Add Function Breakpoint', debugCategory);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(ReapplyBreakpointsAction, ReapplyBreakpointsAction.ID, ReapplyBreakpointsAction.LABEL), 'Debug: Reapply All Breakpoints', debugCategory);
|
||||
@@ -148,10 +138,38 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(RunAction, RunAction.I
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(RemoveAllBreakpointsAction, RemoveAllBreakpointsAction.ID, RemoveAllBreakpointsAction.LABEL), 'Debug: Remove All Breakpoints', debugCategory);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(EnableAllBreakpointsAction, EnableAllBreakpointsAction.ID, EnableAllBreakpointsAction.LABEL), 'Debug: Enable All Breakpoints', debugCategory);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(DisableAllBreakpointsAction, DisableAllBreakpointsAction.ID, DisableAllBreakpointsAction.LABEL), 'Debug: Disable All Breakpoints', debugCategory);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusReplAction, FocusReplAction.ID, FocusReplAction.LABEL), 'Debug: Focus on Debug Console View', debugCategory);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(SelectAndStartAction, SelectAndStartAction.ID, SelectAndStartAction.LABEL), 'Debug: Select and Start Debugging', debugCategory);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(ClearReplAction, ClearReplAction.ID, ClearReplAction.LABEL), 'Debug: Clear Console', debugCategory);
|
||||
|
||||
const registerDebugCommandPaletteItem = (id: string, title: string, when?: ContextKeyExpr, precondition?: ContextKeyExpr) => {
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
|
||||
when,
|
||||
command: {
|
||||
id,
|
||||
title: `Debug: ${title}`,
|
||||
precondition
|
||||
}
|
||||
});
|
||||
};
|
||||
const restartLabel = nls.localize('restartDebug', "Restart");
|
||||
const stepOverLabel = nls.localize('stepOverDebug', "Step Over");
|
||||
const stepIntoLabel = nls.localize('stepIntoDebug', "Step Into");
|
||||
const stepOutLabel = nls.localize('stepOutDebug', "Step Out");
|
||||
const pauseLabel = nls.localize('pauseDebug', "Pause");
|
||||
const disconnectLabel = nls.localize('disconnect', "Disconnect");
|
||||
const stopLabel = nls.localize('stop', "Stop");
|
||||
const continueLabel = nls.localize('continueDebug', "Continue");
|
||||
registerDebugCommandPaletteItem(RESTART_SESSION_ID, restartLabel);
|
||||
registerDebugCommandPaletteItem(TERMINATE_THREAD_ID, nls.localize('terminateThread', "Terminate Thread"), CONTEXT_IN_DEBUG_MODE);
|
||||
registerDebugCommandPaletteItem(STEP_OVER_ID, stepOverLabel, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE.isEqualTo('stopped'));
|
||||
registerDebugCommandPaletteItem(STEP_INTO_ID, stepIntoLabel, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE.isEqualTo('stopped'));
|
||||
registerDebugCommandPaletteItem(STEP_OUT_ID, stepOutLabel, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE.isEqualTo('stopped'));
|
||||
registerDebugCommandPaletteItem(PAUSE_ID, pauseLabel, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE.isEqualTo('running'));
|
||||
registerDebugCommandPaletteItem(DISCONNECT_ID, disconnectLabel, CONTEXT_IN_DEBUG_MODE, CONTEXT_FOCUSED_SESSION_IS_ATTACH);
|
||||
registerDebugCommandPaletteItem(STOP_ID, stopLabel, CONTEXT_IN_DEBUG_MODE, CONTEXT_FOCUSED_SESSION_IS_ATTACH.toNegated());
|
||||
registerDebugCommandPaletteItem(CONTINUE_ID, continueLabel, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE.isEqualTo('stopped'));
|
||||
registerDebugCommandPaletteItem(FOCUS_REPL_ID, nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugFocusConsole' }, 'Focus on Debug Console View'));
|
||||
|
||||
// Register Quick Open
|
||||
(Registry.as<IQuickOpenRegistry>(QuickOpenExtensions.Quickopen)).registerQuickOpenHandler(
|
||||
new QuickOpenHandlerDescriptor(
|
||||
@@ -235,8 +253,6 @@ configurationRegistry.registerConfiguration({
|
||||
}
|
||||
});
|
||||
|
||||
registerCommands();
|
||||
|
||||
// Register Debug Status
|
||||
const statusBar = Registry.as<IStatusbarRegistry>(StatusExtensions.Statusbar);
|
||||
statusBar.registerStatusbarItem(new StatusbarItemDescriptor(DebugStatus, StatusbarAlignment.LEFT, 30 /* Low Priority */));
|
||||
@@ -260,16 +276,40 @@ const registerDebugToolbarItem = (id: string, title: string, icon: string, order
|
||||
});
|
||||
};
|
||||
|
||||
registerDebugToolbarItem(ContinueAction.ID, ContinueAction.LABEL, 'continue', 10, CONTEXT_DEBUG_STATE.notEqualsTo('running'));
|
||||
registerDebugToolbarItem(PauseAction.ID, PauseAction.LABEL, 'pause', 10, CONTEXT_DEBUG_STATE.isEqualTo('running'));
|
||||
registerDebugToolbarItem(StopAction.ID, StopAction.LABEL, 'stop', 70, CONTEXT_FOCUSED_SESSION_IS_ATTACH.toNegated());
|
||||
registerDebugToolbarItem(DisconnectAction.ID, DisconnectAction.LABEL, 'disconnect', 70, CONTEXT_FOCUSED_SESSION_IS_ATTACH);
|
||||
registerDebugToolbarItem(StepOverAction.ID, StepOverAction.LABEL, 'step-over', 20, undefined, CONTEXT_DEBUG_STATE.isEqualTo('stopped'));
|
||||
registerDebugToolbarItem(StepIntoAction.ID, StepIntoAction.LABEL, 'step-into', 30, undefined, CONTEXT_DEBUG_STATE.isEqualTo('stopped'));
|
||||
registerDebugToolbarItem(StepOutAction.ID, StepOutAction.LABEL, 'step-out', 40, undefined, CONTEXT_DEBUG_STATE.isEqualTo('stopped'));
|
||||
registerDebugToolbarItem(RestartAction.ID, RestartAction.LABEL, 'restart', 60);
|
||||
registerDebugToolbarItem(StepBackAction.ID, StepBackAction.LABEL, 'step-back', 50, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_DEBUG_STATE.isEqualTo('stopped'));
|
||||
registerDebugToolbarItem(ReverseContinueAction.ID, ReverseContinueAction.LABEL, 'reverse-continue', 60, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_DEBUG_STATE.isEqualTo('stopped'));
|
||||
registerDebugToolbarItem(CONTINUE_ID, continueLabel, 'continue', 10, CONTEXT_DEBUG_STATE.notEqualsTo('running'));
|
||||
registerDebugToolbarItem(PAUSE_ID, pauseLabel, 'pause', 10, CONTEXT_DEBUG_STATE.isEqualTo('running'));
|
||||
registerDebugToolbarItem(STOP_ID, stopLabel, 'stop', 70, CONTEXT_FOCUSED_SESSION_IS_ATTACH.toNegated());
|
||||
registerDebugToolbarItem(DISCONNECT_ID, disconnectLabel, 'disconnect', 70, CONTEXT_FOCUSED_SESSION_IS_ATTACH);
|
||||
registerDebugToolbarItem(STEP_OVER_ID, stepOverLabel, 'step-over', 20, undefined, CONTEXT_DEBUG_STATE.isEqualTo('stopped'));
|
||||
registerDebugToolbarItem(STEP_INTO_ID, stepIntoLabel, 'step-into', 30, undefined, CONTEXT_DEBUG_STATE.isEqualTo('stopped'));
|
||||
registerDebugToolbarItem(STEP_OUT_ID, stepOutLabel, 'step-out', 40, undefined, CONTEXT_DEBUG_STATE.isEqualTo('stopped'));
|
||||
registerDebugToolbarItem(RESTART_SESSION_ID, restartLabel, 'restart', 60);
|
||||
registerDebugToolbarItem(STEP_BACK_ID, nls.localize('stepBackDebug', "Step Back"), 'step-back', 50, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_DEBUG_STATE.isEqualTo('stopped'));
|
||||
registerDebugToolbarItem(REVERSE_CONTINUE_ID, nls.localize('reverseContinue', "Reverse"), 'reverse-continue', 60, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_DEBUG_STATE.isEqualTo('stopped'));
|
||||
|
||||
// Debug callstack context menu
|
||||
const registerDebugCallstackItem = (id: string, title: string, order: number, when?: ContextKeyExpr, precondition?: ContextKeyExpr, group = 'navigation') => {
|
||||
MenuRegistry.appendMenuItem(MenuId.DebugCallStackContext, {
|
||||
group,
|
||||
when,
|
||||
order,
|
||||
command: {
|
||||
id,
|
||||
title,
|
||||
precondition
|
||||
}
|
||||
});
|
||||
};
|
||||
registerDebugCallstackItem(RESTART_SESSION_ID, restartLabel, 10, CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('session'));
|
||||
registerDebugCallstackItem(STOP_ID, stopLabel, 20, CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('session'));
|
||||
registerDebugCallstackItem(PAUSE_ID, pauseLabel, 10, ContextKeyExpr.and(CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('thread'), CONTEXT_DEBUG_STATE.isEqualTo('running')));
|
||||
registerDebugCallstackItem(CONTINUE_ID, continueLabel, 10, ContextKeyExpr.and(CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('thread'), CONTEXT_DEBUG_STATE.isEqualTo('stopped')));
|
||||
registerDebugCallstackItem(STEP_OVER_ID, stepOverLabel, 20, CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('thread'), CONTEXT_DEBUG_STATE.isEqualTo('stopped'));
|
||||
registerDebugCallstackItem(STEP_INTO_ID, stepIntoLabel, 30, CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('thread'), CONTEXT_DEBUG_STATE.isEqualTo('stopped'));
|
||||
registerDebugCallstackItem(STEP_OUT_ID, stepOutLabel, 40, CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('thread'), CONTEXT_DEBUG_STATE.isEqualTo('stopped'));
|
||||
registerDebugCallstackItem(TERMINATE_THREAD_ID, nls.localize('terminateThread', "Terminate Thread"), 10, CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('thread'), undefined, 'termination');
|
||||
registerDebugCallstackItem(RESTART_FRAME_ID, nls.localize('restartFrame', "Restart Frame"), 10, ContextKeyExpr.and(CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('stackFrame'), CONTEXT_RESTART_FRAME_SUPPORTED));
|
||||
registerDebugCallstackItem(COPY_STACK_TRACE_ID, nls.localize('copyStackTrace', "Copy Call Stack"), 20, CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('stackFrame'));
|
||||
|
||||
// View menu
|
||||
|
||||
@@ -318,7 +358,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, {
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, {
|
||||
group: '1_debug',
|
||||
command: {
|
||||
id: StopAction.ID,
|
||||
id: STOP_ID,
|
||||
title: nls.localize({ key: 'miStopDebugging', comment: ['&& denotes a mnemonic'] }, "&&Stop Debugging"),
|
||||
precondition: CONTEXT_IN_DEBUG_MODE
|
||||
},
|
||||
@@ -328,7 +368,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, {
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, {
|
||||
group: '1_debug',
|
||||
command: {
|
||||
id: RestartAction.ID,
|
||||
id: RESTART_SESSION_ID,
|
||||
title: nls.localize({ key: 'miRestart Debugging', comment: ['&& denotes a mnemonic'] }, "&&Restart Debugging"),
|
||||
precondition: CONTEXT_IN_DEBUG_MODE
|
||||
},
|
||||
@@ -358,7 +398,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, {
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, {
|
||||
group: '3_step',
|
||||
command: {
|
||||
id: StepOverAction.ID,
|
||||
id: STEP_OVER_ID,
|
||||
title: nls.localize({ key: 'miStepOver', comment: ['&& denotes a mnemonic'] }, "Step &&Over"),
|
||||
precondition: CONTEXT_DEBUG_STATE.isEqualTo('stopped')
|
||||
},
|
||||
@@ -368,7 +408,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, {
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, {
|
||||
group: '3_step',
|
||||
command: {
|
||||
id: StepIntoAction.ID,
|
||||
id: STEP_INTO_ID,
|
||||
title: nls.localize({ key: 'miStepInto', comment: ['&& denotes a mnemonic'] }, "Step &&Into"),
|
||||
precondition: CONTEXT_DEBUG_STATE.isEqualTo('stopped')
|
||||
},
|
||||
@@ -378,7 +418,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, {
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, {
|
||||
group: '3_step',
|
||||
command: {
|
||||
id: StepOutAction.ID,
|
||||
id: STEP_OUT_ID,
|
||||
title: nls.localize({ key: 'miStepOut', comment: ['&& denotes a mnemonic'] }, "Step O&&ut"),
|
||||
precondition: CONTEXT_DEBUG_STATE.isEqualTo('stopped')
|
||||
},
|
||||
@@ -388,7 +428,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, {
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, {
|
||||
group: '3_step',
|
||||
command: {
|
||||
id: ContinueAction.ID,
|
||||
id: CONTINUE_ID,
|
||||
title: nls.localize({ key: 'miContinue', comment: ['&& denotes a mnemonic'] }, "&&Continue"),
|
||||
precondition: CONTEXT_DEBUG_STATE.isEqualTo('stopped')
|
||||
},
|
||||
@@ -502,11 +542,11 @@ if (isMacintosh) {
|
||||
|
||||
registerTouchBarEntry(StartAction.ID, StartAction.LABEL, 0, CONTEXT_IN_DEBUG_MODE.toNegated(), 'continue-tb.png');
|
||||
registerTouchBarEntry(RunAction.ID, RunAction.LABEL, 1, CONTEXT_IN_DEBUG_MODE.toNegated(), 'continue-without-debugging-tb.png');
|
||||
registerTouchBarEntry(ContinueAction.ID, ContinueAction.LABEL, 0, CONTEXT_DEBUG_STATE.isEqualTo('stopped'), 'continue-tb.png');
|
||||
registerTouchBarEntry(PauseAction.ID, PauseAction.LABEL, 1, ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, ContextKeyExpr.notEquals('debugState', 'stopped')), 'pause-tb.png');
|
||||
registerTouchBarEntry(StepOverAction.ID, StepOverAction.LABEL, 2, CONTEXT_DEBUG_STATE.isEqualTo('stopped'), 'stepover-tb.png');
|
||||
registerTouchBarEntry(StepIntoAction.ID, StepIntoAction.LABEL, 3, CONTEXT_DEBUG_STATE.isEqualTo('stopped'), 'stepinto-tb.png');
|
||||
registerTouchBarEntry(StepOutAction.ID, StepOutAction.LABEL, 4, CONTEXT_DEBUG_STATE.isEqualTo('stopped'), 'stepout-tb.png');
|
||||
registerTouchBarEntry(RestartAction.ID, RestartAction.LABEL, 5, CONTEXT_IN_DEBUG_MODE, 'restart-tb.png');
|
||||
registerTouchBarEntry(StopAction.ID, StopAction.LABEL, 6, CONTEXT_IN_DEBUG_MODE, 'stop-tb.png');
|
||||
registerTouchBarEntry(CONTINUE_ID, continueLabel, 0, CONTEXT_DEBUG_STATE.isEqualTo('stopped'), 'continue-tb.png');
|
||||
registerTouchBarEntry(PAUSE_ID, pauseLabel, 1, ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, ContextKeyExpr.notEquals('debugState', 'stopped')), 'pause-tb.png');
|
||||
registerTouchBarEntry(STEP_OVER_ID, stepOverLabel, 2, CONTEXT_DEBUG_STATE.isEqualTo('stopped'), 'stepover-tb.png');
|
||||
registerTouchBarEntry(STEP_INTO_ID, stepIntoLabel, 3, CONTEXT_DEBUG_STATE.isEqualTo('stopped'), 'stepinto-tb.png');
|
||||
registerTouchBarEntry(STEP_OUT_ID, stepOutLabel, 4, CONTEXT_DEBUG_STATE.isEqualTo('stopped'), 'stepout-tb.png');
|
||||
registerTouchBarEntry(RESTART_SESSION_ID, restartLabel, 5, CONTEXT_IN_DEBUG_MODE, 'restart-tb.png');
|
||||
registerTouchBarEntry(STOP_ID, stopLabel, 6, CONTEXT_IN_DEBUG_MODE, 'stop-tb.png');
|
||||
}
|
||||
|
||||
@@ -12,170 +12,11 @@ import * as path from 'vs/base/common/path';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import * as objects from 'vs/base/common/objects';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { ExtensionsChannelId } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { IOutputService } from 'vs/workbench/contrib/output/common/output';
|
||||
import { IDebugAdapter, IDebugAdapterExecutable, IDebuggerContribution, IPlatformSpecificAdapterContribution, IDebugAdapterServer } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IDebugAdapterExecutable, IDebuggerContribution, IPlatformSpecificAdapterContribution, IDebugAdapterServer } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
|
||||
/**
|
||||
* Abstract implementation of the low level API for a debug adapter.
|
||||
* Missing is how this API communicates with the debug adapter.
|
||||
*/
|
||||
export abstract class AbstractDebugAdapter implements IDebugAdapter {
|
||||
|
||||
private sequence: number;
|
||||
private pendingRequests: Map<number, (e: DebugProtocol.Response) => void>;
|
||||
private requestCallback: (request: DebugProtocol.Request) => void;
|
||||
private eventCallback: (request: DebugProtocol.Event) => void;
|
||||
private messageCallback: (message: DebugProtocol.ProtocolMessage) => void;
|
||||
|
||||
protected readonly _onError: Emitter<Error>;
|
||||
protected readonly _onExit: Emitter<number | null>;
|
||||
|
||||
constructor() {
|
||||
this.sequence = 1;
|
||||
this.pendingRequests = new Map();
|
||||
|
||||
this._onError = new Emitter<Error>();
|
||||
this._onExit = new Emitter<number>();
|
||||
}
|
||||
|
||||
abstract startSession(): Promise<void>;
|
||||
abstract stopSession(): Promise<void>;
|
||||
|
||||
abstract sendMessage(message: DebugProtocol.ProtocolMessage): void;
|
||||
|
||||
get onError(): Event<Error> {
|
||||
return this._onError.event;
|
||||
}
|
||||
|
||||
get onExit(): Event<number | null> {
|
||||
return this._onExit.event;
|
||||
}
|
||||
|
||||
onMessage(callback: (message: DebugProtocol.ProtocolMessage) => void): void {
|
||||
if (this.eventCallback) {
|
||||
this._onError.fire(new Error(`attempt to set more than one 'Message' callback`));
|
||||
}
|
||||
this.messageCallback = callback;
|
||||
}
|
||||
|
||||
onEvent(callback: (event: DebugProtocol.Event) => void): void {
|
||||
if (this.eventCallback) {
|
||||
this._onError.fire(new Error(`attempt to set more than one 'Event' callback`));
|
||||
}
|
||||
this.eventCallback = callback;
|
||||
}
|
||||
|
||||
onRequest(callback: (request: DebugProtocol.Request) => void): void {
|
||||
if (this.requestCallback) {
|
||||
this._onError.fire(new Error(`attempt to set more than one 'Request' callback`));
|
||||
}
|
||||
this.requestCallback = callback;
|
||||
}
|
||||
|
||||
sendResponse(response: DebugProtocol.Response): void {
|
||||
if (response.seq > 0) {
|
||||
this._onError.fire(new Error(`attempt to send more than one response for command ${response.command}`));
|
||||
} else {
|
||||
this.internalSend('response', response);
|
||||
}
|
||||
}
|
||||
|
||||
sendRequest(command: string, args: any, clb: (result: DebugProtocol.Response) => void, timeout?: number): void {
|
||||
|
||||
const request: any = {
|
||||
command: command
|
||||
};
|
||||
if (args && Object.keys(args).length > 0) {
|
||||
request.arguments = args;
|
||||
}
|
||||
|
||||
this.internalSend('request', request);
|
||||
|
||||
if (typeof timeout === 'number') {
|
||||
const timer = setTimeout(() => {
|
||||
clearTimeout(timer);
|
||||
const clb = this.pendingRequests.get(request.seq);
|
||||
if (clb) {
|
||||
this.pendingRequests.delete(request.seq);
|
||||
const err: DebugProtocol.Response = {
|
||||
type: 'response',
|
||||
seq: 0,
|
||||
request_seq: request.seq,
|
||||
success: false,
|
||||
command,
|
||||
message: `timeout after ${timeout} ms`
|
||||
};
|
||||
clb(err);
|
||||
}
|
||||
}, timeout);
|
||||
}
|
||||
|
||||
if (clb) {
|
||||
// store callback for this request
|
||||
this.pendingRequests.set(request.seq, clb);
|
||||
}
|
||||
}
|
||||
|
||||
acceptMessage(message: DebugProtocol.ProtocolMessage): void {
|
||||
if (this.messageCallback) {
|
||||
this.messageCallback(message);
|
||||
} else {
|
||||
switch (message.type) {
|
||||
case 'event':
|
||||
if (this.eventCallback) {
|
||||
this.eventCallback(<DebugProtocol.Event>message);
|
||||
}
|
||||
break;
|
||||
case 'request':
|
||||
if (this.requestCallback) {
|
||||
this.requestCallback(<DebugProtocol.Request>message);
|
||||
}
|
||||
break;
|
||||
case 'response':
|
||||
const response = <DebugProtocol.Response>message;
|
||||
const clb = this.pendingRequests.get(response.request_seq);
|
||||
if (clb) {
|
||||
this.pendingRequests.delete(response.request_seq);
|
||||
clb(response);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private internalSend(typ: 'request' | 'response' | 'event', message: DebugProtocol.ProtocolMessage): void {
|
||||
|
||||
message.type = typ;
|
||||
message.seq = this.sequence++;
|
||||
|
||||
this.sendMessage(message);
|
||||
}
|
||||
|
||||
protected cancelPending() {
|
||||
const pending = this.pendingRequests;
|
||||
this.pendingRequests = new Map();
|
||||
setTimeout(_ => {
|
||||
pending.forEach((callback, request_seq) => {
|
||||
const err: DebugProtocol.Response = {
|
||||
type: 'response',
|
||||
seq: 0,
|
||||
request_seq,
|
||||
success: false,
|
||||
command: 'canceled',
|
||||
message: 'canceled'
|
||||
};
|
||||
callback(err);
|
||||
});
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.cancelPending();
|
||||
}
|
||||
}
|
||||
import { AbstractDebugAdapter } from '../common/abstractDebugAdapter';
|
||||
|
||||
/**
|
||||
* An implementation that communicates via two streams with the debug adapter.
|
||||
|
||||
@@ -155,6 +155,7 @@ export class RecommendationWidget extends ExtensionWidget {
|
||||
super();
|
||||
this.render();
|
||||
this._register(toDisposable(() => this.clear()));
|
||||
this._register(this.extensionTipsService.onRecommendationChange(() => this.render()));
|
||||
}
|
||||
|
||||
private clear(): void {
|
||||
@@ -172,27 +173,22 @@ export class RecommendationWidget extends ExtensionWidget {
|
||||
if (!this.extension) {
|
||||
return;
|
||||
}
|
||||
const updateRecommendationMarker = () => {
|
||||
this.clear();
|
||||
const extRecommendations = this.extensionTipsService.getAllRecommendationsWithReason();
|
||||
if (extRecommendations[this.extension.identifier.id.toLowerCase()]) {
|
||||
this.element = append(this.parent, $('div.bookmark'));
|
||||
const recommendation = append(this.element, $('.recommendation'));
|
||||
append(recommendation, $('span.octicon.octicon-star'));
|
||||
const applyBookmarkStyle = (theme) => {
|
||||
const bgColor = theme.getColor(extensionButtonProminentBackground);
|
||||
const fgColor = theme.getColor(extensionButtonProminentForeground);
|
||||
recommendation.style.borderTopColor = bgColor ? bgColor.toString() : 'transparent';
|
||||
recommendation.style.color = fgColor ? fgColor.toString() : 'white';
|
||||
};
|
||||
applyBookmarkStyle(this.themeService.getTheme());
|
||||
this.themeService.onThemeChange(applyBookmarkStyle, this, this.disposables);
|
||||
this.parent.title = extRecommendations[this.extension.identifier.id.toLowerCase()].reasonText;
|
||||
this.parent.setAttribute('aria-label', localize('viewRecommendedExtensionDetailsAria', "{0}. {1} Press enter for extension details.", this.extension.displayName, extRecommendations[this.extension.identifier.id.toLowerCase()].reasonText));
|
||||
}
|
||||
};
|
||||
updateRecommendationMarker();
|
||||
this.extensionTipsService.onRecommendationChange(() => updateRecommendationMarker(), this, this.disposables);
|
||||
const extRecommendations = this.extensionTipsService.getAllRecommendationsWithReason();
|
||||
if (extRecommendations[this.extension.identifier.id.toLowerCase()]) {
|
||||
this.element = append(this.parent, $('div.bookmark'));
|
||||
const recommendation = append(this.element, $('.recommendation'));
|
||||
append(recommendation, $('span.octicon.octicon-star'));
|
||||
const applyBookmarkStyle = (theme) => {
|
||||
const bgColor = theme.getColor(extensionButtonProminentBackground);
|
||||
const fgColor = theme.getColor(extensionButtonProminentForeground);
|
||||
recommendation.style.borderTopColor = bgColor ? bgColor.toString() : 'transparent';
|
||||
recommendation.style.color = fgColor ? fgColor.toString() : 'white';
|
||||
};
|
||||
applyBookmarkStyle(this.themeService.getTheme());
|
||||
this.themeService.onThemeChange(applyBookmarkStyle, this, this.disposables);
|
||||
this.parent.title = extRecommendations[this.extension.identifier.id.toLowerCase()].reasonText;
|
||||
this.parent.setAttribute('aria-label', localize('viewRecommendedExtensionDetailsAria', "{0}. {1} Press enter for extension details.", this.extension.displayName, extRecommendations[this.extension.identifier.id.toLowerCase()].reasonText));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor
|
||||
|
||||
private columnItems: ColumnItem[] = [];
|
||||
private keybindingsListContainer: HTMLElement;
|
||||
private unAssignedKeybindingItemToRevealAndFocus: IKeybindingItemEntry;
|
||||
private unAssignedKeybindingItemToRevealAndFocus: IKeybindingItemEntry | null;
|
||||
private listEntries: IListEntry[];
|
||||
private keybindingsList: List<IListEntry>;
|
||||
|
||||
@@ -109,7 +109,7 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor
|
||||
) {
|
||||
super(KeybindingsEditor.ID, telemetryService, themeService, storageService);
|
||||
this.delayedFiltering = new Delayer<void>(300);
|
||||
this._register(keybindingsService.onDidUpdateKeybindings(() => this.render(true, CancellationToken.None)));
|
||||
this._register(keybindingsService.onDidUpdateKeybindings(() => this.render(true)));
|
||||
|
||||
this.keybindingsEditorContextKey = CONTEXT_KEYBINDINGS_EDITOR.bindTo(this.contextKeyService);
|
||||
this.searchFocusContextKey = CONTEXT_KEYBINDINGS_SEARCH_FOCUS.bindTo(this.contextKeyService);
|
||||
@@ -129,7 +129,7 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor
|
||||
setInput(input: KeybindingsEditorInput, options: EditorOptions, token: CancellationToken): Promise<void> {
|
||||
this.keybindingsEditorContextKey.set(true);
|
||||
return super.setInput(input, options, token)
|
||||
.then(() => this.render(!!(options && options.preserveFocus), token));
|
||||
.then(() => this.render(!!(options && options.preserveFocus)));
|
||||
}
|
||||
|
||||
clearInput(): void {
|
||||
@@ -173,7 +173,7 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor
|
||||
}
|
||||
}
|
||||
|
||||
get activeKeybindingEntry(): IKeybindingItemEntry {
|
||||
get activeKeybindingEntry(): IKeybindingItemEntry | null {
|
||||
const focusedElement = this.keybindingsList.getFocusedElements()[0];
|
||||
return focusedElement && focusedElement.templateId === KEYBINDING_ENTRY_TEMPLATE_ID ? <IKeybindingItemEntry>focusedElement : null;
|
||||
}
|
||||
@@ -252,7 +252,7 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor
|
||||
this.selectEntry(keybinding);
|
||||
this.reportKeybindingAction(KEYBINDINGS_EDITOR_COMMAND_COPY, keybinding.keybindingItem.command, keybinding.keybindingItem.keybinding);
|
||||
const userFriendlyKeybinding: IUserFriendlyKeybinding = {
|
||||
key: keybinding.keybindingItem.keybinding ? keybinding.keybindingItem.keybinding.getUserSettingsLabel() : '',
|
||||
key: keybinding.keybindingItem.keybinding ? keybinding.keybindingItem.keybinding.getUserSettingsLabel() || '' : '',
|
||||
command: keybinding.keybindingItem.command
|
||||
};
|
||||
if (keybinding.keybindingItem.when) {
|
||||
@@ -454,45 +454,35 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor
|
||||
DOM.removeClass(this.keybindingsList.getHTMLElement(), 'focused');
|
||||
this.keybindingFocusContextKey.reset();
|
||||
}));
|
||||
this._register(this.keybindingsList.onMouseDblClick(() => this.defineKeybinding(this.activeKeybindingEntry)));
|
||||
this._register(this.keybindingsList.onMouseDblClick(() => {
|
||||
const activeKeybindingEntry = this.activeKeybindingEntry;
|
||||
if (activeKeybindingEntry) {
|
||||
this.defineKeybinding(activeKeybindingEntry);
|
||||
}
|
||||
}));
|
||||
this._register(this.keybindingsList.onKeyDown(e => {
|
||||
const event = new StandardKeyboardEvent(e);
|
||||
if (event.keyCode === KeyCode.Enter) {
|
||||
const keybindingEntry = this.activeKeybindingEntry;
|
||||
if (keybindingEntry) {
|
||||
this.defineKeybinding(this.activeKeybindingEntry);
|
||||
this.defineKeybinding(keybindingEntry);
|
||||
}
|
||||
e.stopPropagation();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private render(preserveFocus: boolean, token: CancellationToken): Promise<any> {
|
||||
private async render(preserveFocus: boolean): Promise<void> {
|
||||
if (this.input) {
|
||||
return this.input.resolve()
|
||||
.then((keybindingsModel: KeybindingsEditorModel) => {
|
||||
if (token.isCancellationRequested) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
this.keybindingsEditorModel = keybindingsModel;
|
||||
|
||||
const editorActionsLabels: { [id: string]: string; } = EditorExtensionsRegistry.getEditorActions().reduce((editorActions, editorAction) => {
|
||||
editorActions[editorAction.id] = editorAction.label;
|
||||
return editorActions;
|
||||
}, {});
|
||||
|
||||
return this.keybindingsEditorModel.resolve(editorActionsLabels);
|
||||
})
|
||||
.then(() => {
|
||||
if (token.isCancellationRequested) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
this.renderKeybindingsEntries(false, preserveFocus);
|
||||
});
|
||||
const input: KeybindingsEditorInput = this.input as KeybindingsEditorInput;
|
||||
this.keybindingsEditorModel = await input.resolve();
|
||||
const editorActionsLabels: { [id: string]: string; } = EditorExtensionsRegistry.getEditorActions().reduce((editorActions, editorAction) => {
|
||||
editorActions[editorAction.id] = editorAction.label;
|
||||
return editorActions;
|
||||
}, {});
|
||||
await this.keybindingsEditorModel.resolve(editorActionsLabels);
|
||||
this.renderKeybindingsEntries(false, preserveFocus);
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
private filterKeybindings(): void {
|
||||
@@ -1105,7 +1095,7 @@ class WhenColumn extends Column {
|
||||
this.element.title = '';
|
||||
}
|
||||
this.onDidAccept(() => {
|
||||
this.keybindingsEditor.updateKeybinding(keybindingItemEntry, keybindingItemEntry.keybindingItem.keybinding ? keybindingItemEntry.keybindingItem.keybinding.getUserSettingsLabel() : '', this.whenInput.value);
|
||||
this.keybindingsEditor.updateKeybinding(keybindingItemEntry, keybindingItemEntry.keybindingItem.keybinding ? keybindingItemEntry.keybindingItem.keybinding.getUserSettingsLabel() || '' : '', this.whenInput.value);
|
||||
this.keybindingsEditor.selectKeybinding(keybindingItemEntry);
|
||||
}, this, this.disposables);
|
||||
this.onDidReject(() => {
|
||||
|
||||
@@ -45,7 +45,7 @@ export interface ISearchProvider {
|
||||
|
||||
export interface IKeybindingsEditor extends IEditor {
|
||||
|
||||
readonly activeKeybindingEntry: IKeybindingItemEntry;
|
||||
readonly activeKeybindingEntry: IKeybindingItemEntry | null;
|
||||
readonly onDefineWhenExpression: Event<IKeybindingItemEntry>;
|
||||
readonly onLayout: Event<void>;
|
||||
|
||||
|
||||
@@ -227,7 +227,20 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
handler: (accessor, args: any) => {
|
||||
const control = accessor.get(IEditorService).activeControl as IKeybindingsEditor;
|
||||
if (control && control instanceof KeybindingsEditor) {
|
||||
control.defineKeybinding(control.activeKeybindingEntry);
|
||||
control.defineKeybinding(control.activeKeybindingEntry!);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: KEYBINDINGS_EDITOR_COMMAND_DEFINE_WHEN,
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: ContextKeyExpr.and(CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDING_FOCUS),
|
||||
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_E),
|
||||
handler: (accessor, args: any) => {
|
||||
const control = accessor.get(IEditorService).activeControl as IKeybindingsEditor;
|
||||
if (control && control instanceof KeybindingsEditor && control.activeKeybindingEntry!.keybindingItem.keybinding) {
|
||||
control.defineWhenExpression(control.activeKeybindingEntry!);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -256,7 +269,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
handler: (accessor, args: any) => {
|
||||
const control = accessor.get(IEditorService).activeControl as IKeybindingsEditor;
|
||||
if (control && control instanceof KeybindingsEditor) {
|
||||
control.removeKeybinding(control.activeKeybindingEntry);
|
||||
control.removeKeybinding(control.activeKeybindingEntry!);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -269,7 +282,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
handler: (accessor, args: any) => {
|
||||
const control = accessor.get(IEditorService).activeControl as IKeybindingsEditor;
|
||||
if (control && control instanceof KeybindingsEditor) {
|
||||
control.resetKeybinding(control.activeKeybindingEntry);
|
||||
control.resetKeybinding(control.activeKeybindingEntry!);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -323,7 +336,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
handler: (accessor, args: any) => {
|
||||
const control = accessor.get(IEditorService).activeControl as IKeybindingsEditor;
|
||||
if (control) {
|
||||
control.showSimilarKeybindings(control.activeKeybindingEntry);
|
||||
control.showSimilarKeybindings(control.activeKeybindingEntry!);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -336,7 +349,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
handler: (accessor, args: any) => {
|
||||
const control = accessor.get(IEditorService).activeControl as IKeybindingsEditor;
|
||||
if (control) {
|
||||
control.copyKeybinding(control.activeKeybindingEntry);
|
||||
control.copyKeybinding(control.activeKeybindingEntry!);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -349,7 +362,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
handler: (accessor, args: any) => {
|
||||
const control = accessor.get(IEditorService).activeControl as IKeybindingsEditor;
|
||||
if (control) {
|
||||
control.copyKeybindingCommand(control.activeKeybindingEntry);
|
||||
control.copyKeybindingCommand(control.activeKeybindingEntry!);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -393,6 +393,6 @@
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.vs .panel .search-view .monaco-inputbox {
|
||||
.vs .search-panel .search-view .monaco-inputbox {
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
|
||||
@@ -3,13 +3,10 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./media/search.contribution';
|
||||
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { distinct } from 'vs/base/common/arrays';
|
||||
import { illegalArgument } from 'vs/base/common/errors';
|
||||
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import * as objects from 'vs/base/common/objects';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { dirname } from 'vs/base/common/resources';
|
||||
@@ -32,32 +29,31 @@ import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { IListService, WorkbenchListFocusContextKey, WorkbenchObjectTree } from 'vs/platform/list/browser/listService';
|
||||
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { ISearchConfigurationProperties, ISearchConfiguration, VIEWLET_ID, PANEL_ID, VIEW_ID, VIEW_CONTAINER } from 'vs/workbench/services/search/common/search';
|
||||
import { Extensions as PanelExtensions, PanelDescriptor, PanelRegistry } from 'vs/workbench/browser/panel';
|
||||
import { defaultQuickOpenContextKey } from 'vs/workbench/browser/parts/quickopen/quickopen';
|
||||
import { Extensions as QuickOpenExtensions, IQuickOpenRegistry, QuickOpenHandlerDescriptor } from 'vs/workbench/browser/quickopen';
|
||||
import { Extensions as ViewletExtensions, ViewletDescriptor, ViewletRegistry } from 'vs/workbench/browser/viewlet';
|
||||
import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions';
|
||||
import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry, IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
||||
import { ResourceContextKey } from 'vs/workbench/common/resources';
|
||||
import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
|
||||
import { Extensions as ViewExtensions, IViewsRegistry } from 'vs/workbench/common/views';
|
||||
import { getMultiSelectedResources } from 'vs/workbench/contrib/files/browser/files';
|
||||
import { ExplorerFolderContext, ExplorerRootContext, FilesExplorerFocusCondition } from 'vs/workbench/contrib/files/common/files';
|
||||
import { OpenAnythingHandler } from 'vs/workbench/contrib/search/browser/openAnythingHandler';
|
||||
import { OpenSymbolHandler } from 'vs/workbench/contrib/search/browser/openSymbolHandler';
|
||||
import { registerContributions as replaceContributions } from 'vs/workbench/contrib/search/browser/replaceContributions';
|
||||
import { clearHistoryCommand, ClearSearchResultsAction, CloseReplaceAction, CollapseDeepestExpandedLevelAction, copyAllCommand, copyMatchCommand, copyPathCommand, FindInFilesAction, FocusNextInputAction, FocusNextSearchResultAction, FocusPreviousInputAction, FocusPreviousSearchResultAction, focusSearchListCommand, getSearchView, openSearchView, OpenSearchViewletAction, RefreshAction, RemoveAction, ReplaceAction, ReplaceAllAction, ReplaceAllInFolderAction, ReplaceInFilesAction, toggleCaseSensitiveCommand, toggleRegexCommand, toggleWholeWordCommand } from 'vs/workbench/contrib/search/browser/searchActions';
|
||||
import { SearchPanel } from 'vs/workbench/contrib/search/browser/searchPanel';
|
||||
import { SearchView } from 'vs/workbench/contrib/search/browser/searchView';
|
||||
import { SearchViewlet } from 'vs/workbench/contrib/search/browser/searchViewlet';
|
||||
import { registerContributions as searchWidgetContributions } from 'vs/workbench/contrib/search/browser/searchWidget';
|
||||
import * as Constants from 'vs/workbench/contrib/search/common/constants';
|
||||
import { getWorkspaceSymbols } from 'vs/workbench/contrib/search/common/search';
|
||||
import { ISearchHistoryService, SearchHistoryService } from 'vs/workbench/contrib/search/common/searchHistoryService';
|
||||
import { FileMatchOrMatch, ISearchWorkbenchService, RenderableMatch, SearchWorkbenchService } from 'vs/workbench/contrib/search/common/searchModel';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
|
||||
import { ISearchConfiguration, ISearchConfigurationProperties, PANEL_ID, VIEWLET_ID, VIEW_CONTAINER, VIEW_ID } from 'vs/workbench/services/search/common/search';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { PanelRegistry, Extensions as PanelExtensions, PanelDescriptor } from 'vs/workbench/browser/panel';
|
||||
import { ViewletDescriptor, ViewletRegistry, Extensions as ViewletExtensions } from 'vs/workbench/browser/viewlet';
|
||||
import { ISearchHistoryService, SearchHistoryService } from 'vs/workbench/contrib/search/common/searchHistoryService';
|
||||
import { SearchViewlet } from 'vs/workbench/contrib/search/browser/searchViewlet';
|
||||
import { SearchPanel } from 'vs/workbench/contrib/search/browser/searchPanel';
|
||||
import { IViewsRegistry, Extensions as ViewExtensions } from 'vs/workbench/common/views';
|
||||
import { SearchView } from 'vs/workbench/contrib/search/browser/searchView';
|
||||
|
||||
registerSingleton(ISearchWorkbenchService, SearchWorkbenchService, true);
|
||||
registerSingleton(ISearchHistoryService, SearchHistoryService, true);
|
||||
@@ -365,15 +361,15 @@ const searchInFolderCommand: ICommandHandler = (accessor, resource?: URI) => {
|
||||
const panelService = accessor.get(IPanelService);
|
||||
const fileService = accessor.get(IFileService);
|
||||
const configurationService = accessor.get(IConfigurationService);
|
||||
const resources = getMultiSelectedResources(resource, listService, accessor.get(IEditorService));
|
||||
const resources = resource && getMultiSelectedResources(resource, listService, accessor.get(IEditorService));
|
||||
|
||||
return openSearchView(viewletService, panelService, configurationService, true).then(searchView => {
|
||||
if (resources && resources.length) {
|
||||
if (resources && resources.length && searchView) {
|
||||
return fileService.resolveFiles(resources.map(resource => ({ resource }))).then(results => {
|
||||
const folders: URI[] = [];
|
||||
|
||||
results.forEach(result => {
|
||||
if (result.success) {
|
||||
if (result.success && result.stat) {
|
||||
folders.push(result.stat.isDirectory ? result.stat.resource : dirname(result.stat.resource));
|
||||
}
|
||||
});
|
||||
@@ -390,7 +386,7 @@ const FIND_IN_FOLDER_ID = 'filesExplorer.findInFolder';
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: FIND_IN_FOLDER_ID,
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: ContextKeyExpr.and(FilesExplorerFocusCondition, ExplorerFolderContext, ResourceContextKey.Scheme.isEqualTo(Schemas.file)), // todo@remote
|
||||
when: ContextKeyExpr.and(FilesExplorerFocusCondition, ExplorerFolderContext),
|
||||
primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_F,
|
||||
handler: searchInFolderCommand
|
||||
});
|
||||
@@ -414,7 +410,9 @@ CommandsRegistry.registerCommand({
|
||||
id: FIND_IN_WORKSPACE_ID,
|
||||
handler: (accessor) => {
|
||||
return openSearchView(accessor.get(IViewletService), accessor.get(IPanelService), accessor.get(IConfigurationService), true).then(searchView => {
|
||||
searchView.searchInFolders(null);
|
||||
if (searchView) {
|
||||
searchView.searchInFolders();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -426,7 +424,7 @@ MenuRegistry.appendMenuItem(MenuId.ExplorerContext, {
|
||||
id: FIND_IN_FOLDER_ID,
|
||||
title: nls.localize('findInFolder', "Find in Folder...")
|
||||
},
|
||||
when: ContextKeyExpr.and(ExplorerFolderContext, ResourceContextKey.Scheme.isEqualTo(Schemas.file)) // todo@remote
|
||||
when: ContextKeyExpr.and(ExplorerFolderContext)
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.ExplorerContext, {
|
||||
@@ -456,7 +454,7 @@ class ShowAllSymbolsAction extends Action {
|
||||
run(context?: any): Promise<void> {
|
||||
|
||||
let prefix = ShowAllSymbolsAction.ALL_SYMBOLS_PREFIX;
|
||||
let inputSelection: { start: number; end: number; } = undefined;
|
||||
let inputSelection: { start: number; end: number; } | undefined = undefined;
|
||||
const editor = this.editorService.getFocusedCodeEditor();
|
||||
const word = editor && getSelectionSearchString(editor);
|
||||
if (word) {
|
||||
|
||||
@@ -52,14 +52,15 @@ export function appendKeyBindingLabel(label: string, inputKeyBinding: number | R
|
||||
}
|
||||
}
|
||||
|
||||
export function openSearchView(viewletService: IViewletService, panelService: IPanelService, configurationService: IConfigurationService, focus?: boolean): Promise<SearchView> {
|
||||
export function openSearchView(viewletService: IViewletService, panelService: IPanelService, configurationService: IConfigurationService, focus?: boolean): Promise<SearchView | undefined> {
|
||||
if (configurationService.getValue<ISearchConfiguration>().search.location === 'panel') {
|
||||
return Promise.resolve((panelService.openPanel(PANEL_ID, focus) as SearchPanel).getSearchView());
|
||||
}
|
||||
|
||||
return viewletService.openViewlet(VIEWLET_ID, focus).then(viewlet => (viewlet as SearchViewlet).getSearchView());
|
||||
}
|
||||
|
||||
export function getSearchView(viewletService: IViewletService, panelService: IPanelService): SearchView | null {
|
||||
export function getSearchView(viewletService: IViewletService, panelService: IPanelService): SearchView | undefined {
|
||||
const activeViewlet = viewletService.getActiveViewlet();
|
||||
if (activeViewlet && activeViewlet.getId() === VIEWLET_ID) {
|
||||
return (activeViewlet as SearchViewlet).getSearchView();
|
||||
@@ -149,11 +150,13 @@ export abstract class FindOrReplaceInFilesAction extends Action {
|
||||
|
||||
run(): Promise<any> {
|
||||
return openSearchView(this.viewletService, this.panelService, this.configurationService, false).then(openedView => {
|
||||
const searchAndReplaceWidget = openedView.searchAndReplaceWidget;
|
||||
searchAndReplaceWidget.toggleReplace(this.expandSearchReplaceWidget);
|
||||
if (openedView) {
|
||||
const searchAndReplaceWidget = openedView.searchAndReplaceWidget;
|
||||
searchAndReplaceWidget.toggleReplace(this.expandSearchReplaceWidget);
|
||||
|
||||
const updatedText = openedView.updateTextFromSelection(!this.expandSearchReplaceWidget);
|
||||
openedView.searchAndReplaceWidget.focus(undefined, updatedText, updatedText);
|
||||
const updatedText = openedView.updateTextFromSelection(!this.expandSearchReplaceWidget);
|
||||
openedView.searchAndReplaceWidget.focus(undefined, updatedText, updatedText);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -240,7 +243,7 @@ export class RefreshAction extends Action {
|
||||
static readonly ID: string = 'search.action.refreshSearchResults';
|
||||
static LABEL: string = nls.localize('RefreshAction.label', "Refresh");
|
||||
|
||||
private searchView: SearchView | null;
|
||||
private searchView: SearchView | undefined;
|
||||
|
||||
constructor(id: string, label: string,
|
||||
@IViewletService private readonly viewletService: IViewletService,
|
||||
@@ -394,7 +397,9 @@ export class FocusNextSearchResultAction extends Action {
|
||||
|
||||
run(): Promise<any> {
|
||||
return openSearchView(this.viewletService, this.panelService, this.configurationService).then(searchView => {
|
||||
searchView.selectNextMatch();
|
||||
if (searchView) {
|
||||
searchView.selectNextMatch();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -413,7 +418,9 @@ export class FocusPreviousSearchResultAction extends Action {
|
||||
|
||||
run(): Promise<any> {
|
||||
return openSearchView(this.viewletService, this.panelService, this.configurationService).then(searchView => {
|
||||
searchView.selectPreviousMatch();
|
||||
if (searchView) {
|
||||
searchView.selectPreviousMatch();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -771,6 +778,8 @@ export const focusSearchListCommand: ICommandHandler = accessor => {
|
||||
const panelService = accessor.get(IPanelService);
|
||||
const configurationService = accessor.get(IConfigurationService);
|
||||
openSearchView(viewletService, panelService, configurationService).then(searchView => {
|
||||
searchView.moveFocusToResults();
|
||||
if (searchView) {
|
||||
searchView.moveFocusToResults();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -31,7 +31,7 @@ export class SearchPanel extends Panel {
|
||||
}
|
||||
|
||||
create(parent: HTMLElement): void {
|
||||
dom.addClass(parent, 'monaco-panel-view');
|
||||
dom.addClasses(parent, 'monaco-panel-view', 'search-panel');
|
||||
this.searchView.render();
|
||||
dom.append(parent, this.searchView.element);
|
||||
this.searchView.setExpanded(true);
|
||||
@@ -64,7 +64,7 @@ export class SearchPanel extends Panel {
|
||||
super.saveState();
|
||||
}
|
||||
|
||||
getSearchView(): SearchView | null {
|
||||
getSearchView(): SearchView {
|
||||
return this.searchView;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,13 +115,13 @@ export class SearchView extends ViewletPanel {
|
||||
private inputPatternIncludes: PatternInputWidget;
|
||||
private resultsElement: HTMLElement;
|
||||
|
||||
private currentSelectedFileMatch: FileMatch;
|
||||
private currentSelectedFileMatch: FileMatch | undefined;
|
||||
|
||||
private readonly selectCurrentMatchEmitter: Emitter<string | undefined>;
|
||||
private delayedRefresh: Delayer<void>;
|
||||
private changedWhileHidden: boolean;
|
||||
|
||||
private searchWithoutFolderMessageElement: HTMLElement;
|
||||
private searchWithoutFolderMessageElement: HTMLElement | undefined;
|
||||
|
||||
private currentSearchQ = Promise.resolve();
|
||||
|
||||
@@ -474,7 +474,7 @@ export class SearchView extends ViewletPanel {
|
||||
});
|
||||
}
|
||||
|
||||
private createFolderIterator(folderMatch: FolderMatch, collapseResults: ISearchConfigurationProperties['collapseResults']): Iterator<ITreeElement<RenderableMatch>> {
|
||||
private createFolderIterator(folderMatch: BaseFolderMatch, collapseResults: ISearchConfigurationProperties['collapseResults']): Iterator<ITreeElement<RenderableMatch>> {
|
||||
const filesIt = Iterator.fromArray(
|
||||
folderMatch.matches()
|
||||
.sort(searchMatchComparer));
|
||||
@@ -797,7 +797,7 @@ export class SearchView extends ViewletPanel {
|
||||
|
||||
updateTextFromSelection(allowUnselectedWord = true): boolean {
|
||||
let updatedText = false;
|
||||
const seedSearchStringFromSelection = this.configurationService.getValue<IEditorOptions>('editor').find.seedSearchStringFromSelection;
|
||||
const seedSearchStringFromSelection = this.configurationService.getValue<IEditorOptions>('editor').find!.seedSearchStringFromSelection;
|
||||
if (seedSearchStringFromSelection) {
|
||||
let selectedText = this.getSearchTextFromEditor(allowUnselectedWord);
|
||||
if (selectedText) {
|
||||
@@ -1074,7 +1074,7 @@ export class SearchView extends ViewletPanel {
|
||||
}
|
||||
}
|
||||
|
||||
searchInFolders(resources: URI[]): void {
|
||||
searchInFolders(resources?: URI[]): void {
|
||||
const folderPaths: string[] = [];
|
||||
const workspace = this.contextService.getWorkspace();
|
||||
|
||||
@@ -1338,7 +1338,7 @@ export class SearchView extends ViewletPanel {
|
||||
|
||||
const onError = (e: any) => {
|
||||
if (errors.isPromiseCanceledError(e)) {
|
||||
return onComplete(null);
|
||||
return onComplete(undefined);
|
||||
} else {
|
||||
this.searching = false;
|
||||
this.updateActions();
|
||||
@@ -1422,7 +1422,7 @@ export class SearchView extends ViewletPanel {
|
||||
this.openSettings('.exclude');
|
||||
}
|
||||
|
||||
private openSettings(query: string): Promise<IEditor> {
|
||||
private openSettings(query: string): Promise<IEditor | null> {
|
||||
const options: ISettingsEditorOptions = { query };
|
||||
return this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ?
|
||||
this.preferencesService.openWorkspaceSettings(undefined, options) :
|
||||
@@ -1480,7 +1480,7 @@ export class SearchView extends ViewletPanel {
|
||||
|
||||
const actionClass = env.isMacintosh ? OpenFileFolderAction : OpenFolderAction;
|
||||
const action = this.instantiationService.createInstance<string, string, IAction>(actionClass, actionClass.ID, actionClass.LABEL);
|
||||
this.actionRunner.run(action).then(() => {
|
||||
this.actionRunner!.run(action).then(() => {
|
||||
action.dispose();
|
||||
}, err => {
|
||||
action.dispose();
|
||||
@@ -1499,7 +1499,7 @@ export class SearchView extends ViewletPanel {
|
||||
// this.replaceService.disposeAllReplacePreviews();
|
||||
dom.hide(this.messagesElement);
|
||||
dom.show(this.resultsElement);
|
||||
this.currentSelectedFileMatch = null;
|
||||
this.currentSelectedFileMatch = undefined;
|
||||
}
|
||||
|
||||
private onFocus(lineMatch: any, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): Promise<any> {
|
||||
@@ -1514,7 +1514,7 @@ export class SearchView extends ViewletPanel {
|
||||
this.open(lineMatch, preserveFocus, sideBySide, pinned);
|
||||
}
|
||||
|
||||
open(element: FileMatchOrMatch, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): Promise<any> {
|
||||
open(element: FileMatchOrMatch, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): Promise<void> {
|
||||
const selection = this.getSelectionFrom(element);
|
||||
const resource = element instanceof Match ? element.parent().resource() : (<FileMatch>element).resource();
|
||||
return this.editorService.openEditor({
|
||||
@@ -1528,7 +1528,7 @@ export class SearchView extends ViewletPanel {
|
||||
}, sideBySide ? SIDE_GROUP : ACTIVE_GROUP).then(editor => {
|
||||
if (editor && element instanceof Match && preserveFocus) {
|
||||
this.viewModel.searchResult.rangeHighlightDecorations.highlightRange(
|
||||
(<ICodeEditor>editor.getControl()).getModel(),
|
||||
(<ICodeEditor>editor.getControl()).getModel()!,
|
||||
element.range()
|
||||
);
|
||||
} else {
|
||||
@@ -1536,9 +1536,7 @@ export class SearchView extends ViewletPanel {
|
||||
}
|
||||
|
||||
if (editor) {
|
||||
return this.editorGroupsService.activateGroup(editor.group);
|
||||
} else {
|
||||
return Promise.resolve(null);
|
||||
this.editorGroupsService.activateGroup(editor.group!);
|
||||
}
|
||||
}, errors.onUnexpectedError);
|
||||
}
|
||||
@@ -1714,4 +1712,4 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
|
||||
if (outlineSelectionColor) {
|
||||
collector.addRule(`.monaco-workbench .search-view .monaco-list.element-focused .monaco-list-row.focused.selected:not(.highlighted) .action-label:focus { outline-color: ${outlineSelectionColor} }`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -38,8 +38,8 @@ export class SearchViewlet extends ViewContainerViewlet {
|
||||
return Registry.as<ViewletRegistry>(Extensions.Viewlets).getViewlet(this.getId()).name;
|
||||
}
|
||||
|
||||
getSearchView(): SearchView | null {
|
||||
getSearchView(): SearchView | undefined {
|
||||
const view = super.getView(VIEW_ID);
|
||||
return view ? view as SearchView : null;
|
||||
return view ? view as SearchView : undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ class ReplaceAllAction extends Action {
|
||||
return ReplaceAllAction.fgInstance;
|
||||
}
|
||||
|
||||
private _searchWidget: SearchWidget | null = null;
|
||||
private _searchWidget: SearchWidget;
|
||||
|
||||
constructor() {
|
||||
super(ReplaceAllAction.ID, '', 'action-replace-all', false);
|
||||
@@ -113,8 +113,8 @@ export class SearchWidget extends Widget {
|
||||
private _onReplaceStateChange = this._register(new Emitter<boolean>());
|
||||
readonly onReplaceStateChange: Event<boolean> = this._onReplaceStateChange.event;
|
||||
|
||||
private _onReplaceValueChanged = this._register(new Emitter<string | undefined>());
|
||||
readonly onReplaceValueChanged: Event<string> = this._onReplaceValueChanged.event;
|
||||
private _onReplaceValueChanged = this._register(new Emitter<void>());
|
||||
readonly onReplaceValueChanged: Event<void> = this._onReplaceValueChanged.event;
|
||||
|
||||
private _onReplaceAll = this._register(new Emitter<void>());
|
||||
readonly onReplaceAll: Event<void> = this._onReplaceAll.event;
|
||||
@@ -343,7 +343,7 @@ export class SearchWidget extends Widget {
|
||||
this._register(attachInputBoxStyler(this.replaceInput, this.themeService));
|
||||
this.onkeydown(this.replaceInput.inputElement, (keyboardEvent) => this.onReplaceInputKeyDown(keyboardEvent));
|
||||
this.replaceInput.value = options.replaceValue || '';
|
||||
this._register(this.replaceInput.onDidChange(() => this._onReplaceValueChanged.fire(undefined)));
|
||||
this._register(this.replaceInput.onDidChange(() => this._onReplaceValueChanged.fire()));
|
||||
this._register(this.replaceInput.onDidHeightChange(() => this._onDidHeightChange.fire()));
|
||||
|
||||
this.replaceAllAction = ReplaceAllAction.INSTANCE;
|
||||
@@ -532,8 +532,6 @@ export class SearchWidget extends Widget {
|
||||
|
||||
dispose(): void {
|
||||
this.setReplaceAllActionState(false);
|
||||
this.replaceAllAction.searchWidget = null;
|
||||
this.replaceActionBar = null;
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { getExcludes, ICommonQueryProps, IFileQuery, IFolderQuery, IPatternInfo, ISearchConfiguration, ITextQuery, ITextSearchPreviewOptions, pathIncludedInQuery, QueryType } from 'vs/workbench/services/search/common/search';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
|
||||
/**
|
||||
* One folder to search and a glob expression that should be applied.
|
||||
@@ -320,6 +321,13 @@ export class QueryBuilder {
|
||||
*/
|
||||
private expandOneSearchPath(searchPath: string): IOneSearchPathPattern[] {
|
||||
if (path.isAbsolute(searchPath)) {
|
||||
const workspaceFolders = this.workspaceContextService.getWorkspace().folders;
|
||||
if (workspaceFolders[0] && workspaceFolders[0].uri.scheme !== Schemas.file) {
|
||||
return [{
|
||||
searchPath: workspaceFolders[0].uri.with({ path: searchPath })
|
||||
}];
|
||||
}
|
||||
|
||||
// Currently only local resources can be searched for with absolute search paths.
|
||||
// TODO convert this to a workspace folder + pattern, so excludes will be resolved properly for an absolute path inside a workspace folder
|
||||
return [{
|
||||
|
||||
@@ -389,7 +389,7 @@ export class FileMatch extends Disposable {
|
||||
}
|
||||
|
||||
export interface IChangeEvent {
|
||||
elements: (FileMatch | FolderMatch | SearchResult | null)[];
|
||||
elements: (FileMatch | FolderMatch | SearchResult)[];
|
||||
added?: boolean;
|
||||
removed?: boolean;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema';
|
||||
import commonSchema from './jsonSchemaCommon';
|
||||
|
||||
import { ProblemMatcherRegistry } from 'vs/workbench/contrib/tasks/common/problemMatcher';
|
||||
import { TaskDefinitionRegistry } from '../common/taskDefinitionRegistry';
|
||||
import { TaskDefinitionRegistry } from './taskDefinitionRegistry';
|
||||
import * as ConfigurationResolverUtils from 'vs/workbench/services/configurationResolver/common/configurationResolverUtils';
|
||||
import { inputsSchema } from 'vs/workbench/services/configurationResolver/common/configurationResolverSchema';
|
||||
|
||||
@@ -440,6 +440,7 @@ processTask.properties!.type = {
|
||||
description: nls.localize('JsonSchema.tasks.type', 'Defines whether the task is run as a process or as a command inside a shell.')
|
||||
};
|
||||
processTask.required!.push('command');
|
||||
processTask.required!.push('type');
|
||||
|
||||
taskDefinitions.push(processTask);
|
||||
|
||||
|
Before Width: | Height: | Size: 927 B After Width: | Height: | Size: 927 B |
|
Before Width: | Height: | Size: 927 B After Width: | Height: | Size: 927 B |
|
Before Width: | Height: | Size: 335 B After Width: | Height: | Size: 335 B |
|
Before Width: | Height: | Size: 257 B After Width: | Height: | Size: 257 B |
|
Before Width: | Height: | Size: 249 B After Width: | Height: | Size: 249 B |
|
Before Width: | Height: | Size: 458 B After Width: | Height: | Size: 458 B |
@@ -3,7 +3,7 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./media/task.contribution';
|
||||
import 'vs/css!../common/media/task.contribution';
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import * as semver from 'semver';
|
||||
@@ -2701,8 +2701,8 @@ let schema: IJSONSchema = {
|
||||
}
|
||||
};
|
||||
|
||||
import schemaVersion1 from './jsonSchema_v1';
|
||||
import schemaVersion2, { updateProblemMatchers } from './jsonSchema_v2';
|
||||
import schemaVersion1 from '../common/jsonSchema_v1';
|
||||
import schemaVersion2, { updateProblemMatchers } from '../common/jsonSchema_v2';
|
||||
schema.definitions = {
|
||||
...schemaVersion1.definitions,
|
||||
...schemaVersion2.definitions,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
@@ -262,7 +262,7 @@ configurationRegistry.registerConfiguration({
|
||||
'terminal.integrated.windowsEnableConpty': {
|
||||
description: nls.localize('terminal.integrated.windowsEnableConpty', "Whether to use ConPTY for Windows terminal process communication (requires Windows 10 build number 18309+). Winpty will be used if this is false."),
|
||||
type: 'boolean',
|
||||
default: false
|
||||
default: true
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -27,6 +27,11 @@ import { IRemoteEnvironmentService } from 'vs/workbench/services/remote/common/r
|
||||
/** The amount of time to consider terminal errors to be related to the launch */
|
||||
const LAUNCHING_DURATION = 500;
|
||||
|
||||
/**
|
||||
* The minimum amount of time between latency requests.
|
||||
*/
|
||||
const LATENCY_MEASURING_INTERVAL = 1000;
|
||||
|
||||
/**
|
||||
* Holds all state related to the creation and management of terminal processes.
|
||||
*
|
||||
@@ -46,6 +51,9 @@ export class TerminalProcessManager implements ITerminalProcessManager {
|
||||
private _process: ITerminalChildProcess | null = null;
|
||||
private _preLaunchInputQueue: string[] = [];
|
||||
private _disposables: IDisposable[] = [];
|
||||
private _latency: number = -1;
|
||||
private _latencyRequest: Promise<number>;
|
||||
private _latencyLastMeasured: number = 0;
|
||||
|
||||
private readonly _onProcessReady = new Emitter<void>();
|
||||
public get onProcessReady(): Event<void> { return this._onProcessReady.event; }
|
||||
@@ -77,6 +85,7 @@ export class TerminalProcessManager implements ITerminalProcessManager {
|
||||
c(undefined);
|
||||
});
|
||||
});
|
||||
this.ptyProcessReady.then(async () => await this.getLatency());
|
||||
}
|
||||
|
||||
public dispose(immediate: boolean = false): void {
|
||||
@@ -240,6 +249,18 @@ export class TerminalProcessManager implements ITerminalProcessManager {
|
||||
return this._process.getCwd();
|
||||
}
|
||||
|
||||
public async getLatency(): Promise<number> {
|
||||
if (!this._process) {
|
||||
return Promise.resolve(0);
|
||||
}
|
||||
if (this._latencyLastMeasured === 0 || this._latencyLastMeasured + LATENCY_MEASURING_INTERVAL < Date.now()) {
|
||||
this._latencyRequest = this._process.getLatency();
|
||||
this._latency = await this._latencyRequest;
|
||||
this._latencyLastMeasured = Date.now();
|
||||
}
|
||||
return Promise.resolve(this._latency);
|
||||
}
|
||||
|
||||
private _onExit(exitCode: number): void {
|
||||
this._process = null;
|
||||
|
||||
|
||||
@@ -654,6 +654,7 @@ export interface ITerminalProcessManager extends IDisposable {
|
||||
|
||||
getInitialCwd(): Promise<string>;
|
||||
getCwd(): Promise<string>;
|
||||
getLatency(): Promise<number>;
|
||||
}
|
||||
|
||||
export const enum ProcessState {
|
||||
@@ -685,12 +686,14 @@ export interface ITerminalProcessExtHostProxy extends IDisposable {
|
||||
emitExit(exitCode: number): void;
|
||||
emitInitialCwd(initialCwd: string): void;
|
||||
emitCwd(cwd: string): void;
|
||||
emitLatency(latency: number): void;
|
||||
|
||||
onInput: Event<string>;
|
||||
onResize: Event<{ cols: number, rows: number }>;
|
||||
onShutdown: Event<boolean>;
|
||||
onRequestInitialCwd: Event<void>;
|
||||
onRequestCwd: Event<void>;
|
||||
onRequestLatency: Event<void>;
|
||||
}
|
||||
|
||||
export interface ITerminalProcessExtHostRequest {
|
||||
@@ -733,4 +736,5 @@ export interface ITerminalChildProcess {
|
||||
|
||||
getInitialCwd(): Promise<string>;
|
||||
getCwd(): Promise<string>;
|
||||
}
|
||||
getLatency(): Promise<number>;
|
||||
}
|
||||
|
||||
@@ -31,9 +31,12 @@ export class TerminalProcessExtHostProxy implements ITerminalChildProcess, ITerm
|
||||
public get onRequestInitialCwd(): Event<void> { return this._onRequestInitialCwd.event; }
|
||||
private readonly _onRequestCwd = new Emitter<void>();
|
||||
public get onRequestCwd(): Event<void> { return this._onRequestCwd.event; }
|
||||
private readonly _onRequestLatency = new Emitter<void>();
|
||||
public get onRequestLatency(): Event<void> { return this._onRequestLatency.event; }
|
||||
|
||||
private _pendingInitialCwdRequests: ((value?: string | Thenable<string>) => void)[] = [];
|
||||
private _pendingCwdRequests: ((value?: string | Thenable<string>) => void)[] = [];
|
||||
private _pendingLatencyRequests: ((value?: number | Thenable<number>) => void)[] = [];
|
||||
|
||||
constructor(
|
||||
public terminalId: number,
|
||||
@@ -86,6 +89,12 @@ export class TerminalProcessExtHostProxy implements ITerminalChildProcess, ITerm
|
||||
}
|
||||
}
|
||||
|
||||
public emitLatency(latency: number): void {
|
||||
while (this._pendingLatencyRequests.length > 0) {
|
||||
this._pendingLatencyRequests.pop()!(latency);
|
||||
}
|
||||
}
|
||||
|
||||
public shutdown(immediate: boolean): void {
|
||||
this._onShutdown.fire(immediate);
|
||||
}
|
||||
@@ -111,4 +120,11 @@ export class TerminalProcessExtHostProxy implements ITerminalChildProcess, ITerm
|
||||
this._pendingCwdRequests.push(resolve);
|
||||
});
|
||||
}
|
||||
|
||||
public getLatency(): Promise<number> {
|
||||
return new Promise<number>(resolve => {
|
||||
this._onRequestLatency.fire();
|
||||
this._pendingLatencyRequests.push(resolve);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -70,7 +70,7 @@ export abstract class TerminalService implements ITerminalService {
|
||||
@INotificationService protected readonly _notificationService: INotificationService,
|
||||
@IDialogService private readonly _dialogService: IDialogService,
|
||||
@IExtensionService private readonly _extensionService: IExtensionService,
|
||||
@IFileService private readonly _fileService: IFileService
|
||||
@IFileService protected readonly _fileService: IFileService
|
||||
) {
|
||||
this._activeTabIndex = 0;
|
||||
this._isShuttingDown = false;
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import * as pfs from 'vs/base/node/pfs';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
@@ -27,6 +26,7 @@ import { coalesce } from 'vs/base/common/arrays';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { escapeNonWindowsPath } from 'vs/workbench/contrib/terminal/common/terminalEnvironment';
|
||||
import { execFile } from 'child_process';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
export class TerminalService extends BrowserTerminalService implements ITerminalService {
|
||||
public get configHelper(): ITerminalConfigHelper { return this._configHelper; }
|
||||
@@ -54,7 +54,8 @@ export class TerminalService extends BrowserTerminalService implements ITerminal
|
||||
// the termProgram variable) and we are instructed to wait for editors close, wait for the
|
||||
// marker file to get deleted and then focus back to the integrated terminal.
|
||||
if (request.termProgram === 'vscode' && request.filesToWait) {
|
||||
pfs.whenDeleted(request.filesToWait.waitMarkerFilePath).then(() => {
|
||||
const waitMarkerFileUri = URI.revive(request.filesToWait.waitMarkerFileUri);
|
||||
this.whenDeleted(waitMarkerFileUri).then(() => {
|
||||
if (this.terminalInstances.length > 0) {
|
||||
const terminal = this.getActiveInstance();
|
||||
if (terminal) {
|
||||
@@ -73,6 +74,27 @@ export class TerminalService extends BrowserTerminalService implements ITerminal
|
||||
});
|
||||
}
|
||||
|
||||
private whenDeleted(path: URI): Promise<void> {
|
||||
|
||||
// Complete when wait marker file is deleted
|
||||
return new Promise<void>(resolve => {
|
||||
let running = false;
|
||||
const interval = setInterval(() => {
|
||||
if (!running) {
|
||||
running = true;
|
||||
this._fileService.existsFile(path).then(exists => {
|
||||
running = false;
|
||||
|
||||
if (!exists) {
|
||||
clearInterval(interval);
|
||||
resolve(undefined);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, 1000);
|
||||
});
|
||||
}
|
||||
|
||||
protected _getDefaultShell(p: platform.Platform): string {
|
||||
return getDefaultShell(p);
|
||||
}
|
||||
|
||||
@@ -213,4 +213,8 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable {
|
||||
resolve(this._initialCwd);
|
||||
});
|
||||
}
|
||||
|
||||
public getLatency(): Promise<number> {
|
||||
return Promise.resolve(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,8 +68,10 @@ export class WelcomePageContribution implements IWorkbenchContribution {
|
||||
if (openWithReadme) {
|
||||
return Promise.all(contextService.getWorkspace().folders.map(folder => {
|
||||
const folderUri = folder.uri;
|
||||
return fileService.readFolder(folderUri)
|
||||
.then(files => {
|
||||
return fileService.resolveFile(folderUri)
|
||||
.then(folder => {
|
||||
const files = folder.children ? folder.children.map(child => child.name) : [];
|
||||
|
||||
const file = arrays.find(files.sort(), file => strings.startsWith(file.toLowerCase(), 'readme'));
|
||||
if (file) {
|
||||
return joinPath(folderUri, file);
|
||||
|
||||