Archived
Merge from vscode e5834d3280fcd04898efeac32b9cf1b893f9b127 (#9385)
* Merge from vscode e5834d3280fcd04898efeac32b9cf1b893f9b127 * distro
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
|
||||
@font-face {
|
||||
font-family: "codicon";
|
||||
src: url("./codicon.ttf?279add2ec8b3d516ca20a123230cbf9f") format("truetype");
|
||||
src: url("./codicon.ttf?b5dd8f5aa953889dc1f4c9fa9b44d3dd") format("truetype");
|
||||
}
|
||||
|
||||
.codicon[class*='codicon-'] {
|
||||
@@ -415,5 +415,6 @@
|
||||
.codicon-group-by-ref-type:before { content: "\eb97" }
|
||||
.codicon-ungroup-by-ref-type:before { content: "\eb98" }
|
||||
.codicon-bell-dot:before { content: "\f101" }
|
||||
.codicon-debug-alt-2:before { content: "\f102" }
|
||||
.codicon-debug-alt:before { content: "\f103" }
|
||||
.codicon-bell-progress:before { content: "\f102" }
|
||||
.codicon-debug-alt-2:before { content: "\f103" }
|
||||
.codicon-debug-alt:before { content: "\f104" }
|
||||
|
||||
Binary file not shown.
@@ -554,7 +554,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
|
||||
|
||||
if (!item.row) {
|
||||
item.row = this.cache.alloc(item.templateId);
|
||||
const role = this.ariaProvider.getRole ? this.ariaProvider.getRole(item.element) : 'treeitem';
|
||||
const role = this.ariaProvider.getRole ? this.ariaProvider.getRole(item.element) : 'listitem';
|
||||
item.row!.domNode!.setAttribute('role', role);
|
||||
const checked = this.ariaProvider.isChecked ? this.ariaProvider.isChecked(item.element) : undefined;
|
||||
if (typeof checked !== 'undefined') {
|
||||
|
||||
@@ -196,7 +196,7 @@ function asListOptions<T, TFilterData, TRef>(modelProvider: () => ITreeModel<T,
|
||||
} : undefined,
|
||||
getRole: options.ariaProvider && options.ariaProvider.getRole ? (node) => {
|
||||
return options.ariaProvider!.getRole!(node.element);
|
||||
} : undefined
|
||||
} : () => 'treeitem'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ function asObjectTreeOptions<TInput, T, TFilterData>(options?: IAsyncDataTreeOpt
|
||||
},
|
||||
getRole: options.ariaProvider!.getRole ? (el) => {
|
||||
return options.ariaProvider!.getRole!(el.element as T);
|
||||
} : undefined,
|
||||
} : () => 'treeitem',
|
||||
isChecked: options.ariaProvider!.isChecked ? (e) => {
|
||||
return options.ariaProvider?.isChecked!(e.element as T);
|
||||
} : undefined
|
||||
|
||||
@@ -23,7 +23,7 @@ export class LinkedText {
|
||||
}
|
||||
}
|
||||
|
||||
const LINK_REGEX = /\[([^\]]+)\]\(((?:https?:\/\/|command:)[^\)\s]+)(?: "([^"]+)")?\)/gi;
|
||||
const LINK_REGEX = /\[([^\]]+)\]\(((?:https?:\/\/|command:)[^\)\s]+)(?: ("|')([^\3]+)(\3))?\)/gi;
|
||||
|
||||
export function parseLinkedText(text: string): LinkedText {
|
||||
const result: LinkedTextNode[] = [];
|
||||
@@ -36,7 +36,7 @@ export function parseLinkedText(text: string): LinkedText {
|
||||
result.push(text.substring(index, match.index));
|
||||
}
|
||||
|
||||
const [, label, href, title] = match;
|
||||
const [, label, href, , title] = match;
|
||||
|
||||
if (title) {
|
||||
result.push({ label, href, title });
|
||||
|
||||
@@ -21,4 +21,5 @@ export class CompositeDragAndDropData implements IDragAndDropData {
|
||||
export interface ICompositeDragAndDrop {
|
||||
drop(data: IDragAndDropData, target: string | undefined, originalEvent: DragEvent): void;
|
||||
onDragOver(data: IDragAndDropData, target: string | undefined, originalEvent: DragEvent): boolean;
|
||||
onDragEnter(data: IDragAndDropData, target: string | undefined, originalEvent: DragEvent): boolean;
|
||||
}
|
||||
|
||||
@@ -397,7 +397,7 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
|
||||
private _valueSelection: Readonly<[number, number]> | undefined;
|
||||
private valueSelectionUpdated = true;
|
||||
private _validationMessage: string | undefined;
|
||||
private _ok = false;
|
||||
private _ok: boolean | 'default' = 'default';
|
||||
private _customButton = false;
|
||||
private _customButtonLabel: string | undefined;
|
||||
private _customButtonHover: string | undefined;
|
||||
@@ -566,7 +566,7 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
|
||||
return this._ok;
|
||||
}
|
||||
|
||||
set ok(showOkButton: boolean) {
|
||||
set ok(showOkButton: boolean | 'default') {
|
||||
this._ok = showOkButton;
|
||||
this.update();
|
||||
}
|
||||
@@ -575,6 +575,10 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
|
||||
return this.visible ? this.ui.inputBox.hasFocus() : false;
|
||||
}
|
||||
|
||||
public focusOnInput() {
|
||||
this.ui.inputBox.setFocus();
|
||||
}
|
||||
|
||||
onDidChangeSelection = this.onDidChangeSelectionEmitter.event;
|
||||
|
||||
onDidTriggerItemButton = this.onDidTriggerItemButtonEmitter.event;
|
||||
@@ -753,7 +757,8 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
|
||||
if (!this.visible) {
|
||||
return;
|
||||
}
|
||||
this.ui.setVisibilities(this.canSelectMany ? { title: !!this.title || !!this.step, description: !!this.description, checkAll: true, inputBox: true, visibleCount: true, count: true, ok: this.ok, list: true, message: !!this.validationMessage, customButton: this.customButton } : { title: !!this.title || !!this.step, description: !!this.description, inputBox: true, visibleCount: true, list: true, message: !!this.validationMessage, customButton: this.customButton, ok: this.ok });
|
||||
const ok = this.ok === 'default' ? this.canSelectMany : this.ok;
|
||||
this.ui.setVisibilities(this.canSelectMany ? { title: !!this.title || !!this.step, description: !!this.description, checkAll: true, inputBox: true, visibleCount: true, count: true, ok, list: true, message: !!this.validationMessage, customButton: this.customButton } : { title: !!this.title || !!this.step, description: !!this.description, inputBox: true, visibleCount: true, list: true, message: !!this.validationMessage, customButton: this.customButton, ok });
|
||||
super.update();
|
||||
if (this.ui.inputBox.value !== this.value) {
|
||||
this.ui.inputBox.value = this.value;
|
||||
|
||||
@@ -113,7 +113,7 @@ export interface IInputOptions {
|
||||
placeHolder?: string;
|
||||
|
||||
/**
|
||||
* set to true to show a password prompt that will not show the typed value
|
||||
* Controls if a password input is shown. Password input hides the typed text.
|
||||
*/
|
||||
password?: boolean;
|
||||
|
||||
@@ -162,7 +162,7 @@ export interface IQuickPick<T extends IQuickPickItem> extends IQuickInput {
|
||||
|
||||
readonly onDidAccept: Event<void>;
|
||||
|
||||
ok: boolean;
|
||||
ok: boolean | 'default';
|
||||
|
||||
readonly onDidCustom: Event<void>;
|
||||
|
||||
@@ -209,6 +209,8 @@ export interface IQuickPick<T extends IQuickPickItem> extends IQuickInput {
|
||||
validationMessage: string | undefined;
|
||||
|
||||
inputHasFocus(): boolean;
|
||||
|
||||
focusOnInput(): void;
|
||||
}
|
||||
|
||||
export interface IInputBox extends IQuickInput {
|
||||
|
||||
@@ -27,7 +27,8 @@ export interface IUpdateRequest {
|
||||
}
|
||||
|
||||
export interface IStorageItemsChangeEvent {
|
||||
items: Map<string, string>;
|
||||
changed?: Map<string, string>;
|
||||
deleted?: Set<string>;
|
||||
}
|
||||
|
||||
export interface IStorageDatabase {
|
||||
@@ -104,10 +105,11 @@ export class Storage extends Disposable implements IStorage {
|
||||
// items that change external require us to update our
|
||||
// caches with the values. we just accept the value and
|
||||
// emit an event if there is a change.
|
||||
e.items.forEach((value, key) => this.accept(key, value));
|
||||
e.changed?.forEach((value, key) => this.accept(key, value));
|
||||
e.deleted?.forEach(key => this.accept(key, undefined));
|
||||
}
|
||||
|
||||
private accept(key: string, value: string): void {
|
||||
private accept(key: string, value: string | undefined): void {
|
||||
if (this.state === StorageState.Closed) {
|
||||
return; // Return early if we are already closed
|
||||
}
|
||||
@@ -315,4 +317,4 @@ export class InMemoryStorageDatabase implements IStorageDatabase {
|
||||
close(): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,28 +124,27 @@ suite('Storage Library', () => {
|
||||
changes.clear();
|
||||
|
||||
// Nothing happens if changing to same value
|
||||
const change = new Map<string, string>();
|
||||
change.set('foo', 'bar');
|
||||
database.fireDidChangeItemsExternal({ items: change });
|
||||
const changed = new Map<string, string>();
|
||||
changed.set('foo', 'bar');
|
||||
database.fireDidChangeItemsExternal({ changed });
|
||||
equal(changes.size, 0);
|
||||
|
||||
// Change is accepted if valid
|
||||
change.set('foo', 'bar1');
|
||||
database.fireDidChangeItemsExternal({ items: change });
|
||||
changed.set('foo', 'bar1');
|
||||
database.fireDidChangeItemsExternal({ changed });
|
||||
ok(changes.has('foo'));
|
||||
equal(storage.get('foo'), 'bar1');
|
||||
changes.clear();
|
||||
|
||||
// Delete is accepted
|
||||
change.set('foo', undefined!);
|
||||
database.fireDidChangeItemsExternal({ items: change });
|
||||
const deleted = new Set<string>(['foo']);
|
||||
database.fireDidChangeItemsExternal({ deleted });
|
||||
ok(changes.has('foo'));
|
||||
equal(storage.get('foo', null!), null);
|
||||
equal(storage.get('foo', undefined), undefined);
|
||||
changes.clear();
|
||||
|
||||
// Nothing happens if changing to same value
|
||||
change.set('foo', undefined!);
|
||||
database.fireDidChangeItemsExternal({ items: change });
|
||||
database.fireDidChangeItemsExternal({ deleted });
|
||||
equal(changes.size, 0);
|
||||
|
||||
await storage.close();
|
||||
|
||||
@@ -21,6 +21,21 @@ suite('LinkedText', () => {
|
||||
{ label: 'link text', href: 'http://link.href', title: 'and a title' },
|
||||
'.'
|
||||
]);
|
||||
assert.deepEqual(parseLinkedText('Some message with [link text](http://link.href \'and a title\').').nodes, [
|
||||
'Some message with ',
|
||||
{ label: 'link text', href: 'http://link.href', title: 'and a title' },
|
||||
'.'
|
||||
]);
|
||||
assert.deepEqual(parseLinkedText('Some message with [link text](http://link.href "and a \'title\'").').nodes, [
|
||||
'Some message with ',
|
||||
{ label: 'link text', href: 'http://link.href', title: 'and a \'title\'' },
|
||||
'.'
|
||||
]);
|
||||
assert.deepEqual(parseLinkedText('Some message with [link text](http://link.href \'and a "title"\').').nodes, [
|
||||
'Some message with ',
|
||||
{ label: 'link text', href: 'http://link.href', title: 'and a "title"' },
|
||||
'.'
|
||||
]);
|
||||
assert.deepEqual(parseLinkedText('Some message with [link text](random stuff).').nodes, [
|
||||
'Some message with [link text](random stuff).'
|
||||
]);
|
||||
|
||||
@@ -75,7 +75,11 @@ function getProcessItem(processes: FormattedProcessItem[], item: ProcessItem, in
|
||||
|
||||
// Recurse into children if any
|
||||
if (Array.isArray(item.children)) {
|
||||
item.children.forEach(child => getProcessItem(processes, child, indent + 1, isLocal));
|
||||
item.children.forEach(child => {
|
||||
if (child) {
|
||||
getProcessItem(processes, child, indent + 1, isLocal);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IDialogMainService } from 'vs/platform/dialogs/electron-main/dialogs';
|
||||
import { mnemonicButtonLabel } from 'vs/base/common/labels';
|
||||
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||
import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
|
||||
|
||||
const RUN_TEXTMATE_IN_WORKER = false;
|
||||
|
||||
@@ -100,7 +101,8 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
||||
@IWorkspacesMainService private readonly workspacesMainService: IWorkspacesMainService,
|
||||
@IBackupMainService private readonly backupMainService: IBackupMainService,
|
||||
@ITelemetryService private readonly telemetryService: ITelemetryService,
|
||||
@IDialogMainService private readonly dialogMainService: IDialogMainService
|
||||
@IDialogMainService private readonly dialogMainService: IDialogMainService,
|
||||
@ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService
|
||||
) {
|
||||
super();
|
||||
|
||||
@@ -244,6 +246,8 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
||||
|
||||
get isExtensionTestHost(): boolean { return !!(this.config && this.config.extensionTestsPath); }
|
||||
|
||||
get isExtensionDevelopmentTestFromCli(): boolean { return this.isExtensionDevelopmentHost && this.isExtensionTestHost && !this.config?.debugId; }
|
||||
|
||||
setRepresentedFilename(filename: string): void {
|
||||
if (isMacintosh) {
|
||||
this.win.setRepresentedFilename(filename);
|
||||
@@ -447,6 +451,15 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
||||
private onWindowError(error: WindowError): void {
|
||||
this.logService.error(error === WindowError.CRASHED ? '[VS Code]: render process crashed!' : '[VS Code]: detected unresponsive');
|
||||
|
||||
// If we run extension tests from CLI, showing a dialog is not
|
||||
// very helpful in this case. Rather, we bring down the test run
|
||||
// to signal back a failing run.
|
||||
if (this.isExtensionDevelopmentTestFromCli) {
|
||||
this.lifecycleMainService.kill(1);
|
||||
return;
|
||||
}
|
||||
|
||||
// Telemetry
|
||||
type WindowErrorClassification = {
|
||||
type: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true };
|
||||
};
|
||||
|
||||
@@ -16,6 +16,7 @@ export interface IBulkEditOptions {
|
||||
progress?: IProgress<IProgressStep>;
|
||||
showPreview?: boolean;
|
||||
label?: string;
|
||||
quotableLabel?: string;
|
||||
}
|
||||
|
||||
export interface IBulkEditResult {
|
||||
|
||||
@@ -46,7 +46,7 @@ class MinimapOptions {
|
||||
|
||||
public readonly renderMinimap: RenderMinimap;
|
||||
|
||||
public readonly mode: 'actual' | 'cover' | 'contain';
|
||||
public readonly size: 'proportional' | 'fill' | 'fit';
|
||||
|
||||
public readonly minimapHeightIsEditorHeight: boolean;
|
||||
|
||||
@@ -108,7 +108,7 @@ class MinimapOptions {
|
||||
const minimapOpts = options.get(EditorOption.minimap);
|
||||
|
||||
this.renderMinimap = layoutInfo.renderMinimap | 0;
|
||||
this.mode = minimapOpts.mode;
|
||||
this.size = minimapOpts.size;
|
||||
this.minimapHeightIsEditorHeight = layoutInfo.minimapHeightIsEditorHeight;
|
||||
this.scrollBeyondLastLine = options.get(EditorOption.scrollBeyondLastLine);
|
||||
this.showSlider = minimapOpts.showSlider;
|
||||
@@ -144,7 +144,7 @@ class MinimapOptions {
|
||||
|
||||
public equals(other: MinimapOptions): boolean {
|
||||
return (this.renderMinimap === other.renderMinimap
|
||||
&& this.mode === other.mode
|
||||
&& this.size === other.size
|
||||
&& this.minimapHeightIsEditorHeight === other.minimapHeightIsEditorHeight
|
||||
&& this.scrollBeyondLastLine === other.scrollBeyondLastLine
|
||||
&& this.showSlider === other.showSlider
|
||||
@@ -1111,7 +1111,7 @@ class InnerMinimap extends Disposable {
|
||||
if (!this._lastRenderData) {
|
||||
return;
|
||||
}
|
||||
if (this._model.options.minimapHeightIsEditorHeight) {
|
||||
if (this._model.options.size !== 'proportional') {
|
||||
if (e.leftButton && this._lastRenderData) {
|
||||
// pretend the click occured in the center of the slider
|
||||
const position = dom.getDomNodePagePosition(this._slider.domNode);
|
||||
|
||||
@@ -617,10 +617,11 @@ export class DiffReview extends Disposable {
|
||||
header.setAttribute('role', 'listitem');
|
||||
container.appendChild(header);
|
||||
|
||||
const lineHeight = modifiedOptions.get(EditorOption.lineHeight);
|
||||
let modLine = minModifiedLine;
|
||||
for (let i = 0, len = diffs.length; i < len; i++) {
|
||||
const diffEntry = diffs[i];
|
||||
DiffReview._renderSection(container, diffEntry, modLine, this._width, originalOptions, originalModel, originalModelOpts, modifiedOptions, modifiedModel, modifiedModelOpts);
|
||||
DiffReview._renderSection(container, diffEntry, modLine, lineHeight, this._width, originalOptions, originalModel, originalModelOpts, modifiedOptions, modifiedModel, modifiedModelOpts);
|
||||
if (diffEntry.modifiedLineStart !== 0) {
|
||||
modLine = diffEntry.modifiedLineEnd;
|
||||
}
|
||||
@@ -632,7 +633,7 @@ export class DiffReview extends Disposable {
|
||||
}
|
||||
|
||||
private static _renderSection(
|
||||
dest: HTMLElement, diffEntry: DiffEntry, modLine: number, width: number,
|
||||
dest: HTMLElement, diffEntry: DiffEntry, modLine: number, lineHeight: number, width: number,
|
||||
originalOptions: IComputedEditorOptions, originalModel: ITextModel, originalModelOpts: TextModelResolvedOptions,
|
||||
modifiedOptions: IComputedEditorOptions, modifiedModel: ITextModel, modifiedModelOpts: TextModelResolvedOptions
|
||||
): void {
|
||||
@@ -641,17 +642,18 @@ export class DiffReview extends Disposable {
|
||||
|
||||
let rowClassName: string = 'diff-review-row';
|
||||
let lineNumbersExtraClassName: string = '';
|
||||
let spacerClassName: string = 'diff-review-spacer';
|
||||
const spacerClassName: string = 'diff-review-spacer';
|
||||
let spacerCodiconName: string | null = null;
|
||||
switch (type) {
|
||||
case DiffEntryType.Insert:
|
||||
rowClassName = 'diff-review-row line-insert';
|
||||
lineNumbersExtraClassName = ' char-insert';
|
||||
spacerClassName = 'diff-review-spacer insert-sign';
|
||||
spacerCodiconName = 'codicon codicon-add';
|
||||
break;
|
||||
case DiffEntryType.Delete:
|
||||
rowClassName = 'diff-review-row line-delete';
|
||||
lineNumbersExtraClassName = ' char-delete';
|
||||
spacerClassName = 'diff-review-spacer delete-sign';
|
||||
spacerCodiconName = 'codicon codicon-remove';
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -686,6 +688,7 @@ export class DiffReview extends Disposable {
|
||||
|
||||
let cell = document.createElement('div');
|
||||
cell.className = 'diff-review-cell';
|
||||
cell.style.height = `${lineHeight}px`;
|
||||
row.appendChild(cell);
|
||||
|
||||
const originalLineNumber = document.createElement('span');
|
||||
@@ -713,7 +716,15 @@ export class DiffReview extends Disposable {
|
||||
|
||||
const spacer = document.createElement('span');
|
||||
spacer.className = spacerClassName;
|
||||
spacer.innerHTML = '  ';
|
||||
|
||||
if (spacerCodiconName) {
|
||||
const spacerCodicon = document.createElement('span');
|
||||
spacerCodicon.className = spacerCodiconName;
|
||||
spacerCodicon.innerHTML = '  ';
|
||||
spacer.appendChild(spacerCodicon);
|
||||
} else {
|
||||
spacer.innerHTML = '  ';
|
||||
}
|
||||
cell.appendChild(spacer);
|
||||
|
||||
let lineContent: string;
|
||||
|
||||
@@ -37,13 +37,14 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.monaco-diff-editor .diff-review-cell {
|
||||
display: table-cell;
|
||||
}
|
||||
|
||||
.monaco-diff-editor .diff-review-spacer {
|
||||
display: inline-block;
|
||||
width: 10px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.monaco-diff-editor .diff-review-spacer > .codicon {
|
||||
font-size: 9px !important;
|
||||
}
|
||||
|
||||
.monaco-diff-editor .diff-review-actions {
|
||||
|
||||
@@ -510,7 +510,7 @@ export interface IEditorOptions {
|
||||
* Controls whether clicking on the empty content after a folded line will unfold the line.
|
||||
* Defaults to false.
|
||||
*/
|
||||
unfoldOnClickInEmptyContent?: boolean;
|
||||
unfoldOnClickAfterEndOfLine?: boolean;
|
||||
/**
|
||||
* Enable highlighting of matching brackets.
|
||||
* Defaults to 'always'.
|
||||
@@ -1808,7 +1808,7 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption<EditorOption.
|
||||
const minimapRenderCharacters = minimap.renderCharacters;
|
||||
let minimapScale = (pixelRatio >= 2 ? Math.round(minimap.scale * 2) : minimap.scale);
|
||||
const minimapMaxColumn = minimap.maxColumn | 0;
|
||||
const minimapMode = minimap.mode;
|
||||
const minimapSize = minimap.size;
|
||||
|
||||
const scrollbar = options.get(EditorOption.scrollbar);
|
||||
const verticalScrollbarWidth = scrollbar.verticalScrollbarSize | 0;
|
||||
@@ -1872,7 +1872,7 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption<EditorOption.
|
||||
let minimapCharWidth = minimapScale / pixelRatio;
|
||||
let minimapWidthMultiplier: number = 1;
|
||||
|
||||
if (minimapMode === 'cover' || minimapMode === 'contain') {
|
||||
if (minimapSize === 'fill' || minimapSize === 'fit') {
|
||||
const viewLineCount = env.viewLineCount;
|
||||
const { typicalViewportLineCount, extraLinesBeyondLastLine, desiredRatio, minimapLineCount } = EditorLayoutInfoComputer.computeContainedMinimapLineCount({
|
||||
viewLineCount: viewLineCount,
|
||||
@@ -1893,7 +1893,7 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption<EditorOption.
|
||||
minimapCharWidth = minimapScale / pixelRatio;
|
||||
} else {
|
||||
const effectiveMinimapHeight = Math.ceil((viewLineCount + extraLinesBeyondLastLine) * minimapLineHeight);
|
||||
if (minimapMode === 'cover' || effectiveMinimapHeight > minimapCanvasInnerHeight) {
|
||||
if (minimapSize === 'fill' || effectiveMinimapHeight > minimapCanvasInnerHeight) {
|
||||
minimapHeightIsEditorHeight = true;
|
||||
const configuredFontScale = minimapScale;
|
||||
minimapLineHeight = Math.min(lineHeight * pixelRatio, Math.max(1, Math.floor(1 / desiredRatio)));
|
||||
@@ -2080,7 +2080,7 @@ export interface IEditorMinimapOptions {
|
||||
* Control the minimap rendering mode.
|
||||
* Defaults to 'actual'.
|
||||
*/
|
||||
mode?: 'actual' | 'cover' | 'contain';
|
||||
size?: 'proportional' | 'fill' | 'fit';
|
||||
/**
|
||||
* Control the rendering of the minimap slider.
|
||||
* Defaults to 'mouseover'.
|
||||
@@ -2109,7 +2109,7 @@ class EditorMinimap extends BaseEditorOption<EditorOption.minimap, EditorMinimap
|
||||
constructor() {
|
||||
const defaults: EditorMinimapOptions = {
|
||||
enabled: false, // {{SQL CARBON EDIT}} disable minimap by default
|
||||
mode: 'actual',
|
||||
size: 'proportional',
|
||||
side: 'right',
|
||||
showSlider: 'mouseover',
|
||||
renderCharacters: true,
|
||||
@@ -2124,16 +2124,16 @@ class EditorMinimap extends BaseEditorOption<EditorOption.minimap, EditorMinimap
|
||||
default: defaults.enabled,
|
||||
description: nls.localize('minimap.enabled', "Controls whether the minimap is shown.")
|
||||
},
|
||||
'editor.minimap.mode': {
|
||||
'editor.minimap.size': {
|
||||
type: 'string',
|
||||
enum: ['actual', 'cover', 'contain'],
|
||||
enum: ['proportional', 'fill', 'fit'],
|
||||
enumDescriptions: [
|
||||
nls.localize('minimap.mode.actual', "The minimap will be displayed in its original size, so it might be higher than the editor."),
|
||||
nls.localize('minimap.mode.cover', "The minimap will always have the height of the editor and will stretch or shrink as necessary."),
|
||||
nls.localize('minimap.mode.contain', "The minimap will shrink as necessary to never be higher than the editor."),
|
||||
nls.localize('minimap.size.proportional', "The minimap has the same size as the editor contents (and might scroll)."),
|
||||
nls.localize('minimap.size.fill', "The minimap will stretch or shrink as necessary to fill the height of the editor (no scrolling)."),
|
||||
nls.localize('minimap.size.fit', "The minimap will shrink as necessary to never be larger than the editor (no scrolling)."),
|
||||
],
|
||||
default: defaults.mode,
|
||||
description: nls.localize('minimap.mode', "Controls the rendering mode of the minimap.")
|
||||
default: defaults.size,
|
||||
description: nls.localize('minimap.size', "Controls the size of the minimap.")
|
||||
},
|
||||
'editor.minimap.side': {
|
||||
type: 'string',
|
||||
@@ -2152,7 +2152,8 @@ class EditorMinimap extends BaseEditorOption<EditorOption.minimap, EditorMinimap
|
||||
default: defaults.scale,
|
||||
minimum: 1,
|
||||
maximum: 3,
|
||||
description: nls.localize('minimap.scale', "Scale of content drawn in the minimap.")
|
||||
enum: [1, 2, 3],
|
||||
description: nls.localize('minimap.scale', "Scale of content drawn in the minimap: 1, 2 or 3.")
|
||||
},
|
||||
'editor.minimap.renderCharacters': {
|
||||
type: 'boolean',
|
||||
@@ -2175,7 +2176,7 @@ class EditorMinimap extends BaseEditorOption<EditorOption.minimap, EditorMinimap
|
||||
const input = _input as IEditorMinimapOptions;
|
||||
return {
|
||||
enabled: EditorBooleanOption.boolean(input.enabled, this.defaultValue.enabled),
|
||||
mode: EditorStringEnumOption.stringSet<'actual' | 'cover' | 'contain'>(input.mode, this.defaultValue.mode, ['actual', 'cover', 'contain']),
|
||||
size: EditorStringEnumOption.stringSet<'proportional' | 'fill' | 'fit'>(input.size, this.defaultValue.size, ['proportional', 'fill', 'fit']),
|
||||
side: EditorStringEnumOption.stringSet<'right' | 'left'>(input.side, this.defaultValue.side, ['right', 'left']),
|
||||
showSlider: EditorStringEnumOption.stringSet<'always' | 'mouseover'>(input.showSlider, this.defaultValue.showSlider, ['always', 'mouseover']),
|
||||
renderCharacters: EditorBooleanOption.boolean(input.renderCharacters, this.defaultValue.renderCharacters),
|
||||
@@ -2840,9 +2841,14 @@ export interface ISuggestOptions {
|
||||
*/
|
||||
showSnippets?: boolean;
|
||||
/**
|
||||
* Controls the visibility of the status bar at the bottom of the suggest widget.
|
||||
* Status bar related settings.
|
||||
*/
|
||||
hideStatusBar?: boolean;
|
||||
statusBar?: {
|
||||
/**
|
||||
* Controls the visibility of the status bar at the bottom of the suggest widget.
|
||||
*/
|
||||
visible?: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
export type InternalSuggestOptions = Readonly<Required<ISuggestOptions>>;
|
||||
@@ -2884,7 +2890,9 @@ class EditorSuggest extends BaseEditorOption<EditorOption.suggest, InternalSugge
|
||||
showFolders: true,
|
||||
showTypeParameters: true,
|
||||
showSnippets: true,
|
||||
hideStatusBar: true
|
||||
statusBar: {
|
||||
visible: false
|
||||
}
|
||||
};
|
||||
super(
|
||||
EditorOption.suggest, 'suggest', defaults,
|
||||
@@ -3070,10 +3078,10 @@ class EditorSuggest extends BaseEditorOption<EditorOption.suggest, InternalSugge
|
||||
default: true,
|
||||
markdownDescription: nls.localize('editor.suggest.showSnippets', "When enabled IntelliSense shows `snippet`-suggestions.")
|
||||
},
|
||||
'editor.suggest.hideStatusBar': {
|
||||
'editor.suggest.statusBar.visible': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('editor.suggest.hideStatusBar', "Controls the visibility of the status bar at the bottom of the suggest widget.")
|
||||
default: false,
|
||||
markdownDescription: nls.localize('editor.suggest.statusBar.visible', "Controls the visibility of the status bar at the bottom of the suggest widget.")
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -3118,7 +3126,9 @@ class EditorSuggest extends BaseEditorOption<EditorOption.suggest, InternalSugge
|
||||
showFolders: EditorBooleanOption.boolean(input.showFolders, this.defaultValue.showFolders),
|
||||
showTypeParameters: EditorBooleanOption.boolean(input.showTypeParameters, this.defaultValue.showTypeParameters),
|
||||
showSnippets: EditorBooleanOption.boolean(input.showSnippets, this.defaultValue.showSnippets),
|
||||
hideStatusBar: EditorBooleanOption.boolean(input.hideStatusBar, this.defaultValue.hideStatusBar),
|
||||
statusBar: {
|
||||
visible: EditorBooleanOption.boolean(input.statusBar?.visible, !!this.defaultValue.statusBar.visible)
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -3333,7 +3343,7 @@ export const enum EditorOption {
|
||||
folding,
|
||||
foldingStrategy,
|
||||
foldingHighlight,
|
||||
unfoldOnClickInEmptyContent,
|
||||
unfoldOnClickAfterEndOfLine,
|
||||
fontFamily,
|
||||
fontInfo,
|
||||
fontLigatures,
|
||||
@@ -3633,9 +3643,9 @@ export const EditorOptions = {
|
||||
EditorOption.foldingHighlight, 'foldingHighlight', true,
|
||||
{ description: nls.localize('foldingHighlight', "Controls whether the editor should highlight folded ranges.") }
|
||||
)),
|
||||
unfoldOnClickInEmptyContent: register(new EditorBooleanOption(
|
||||
EditorOption.unfoldOnClickInEmptyContent, 'unfoldOnClickInEmptyContent', false,
|
||||
{ description: nls.localize('unfoldOnClickInEmptyContent', "Controls whether clicking on the empty content after a folded line will unfold the line.") }
|
||||
unfoldOnClickAfterEndOfLine: register(new EditorBooleanOption(
|
||||
EditorOption.unfoldOnClickAfterEndOfLine, 'unfoldOnClickAfterEndOfLine', false,
|
||||
{ description: nls.localize('unfoldOnClickAfterEndOfLine', "Controls whether clicking on the empty content after a folded line will unfold the line.") }
|
||||
)),
|
||||
fontFamily: register(new EditorStringOption(
|
||||
EditorOption.fontFamily, 'fontFamily', EDITOR_FONT_DEFAULTS.fontFamily,
|
||||
@@ -3777,7 +3787,7 @@ export const EditorOptions = {
|
||||
)),
|
||||
definitionLinkOpensInPeek: register(new EditorBooleanOption(
|
||||
EditorOption.definitionLinkOpensInPeek, 'definitionLinkOpensInPeek', false,
|
||||
{ description: nls.localize('definitionLinkOpensInPeek', "Controls whether the definition link opens element in the peek widget.") }
|
||||
{ description: nls.localize('definitionLinkOpensInPeek', "Controls whether the Go to Definition mouse gesture always opens the peek widget.") }
|
||||
)),
|
||||
quickSuggestions: register(new EditorQuickSuggestions()),
|
||||
quickSuggestionsDelay: register(new EditorIntOption(
|
||||
|
||||
@@ -430,15 +430,14 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
|
||||
return this._columnSelectData;
|
||||
}
|
||||
const primaryCursor = this._cursors.getPrimaryCursor();
|
||||
const primaryPos = primaryCursor.viewState.selectionStart.getStartPosition();
|
||||
const viewLineNumber = primaryPos.lineNumber;
|
||||
const viewVisualColumn = CursorColumns.visibleColumnFromColumn2(this.context.config, this.context.viewModel, primaryPos);
|
||||
const viewSelectionStart = primaryCursor.viewState.selectionStart.getStartPosition();
|
||||
const viewPosition = primaryCursor.viewState.position;
|
||||
return {
|
||||
isReal: false,
|
||||
fromViewLineNumber: viewLineNumber,
|
||||
fromViewVisualColumn: viewVisualColumn,
|
||||
toViewLineNumber: viewLineNumber,
|
||||
toViewVisualColumn: viewVisualColumn,
|
||||
fromViewLineNumber: viewSelectionStart.lineNumber,
|
||||
fromViewVisualColumn: CursorColumns.visibleColumnFromColumn2(this.context.config, this.context.viewModel, viewSelectionStart),
|
||||
toViewLineNumber: viewPosition.lineNumber,
|
||||
toViewVisualColumn: CursorColumns.visibleColumnFromColumn2(this.context.config, this.context.viewModel, viewPosition),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@ export interface CompletionItem {
|
||||
preselect?: boolean;
|
||||
/**
|
||||
* A string or snippet that should be inserted in a document when selecting
|
||||
* this completion. When `falsy` the [label](#CompletionItem.label)
|
||||
* this completion.
|
||||
* is used.
|
||||
*/
|
||||
insertText: string;
|
||||
@@ -1373,7 +1373,7 @@ export interface RenameProvider {
|
||||
*/
|
||||
export interface AuthenticationSession {
|
||||
id: string;
|
||||
accessToken(): Promise<string>;
|
||||
getAccessToken(): Thenable<string>;
|
||||
accountName: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -199,7 +199,7 @@ export enum EditorOption {
|
||||
folding = 31,
|
||||
foldingStrategy = 32,
|
||||
foldingHighlight = 33,
|
||||
unfoldOnClickInEmptyContent = 34,
|
||||
unfoldOnClickAfterEndOfLine = 34,
|
||||
fontFamily = 35,
|
||||
fontInfo = 36,
|
||||
fontLigatures = 37,
|
||||
|
||||
@@ -161,6 +161,7 @@ class ExecCommandPasteAction extends ExecCommandAction {
|
||||
kbExpr: EditorContextKeys.textInputFocus,
|
||||
primary: KeyMod.CtrlCmd | KeyCode.KEY_V,
|
||||
win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_V, secondary: [KeyMod.Shift | KeyCode.Insert] },
|
||||
linux: { primary: KeyMod.CtrlCmd | KeyCode.KEY_V, secondary: [KeyMod.Shift | KeyCode.Insert] },
|
||||
weight: KeybindingWeight.EditorContrib
|
||||
};
|
||||
// Do not bind paste keybindings in the browser,
|
||||
|
||||
@@ -164,7 +164,7 @@ export async function applyCodeAction(
|
||||
});
|
||||
|
||||
if (action.edit) {
|
||||
await bulkEditService.apply(action.edit, { editor });
|
||||
await bulkEditService.apply(action.edit, { editor, label: action.title });
|
||||
}
|
||||
|
||||
if (action.command) {
|
||||
|
||||
@@ -62,7 +62,7 @@ export class FoldingController extends Disposable implements IEditorContribution
|
||||
private readonly editor: ICodeEditor;
|
||||
private _isEnabled: boolean;
|
||||
private _useFoldingProviders: boolean;
|
||||
private _unfoldOnClickInEmptyContent: boolean;
|
||||
private _unfoldOnClickAfterEndOfLine: boolean;
|
||||
|
||||
private readonly foldingDecorationProvider: FoldingDecorationProvider;
|
||||
|
||||
@@ -92,7 +92,7 @@ export class FoldingController extends Disposable implements IEditorContribution
|
||||
const options = this.editor.getOptions();
|
||||
this._isEnabled = options.get(EditorOption.folding);
|
||||
this._useFoldingProviders = options.get(EditorOption.foldingStrategy) !== 'indentation';
|
||||
this._unfoldOnClickInEmptyContent = options.get(EditorOption.unfoldOnClickInEmptyContent);
|
||||
this._unfoldOnClickAfterEndOfLine = options.get(EditorOption.unfoldOnClickAfterEndOfLine);
|
||||
|
||||
this.foldingModel = null;
|
||||
this.hiddenRangeModel = null;
|
||||
@@ -114,8 +114,7 @@ export class FoldingController extends Disposable implements IEditorContribution
|
||||
|
||||
this._register(this.editor.onDidChangeConfiguration((e: ConfigurationChangedEvent) => {
|
||||
if (e.hasChanged(EditorOption.folding)) {
|
||||
const options = this.editor.getOptions();
|
||||
this._isEnabled = options.get(EditorOption.folding);
|
||||
this._isEnabled = this.editor.getOptions().get(EditorOption.folding);
|
||||
this.foldingEnabled.set(this._isEnabled);
|
||||
this.onModelChanged();
|
||||
}
|
||||
@@ -126,12 +125,11 @@ export class FoldingController extends Disposable implements IEditorContribution
|
||||
this.onModelContentChanged();
|
||||
}
|
||||
if (e.hasChanged(EditorOption.foldingStrategy)) {
|
||||
const options = this.editor.getOptions();
|
||||
this._useFoldingProviders = options.get(EditorOption.foldingStrategy) !== 'indentation';
|
||||
this._useFoldingProviders = this.editor.getOptions().get(EditorOption.foldingStrategy) !== 'indentation';
|
||||
this.onFoldingStrategyChanged();
|
||||
}
|
||||
if (e.hasChanged(EditorOption.unfoldOnClickInEmptyContent)) {
|
||||
this._unfoldOnClickInEmptyContent = options.get(EditorOption.unfoldOnClickInEmptyContent);
|
||||
if (e.hasChanged(EditorOption.unfoldOnClickAfterEndOfLine)) {
|
||||
this._unfoldOnClickAfterEndOfLine = this.editor.getOptions().get(EditorOption.unfoldOnClickAfterEndOfLine);
|
||||
}
|
||||
}));
|
||||
this.onModelChanged();
|
||||
@@ -370,7 +368,7 @@ export class FoldingController extends Disposable implements IEditorContribution
|
||||
iconClicked = true;
|
||||
break;
|
||||
case MouseTargetType.CONTENT_EMPTY: {
|
||||
if (this._unfoldOnClickInEmptyContent && this.hiddenRangeModel.hasRanges()) {
|
||||
if (this._unfoldOnClickAfterEndOfLine && this.hiddenRangeModel.hasRanges()) {
|
||||
const data = e.target.detail as IEmptyContentData;
|
||||
if (!data.isAfterLines) {
|
||||
break;
|
||||
|
||||
@@ -338,9 +338,8 @@ export class GotoDefinitionAtPositionEditorContribution implements IEditorContri
|
||||
|
||||
private gotoDefinition(position: Position, openToSide: boolean): Promise<any> {
|
||||
this.editor.setPosition(position);
|
||||
const definitionLinkOpensInPeek = this.editor.getOption(EditorOption.definitionLinkOpensInPeek);
|
||||
return this.editor.invokeWithinContext((accessor) => {
|
||||
const canPeek = definitionLinkOpensInPeek && !this.isInPeekEditor(accessor);
|
||||
const canPeek = !openToSide && this.editor.getOption(EditorOption.definitionLinkOpensInPeek) && !this.isInPeekEditor(accessor);
|
||||
const action = new DefinitionAction({ openToSide, openInPeek: canPeek, muteMessage: true }, { alias: '', label: '', id: '', precondition: undefined });
|
||||
return action.run(accessor, this.editor);
|
||||
});
|
||||
|
||||
@@ -77,8 +77,8 @@ export class LinksList extends Disposable {
|
||||
const newLinks = list.links.map(link => new Link(link, provider));
|
||||
links = LinksList._union(links, newLinks);
|
||||
// register disposables
|
||||
if (isDisposable(provider)) {
|
||||
this._register(provider);
|
||||
if (isDisposable(list)) {
|
||||
this._register(list);
|
||||
}
|
||||
}
|
||||
this.links = links;
|
||||
|
||||
@@ -206,7 +206,8 @@ class RenameController implements IEditorContribution {
|
||||
this._bulkEditService.apply(renameResult, {
|
||||
editor: this.editor,
|
||||
showPreview: inputFieldResult.wantsPreview,
|
||||
label: nls.localize('label', "Renaming '{0}'", loc?.text)
|
||||
label: nls.localize('label', "Renaming '{0}'", loc?.text),
|
||||
quotableLabel: nls.localize('quotableLabel', "Renaming {0}", loc?.text),
|
||||
}).then(result => {
|
||||
if (result.ariaSummary) {
|
||||
alert(nls.localize('aria', "Successfully renamed '{0}' to '{1}'. Summary: {2}", loc!.text, inputFieldResult.newName, result.ariaSummary));
|
||||
|
||||
@@ -234,7 +234,7 @@
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row:not(.string-label) > .contents > .main > .left > .monaco-icon-label {
|
||||
max-width: 80%;
|
||||
max-width: 100%;
|
||||
}
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row.string-label > .contents > .main > .left > .monaco-icon-label {
|
||||
flex-shrink: 1;
|
||||
@@ -392,6 +392,10 @@
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .docs.markdown-docs .codicon {
|
||||
vertical-align: sub;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > p:empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -552,7 +552,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
||||
this.messageElement = append(this.element, $('.message'));
|
||||
this.listElement = append(this.element, $('.tree'));
|
||||
|
||||
const applyStatusBarStyle = () => toggleClass(this.element, 'with-status-bar', !this.editor.getOption(EditorOption.suggest).hideStatusBar);
|
||||
const applyStatusBarStyle = () => toggleClass(this.element, 'with-status-bar', this.editor.getOption(EditorOption.suggest).statusBar.visible);
|
||||
applyStatusBarStyle();
|
||||
|
||||
this.statusBarElement = append(this.element, $('.suggest-status-bar'));
|
||||
|
||||
@@ -33,7 +33,7 @@ interface IEditorLayoutProviderOpts {
|
||||
readonly minimapSide: 'left' | 'right';
|
||||
readonly minimapRenderCharacters: boolean;
|
||||
readonly minimapMaxColumn: number;
|
||||
minimapMode?: 'actual' | 'cover' | 'contain';
|
||||
minimapSize?: 'proportional' | 'fill' | 'fit';
|
||||
readonly pixelRatio: number;
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
|
||||
options._write(EditorOption.folding, false);
|
||||
const minimapOptions: EditorMinimapOptions = {
|
||||
enabled: input.minimap,
|
||||
mode: input.minimapMode || 'actual',
|
||||
size: input.minimapSize || 'proportional',
|
||||
side: input.minimapSide,
|
||||
renderCharacters: input.minimapRenderCharacters,
|
||||
maxColumn: input.minimapMaxColumn,
|
||||
@@ -978,7 +978,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
|
||||
minimapSide: 'right',
|
||||
minimapRenderCharacters: true,
|
||||
minimapMaxColumn: 150,
|
||||
minimapMode: 'cover',
|
||||
minimapSize: 'fill',
|
||||
pixelRatio: 2,
|
||||
}, {
|
||||
width: 1000,
|
||||
@@ -1042,7 +1042,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
|
||||
minimapSide: 'right',
|
||||
minimapRenderCharacters: true,
|
||||
minimapMaxColumn: 150,
|
||||
minimapMode: 'cover',
|
||||
minimapSize: 'fill',
|
||||
pixelRatio: 2,
|
||||
}, {
|
||||
width: 1000,
|
||||
@@ -1106,7 +1106,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
|
||||
minimapSide: 'right',
|
||||
minimapRenderCharacters: true,
|
||||
minimapMaxColumn: 150,
|
||||
minimapMode: 'contain',
|
||||
minimapSize: 'fit',
|
||||
pixelRatio: 2,
|
||||
}, {
|
||||
width: 1000,
|
||||
@@ -1170,7 +1170,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
|
||||
minimapSide: 'right',
|
||||
minimapRenderCharacters: true,
|
||||
minimapMaxColumn: 150,
|
||||
minimapMode: 'contain',
|
||||
minimapSize: 'fit',
|
||||
pixelRatio: 2,
|
||||
}, {
|
||||
width: 1000,
|
||||
|
||||
Vendored
+12
-7
@@ -3035,7 +3035,7 @@ declare namespace monaco.editor {
|
||||
* Controls whether clicking on the empty content after a folded line will unfold the line.
|
||||
* Defaults to false.
|
||||
*/
|
||||
unfoldOnClickInEmptyContent?: boolean;
|
||||
unfoldOnClickAfterEndOfLine?: boolean;
|
||||
/**
|
||||
* Enable highlighting of matching brackets.
|
||||
* Defaults to 'always'.
|
||||
@@ -3448,7 +3448,7 @@ declare namespace monaco.editor {
|
||||
* Control the minimap rendering mode.
|
||||
* Defaults to 'actual'.
|
||||
*/
|
||||
mode?: 'actual' | 'cover' | 'contain';
|
||||
size?: 'proportional' | 'fill' | 'fit';
|
||||
/**
|
||||
* Control the rendering of the minimap slider.
|
||||
* Defaults to 'mouseover'.
|
||||
@@ -3758,9 +3758,14 @@ declare namespace monaco.editor {
|
||||
*/
|
||||
showSnippets?: boolean;
|
||||
/**
|
||||
* Controls the visibility of the status bar at the bottom of the suggest widget.
|
||||
* Status bar related settings.
|
||||
*/
|
||||
hideStatusBar?: boolean;
|
||||
statusBar?: {
|
||||
/**
|
||||
* Controls the visibility of the status bar at the bottom of the suggest widget.
|
||||
*/
|
||||
visible?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export type InternalSuggestOptions = Readonly<Required<ISuggestOptions>>;
|
||||
@@ -3829,7 +3834,7 @@ declare namespace monaco.editor {
|
||||
folding = 31,
|
||||
foldingStrategy = 32,
|
||||
foldingHighlight = 33,
|
||||
unfoldOnClickInEmptyContent = 34,
|
||||
unfoldOnClickAfterEndOfLine = 34,
|
||||
fontFamily = 35,
|
||||
fontInfo = 36,
|
||||
fontLigatures = 37,
|
||||
@@ -3945,7 +3950,7 @@ declare namespace monaco.editor {
|
||||
folding: IEditorOption<EditorOption.folding, boolean>;
|
||||
foldingStrategy: IEditorOption<EditorOption.foldingStrategy, 'auto' | 'indentation'>;
|
||||
foldingHighlight: IEditorOption<EditorOption.foldingHighlight, boolean>;
|
||||
unfoldOnClickInEmptyContent: IEditorOption<EditorOption.unfoldOnClickInEmptyContent, boolean>;
|
||||
unfoldOnClickAfterEndOfLine: IEditorOption<EditorOption.unfoldOnClickAfterEndOfLine, boolean>;
|
||||
fontFamily: IEditorOption<EditorOption.fontFamily, string>;
|
||||
fontInfo: IEditorOption<EditorOption.fontInfo, FontInfo>;
|
||||
fontLigatures2: IEditorOption<EditorOption.fontLigatures, string>;
|
||||
@@ -5451,7 +5456,7 @@ declare namespace monaco.languages {
|
||||
preselect?: boolean;
|
||||
/**
|
||||
* A string or snippet that should be inserted in a document when selecting
|
||||
* this completion. When `falsy` the [label](#CompletionItem.label)
|
||||
* this completion.
|
||||
* is used.
|
||||
*/
|
||||
insertText: string;
|
||||
|
||||
@@ -112,7 +112,7 @@ export class EnvironmentService implements IEnvironmentService {
|
||||
get settingsResource(): URI { return resources.joinPath(this.userRoamingDataHome, 'settings.json'); }
|
||||
|
||||
@memoize
|
||||
get userDataSyncHome(): URI { return resources.joinPath(this.userRoamingDataHome, '.sync'); }
|
||||
get userDataSyncHome(): URI { return resources.joinPath(this.userRoamingDataHome, 'sync'); }
|
||||
|
||||
@memoize
|
||||
get settingsSyncPreviewResource(): URI { return resources.joinPath(this.userDataSyncHome, 'settings.json'); }
|
||||
|
||||
@@ -101,6 +101,12 @@ export interface INotification extends INotificationProperties {
|
||||
* this usecase and much easier to use!
|
||||
*/
|
||||
actions?: INotificationActions;
|
||||
|
||||
/**
|
||||
* The initial set of progress properties for the notification. To update progress
|
||||
* later on, access the `INotificationHandle.progress` property.
|
||||
*/
|
||||
progress?: INotificationProgressProperties;
|
||||
}
|
||||
|
||||
export interface INotificationActions {
|
||||
@@ -119,6 +125,24 @@ export interface INotificationActions {
|
||||
secondary?: ReadonlyArray<IAction>;
|
||||
}
|
||||
|
||||
export interface INotificationProgressProperties {
|
||||
|
||||
/**
|
||||
* Causes the progress bar to spin infinitley.
|
||||
*/
|
||||
infinite?: boolean;
|
||||
|
||||
/**
|
||||
* Indicate the total amount of work.
|
||||
*/
|
||||
total?: number;
|
||||
|
||||
/**
|
||||
* Indicate that a specific chunk of work is done.
|
||||
*/
|
||||
worked?: number;
|
||||
}
|
||||
|
||||
export interface INotificationProgress {
|
||||
|
||||
/**
|
||||
@@ -149,6 +173,13 @@ export interface INotificationHandle {
|
||||
*/
|
||||
readonly onDidClose: Event<void>;
|
||||
|
||||
/**
|
||||
* Will be fired whenever the visibility of the notification changes.
|
||||
* A notification can either be visible as toast or inside the notification
|
||||
* center if it is visible.
|
||||
*/
|
||||
readonly onDidChangeVisibility: Event<boolean>;
|
||||
|
||||
/**
|
||||
* Allows to indicate progress on the notification even after the
|
||||
* notification is already visible.
|
||||
|
||||
@@ -240,9 +240,36 @@ export class FileStorageDatabase extends Disposable implements IStorageDatabase
|
||||
private async onDidStorageChangeExternal(): Promise<void> {
|
||||
const items = await this.doGetItemsFromFile();
|
||||
|
||||
// pervious cache, diff for changes
|
||||
let changed = new Map<string, string>();
|
||||
let deleted = new Set<string>();
|
||||
if (this.cache) {
|
||||
items.forEach((value, key) => {
|
||||
const existingValue = this.cache?.get(key);
|
||||
if (existingValue !== value) {
|
||||
changed.set(key, value);
|
||||
}
|
||||
});
|
||||
|
||||
this.cache.forEach((_, key) => {
|
||||
if (!items.has(key)) {
|
||||
deleted.add(key);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// no previous cache, consider all as changed
|
||||
else {
|
||||
changed = items;
|
||||
}
|
||||
|
||||
// Update cache
|
||||
this.cache = items;
|
||||
|
||||
this._onDidChangeItemsExternal.fire({ items });
|
||||
// Emit as event as needed
|
||||
if (changed.size > 0 || deleted.size > 0) {
|
||||
this._onDidChangeItemsExternal.fire({ changed, deleted });
|
||||
}
|
||||
}
|
||||
|
||||
async getItems(): Promise<Map<string, string>> {
|
||||
|
||||
@@ -24,15 +24,16 @@ interface ISerializableUpdateRequest {
|
||||
}
|
||||
|
||||
interface ISerializableItemsChangeEvent {
|
||||
items: Item[];
|
||||
changed?: Item[];
|
||||
deleted?: Key[];
|
||||
}
|
||||
|
||||
export class GlobalStorageDatabaseChannel extends Disposable implements IServerChannel {
|
||||
|
||||
private static readonly STORAGE_CHANGE_DEBOUNCE_TIME = 100;
|
||||
|
||||
private readonly _onDidChangeItems: Emitter<ISerializableItemsChangeEvent> = this._register(new Emitter<ISerializableItemsChangeEvent>());
|
||||
readonly onDidChangeItems: Event<ISerializableItemsChangeEvent> = this._onDidChangeItems.event;
|
||||
private readonly _onDidChangeItems = this._register(new Emitter<ISerializableItemsChangeEvent>());
|
||||
readonly onDidChangeItems = this._onDidChangeItems.event;
|
||||
|
||||
private whenReady: Promise<void>;
|
||||
|
||||
@@ -99,15 +100,18 @@ export class GlobalStorageDatabaseChannel extends Disposable implements IServerC
|
||||
}
|
||||
|
||||
private serializeEvents(events: IStorageChangeEvent[]): ISerializableItemsChangeEvent {
|
||||
const items = new Map<Key, Value>();
|
||||
const changed = new Map<Key, Value>();
|
||||
const deleted = new Set<Key>();
|
||||
events.forEach(event => {
|
||||
const existing = this.storageMainService.get(event.key);
|
||||
if (typeof existing === 'string') {
|
||||
items.set(event.key, existing);
|
||||
changed.set(event.key, existing);
|
||||
} else {
|
||||
deleted.add(event.key);
|
||||
}
|
||||
});
|
||||
|
||||
return { items: mapToSerializable(items) };
|
||||
return { changed: mapToSerializable(changed), deleted: values(deleted) };
|
||||
}
|
||||
|
||||
listen(_: unknown, event: string): Event<any> {
|
||||
@@ -170,8 +174,11 @@ export class GlobalStorageDatabaseChannelClient extends Disposable implements IS
|
||||
}
|
||||
|
||||
private onDidChangeItemsOnMain(e: ISerializableItemsChangeEvent): void {
|
||||
if (Array.isArray(e.items)) {
|
||||
this._onDidChangeItemsExternal.fire({ items: serializableToMap(e.items) });
|
||||
if (Array.isArray(e.changed) || Array.isArray(e.deleted)) {
|
||||
this._onDidChangeItemsExternal.fire({
|
||||
changed: e.changed ? serializableToMap(e.changed) : undefined,
|
||||
deleted: e.deleted ? new Set<string>(e.deleted) : undefined
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { ILogService, LogLevel } from 'vs/platform/log/common/log';
|
||||
import { IWorkspaceStorageChangeEvent, IStorageService, StorageScope, IWillSaveStateEvent, WillSaveStateReason, logStorage } from 'vs/platform/storage/common/storage';
|
||||
import { SQLiteStorageDatabase, ISQLiteStorageDatabaseLoggingOptions } from 'vs/base/parts/storage/node/storage';
|
||||
@@ -25,11 +25,11 @@ export class NativeStorageService extends Disposable implements IStorageService
|
||||
private static readonly WORKSPACE_STORAGE_NAME = 'state.vscdb';
|
||||
private static readonly WORKSPACE_META_NAME = 'workspace.json';
|
||||
|
||||
private readonly _onDidChangeStorage: Emitter<IWorkspaceStorageChangeEvent> = this._register(new Emitter<IWorkspaceStorageChangeEvent>());
|
||||
readonly onDidChangeStorage: Event<IWorkspaceStorageChangeEvent> = this._onDidChangeStorage.event;
|
||||
private readonly _onDidChangeStorage = this._register(new Emitter<IWorkspaceStorageChangeEvent>());
|
||||
readonly onDidChangeStorage = this._onDidChangeStorage.event;
|
||||
|
||||
private readonly _onWillSaveState: Emitter<IWillSaveStateEvent> = this._register(new Emitter<IWillSaveStateEvent>());
|
||||
readonly onWillSaveState: Event<IWillSaveStateEvent> = this._onWillSaveState.event;
|
||||
private readonly _onWillSaveState = this._register(new Emitter<IWillSaveStateEvent>());
|
||||
readonly onWillSaveState = this._onWillSaveState.event;
|
||||
|
||||
private globalStorage: IStorage;
|
||||
|
||||
|
||||
@@ -260,7 +260,7 @@ export class UndoRedoService implements IUndoRedoService {
|
||||
this._splitPastWorkspaceElement(element, element.removedResources.set);
|
||||
const message = nls.localize('cannotWorkspaceUndo', "Could not undo '{0}' across all files. {1}", element.label, element.removedResources.createMessage());
|
||||
this._notificationService.info(message);
|
||||
return;
|
||||
return this.undo(resource);
|
||||
}
|
||||
|
||||
// this must be the last past element in all the impacted resources!
|
||||
@@ -281,18 +281,25 @@ export class UndoRedoService implements IUndoRedoService {
|
||||
const paths = cannotUndoDueToResources.map(r => r.scheme === Schemas.file ? r.fsPath : r.path);
|
||||
const message = nls.localize('cannotWorkspaceUndoDueToChanges', "Could not undo '{0}' across all files because changes were made to {1}", element.label, paths.join(', '));
|
||||
this._notificationService.info(message);
|
||||
return;
|
||||
return this.undo(resource);
|
||||
}
|
||||
|
||||
return this._dialogService.show(
|
||||
Severity.Info,
|
||||
nls.localize('confirmWorkspace', "Would you like to undo '{0}' across all files?", element.label),
|
||||
[
|
||||
nls.localize('ok', "Yes, change {0} files.", affectedEditStacks.length),
|
||||
nls.localize('nok', "No, change only this file.")
|
||||
]
|
||||
nls.localize('ok', "Undo In {0} Files", affectedEditStacks.length),
|
||||
nls.localize('nok', "Undo This File"),
|
||||
nls.localize('cancel', "Cancel"),
|
||||
],
|
||||
{
|
||||
cancelId: 2
|
||||
}
|
||||
).then((result) => {
|
||||
if (result.choice === 0) {
|
||||
if (result.choice === 2) {
|
||||
// cancel
|
||||
return;
|
||||
} else if (result.choice === 0) {
|
||||
for (const editStack of affectedEditStacks) {
|
||||
editStack.past.pop();
|
||||
editStack.future.push(element);
|
||||
@@ -344,7 +351,7 @@ export class UndoRedoService implements IUndoRedoService {
|
||||
this._splitFutureWorkspaceElement(element, element.removedResources.set);
|
||||
const message = nls.localize('cannotWorkspaceRedo', "Could not redo '{0}' across all files. {1}", element.label, element.removedResources.createMessage());
|
||||
this._notificationService.info(message);
|
||||
return;
|
||||
return this.redo(resource);
|
||||
}
|
||||
|
||||
// this must be the last future element in all the impacted resources!
|
||||
@@ -365,7 +372,7 @@ export class UndoRedoService implements IUndoRedoService {
|
||||
const paths = cannotRedoDueToResources.map(r => r.scheme === Schemas.file ? r.fsPath : r.path);
|
||||
const message = nls.localize('cannotWorkspaceRedoDueToChanges', "Could not redo '{0}' across all files because changes were made to {1}", element.label, paths.join(', '));
|
||||
this._notificationService.info(message);
|
||||
return;
|
||||
return this.redo(resource);
|
||||
}
|
||||
|
||||
for (const editStack of affectedEditStacks) {
|
||||
|
||||
@@ -68,7 +68,7 @@ export abstract class AbstractSynchroniser extends Disposable {
|
||||
) {
|
||||
super();
|
||||
this.syncFolder = joinPath(environmentService.userDataSyncHome, source);
|
||||
this.lastSyncResource = joinPath(this.syncFolder, `.lastSync${source}.json`);
|
||||
this.lastSyncResource = joinPath(this.syncFolder, `lastSync${source}.json`);
|
||||
this.cleanUpDelayer = new ThrottledDelayer(50);
|
||||
this.cleanUpBackup();
|
||||
}
|
||||
@@ -202,7 +202,7 @@ export abstract class AbstractSynchroniser extends Disposable {
|
||||
}
|
||||
|
||||
protected async backupLocal(content: VSBuffer): Promise<void> {
|
||||
const resource = joinPath(this.syncFolder, toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, ''));
|
||||
const resource = joinPath(this.syncFolder, `${toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')}.json`);
|
||||
try {
|
||||
await this.fileService.writeFile(resource, content);
|
||||
} catch (e) {
|
||||
@@ -213,9 +213,13 @@ export abstract class AbstractSynchroniser extends Disposable {
|
||||
|
||||
private async cleanUpBackup(): Promise<void> {
|
||||
try {
|
||||
if (!(await this.fileService.exists(this.syncFolder))) {
|
||||
return;
|
||||
}
|
||||
const stat = await this.fileService.resolve(this.syncFolder);
|
||||
if (stat.children) {
|
||||
const all = stat.children.filter(stat => stat.isFile && /^\d{8}T\d{6}$/.test(stat.name)).sort();
|
||||
const all = stat.children.filter(stat => stat.isFile && /^\d{8}T\d{6}(\.json)?$/.test(stat.name)).sort();
|
||||
console.log(all.map(a => a.name));
|
||||
const backUpMaxAge = 1000 * 60 * 60 * 24 * (this.configurationService.getValue<number>('sync.localBackupDuration') || 30 /* Default 30 days */);
|
||||
let toDelete = all.filter(stat => {
|
||||
const ctime = stat.ctime || new Date(
|
||||
|
||||
@@ -194,7 +194,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
|
||||
if (added.length || removed.length || updated.length) {
|
||||
// back up all disabled or market place extensions
|
||||
const backUpExtensions = localExtensions.filter(e => e.disabled || !!e.identifier.uuid);
|
||||
await this.backupLocal(VSBuffer.fromString(JSON.stringify(backUpExtensions)));
|
||||
await this.backupLocal(VSBuffer.fromString(JSON.stringify(backUpExtensions, null, '\t')));
|
||||
skippedExtensions = await this.updateLocalExtensions(added, removed, updated, skippedExtensions);
|
||||
}
|
||||
|
||||
|
||||
@@ -165,7 +165,7 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
|
||||
if (local) {
|
||||
// update local
|
||||
this.logService.trace('UI State: Updating local ui state...');
|
||||
await this.backupLocal(VSBuffer.fromString(JSON.stringify(localUserData)));
|
||||
await this.backupLocal(VSBuffer.fromString(JSON.stringify(localUserData, null, '\t')));
|
||||
await this.writeLocalGlobalState(local);
|
||||
this.logService.info('UI State: Updated local ui state');
|
||||
}
|
||||
|
||||
@@ -8,6 +8,11 @@ import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IUserDataSyncLogService, IUserDataSyncService, SyncStatus, IUserDataAutoSyncService, UserDataSyncError, UserDataSyncErrorCode, IUserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync';
|
||||
import { IAuthenticationTokenService } from 'vs/platform/authentication/common/authentication';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
|
||||
type AutoSyncTriggerClassification = {
|
||||
source: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
|
||||
};
|
||||
|
||||
export class UserDataAutoSyncService extends Disposable implements IUserDataAutoSyncService {
|
||||
|
||||
@@ -25,6 +30,7 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto
|
||||
@IUserDataSyncService private readonly userDataSyncService: IUserDataSyncService,
|
||||
@IUserDataSyncLogService private readonly logService: IUserDataSyncLogService,
|
||||
@IAuthenticationTokenService private readonly authTokenService: IAuthenticationTokenService,
|
||||
@ITelemetryService private readonly telemetryService: ITelemetryService,
|
||||
) {
|
||||
super();
|
||||
this.updateEnablement(false, true);
|
||||
@@ -32,7 +38,7 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto
|
||||
this._register(Event.any<any>(authTokenService.onDidChangeToken)(() => this.updateEnablement(true, true)));
|
||||
this._register(Event.any<any>(userDataSyncService.onDidChangeStatus)(() => this.updateEnablement(true, true)));
|
||||
this._register(this.userDataSyncEnablementService.onDidChangeEnablement(() => this.updateEnablement(true, false)));
|
||||
this._register(this.userDataSyncEnablementService.onDidChangeResourceEnablement(() => this.triggerAutoSync()));
|
||||
this._register(this.userDataSyncEnablementService.onDidChangeResourceEnablement(() => this.triggerAutoSync(['resourceEnablement'])));
|
||||
}
|
||||
|
||||
private async updateEnablement(stopIfDisabled: boolean, auto: boolean): Promise<void> {
|
||||
@@ -99,7 +105,8 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto
|
||||
this.successiveFailures = 0;
|
||||
}
|
||||
|
||||
async triggerAutoSync(): Promise<void> {
|
||||
async triggerAutoSync(sources: string[]): Promise<void> {
|
||||
sources.forEach(source => this.telemetryService.publicLog2<{ source: string }, AutoSyncTriggerClassification>('sync/triggerAutoSync', { source }));
|
||||
if (this.enabled) {
|
||||
return this.syncDelayer.trigger(() => {
|
||||
this.logService.info('Auto Sync: Triggered.');
|
||||
|
||||
@@ -278,7 +278,7 @@ export interface IUserDataSyncService {
|
||||
readonly conflictsSources: SyncSource[];
|
||||
readonly onDidChangeConflicts: Event<SyncSource[]>;
|
||||
|
||||
readonly onDidChangeLocal: Event<void>;
|
||||
readonly onDidChangeLocal: Event<SyncSource>;
|
||||
readonly onSyncErrors: Event<[SyncSource, UserDataSyncError][]>;
|
||||
|
||||
readonly lastSyncTime: number | undefined;
|
||||
@@ -299,7 +299,7 @@ export const IUserDataAutoSyncService = createDecorator<IUserDataAutoSyncService
|
||||
export interface IUserDataAutoSyncService {
|
||||
_serviceBrand: any;
|
||||
readonly onError: Event<UserDataSyncError>;
|
||||
triggerAutoSync(): Promise<void>;
|
||||
triggerAutoSync(sources: string[]): Promise<void>;
|
||||
}
|
||||
|
||||
export const IUserDataSyncUtilService = createDecorator<IUserDataSyncUtilService>('IUserDataSyncUtilService');
|
||||
|
||||
@@ -86,7 +86,7 @@ export class UserDataAutoSyncChannel implements IServerChannel {
|
||||
|
||||
call(context: any, command: string, args?: any): Promise<any> {
|
||||
switch (command) {
|
||||
case 'triggerAutoSync': return this.service.triggerAutoSync();
|
||||
case 'triggerAutoSync': return this.service.triggerAutoSync(args[0]);
|
||||
}
|
||||
throw new Error('Invalid call');
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
|
||||
private _onDidChangeStatus: Emitter<SyncStatus> = this._register(new Emitter<SyncStatus>());
|
||||
readonly onDidChangeStatus: Event<SyncStatus> = this._onDidChangeStatus.event;
|
||||
|
||||
readonly onDidChangeLocal: Event<void>;
|
||||
readonly onDidChangeLocal: Event<SyncSource>;
|
||||
|
||||
private _conflictsSources: SyncSource[] = [];
|
||||
get conflictsSources(): SyncSource[] { return this._conflictsSources; }
|
||||
@@ -74,7 +74,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
|
||||
}
|
||||
|
||||
this._lastSyncTime = this.storageService.getNumber(LAST_SYNC_TIME_KEY, StorageScope.GLOBAL, undefined);
|
||||
this.onDidChangeLocal = Event.any(...this.synchronisers.map(s => s.onDidChangeLocal));
|
||||
this.onDidChangeLocal = Event.any(...this.synchronisers.map(s => Event.map(s.onDidChangeLocal, () => s.source)));
|
||||
}
|
||||
|
||||
async pull(): Promise<void> {
|
||||
|
||||
@@ -8,6 +8,7 @@ import { Event } from 'vs/base/common/event';
|
||||
import { IElectronService } from 'vs/platform/electron/node/electron';
|
||||
import { UserDataAutoSyncService as BaseUserDataAutoSyncService } from 'vs/platform/userDataSync/common/userDataAutoSyncService';
|
||||
import { IAuthenticationTokenService } from 'vs/platform/authentication/common/authentication';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
|
||||
export class UserDataAutoSyncService extends BaseUserDataAutoSyncService {
|
||||
|
||||
@@ -17,15 +18,15 @@ export class UserDataAutoSyncService extends BaseUserDataAutoSyncService {
|
||||
@IElectronService electronService: IElectronService,
|
||||
@IUserDataSyncLogService logService: IUserDataSyncLogService,
|
||||
@IAuthenticationTokenService authTokenService: IAuthenticationTokenService,
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
) {
|
||||
super(userDataSyncEnablementService, userDataSyncService, logService, authTokenService);
|
||||
super(userDataSyncEnablementService, userDataSyncService, logService, authTokenService, telemetryService);
|
||||
|
||||
// Sync immediately if there is a local change.
|
||||
this._register(Event.debounce(Event.any<any>(
|
||||
electronService.onWindowFocus,
|
||||
electronService.onWindowOpen,
|
||||
this._register(Event.debounce<string, string[]>(Event.any<string>(
|
||||
Event.map(electronService.onWindowFocus, () => 'windowFocus'),
|
||||
Event.map(electronService.onWindowOpen, () => 'windowOpen'),
|
||||
userDataSyncService.onDidChangeLocal,
|
||||
), () => undefined, 500)(() => this.triggerAutoSync()));
|
||||
), (last, source) => last ? [...last, source] : [source], 1000)(sources => this.triggerAutoSync(sources)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Vendored
+20
-9
@@ -1500,7 +1500,7 @@ declare module 'vscode' {
|
||||
|
||||
/**
|
||||
* A file system watcher notifies about changes to files and folders
|
||||
* on disk.
|
||||
* on disk or from other [FileSystemProviders](#FileSystemProvider).
|
||||
*
|
||||
* To get an instance of a `FileSystemWatcher` use
|
||||
* [createFileSystemWatcher](#workspace.createFileSystemWatcher).
|
||||
@@ -1806,7 +1806,7 @@ declare module 'vscode' {
|
||||
placeHolder?: string;
|
||||
|
||||
/**
|
||||
* Set to `true` to show a password prompt that will not show the typed value.
|
||||
* Controls if a password input is shown. Password input hides the typed text.
|
||||
*/
|
||||
password?: boolean;
|
||||
|
||||
@@ -2020,7 +2020,7 @@ declare module 'vscode' {
|
||||
* Base kind for source actions: `source`
|
||||
*
|
||||
* Source code actions apply to the entire file. They must be explicitly requested and will not show in the
|
||||
* normal [light bulb](https://code.visualstudio.com/docs/editor/editingevolved#_code-action) menu. Source actions
|
||||
* normal [lightbulb](https://code.visualstudio.com/docs/editor/editingevolved#_code-action) menu. Source actions
|
||||
* can be run on save using `editor.codeActionsOnSave` and are also shown in the `source` context menu.
|
||||
*/
|
||||
static readonly Source: CodeActionKind;
|
||||
@@ -2086,7 +2086,7 @@ declare module 'vscode' {
|
||||
/**
|
||||
* Requested kind of actions to return.
|
||||
*
|
||||
* Actions not of this kind are filtered out before being shown by the lightbulb.
|
||||
* Actions not of this kind are filtered out before being shown by the [lightbulb](https://code.visualstudio.com/docs/editor/editingevolved#_code-action).
|
||||
*/
|
||||
readonly only?: CodeActionKind;
|
||||
}
|
||||
@@ -2138,7 +2138,15 @@ declare module 'vscode' {
|
||||
/**
|
||||
* Marks that the code action cannot currently be applied.
|
||||
*
|
||||
* Disabled code actions will be surfaced in the refactor UI but cannot be applied.
|
||||
* - Disabled code actions are not shown in automatic [lightbulb](https://code.visualstudio.com/docs/editor/editingevolved#_code-action)
|
||||
* code action menu.
|
||||
*
|
||||
* - Disabled actions are shown as faded out in the code action menu when the user request a more specific type
|
||||
* of code action, such as refactorings.
|
||||
*
|
||||
* - If the user has a [keybinding](https://code.visualstudio.com/docs/editor/refactoring#_keybindings-for-code-actions)
|
||||
* that auto applies a code action and only a disabled code actions are returned, VS Code will show the user a
|
||||
* message with `reason` in the editor.
|
||||
*/
|
||||
disabled?: {
|
||||
/**
|
||||
@@ -2163,7 +2171,7 @@ declare module 'vscode' {
|
||||
|
||||
/**
|
||||
* The code action interface defines the contract between extensions and
|
||||
* the [light bulb](https://code.visualstudio.com/docs/editor/editingevolved#_code-action) feature.
|
||||
* the [lightbulb](https://code.visualstudio.com/docs/editor/editingevolved#_code-action) feature.
|
||||
*
|
||||
* A code action can be any command that is [known](#commands.getCommands) to the system.
|
||||
*/
|
||||
@@ -2495,16 +2503,18 @@ declare module 'vscode' {
|
||||
|
||||
/**
|
||||
* The evaluatable expression provider interface defines the contract between extensions and
|
||||
* the debug hover.
|
||||
* the debug hover. In this contract the provider returns an evaluatable expression for a given position
|
||||
* in a document and VS Code evaluates this expression in the active debug session and shows the result in a debug hover.
|
||||
*/
|
||||
export interface EvaluatableExpressionProvider {
|
||||
|
||||
/**
|
||||
* Provide an evaluatable expression for the given document and position.
|
||||
* VS Code will evaluate this expression in the active debug session and will show the result in the debug hover.
|
||||
* The expression can be implicitly specified by the range in the underlying document or by explicitly returning an expression.
|
||||
*
|
||||
* @param document The document in which the debug hover is opened.
|
||||
* @param position The position in the document where the debug hover is opened.
|
||||
* @param document The document for which the debug hover is about to appear.
|
||||
* @param position The line and character position in the document where the debug hover is about to appear.
|
||||
* @param token A cancellation token.
|
||||
* @return An EvaluatableExpression or a thenable that resolves to such. The lack of a result can be
|
||||
* signaled by returning `undefined` or `null`.
|
||||
@@ -9174,6 +9184,7 @@ declare module 'vscode' {
|
||||
|
||||
/**
|
||||
* Register a provider that locates evaluatable expressions in text documents.
|
||||
* VS Code will evaluate the expression in the active debug session and will show the result in the debug hover.
|
||||
*
|
||||
* If multiple providers are registered for a language an arbitrary provider will be used.
|
||||
*
|
||||
|
||||
Vendored
+32
-23
@@ -20,7 +20,7 @@ declare module 'vscode' {
|
||||
|
||||
export interface AuthenticationSession {
|
||||
id: string;
|
||||
accessToken(): Promise<string>;
|
||||
getAccessToken(): Thenable<string>;
|
||||
accountName: string;
|
||||
scopes: string[]
|
||||
}
|
||||
@@ -58,13 +58,13 @@ declare module 'vscode' {
|
||||
/**
|
||||
* Returns an array of current sessions.
|
||||
*/
|
||||
getSessions(): Promise<ReadonlyArray<AuthenticationSession>>;
|
||||
getSessions(): Thenable<ReadonlyArray<AuthenticationSession>>;
|
||||
|
||||
/**
|
||||
* Prompts a user to login.
|
||||
*/
|
||||
login(scopes: string[]): Promise<AuthenticationSession>;
|
||||
logout(sessionId: string): Promise<void>;
|
||||
login(scopes: string[]): Thenable<AuthenticationSession>;
|
||||
logout(sessionId: string): Thenable<void>;
|
||||
}
|
||||
|
||||
export namespace authentication {
|
||||
@@ -1273,14 +1273,14 @@ declare module 'vscode' {
|
||||
* in an operation that takes time to complete, your extension may decide to finish the ongoing backup rather
|
||||
* than cancelling it to ensure that VS Code has some valid backup.
|
||||
*/
|
||||
backup(cancellation: CancellationToken): Thenable<boolean>;
|
||||
backup(cancellation: CancellationToken): Thenable<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom document for a custom webview editor.
|
||||
*
|
||||
* Custom documents are only used within a given `WebviewCustomEditorProvider`. The lifecycle of a
|
||||
* `WebviewEditorCustomDocument` is managed by VS Code. When more more references remain to a given `WebviewEditorCustomDocument`
|
||||
* Custom documents are only used within a given `CustomEditorProvider`. The lifecycle of a
|
||||
* `CustomDocument` is managed by VS Code. When more more references remain to a given `CustomDocument`
|
||||
* then it is disposed of.
|
||||
*
|
||||
* @param UserDataType Type of custom object that extensions can store on the document.
|
||||
@@ -1297,7 +1297,7 @@ declare module 'vscode' {
|
||||
readonly uri: Uri;
|
||||
|
||||
/**
|
||||
* Event fired when there are no more references to the `WebviewEditorCustomDocument`.
|
||||
* Event fired when there are no more references to the `CustomDocument`.
|
||||
*/
|
||||
readonly onDidDispose: Event<void>;
|
||||
|
||||
@@ -1313,7 +1313,7 @@ declare module 'vscode' {
|
||||
/**
|
||||
* Provider for webview editors that use a custom data model.
|
||||
*
|
||||
* Custom webview editors use [`WebviewEditorCustomDocument`](#WebviewEditorCustomDocument) as their data model.
|
||||
* Custom webview editors use [`CustomDocument`](#CustomDocument) as their data model.
|
||||
* This gives extensions full control over actions such as edit, save, and backup.
|
||||
*
|
||||
* You should use custom text based editors when dealing with binary files or more complex scenarios. For simple text
|
||||
@@ -1321,24 +1321,26 @@ declare module 'vscode' {
|
||||
*/
|
||||
export interface CustomEditorProvider {
|
||||
/**
|
||||
* Create the model for a given
|
||||
* Resolve the model for a given resource.
|
||||
*
|
||||
* @param document Resource being resolved.
|
||||
* @param document Document to resolve.
|
||||
*
|
||||
* @return The capabilities of the resolved document.
|
||||
*/
|
||||
resolveCustomDocument(document: CustomDocument): Thenable<CustomEditorCapabilities>;
|
||||
|
||||
/**
|
||||
* Resolve a webview editor for a given resource.
|
||||
*
|
||||
* To resolve a webview editor, a provider must fill in its initial html content and hook up all
|
||||
* To resolve a webview editor, the provider must fill in its initial html content and hook up all
|
||||
* the event listeners it is interested it. The provider should also take ownership of the passed in `WebviewPanel`.
|
||||
*
|
||||
* @param document Document for resource being resolved.
|
||||
* @param webview Webview being resolved. The provider should take ownership of this webview.
|
||||
* @param document Document for the resource being resolved.
|
||||
* @param webviewPanel Webview to resolve. The provider should take ownership of this webview.
|
||||
*
|
||||
* @return Thenable indicating that the webview editor has been resolved.
|
||||
*/
|
||||
resolveCustomEditor(document: CustomDocument, webview: WebviewPanel): Thenable<void>;
|
||||
resolveCustomEditor(document: CustomDocument, webviewPanel: WebviewPanel): Thenable<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1349,7 +1351,7 @@ declare module 'vscode' {
|
||||
* undo and backup. The provider is responsible for synchronizing text changes between the webview and the `TextDocument`.
|
||||
*
|
||||
* You should use text based webview editors when dealing with text based file formats, such as `xml` or `json`.
|
||||
* For binary files or more specialized use cases, see [WebviewCustomEditorProvider](#WebviewCustomEditorProvider).
|
||||
* For binary files or more specialized use cases, see [CustomEditorProvider](#CustomEditorProvider).
|
||||
*/
|
||||
export interface CustomTextEditorProvider {
|
||||
/**
|
||||
@@ -1359,11 +1361,11 @@ declare module 'vscode' {
|
||||
* the event listeners it is interested it. The provider should also take ownership of the passed in `WebviewPanel`.
|
||||
*
|
||||
* @param document Resource being resolved.
|
||||
* @param webview Webview being resolved. The provider should take ownership of this webview.
|
||||
* @param webviewPanel Webview to resolve. The provider should take ownership of this webview.
|
||||
*
|
||||
* @return Thenable indicating that the webview editor has been resolved.
|
||||
*/
|
||||
resolveCustomTextEditor(document: TextDocument, webview: WebviewPanel): Thenable<void>;
|
||||
resolveCustomTextEditor(document: TextDocument, webviewPanel: WebviewPanel): Thenable<void>;
|
||||
}
|
||||
|
||||
namespace window {
|
||||
@@ -1680,9 +1682,10 @@ declare module 'vscode' {
|
||||
*
|
||||
* The documentation is shown in the code actions menu if either:
|
||||
*
|
||||
* - Code actions of `kind` are requested by VS Code. Note that in this case, we always pick the most specific
|
||||
* documentation. For example, if documentation for both `Refactor` and `RefactorExtract` is provided, and we
|
||||
* request code actions for `RefactorExtract`, we prefer the more specific documentation for `RefactorExtract`.
|
||||
* - Code actions of `kind` are requested by VS Code. In this case, VS Code will show the documentation that
|
||||
* most closely matches the requested code action kind. For example, if a provider has documentation for
|
||||
* both `Refactor` and `RefactorExtract`, when the user requests code actions for `RefactorExtract`,
|
||||
* VS Code will use the documentation for `RefactorExtract` intead of the documentation for `Refactor`.
|
||||
*
|
||||
* - Any code actions of `kind` are returned by the provider.
|
||||
*/
|
||||
@@ -1703,7 +1706,10 @@ declare module 'vscode' {
|
||||
*/
|
||||
export interface OpenDialogOptions {
|
||||
/**
|
||||
* Dialog title
|
||||
* Dialog title.
|
||||
*
|
||||
* Depending on the underlying operating system this parameter might be ignored, since some
|
||||
* systems do not present title on open dialogs.
|
||||
*/
|
||||
title?: string;
|
||||
}
|
||||
@@ -1713,7 +1719,10 @@ declare module 'vscode' {
|
||||
*/
|
||||
export interface SaveDialogOptions {
|
||||
/**
|
||||
* Dialog title
|
||||
* Dialog title.
|
||||
*
|
||||
* Depending on the underlying operating system this parameter might be ignored, since some
|
||||
* systems do not present title on save dialogs.
|
||||
*/
|
||||
title?: string;
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ export class MainThreadAuthenticationProvider {
|
||||
return {
|
||||
id: session.id,
|
||||
accountName: session.accountName,
|
||||
accessToken: () => this._proxy.$getSessionAccessToken(this.id, session.id)
|
||||
getAccessToken: () => this._proxy.$getSessionAccessToken(this.id, session.id)
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -35,7 +35,7 @@ export class MainThreadAuthenticationProvider {
|
||||
return {
|
||||
id: session.id,
|
||||
accountName: session.accountName,
|
||||
accessToken: () => this._proxy.$getSessionAccessToken(this.id, session.id)
|
||||
getAccessToken: () => this._proxy.$getSessionAccessToken(this.id, session.id)
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -75,48 +75,52 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
|
||||
async $getSessionsPrompt(providerId: string, providerName: string, extensionId: string, extensionName: string): Promise<boolean> {
|
||||
const alwaysAllow = this.storageService.get(`${extensionId}-${providerId}`, StorageScope.GLOBAL);
|
||||
if (alwaysAllow) {
|
||||
return true;
|
||||
return alwaysAllow === 'true';
|
||||
}
|
||||
|
||||
const { choice } = await this.dialogService.show(
|
||||
const { choice, checkboxChecked } = await this.dialogService.show(
|
||||
Severity.Info,
|
||||
nls.localize('confirmAuthenticationAccess', "The extension '{0}' is trying to access authentication information from {1}.", extensionName, providerName),
|
||||
[nls.localize('cancel', "Cancel"), nls.localize('allow', "Allow"), nls.localize('alwaysAllow', "Always Allow"),],
|
||||
{ cancelId: 0 }
|
||||
[nls.localize('cancel', "Cancel"), nls.localize('allow', "Allow")],
|
||||
{
|
||||
cancelId: 0,
|
||||
checkbox: {
|
||||
label: nls.localize('neverAgain', "Don't Show Again")
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
switch (choice) {
|
||||
case 1/** Allow */:
|
||||
return true;
|
||||
case 2 /** Always Allow */:
|
||||
this.storageService.store(`${extensionId}-${providerId}`, 'true', StorageScope.GLOBAL);
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
const allow = choice === 1;
|
||||
if (checkboxChecked) {
|
||||
this.storageService.store(`${extensionId}-${providerId}`, allow ? 'true' : 'false', StorageScope.GLOBAL);
|
||||
}
|
||||
|
||||
return allow;
|
||||
}
|
||||
|
||||
async $loginPrompt(providerId: string, providerName: string, extensionId: string, extensionName: string): Promise<boolean> {
|
||||
const alwaysAllow = this.storageService.get(`${extensionId}-${providerId}`, StorageScope.GLOBAL);
|
||||
if (alwaysAllow) {
|
||||
return true;
|
||||
return alwaysAllow === 'true';
|
||||
}
|
||||
|
||||
const { choice } = await this.dialogService.show(
|
||||
const { choice, checkboxChecked } = await this.dialogService.show(
|
||||
Severity.Info,
|
||||
nls.localize('confirmLogin', "The extension '{0}' wants to sign in using {1}.", extensionName, providerName),
|
||||
[nls.localize('cancel', "Cancel"), nls.localize('continue', "Continue"), nls.localize('neverAgain', "Don't Show Again")],
|
||||
{ cancelId: 0 }
|
||||
[nls.localize('cancel', "Cancel"), nls.localize('continue', "Continue")],
|
||||
{
|
||||
cancelId: 0,
|
||||
checkbox: {
|
||||
label: nls.localize('neverAgain', "Don't Show Again")
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
switch (choice) {
|
||||
case 1/** Allow */:
|
||||
return true;
|
||||
case 2 /** Always Allow */:
|
||||
this.storageService.store(`${extensionId}-${providerId}`, 'true', StorageScope.GLOBAL);
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
const allow = choice === 1;
|
||||
if (checkboxChecked) {
|
||||
this.storageService.store(`${extensionId}-${providerId}`, allow ? 'true' : 'false', StorageScope.GLOBAL);
|
||||
}
|
||||
|
||||
return allow;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
||||
this._register(_webviewWorkbenchService.registerResolver({
|
||||
canResolve: (webview: WebviewInput) => {
|
||||
if (webview instanceof CustomEditorInput) {
|
||||
extensionService.activateByEvent(`onWebviewEditor:${webview.viewType}`);
|
||||
extensionService.activateByEvent(`onCustomEditor:${webview.viewType}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -624,7 +624,7 @@ export interface ExtHostWebviewsShape {
|
||||
$onSave(resource: UriComponents, viewType: string): Promise<void>;
|
||||
$onSaveAs(resource: UriComponents, viewType: string, targetResource: UriComponents): Promise<void>;
|
||||
|
||||
$backup(resource: UriComponents, viewType: string, cancellation: CancellationToken): Promise<boolean>;
|
||||
$backup(resource: UriComponents, viewType: string, cancellation: CancellationToken): Promise<void>;
|
||||
}
|
||||
|
||||
export interface MainThreadUrlsShape extends IDisposable {
|
||||
|
||||
@@ -34,7 +34,7 @@ export class AuthenticationProviderWrapper implements vscode.AuthenticationProvi
|
||||
id: session.id,
|
||||
accountName: session.accountName,
|
||||
scopes: session.scopes,
|
||||
accessToken: async () => {
|
||||
getAccessToken: async () => {
|
||||
const isAllowed = await this._proxy.$getSessionsPrompt(
|
||||
this._provider.id,
|
||||
this.displayName,
|
||||
@@ -45,7 +45,7 @@ export class AuthenticationProviderWrapper implements vscode.AuthenticationProvi
|
||||
throw new Error('User did not consent to token access.');
|
||||
}
|
||||
|
||||
return session.accessToken();
|
||||
return session.getAccessToken();
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -60,7 +60,7 @@ export class AuthenticationProviderWrapper implements vscode.AuthenticationProvi
|
||||
return this._provider.login(scopes);
|
||||
}
|
||||
|
||||
logout(sessionId: string): Promise<void> {
|
||||
logout(sessionId: string): Thenable<void> {
|
||||
return this._provider.logout(sessionId);
|
||||
}
|
||||
}
|
||||
@@ -137,7 +137,7 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape {
|
||||
const sessions = await authProvider.getSessions();
|
||||
const session = sessions.find(session => session.id === sessionId);
|
||||
if (session) {
|
||||
return session.accessToken();
|
||||
return session.getAccessToken();
|
||||
}
|
||||
|
||||
throw new Error(`Unable to find session with id: ${sessionId}`);
|
||||
|
||||
@@ -71,11 +71,17 @@ export class ExtHostTimeline implements IExtHostTimeline {
|
||||
scheme: scheme,
|
||||
onDidChange: undefined,
|
||||
async provideTimeline(uri: URI, options: TimelineOptions, token: CancellationToken, internalOptions?: { cacheResults?: boolean }) {
|
||||
timelineDisposables.clear();
|
||||
|
||||
// For now, only allow the caching of a single Uri
|
||||
if (internalOptions?.cacheResults && !itemsBySourceByUriMap.has(getUriKey(uri))) {
|
||||
itemsBySourceByUriMap.clear();
|
||||
if (internalOptions?.cacheResults) {
|
||||
if (options.cursor === undefined) {
|
||||
timelineDisposables.clear();
|
||||
}
|
||||
|
||||
if (!itemsBySourceByUriMap.has(getUriKey(uri))) {
|
||||
itemsBySourceByUriMap.clear();
|
||||
}
|
||||
} else {
|
||||
timelineDisposables.clear();
|
||||
}
|
||||
|
||||
const result = await provider.provideTimeline(uri, options, token);
|
||||
|
||||
@@ -247,143 +247,160 @@ export class ExtHostWebviewEditor extends Disposable implements vscode.WebviewPa
|
||||
|
||||
type EditType = unknown;
|
||||
|
||||
class WebviewEditorCustomDocument extends Disposable implements vscode.CustomDocument {
|
||||
private _currentEditIndex: number = -1;
|
||||
private _savePoint: number = -1;
|
||||
private readonly _edits: Array<EditType> = [];
|
||||
class CustomDocument extends Disposable implements vscode.CustomDocument {
|
||||
|
||||
public userData: unknown;
|
||||
|
||||
public _capabilities?: vscode.CustomEditorCapabilities = undefined;
|
||||
|
||||
constructor(
|
||||
private readonly _proxy: MainThreadWebviewsShape,
|
||||
public readonly viewType: string,
|
||||
public readonly uri: vscode.Uri,
|
||||
) {
|
||||
super();
|
||||
public static create(proxy: MainThreadWebviewsShape, viewType: string, uri: vscode.Uri) {
|
||||
return Object.seal(new CustomDocument(proxy, viewType, uri));
|
||||
}
|
||||
|
||||
_setCapabilities(capabilities: vscode.CustomEditorCapabilities) {
|
||||
if (this._capabilities) {
|
||||
throw new Error('Capabilities already provided');
|
||||
}
|
||||
// Explicitly initialize all properties as we seal the object after creation!
|
||||
|
||||
this._capabilities = capabilities;
|
||||
capabilities.editing?.onDidEdit(edit => {
|
||||
this.pushEdit(edit, this);
|
||||
});
|
||||
#currentEditIndex: number = -1;
|
||||
#savePoint: number = -1;
|
||||
readonly #edits: Array<EditType> = [];
|
||||
|
||||
readonly #proxy: MainThreadWebviewsShape;
|
||||
readonly #viewType: string;
|
||||
readonly #uri: vscode.Uri;
|
||||
|
||||
#capabilities: vscode.CustomEditorCapabilities | undefined = undefined;
|
||||
|
||||
private constructor(proxy: MainThreadWebviewsShape, viewType: string, uri: vscode.Uri) {
|
||||
super();
|
||||
this.#proxy = proxy;
|
||||
this.#viewType = viewType;
|
||||
this.#uri = uri;
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.#onDidDispose.fire();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
//#region Public API
|
||||
|
||||
#_onDidDispose = this._register(new Emitter<void>());
|
||||
public readonly onDidDispose = this.#_onDidDispose.event;
|
||||
public get viewType(): string { return this.#viewType; }
|
||||
|
||||
public get uri(): vscode.Uri { return this.#uri; }
|
||||
|
||||
#onDidDispose = this._register(new Emitter<void>());
|
||||
public readonly onDidDispose = this.#onDidDispose.event;
|
||||
|
||||
public userData: unknown = undefined;
|
||||
|
||||
//#endregion
|
||||
|
||||
dispose() {
|
||||
this.#_onDidDispose.fire();
|
||||
super.dispose();
|
||||
//#region Internal
|
||||
|
||||
/** @internal*/ _setCapabilities(capabilities: vscode.CustomEditorCapabilities) {
|
||||
if (this.#capabilities) {
|
||||
throw new Error('Capabilities already provided');
|
||||
}
|
||||
|
||||
this.#capabilities = capabilities;
|
||||
capabilities.editing?.onDidEdit(edit => {
|
||||
this.pushEdit(edit);
|
||||
});
|
||||
}
|
||||
|
||||
private pushEdit(edit: EditType, trigger: any) {
|
||||
this.spliceEdits(edit);
|
||||
|
||||
this._currentEditIndex = this._edits.length - 1;
|
||||
this.updateState();
|
||||
// this._onApplyEdit.fire({ edits: [edit], trigger });
|
||||
}
|
||||
|
||||
private updateState() {
|
||||
const dirty = this._edits.length > 0 && this._savePoint !== this._currentEditIndex;
|
||||
this._proxy.$onDidChangeCustomDocumentState(this.uri, this.viewType, { dirty });
|
||||
}
|
||||
|
||||
private spliceEdits(editToInsert?: EditType) {
|
||||
const start = this._currentEditIndex + 1;
|
||||
const toRemove = this._edits.length - this._currentEditIndex;
|
||||
|
||||
editToInsert
|
||||
? this._edits.splice(start, toRemove, editToInsert)
|
||||
: this._edits.splice(start, toRemove);
|
||||
}
|
||||
|
||||
revert() {
|
||||
/** @internal*/ _revert() {
|
||||
const editing = this.getEditingCapability();
|
||||
if (this._currentEditIndex === this._savePoint) {
|
||||
if (this.#currentEditIndex === this.#savePoint) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this._currentEditIndex >= this._savePoint) {
|
||||
const editsToUndo = this._edits.slice(this._savePoint, this._currentEditIndex);
|
||||
if (this.#currentEditIndex >= this.#savePoint) {
|
||||
const editsToUndo = this.#edits.slice(this.#savePoint, this.#currentEditIndex);
|
||||
editing.undoEdits(editsToUndo.reverse());
|
||||
} else if (this._currentEditIndex < this._savePoint) {
|
||||
const editsToRedo = this._edits.slice(this._currentEditIndex, this._savePoint);
|
||||
} else if (this.#currentEditIndex < this.#savePoint) {
|
||||
const editsToRedo = this.#edits.slice(this.#currentEditIndex, this.#savePoint);
|
||||
editing.applyEdits(editsToRedo);
|
||||
}
|
||||
|
||||
this._currentEditIndex = this._savePoint;
|
||||
this.#currentEditIndex = this.#savePoint;
|
||||
this.spliceEdits();
|
||||
|
||||
this.updateState();
|
||||
return true;
|
||||
}
|
||||
|
||||
undo() {
|
||||
/** @internal*/ _undo() {
|
||||
const editing = this.getEditingCapability();
|
||||
if (this._currentEditIndex < 0) {
|
||||
if (this.#currentEditIndex < 0) {
|
||||
// nothing to undo
|
||||
return;
|
||||
}
|
||||
|
||||
const undoneEdit = this._edits[this._currentEditIndex];
|
||||
--this._currentEditIndex;
|
||||
const undoneEdit = this.#edits[this.#currentEditIndex];
|
||||
--this.#currentEditIndex;
|
||||
editing.undoEdits([undoneEdit]);
|
||||
this.updateState();
|
||||
}
|
||||
|
||||
redo() {
|
||||
/** @internal*/ _redo() {
|
||||
const editing = this.getEditingCapability();
|
||||
if (this._currentEditIndex >= this._edits.length - 1) {
|
||||
if (this.#currentEditIndex >= this.#edits.length - 1) {
|
||||
// nothing to redo
|
||||
return;
|
||||
}
|
||||
|
||||
++this._currentEditIndex;
|
||||
const redoneEdit = this._edits[this._currentEditIndex];
|
||||
++this.#currentEditIndex;
|
||||
const redoneEdit = this.#edits[this.#currentEditIndex];
|
||||
editing.applyEdits([redoneEdit]);
|
||||
this.updateState();
|
||||
}
|
||||
|
||||
save() {
|
||||
/** @internal*/ _save() {
|
||||
return this.getEditingCapability().save();
|
||||
}
|
||||
|
||||
saveAs(target: vscode.Uri) {
|
||||
/** @internal*/ _saveAs(target: vscode.Uri) {
|
||||
return this.getEditingCapability().saveAs(target);
|
||||
}
|
||||
|
||||
backup(cancellation: CancellationToken) {
|
||||
/** @internal*/ _backup(cancellation: CancellationToken) {
|
||||
return this.getEditingCapability().backup(cancellation);
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
private pushEdit(edit: EditType) {
|
||||
this.spliceEdits(edit);
|
||||
|
||||
this.#currentEditIndex = this.#edits.length - 1;
|
||||
this.updateState();
|
||||
}
|
||||
|
||||
private updateState() {
|
||||
const dirty = this.#edits.length > 0 && this.#savePoint !== this.#currentEditIndex;
|
||||
this.#proxy.$onDidChangeCustomDocumentState(this.uri, this.viewType, { dirty });
|
||||
}
|
||||
|
||||
private spliceEdits(editToInsert?: EditType) {
|
||||
const start = this.#currentEditIndex + 1;
|
||||
const toRemove = this.#edits.length - this.#currentEditIndex;
|
||||
|
||||
editToInsert
|
||||
? this.#edits.splice(start, toRemove, editToInsert)
|
||||
: this.#edits.splice(start, toRemove);
|
||||
}
|
||||
|
||||
private getEditingCapability(): vscode.CustomEditorEditingCapability {
|
||||
if (!this._capabilities?.editing) {
|
||||
if (!this.#capabilities?.editing) {
|
||||
throw new Error('Document is not editable');
|
||||
}
|
||||
return this._capabilities.editing;
|
||||
return this.#capabilities.editing;
|
||||
}
|
||||
}
|
||||
|
||||
class WebviewDocumentStore {
|
||||
private readonly _documents = new Map<string, WebviewEditorCustomDocument>();
|
||||
private readonly _documents = new Map<string, CustomDocument>();
|
||||
|
||||
public get(viewType: string, resource: vscode.Uri): WebviewEditorCustomDocument | undefined {
|
||||
public get(viewType: string, resource: vscode.Uri): CustomDocument | undefined {
|
||||
return this._documents.get(this.key(viewType, resource));
|
||||
}
|
||||
|
||||
public add(document: WebviewEditorCustomDocument) {
|
||||
public add(document: CustomDocument) {
|
||||
const key = this.key(document.viewType, document.uri);
|
||||
if (this._documents.has(key)) {
|
||||
throw new Error(`Document already exists for viewType:${document.viewType} resource:${document.uri}`);
|
||||
@@ -391,7 +408,7 @@ class WebviewDocumentStore {
|
||||
this._documents.set(key, document);
|
||||
}
|
||||
|
||||
public delete(document: WebviewEditorCustomDocument) {
|
||||
public delete(document: CustomDocument) {
|
||||
const key = this.key(document.viewType, document.uri);
|
||||
this._documents.delete(key);
|
||||
}
|
||||
@@ -622,7 +639,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
|
||||
}
|
||||
|
||||
const revivedResource = URI.revive(resource);
|
||||
const document = Object.seal(new WebviewEditorCustomDocument(this._proxy, viewType, revivedResource));
|
||||
const document = CustomDocument.create(this._proxy, viewType, revivedResource);
|
||||
const capabilities = await entry.provider.resolveCustomDocument(document);
|
||||
document._setCapabilities(capabilities);
|
||||
this._documents.add(document);
|
||||
@@ -687,39 +704,39 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
|
||||
|
||||
async $undo(resourceComponents: UriComponents, viewType: string): Promise<void> {
|
||||
const document = this.getDocument(viewType, resourceComponents);
|
||||
document.undo();
|
||||
document._undo();
|
||||
}
|
||||
|
||||
async $redo(resourceComponents: UriComponents, viewType: string): Promise<void> {
|
||||
const document = this.getDocument(viewType, resourceComponents);
|
||||
document.redo();
|
||||
document._redo();
|
||||
}
|
||||
|
||||
async $revert(resourceComponents: UriComponents, viewType: string): Promise<void> {
|
||||
const document = this.getDocument(viewType, resourceComponents);
|
||||
document.revert();
|
||||
document._revert();
|
||||
}
|
||||
|
||||
async $onSave(resourceComponents: UriComponents, viewType: string): Promise<void> {
|
||||
const document = this.getDocument(viewType, resourceComponents);
|
||||
document.save();
|
||||
document._save();
|
||||
}
|
||||
|
||||
async $onSaveAs(resourceComponents: UriComponents, viewType: string, targetResource: UriComponents): Promise<void> {
|
||||
const document = this.getDocument(viewType, resourceComponents);
|
||||
return document.saveAs(URI.revive(targetResource));
|
||||
return document._saveAs(URI.revive(targetResource));
|
||||
}
|
||||
|
||||
async $backup(resourceComponents: UriComponents, viewType: string, cancellation: CancellationToken): Promise<boolean> {
|
||||
async $backup(resourceComponents: UriComponents, viewType: string, cancellation: CancellationToken): Promise<void> {
|
||||
const document = this.getDocument(viewType, resourceComponents);
|
||||
return document.backup(cancellation);
|
||||
return document._backup(cancellation);
|
||||
}
|
||||
|
||||
private getWebviewPanel(handle: WebviewPanelHandle): ExtHostWebviewEditor | undefined {
|
||||
return this._webviewPanels.get(handle);
|
||||
}
|
||||
|
||||
private getDocument(viewType: string, resource: UriComponents): WebviewEditorCustomDocument {
|
||||
private getDocument(viewType: string, resource: UriComponents): CustomDocument {
|
||||
const document = this._documents.get(viewType, URI.revive(resource));
|
||||
if (!document) {
|
||||
throw new Error('No webview editor custom document found');
|
||||
|
||||
@@ -25,9 +25,10 @@ import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/commo
|
||||
import { SideBarVisibleContext } from 'vs/workbench/common/viewlet';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { IViewDescriptorService, IViewContainersRegistry, Extensions as ViewContainerExtensions, IViewsService, FocusedViewContext, ViewContainerLocation } from 'vs/workbench/common/views';
|
||||
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { IQuickInputService, IQuickPickItem, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { IActivityBarService } from 'vs/workbench/services/activityBar/browser/activityBarService';
|
||||
|
||||
const registry = Registry.as<IWorkbenchActionRegistry>(WorkbenchExtensions.WorkbenchActions);
|
||||
const viewCategory = nls.localize('view', "View");
|
||||
@@ -534,6 +535,7 @@ export class MoveFocusedViewAction extends Action {
|
||||
@IQuickInputService private quickInputService: IQuickInputService,
|
||||
@IContextKeyService private contextKeyService: IContextKeyService,
|
||||
@INotificationService private notificationService: INotificationService,
|
||||
@IActivityBarService private activityBarService: IActivityBarService,
|
||||
@IViewletService private viewletService: IViewletService
|
||||
) {
|
||||
super(id, label);
|
||||
@@ -542,58 +544,64 @@ export class MoveFocusedViewAction extends Action {
|
||||
run(): Promise<void> {
|
||||
const viewContainerRegistry = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry);
|
||||
|
||||
const focusedView = FocusedViewContext.getValue(this.contextKeyService);
|
||||
const focusedViewId = FocusedViewContext.getValue(this.contextKeyService);
|
||||
|
||||
if (focusedView === undefined || focusedView.trim() === '') {
|
||||
if (focusedViewId === undefined || focusedViewId.trim() === '') {
|
||||
this.notificationService.error(nls.localize('moveFocusedView.error.noFocusedView', "There is no view currently focused."));
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const viewDescriptor = this.viewDescriptorService.getViewDescriptor(focusedView);
|
||||
const viewDescriptor = this.viewDescriptorService.getViewDescriptor(focusedViewId);
|
||||
if (!viewDescriptor || !viewDescriptor.canMoveView) {
|
||||
this.notificationService.error(nls.localize('moveFocusedView.error.nonMovableView', "The currently focused view is not movable {0}.", focusedView));
|
||||
this.notificationService.error(nls.localize('moveFocusedView.error.nonMovableView', "The currently focused view is not movable."));
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const quickPick = this.quickInputService.createQuickPick();
|
||||
quickPick.placeholder = nls.localize('moveFocusedView.selectDestination', "Select a destination area for the view...");
|
||||
quickPick.autoFocusOnList = true;
|
||||
quickPick.placeholder = nls.localize('moveFocusedView.selectDestination', "Select a Destination for the View");
|
||||
|
||||
quickPick.items = [
|
||||
{
|
||||
id: 'sidebar',
|
||||
label: nls.localize('sidebar', "Sidebar")
|
||||
},
|
||||
{
|
||||
id: 'panel',
|
||||
const pinnedViewlets = this.activityBarService.getPinnedViewletIds();
|
||||
const items: Array<IQuickPickItem | IQuickPickSeparator> = this.viewletService.getViewlets()
|
||||
.filter(viewlet => {
|
||||
if (viewlet.id === this.viewDescriptorService.getViewContainer(focusedViewId)!.id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !viewContainerRegistry.get(viewlet.id)!.rejectAddedViews && pinnedViewlets.indexOf(viewlet.id) !== -1;
|
||||
})
|
||||
.map(viewlet => {
|
||||
return {
|
||||
id: viewlet.id,
|
||||
label: viewlet.name,
|
||||
};
|
||||
});
|
||||
|
||||
if (this.viewDescriptorService.getViewLocation(focusedViewId) !== ViewContainerLocation.Panel) {
|
||||
items.unshift({
|
||||
type: 'separator',
|
||||
label: nls.localize('sidebar', "Side Bar")
|
||||
});
|
||||
items.push({
|
||||
type: 'separator',
|
||||
label: nls.localize('panel', "Panel")
|
||||
}
|
||||
];
|
||||
});
|
||||
items.push({
|
||||
id: '_.panel.newcontainer',
|
||||
label: nls.localize('moveFocusedView.newContainerInPanel', "New Container in Panel"),
|
||||
});
|
||||
}
|
||||
|
||||
quickPick.items = items;
|
||||
|
||||
quickPick.onDidAccept(() => {
|
||||
const destination = quickPick.selectedItems[0];
|
||||
|
||||
if (destination.id === 'panel') {
|
||||
quickPick.hide();
|
||||
if (destination.id === '_.panel.newcontainer') {
|
||||
this.viewDescriptorService.moveViewToLocation(viewDescriptor!, ViewContainerLocation.Panel);
|
||||
this.viewsService.openView(focusedView, true);
|
||||
|
||||
return;
|
||||
} else if (destination.id === 'sidebar') {
|
||||
quickPick.placeholder = nls.localize('moveFocusedView.selectDestinationContainer', "Select a destination view group...");
|
||||
quickPick.items = this.viewletService.getViewlets().map(viewlet => {
|
||||
return {
|
||||
id: viewlet.id,
|
||||
label: viewlet.name
|
||||
};
|
||||
});
|
||||
|
||||
return;
|
||||
this.viewsService.openView(focusedViewId, true);
|
||||
} else if (destination.id) {
|
||||
quickPick.hide();
|
||||
this.viewDescriptorService.moveViewsToContainer([viewDescriptor], viewContainerRegistry.get(destination.id)!);
|
||||
this.viewsService.openView(focusedView, true);
|
||||
return;
|
||||
this.viewsService.openView(focusedViewId, true);
|
||||
}
|
||||
|
||||
quickPick.hide();
|
||||
@@ -605,7 +613,7 @@ export class MoveFocusedViewAction extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.create(MoveFocusedViewAction, MoveFocusedViewAction.ID, MoveFocusedViewAction.LABEL), 'View: Move Focused View', viewCategory);
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.create(MoveFocusedViewAction, MoveFocusedViewAction.ID, MoveFocusedViewAction.LABEL), 'View: Move Focused View', viewCategory, FocusedViewContext.notEqualsTo(''));
|
||||
|
||||
|
||||
// --- Resize View
|
||||
|
||||
@@ -27,6 +27,10 @@
|
||||
margin-bottom: auto;
|
||||
}
|
||||
|
||||
.monaco-workbench .activitybar > .content > .composite-bar-excess {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.monaco-workbench .activitybar .menubar {
|
||||
width: 100%;
|
||||
height: 35px;
|
||||
|
||||
@@ -17,13 +17,14 @@ import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { Widget } from 'vs/base/browser/ui/widget';
|
||||
import { isUndefinedOrNull } from 'vs/base/common/types';
|
||||
import { LocalSelectionTransfer } from 'vs/workbench/browser/dnd';
|
||||
import { ITheme } from 'vs/platform/theme/common/themeService';
|
||||
import { LocalSelectionTransfer, DragAndDropObserver } from 'vs/workbench/browser/dnd';
|
||||
import { ITheme, IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { DraggedViewIdentifier } from 'vs/workbench/browser/parts/views/viewPaneContainer';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IViewContainersRegistry, Extensions as ViewContainerExtensions, ViewContainerLocation, IViewDescriptorService } from 'vs/workbench/common/views';
|
||||
import { ICompositeDragAndDrop, CompositeDragAndDropData } from 'vs/base/parts/composite/browser/compositeDnd';
|
||||
import { IPaneComposite } from 'vs/workbench/common/panecomposite';
|
||||
|
||||
export interface ICompositeBarItem {
|
||||
id: string;
|
||||
@@ -38,7 +39,7 @@ export class CompositeDragAndDrop implements ICompositeDragAndDrop {
|
||||
constructor(
|
||||
private viewDescriptorService: IViewDescriptorService,
|
||||
private targetContainerLocation: ViewContainerLocation,
|
||||
private openComposite: (id: string, focus?: boolean) => void,
|
||||
private openComposite: (id: string, focus?: boolean) => Promise<IPaneComposite | undefined>,
|
||||
private moveComposite: (from: string, to: string) => void,
|
||||
private getVisibleCompositeIds: () => string[]
|
||||
) { }
|
||||
@@ -52,9 +53,14 @@ export class CompositeDragAndDrop implements ICompositeDragAndDrop {
|
||||
if (targetCompositeId) {
|
||||
if (currentLocation !== this.targetContainerLocation && this.targetContainerLocation !== ViewContainerLocation.Panel) {
|
||||
const destinationContainer = viewContainerRegistry.get(targetCompositeId);
|
||||
if (destinationContainer) {
|
||||
this.viewDescriptorService.moveViewsToContainer(this.viewDescriptorService.getViewDescriptors(currentContainer)!.allViewDescriptors.filter(vd => vd.canMoveView), destinationContainer);
|
||||
this.openComposite(targetCompositeId, true);
|
||||
if (destinationContainer && !destinationContainer.rejectAddedViews) {
|
||||
const viewsToMove = this.viewDescriptorService.getViewDescriptors(currentContainer)!.allViewDescriptors.filter(vd => vd.canMoveView);
|
||||
this.viewDescriptorService.moveViewsToContainer(viewsToMove, destinationContainer);
|
||||
this.openComposite(targetCompositeId, true).then(composite => {
|
||||
if (composite && viewsToMove.length === 1) {
|
||||
composite.openView(viewsToMove[0].id, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.moveComposite(dragData.id, targetCompositeId);
|
||||
@@ -73,10 +79,14 @@ export class CompositeDragAndDrop implements ICompositeDragAndDrop {
|
||||
if (viewDescriptor && viewDescriptor.canMoveView) {
|
||||
if (targetCompositeId) {
|
||||
const destinationContainer = viewContainerRegistry.get(targetCompositeId);
|
||||
if (destinationContainer) {
|
||||
if (destinationContainer && !destinationContainer.rejectAddedViews) {
|
||||
if (this.targetContainerLocation === ViewContainerLocation.Sidebar) {
|
||||
this.viewDescriptorService.moveViewsToContainer([viewDescriptor], destinationContainer);
|
||||
this.openComposite(targetCompositeId, true);
|
||||
this.openComposite(targetCompositeId, true).then(composite => {
|
||||
if (composite) {
|
||||
composite.openView(viewDescriptor.id, true);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.viewDescriptorService.moveViewToLocation(viewDescriptor, this.targetContainerLocation);
|
||||
this.moveComposite(this.viewDescriptorService.getViewContainer(viewDescriptor.id)!.id, targetCompositeId);
|
||||
@@ -91,13 +101,25 @@ export class CompositeDragAndDrop implements ICompositeDragAndDrop {
|
||||
this.moveComposite(newCompositeId, targetId);
|
||||
}
|
||||
|
||||
this.openComposite(newCompositeId, true);
|
||||
this.openComposite(newCompositeId, true).then(composite => {
|
||||
if (composite) {
|
||||
composite.openView(viewDescriptor.id, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onDragEnter(data: CompositeDragAndDropData, targetCompositeId: string | undefined, originalEvent: DragEvent): boolean {
|
||||
return this.canDrop(data, targetCompositeId);
|
||||
}
|
||||
|
||||
onDragOver(data: CompositeDragAndDropData, targetCompositeId: string | undefined, originalEvent: DragEvent): boolean {
|
||||
return this.canDrop(data, targetCompositeId);
|
||||
}
|
||||
|
||||
private canDrop(data: CompositeDragAndDropData, targetCompositeId: string | undefined): boolean {
|
||||
const dragData = data.getData();
|
||||
const viewContainerRegistry = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry);
|
||||
|
||||
@@ -134,6 +156,7 @@ export class CompositeDragAndDrop implements ICompositeDragAndDrop {
|
||||
if (this.targetContainerLocation === ViewContainerLocation.Sidebar) {
|
||||
const destinationContainer = viewContainerRegistry.get(targetCompositeId);
|
||||
return !!destinationContainer &&
|
||||
!destinationContainer.rejectAddedViews &&
|
||||
this.viewDescriptorService.getViewDescriptors(currentContainer)!.allViewDescriptors.some(vd => vd.canMoveView);
|
||||
}
|
||||
// ... from sidebar to the panel
|
||||
@@ -155,10 +178,9 @@ export class CompositeDragAndDrop implements ICompositeDragAndDrop {
|
||||
}
|
||||
|
||||
// ... into a destination
|
||||
return true;
|
||||
const destinationContainer = viewContainerRegistry.get(targetCompositeId);
|
||||
return !!destinationContainer && !destinationContainer.rejectAddedViews;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,6 +222,7 @@ export class CompositeBar extends Widget implements ICompositeBar {
|
||||
constructor(
|
||||
items: ICompositeBarItem[],
|
||||
private options: ICompositeBarOptions,
|
||||
@IThemeService private readonly themeService: IThemeService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IContextMenuService private readonly contextMenuService: IContextMenuService
|
||||
) {
|
||||
@@ -228,6 +251,7 @@ export class CompositeBar extends Widget implements ICompositeBar {
|
||||
|
||||
create(parent: HTMLElement): HTMLElement {
|
||||
const actionBarDiv = parent.appendChild($('.composite-bar'));
|
||||
const excessDiv = parent.appendChild($('.composite-bar-excess'));
|
||||
|
||||
this.compositeSwitcherBar = this._register(new ActionBar(actionBarDiv, {
|
||||
actionViewItemProvider: (action: IAction) => {
|
||||
@@ -254,58 +278,99 @@ export class CompositeBar extends Widget implements ICompositeBar {
|
||||
this._register(addDisposableListener(parent, EventType.CONTEXT_MENU, e => this.showContextMenu(e)));
|
||||
|
||||
// Allow to drop at the end to move composites to the end
|
||||
this._register(addDisposableListener(parent, EventType.DROP, (e: DragEvent) => {
|
||||
if (this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype)) {
|
||||
EventHelper.stop(e, true);
|
||||
this._register(new DragAndDropObserver(excessDiv, {
|
||||
onDragOver: (e: DragEvent) => {
|
||||
if (this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype)) {
|
||||
EventHelper.stop(e, true);
|
||||
|
||||
const data = this.compositeTransfer.getData(DraggedCompositeIdentifier.prototype);
|
||||
if (Array.isArray(data)) {
|
||||
const draggedCompositeId = data[0].id;
|
||||
this.compositeTransfer.clearData(DraggedCompositeIdentifier.prototype);
|
||||
const data = this.compositeTransfer.getData(DraggedCompositeIdentifier.prototype);
|
||||
if (Array.isArray(data)) {
|
||||
const draggedCompositeId = data[0].id;
|
||||
|
||||
this.options.dndHandler.drop(new CompositeDragAndDropData('composite', draggedCompositeId), undefined, e);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.compositeTransfer.hasData(DraggedViewIdentifier.prototype)) {
|
||||
const data = this.compositeTransfer.getData(DraggedViewIdentifier.prototype);
|
||||
if (Array.isArray(data)) {
|
||||
const draggedViewId = data[0].id;
|
||||
this.compositeTransfer.clearData(DraggedViewIdentifier.prototype);
|
||||
|
||||
this.options.dndHandler.drop(new CompositeDragAndDropData('view', draggedViewId), undefined, e);
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
this._register(addDisposableListener(parent, EventType.DRAG_OVER, (e: DragEvent) => {
|
||||
if (this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype)) {
|
||||
EventHelper.stop(e, true);
|
||||
|
||||
const data = this.compositeTransfer.getData(DraggedCompositeIdentifier.prototype);
|
||||
if (Array.isArray(data)) {
|
||||
const draggedCompositeId = data[0].id;
|
||||
|
||||
// Check if drop is allowed
|
||||
if (e.dataTransfer && !this.options.dndHandler.onDragOver(new CompositeDragAndDropData('composite', draggedCompositeId), undefined, e)) {
|
||||
e.dataTransfer.dropEffect = 'none';
|
||||
// Check if drop is allowed
|
||||
if (e.dataTransfer && !this.options.dndHandler.onDragOver(new CompositeDragAndDropData('composite', draggedCompositeId), undefined, e)) {
|
||||
e.dataTransfer.dropEffect = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.compositeTransfer.hasData(DraggedViewIdentifier.prototype)) {
|
||||
EventHelper.stop(e, true);
|
||||
if (this.compositeTransfer.hasData(DraggedViewIdentifier.prototype)) {
|
||||
EventHelper.stop(e, true);
|
||||
|
||||
const data = this.compositeTransfer.getData(DraggedViewIdentifier.prototype);
|
||||
if (Array.isArray(data)) {
|
||||
const draggedViewId = data[0].id;
|
||||
const data = this.compositeTransfer.getData(DraggedViewIdentifier.prototype);
|
||||
if (Array.isArray(data)) {
|
||||
const draggedViewId = data[0].id;
|
||||
|
||||
// Check if drop is allowed
|
||||
if (e.dataTransfer && !this.options.dndHandler.onDragOver(new CompositeDragAndDropData('view', draggedViewId), undefined, e)) {
|
||||
e.dataTransfer.dropEffect = 'none';
|
||||
// Check if drop is allowed
|
||||
if (e.dataTransfer && !this.options.dndHandler.onDragOver(new CompositeDragAndDropData('view', draggedViewId), undefined, e)) {
|
||||
e.dataTransfer.dropEffect = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onDragEnter: (e: DragEvent) => {
|
||||
if (this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype)) {
|
||||
EventHelper.stop(e, true);
|
||||
|
||||
const data = this.compositeTransfer.getData(DraggedCompositeIdentifier.prototype);
|
||||
if (Array.isArray(data)) {
|
||||
const draggedCompositeId = data[0].id;
|
||||
|
||||
// Check if drop is allowed
|
||||
const validDropTarget = this.options.dndHandler.onDragEnter(new CompositeDragAndDropData('composite', draggedCompositeId), undefined, e);
|
||||
this.updateFromDragging(excessDiv, validDropTarget);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.compositeTransfer.hasData(DraggedViewIdentifier.prototype)) {
|
||||
EventHelper.stop(e, true);
|
||||
|
||||
const data = this.compositeTransfer.getData(DraggedViewIdentifier.prototype);
|
||||
if (Array.isArray(data)) {
|
||||
const draggedViewId = data[0].id;
|
||||
|
||||
// Check if drop is allowed
|
||||
const validDropTarget = this.options.dndHandler.onDragEnter(new CompositeDragAndDropData('view', draggedViewId), undefined, e);
|
||||
this.updateFromDragging(excessDiv, validDropTarget);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onDragLeave: (e: DragEvent) => {
|
||||
if (this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype) ||
|
||||
this.compositeTransfer.hasData(DraggedViewIdentifier.prototype)) {
|
||||
this.updateFromDragging(excessDiv, false);
|
||||
}
|
||||
},
|
||||
onDragEnd: (e: DragEvent) => {
|
||||
// no-op, will not be called
|
||||
},
|
||||
onDrop: (e: DragEvent) => {
|
||||
if (this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype)) {
|
||||
EventHelper.stop(e, true);
|
||||
|
||||
const data = this.compositeTransfer.getData(DraggedCompositeIdentifier.prototype);
|
||||
if (Array.isArray(data)) {
|
||||
const draggedCompositeId = data[0].id;
|
||||
this.compositeTransfer.clearData(DraggedCompositeIdentifier.prototype);
|
||||
|
||||
this.options.dndHandler.drop(new CompositeDragAndDropData('composite', draggedCompositeId), undefined, e);
|
||||
this.updateFromDragging(excessDiv, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.compositeTransfer.hasData(DraggedViewIdentifier.prototype)) {
|
||||
const data = this.compositeTransfer.getData(DraggedViewIdentifier.prototype);
|
||||
if (Array.isArray(data)) {
|
||||
const draggedViewId = data[0].id;
|
||||
this.compositeTransfer.clearData(DraggedViewIdentifier.prototype);
|
||||
|
||||
this.options.dndHandler.drop(new CompositeDragAndDropData('view', draggedViewId), undefined, e);
|
||||
this.updateFromDragging(excessDiv, false);
|
||||
}
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
return actionBarDiv;
|
||||
@@ -410,6 +475,13 @@ export class CompositeBar extends Widget implements ICompositeBar {
|
||||
}
|
||||
}
|
||||
|
||||
private updateFromDragging(element: HTMLElement, isDragging: boolean): void {
|
||||
const theme = this.themeService.getTheme();
|
||||
const dragBackground = this.options.colors(theme).dragAndDropBackground;
|
||||
|
||||
element.style.backgroundColor = isDragging && dragBackground ? dragBackground.toString() : '';
|
||||
}
|
||||
|
||||
private resetActiveComposite(compositeId: string) {
|
||||
const defaultCompositeId = this.options.getDefaultCompositeId();
|
||||
|
||||
|
||||
@@ -544,14 +544,16 @@ export class CompositeActionViewItem extends ActivityActionViewItem {
|
||||
if (this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype)) {
|
||||
const data = this.compositeTransfer.getData(DraggedCompositeIdentifier.prototype);
|
||||
if (Array.isArray(data) && data[0].id !== this.activity.id) {
|
||||
this.updateFromDragging(container, true);
|
||||
const validDropTarget = this.dndHandler.onDragEnter(new CompositeDragAndDropData('composite', data[0].id), this.activity.id, e);
|
||||
this.updateFromDragging(container, validDropTarget);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.compositeTransfer.hasData(DraggedViewIdentifier.prototype)) {
|
||||
const data = this.compositeTransfer.getData(DraggedViewIdentifier.prototype);
|
||||
if (Array.isArray(data) && data[0].id !== this.activity.id) {
|
||||
this.updateFromDragging(container, true);
|
||||
const validDropTarget = this.dndHandler.onDragEnter(new CompositeDragAndDropData('view', data[0].id), this.activity.id, e);
|
||||
this.updateFromDragging(container, validDropTarget);
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -616,6 +618,8 @@ export class CompositeActionViewItem extends ActivityActionViewItem {
|
||||
const data = this.compositeTransfer.getData(DraggedViewIdentifier.prototype);
|
||||
if (Array.isArray(data)) {
|
||||
const draggedViewId = data[0].id;
|
||||
this.updateFromDragging(container, false);
|
||||
this.compositeTransfer.clearData(DraggedViewIdentifier.prototype);
|
||||
|
||||
this.dndHandler.drop(new CompositeDragAndDropData('view', draggedViewId), this.activity.id, e);
|
||||
}
|
||||
|
||||
@@ -147,6 +147,7 @@ class StateChange {
|
||||
encoding: boolean = false;
|
||||
EOL: boolean = false;
|
||||
tabFocusMode: boolean = false;
|
||||
columnSelectionMode: boolean = false;
|
||||
screenReaderMode: boolean = false;
|
||||
metadata: boolean = false;
|
||||
|
||||
@@ -157,6 +158,7 @@ class StateChange {
|
||||
this.encoding = this.encoding || other.encoding;
|
||||
this.EOL = this.EOL || other.EOL;
|
||||
this.tabFocusMode = this.tabFocusMode || other.tabFocusMode;
|
||||
this.columnSelectionMode = this.columnSelectionMode || other.columnSelectionMode;
|
||||
this.screenReaderMode = this.screenReaderMode || other.screenReaderMode;
|
||||
this.metadata = this.metadata || other.metadata;
|
||||
}
|
||||
@@ -168,21 +170,23 @@ class StateChange {
|
||||
|| this.encoding
|
||||
|| this.EOL
|
||||
|| this.tabFocusMode
|
||||
|| this.columnSelectionMode
|
||||
|| this.screenReaderMode
|
||||
|| this.metadata;
|
||||
}
|
||||
}
|
||||
|
||||
interface StateDelta {
|
||||
selectionStatus?: string;
|
||||
mode?: string;
|
||||
encoding?: string;
|
||||
EOL?: string;
|
||||
indentation?: string;
|
||||
tabFocusMode?: boolean;
|
||||
screenReaderMode?: boolean;
|
||||
metadata?: string | undefined;
|
||||
}
|
||||
type StateDelta = (
|
||||
{ type: 'selectionStatus'; selectionStatus: string | undefined; }
|
||||
| { type: 'mode'; mode: string | undefined; }
|
||||
| { type: 'encoding'; encoding: string | undefined; }
|
||||
| { type: 'EOL'; EOL: string | undefined; }
|
||||
| { type: 'indentation'; indentation: string | undefined; }
|
||||
| { type: 'tabFocusMode'; tabFocusMode: boolean; }
|
||||
| { type: 'columnSelectionMode'; columnSelectionMode: boolean; }
|
||||
| { type: 'screenReaderMode'; screenReaderMode: boolean; }
|
||||
| { type: 'metadata'; metadata: string | undefined; }
|
||||
);
|
||||
|
||||
class State {
|
||||
|
||||
@@ -204,6 +208,9 @@ class State {
|
||||
private _tabFocusMode: boolean | undefined;
|
||||
get tabFocusMode(): boolean | undefined { return this._tabFocusMode; }
|
||||
|
||||
private _columnSelectionMode: boolean | undefined;
|
||||
get columnSelectionMode(): boolean | undefined { return this._columnSelectionMode; }
|
||||
|
||||
private _screenReaderMode: boolean | undefined;
|
||||
get screenReaderMode(): boolean | undefined { return this._screenReaderMode; }
|
||||
|
||||
@@ -213,56 +220,63 @@ class State {
|
||||
update(update: StateDelta): StateChange {
|
||||
const change = new StateChange();
|
||||
|
||||
if ('selectionStatus' in update) {
|
||||
if (update.type === 'selectionStatus') {
|
||||
if (this._selectionStatus !== update.selectionStatus) {
|
||||
this._selectionStatus = update.selectionStatus;
|
||||
change.selectionStatus = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ('indentation' in update) {
|
||||
if (update.type === 'indentation') {
|
||||
if (this._indentation !== update.indentation) {
|
||||
this._indentation = update.indentation;
|
||||
change.indentation = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ('mode' in update) {
|
||||
if (update.type === 'mode') {
|
||||
if (this._mode !== update.mode) {
|
||||
this._mode = update.mode;
|
||||
change.mode = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ('encoding' in update) {
|
||||
if (update.type === 'encoding') {
|
||||
if (this._encoding !== update.encoding) {
|
||||
this._encoding = update.encoding;
|
||||
change.encoding = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ('EOL' in update) {
|
||||
if (update.type === 'EOL') {
|
||||
if (this._EOL !== update.EOL) {
|
||||
this._EOL = update.EOL;
|
||||
change.EOL = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ('tabFocusMode' in update) {
|
||||
if (update.type === 'tabFocusMode') {
|
||||
if (this._tabFocusMode !== update.tabFocusMode) {
|
||||
this._tabFocusMode = update.tabFocusMode;
|
||||
change.tabFocusMode = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ('screenReaderMode' in update) {
|
||||
if (update.type === 'columnSelectionMode') {
|
||||
if (this._columnSelectionMode !== update.columnSelectionMode) {
|
||||
this._columnSelectionMode = update.columnSelectionMode;
|
||||
change.columnSelectionMode = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (update.type === 'screenReaderMode') {
|
||||
if (this._screenReaderMode !== update.screenReaderMode) {
|
||||
this._screenReaderMode = update.screenReaderMode;
|
||||
change.screenReaderMode = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ('metadata' in update) {
|
||||
if (update.type === 'metadata') {
|
||||
if (this._metadata !== update.metadata) {
|
||||
this._metadata = update.metadata;
|
||||
change.metadata = true;
|
||||
@@ -282,6 +296,7 @@ const nlsEOLCRLF = nls.localize('endOfLineCarriageReturnLineFeed', "CRLF");
|
||||
|
||||
export class EditorStatus extends Disposable implements IWorkbenchContribution {
|
||||
private readonly tabFocusModeElement = this._register(new MutableDisposable<IStatusbarEntryAccessor>());
|
||||
private readonly columnSelectionModeElement = this._register(new MutableDisposable<IStatusbarEntryAccessor>());
|
||||
private readonly screenRedearModeElement = this._register(new MutableDisposable<IStatusbarEntryAccessor>());
|
||||
private readonly indentationElement = this._register(new MutableDisposable<IStatusbarEntryAccessor>());
|
||||
private readonly selectionElement = this._register(new MutableDisposable<IStatusbarEntryAccessor>());
|
||||
@@ -402,6 +417,22 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
|
||||
}
|
||||
}
|
||||
|
||||
private updateColumnSelectionModeElement(visible: boolean): void {
|
||||
if (visible) {
|
||||
if (!this.columnSelectionModeElement.value) {
|
||||
this.columnSelectionModeElement.value = this.statusbarService.addEntry({
|
||||
text: nls.localize('columnSelectionModeEnabled', "Column Selection"),
|
||||
tooltip: nls.localize('disableColumnSelectionMode', "Disable Column Selection Mode"),
|
||||
command: 'editor.action.toggleColumnSelection',
|
||||
backgroundColor: themeColorFromId(STATUS_BAR_PROMINENT_ITEM_BACKGROUND),
|
||||
color: themeColorFromId(STATUS_BAR_PROMINENT_ITEM_FOREGROUND)
|
||||
}, 'status.editor.columnSelectionMode', nls.localize('status.editor.columnSelectionMode', "Column Selection Mode"), StatusbarAlignment.RIGHT, 100.8);
|
||||
}
|
||||
} else {
|
||||
this.columnSelectionModeElement.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private updateScreenReaderModeElement(visible: boolean): void {
|
||||
if (visible) {
|
||||
if (!this.screenRedearModeElement.value) {
|
||||
@@ -544,6 +575,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
|
||||
|
||||
private doRenderNow(changed: StateChange): void {
|
||||
this.updateTabFocusModeElement(!!this.state.tabFocusMode);
|
||||
this.updateColumnSelectionModeElement(!!this.state.columnSelectionMode);
|
||||
this.updateScreenReaderModeElement(!!this.state.screenReaderMode);
|
||||
this.updateIndentationElement(this.state.indentation);
|
||||
this.updateSelectionElement(this.state.selectionStatus && !this.state.screenReaderMode ? this.state.selectionStatus : undefined);
|
||||
@@ -583,6 +615,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
|
||||
const activeCodeEditor = activeControl ? withNullAsUndefined(getCodeEditor(activeControl.getControl())) : undefined;
|
||||
|
||||
// Update all states
|
||||
this.onColumnSelectionModeChange(activeCodeEditor);
|
||||
this.onScreenReaderModeChange(activeCodeEditor);
|
||||
this.onSelectionChange(activeCodeEditor);
|
||||
this.onModeChange(activeCodeEditor, activeInput);
|
||||
@@ -600,6 +633,9 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
|
||||
|
||||
// Hook Listener for Configuration changes
|
||||
this.activeEditorListeners.add(activeCodeEditor.onDidChangeConfiguration((event: ConfigurationChangedEvent) => {
|
||||
if (event.hasChanged(EditorOption.columnSelection)) {
|
||||
this.onColumnSelectionModeChange(activeCodeEditor);
|
||||
}
|
||||
if (event.hasChanged(EditorOption.accessibilitySupport)) {
|
||||
this.onScreenReaderModeChange(activeCodeEditor);
|
||||
}
|
||||
@@ -668,14 +704,14 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
|
||||
}
|
||||
|
||||
private onModeChange(editorWidget: ICodeEditor | undefined, editorInput: IEditorInput | undefined): void {
|
||||
let info: StateDelta = { mode: undefined };
|
||||
let info: StateDelta = { type: 'mode', mode: undefined };
|
||||
|
||||
// We only support text based editors
|
||||
if (editorWidget && editorInput && toEditorWithModeSupport(editorInput)) {
|
||||
const textModel = editorWidget.getModel();
|
||||
if (textModel) {
|
||||
const modeId = textModel.getLanguageIdentifier().language;
|
||||
info = { mode: withNullAsUndefined(this.modeService.getLanguageName(modeId)) };
|
||||
info.mode = withNullAsUndefined(this.modeService.getLanguageName(modeId));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -683,7 +719,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
|
||||
}
|
||||
|
||||
private onIndentationChange(editorWidget: ICodeEditor | undefined): void {
|
||||
const update: StateDelta = { indentation: undefined };
|
||||
const update: StateDelta = { type: 'indentation', indentation: undefined };
|
||||
|
||||
if (editorWidget) {
|
||||
const model = editorWidget.getModel();
|
||||
@@ -701,7 +737,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
|
||||
}
|
||||
|
||||
private onMetadataChange(editor: IBaseEditor | undefined): void {
|
||||
const update: StateDelta = { metadata: undefined };
|
||||
const update: StateDelta = { type: 'metadata', metadata: undefined };
|
||||
|
||||
if (editor instanceof BaseBinaryResourceEditor || editor instanceof BinaryResourceDiffEditor) {
|
||||
update.metadata = editor.getMetadata();
|
||||
@@ -710,6 +746,16 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
|
||||
this.updateState(update);
|
||||
}
|
||||
|
||||
private onColumnSelectionModeChange(editorWidget: ICodeEditor | undefined): void {
|
||||
const info: StateDelta = { type: 'columnSelectionMode', columnSelectionMode: false };
|
||||
|
||||
if (editorWidget && editorWidget.getOption(EditorOption.columnSelection)) {
|
||||
info.columnSelectionMode = true;
|
||||
}
|
||||
|
||||
this.updateState(info);
|
||||
}
|
||||
|
||||
private onScreenReaderModeChange(editorWidget: ICodeEditor | undefined): void {
|
||||
let screenReaderMode = false;
|
||||
|
||||
@@ -733,7 +779,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
|
||||
this.screenReaderNotification.close();
|
||||
}
|
||||
|
||||
this.updateState({ screenReaderMode: screenReaderMode });
|
||||
this.updateState({ type: 'screenReaderMode', screenReaderMode: screenReaderMode });
|
||||
}
|
||||
|
||||
private onSelectionChange(editorWidget: ICodeEditor | undefined): void {
|
||||
@@ -773,11 +819,11 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
|
||||
}
|
||||
}
|
||||
|
||||
this.updateState({ selectionStatus: this.getSelectionLabel(info) });
|
||||
this.updateState({ type: 'selectionStatus', selectionStatus: this.getSelectionLabel(info) });
|
||||
}
|
||||
|
||||
private onEOLChange(editorWidget: ICodeEditor | undefined): void {
|
||||
const info: StateDelta = { EOL: undefined };
|
||||
const info: StateDelta = { type: 'EOL', EOL: undefined };
|
||||
|
||||
if (editorWidget && !editorWidget.getOption(EditorOption.readOnly)) {
|
||||
const codeEditorModel = editorWidget.getModel();
|
||||
@@ -794,7 +840,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
|
||||
return;
|
||||
}
|
||||
|
||||
const info: StateDelta = { encoding: undefined };
|
||||
const info: StateDelta = { type: 'encoding', encoding: undefined };
|
||||
|
||||
// We only support text based editors that have a model associated
|
||||
// This ensures we do not show the encoding picker while an editor
|
||||
@@ -828,7 +874,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
|
||||
}
|
||||
|
||||
private onTabFocusModeChange(): void {
|
||||
const info: StateDelta = { tabFocusMode: TabFocus.getTabFocusMode() };
|
||||
const info: StateDelta = { type: 'tabFocusMode', tabFocusMode: TabFocus.getTabFocusMode() };
|
||||
|
||||
this.updateState(info);
|
||||
}
|
||||
|
||||
@@ -26,10 +26,8 @@ export class ClearNotificationAction extends Action {
|
||||
super(id, label, 'codicon-close');
|
||||
}
|
||||
|
||||
run(notification: INotificationViewItem): Promise<any> {
|
||||
async run(notification: INotificationViewItem): Promise<any> {
|
||||
this.commandService.executeCommand(CLEAR_NOTIFICATION, notification);
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,10 +44,8 @@ export class ClearAllNotificationsAction extends Action {
|
||||
super(id, label, 'codicon-clear-all');
|
||||
}
|
||||
|
||||
run(notification: INotificationViewItem): Promise<any> {
|
||||
async run(notification: INotificationViewItem): Promise<any> {
|
||||
this.commandService.executeCommand(CLEAR_ALL_NOTIFICATIONS);
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,13 +59,11 @@ export class HideNotificationsCenterAction extends Action {
|
||||
label: string,
|
||||
@ICommandService private readonly commandService: ICommandService
|
||||
) {
|
||||
super(id, label, 'codicon-close');
|
||||
super(id, label, 'codicon-chevron-down');
|
||||
}
|
||||
|
||||
run(notification: INotificationViewItem): Promise<any> {
|
||||
async run(notification: INotificationViewItem): Promise<any> {
|
||||
this.commandService.executeCommand(HIDE_NOTIFICATIONS_CENTER);
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,10 +80,8 @@ export class ExpandNotificationAction extends Action {
|
||||
super(id, label, 'codicon-chevron-up');
|
||||
}
|
||||
|
||||
run(notification: INotificationViewItem): Promise<any> {
|
||||
async run(notification: INotificationViewItem): Promise<any> {
|
||||
this.commandService.executeCommand(EXPAND_NOTIFICATION, notification);
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,10 +98,8 @@ export class CollapseNotificationAction extends Action {
|
||||
super(id, label, 'codicon-chevron-down');
|
||||
}
|
||||
|
||||
run(notification: INotificationViewItem): Promise<any> {
|
||||
async run(notification: INotificationViewItem): Promise<any> {
|
||||
this.commandService.executeCommand(COLLAPSE_NOTIFICATION, notification);
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -100,6 +100,9 @@ export class NotificationsCenter extends Themable implements INotificationsCente
|
||||
// Theming
|
||||
this.updateStyles();
|
||||
|
||||
// Mark as visible
|
||||
this.model.notifications.forEach(notification => notification.updateVisibility(true));
|
||||
|
||||
// Context Key
|
||||
this.notificationsCenterVisibleContextKey.set(true);
|
||||
|
||||
@@ -115,7 +118,7 @@ export class NotificationsCenter extends Themable implements INotificationsCente
|
||||
clearAllAction.enabled = false;
|
||||
} else {
|
||||
notificationsCenterTitle.textContent = localize('notifications', "Notifications");
|
||||
clearAllAction.enabled = true;
|
||||
clearAllAction.enabled = this.model.notifications.some(notification => !notification.hasProgress);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,20 +175,22 @@ export class NotificationsCenter extends Themable implements INotificationsCente
|
||||
return; // only if visible
|
||||
}
|
||||
|
||||
let focusGroup = false;
|
||||
let focusEditor = false;
|
||||
|
||||
// Update notifications list based on event
|
||||
const [notificationsList, notificationsCenterContainer] = assertAllDefined(this.notificationsList, this.notificationsCenterContainer);
|
||||
switch (e.kind) {
|
||||
case NotificationChangeType.ADD:
|
||||
notificationsList.updateNotificationsList(e.index, 0, [e.item]);
|
||||
e.item.updateVisibility(true);
|
||||
break;
|
||||
case NotificationChangeType.CHANGE:
|
||||
notificationsList.updateNotificationsList(e.index, 1, [e.item]);
|
||||
break;
|
||||
case NotificationChangeType.REMOVE:
|
||||
focusGroup = isAncestor(document.activeElement, notificationsCenterContainer);
|
||||
focusEditor = isAncestor(document.activeElement, notificationsCenterContainer);
|
||||
notificationsList.updateNotificationsList(e.index, 1);
|
||||
e.item.updateVisibility(false);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -197,7 +202,7 @@ export class NotificationsCenter extends Themable implements INotificationsCente
|
||||
this.hide();
|
||||
|
||||
// Restore focus to editor group if we had focus
|
||||
if (focusGroup) {
|
||||
if (focusEditor) {
|
||||
this.editorGroupService.activeGroup.focus();
|
||||
}
|
||||
}
|
||||
@@ -208,13 +213,16 @@ export class NotificationsCenter extends Themable implements INotificationsCente
|
||||
return; // already hidden
|
||||
}
|
||||
|
||||
const focusGroup = isAncestor(document.activeElement, this.notificationsCenterContainer);
|
||||
const focusEditor = isAncestor(document.activeElement, this.notificationsCenterContainer);
|
||||
|
||||
// Hide
|
||||
this._isVisible = false;
|
||||
removeClass(this.notificationsCenterContainer, 'visible');
|
||||
this.notificationsList.hide();
|
||||
|
||||
// Mark as hidden
|
||||
this.model.notifications.forEach(notification => notification.updateVisibility(false));
|
||||
|
||||
// Context Key
|
||||
this.notificationsCenterVisibleContextKey.set(false);
|
||||
|
||||
@@ -222,7 +230,7 @@ export class NotificationsCenter extends Themable implements INotificationsCente
|
||||
this._onDidChangeVisibility.fire();
|
||||
|
||||
// Restore focus to editor group if we had focus
|
||||
if (focusGroup) {
|
||||
if (focusEditor) {
|
||||
this.editorGroupService.activeGroup.focus();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,6 +75,7 @@ export function registerNotificationCommands(center: INotificationsCenterControl
|
||||
|
||||
// Show Notifications Cneter
|
||||
CommandsRegistry.registerCommand(SHOW_NOTIFICATIONS_CENTER, () => {
|
||||
toasts.hide();
|
||||
center.show();
|
||||
});
|
||||
|
||||
@@ -92,6 +93,7 @@ export function registerNotificationCommands(center: INotificationsCenterControl
|
||||
if (center.isVisible) {
|
||||
center.hide();
|
||||
} else {
|
||||
toasts.hide();
|
||||
center.show();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -46,7 +46,7 @@ export class NotificationsStatus extends Disposable {
|
||||
if (!this.isNotificationsCenterVisible) {
|
||||
if (e.kind === NotificationChangeType.ADD) {
|
||||
this.newNotificationsCount++;
|
||||
} else if (e.kind === NotificationChangeType.REMOVE) {
|
||||
} else if (e.kind === NotificationChangeType.REMOVE && this.newNotificationsCount > 0) {
|
||||
this.newNotificationsCount--;
|
||||
}
|
||||
}
|
||||
@@ -69,8 +69,9 @@ export class NotificationsStatus extends Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
// Show the bell with a dot if there are unread or in-progress notifications
|
||||
const statusProperties: IStatusbarEntry = {
|
||||
text: `${this.newNotificationsCount === 0 ? '$(bell)' : '$(bell-dot)'}${notificationsInProgress > 0 ? ' $(sync~spin)' : ''}`,
|
||||
text: `${notificationsInProgress > 0 || this.newNotificationsCount > 0 ? '$(bell-dot)' : '$(bell)'}`,
|
||||
command: this.isNotificationsCenterVisible ? HIDE_NOTIFICATIONS_CENTER : SHOW_NOTIFICATIONS_CENTER,
|
||||
tooltip: this.getTooltip(notificationsInProgress),
|
||||
showBeak: this.isNotificationsCenterVisible
|
||||
|
||||
@@ -179,11 +179,8 @@ export class NotificationsToasts extends Themable implements INotificationsToast
|
||||
const toast: INotificationToast = { item, list: notificationList, container: notificationToastContainer, toast: notificationToast, toDispose: itemDisposables };
|
||||
this.mapNotificationToToast.set(item, toast);
|
||||
|
||||
itemDisposables.add(toDisposable(() => {
|
||||
if (this.isToastVisible(toast) && notificationsToastsContainer) {
|
||||
notificationsToastsContainer.removeChild(toast.container);
|
||||
}
|
||||
}));
|
||||
// When disposed, remove as visible
|
||||
itemDisposables.add(toDisposable(() => this.updateToastVisibility(toast, false)));
|
||||
|
||||
// Make visible
|
||||
notificationList.show();
|
||||
@@ -236,6 +233,9 @@ export class NotificationsToasts extends Themable implements INotificationsToast
|
||||
addClass(notificationToast, 'notification-fade-in-done');
|
||||
}));
|
||||
|
||||
// Mark as visible
|
||||
item.updateVisibility(true);
|
||||
|
||||
// Events
|
||||
if (!this._isVisible) {
|
||||
this._isVisible = true;
|
||||
@@ -292,12 +292,13 @@ export class NotificationsToasts extends Themable implements INotificationsToast
|
||||
}
|
||||
|
||||
private removeToast(item: INotificationViewItem): void {
|
||||
let focusEditor = false;
|
||||
|
||||
const notificationToast = this.mapNotificationToToast.get(item);
|
||||
let focusGroup = false;
|
||||
if (notificationToast) {
|
||||
const toastHasDOMFocus = isAncestor(document.activeElement, notificationToast.container);
|
||||
if (toastHasDOMFocus) {
|
||||
focusGroup = !(this.focusNext() || this.focusPrevious()); // focus next if any, otherwise focus editor
|
||||
focusEditor = !(this.focusNext() || this.focusPrevious()); // focus next if any, otherwise focus editor
|
||||
}
|
||||
|
||||
// Listeners
|
||||
@@ -317,7 +318,7 @@ export class NotificationsToasts extends Themable implements INotificationsToast
|
||||
this.doHide();
|
||||
|
||||
// Move focus back to editor group as needed
|
||||
if (focusGroup) {
|
||||
if (focusEditor) {
|
||||
this.editorGroupService.activeGroup.focus();
|
||||
}
|
||||
}
|
||||
@@ -346,11 +347,11 @@ export class NotificationsToasts extends Themable implements INotificationsToast
|
||||
}
|
||||
|
||||
hide(): void {
|
||||
const focusGroup = this.notificationsToastsContainer ? isAncestor(document.activeElement, this.notificationsToastsContainer) : false;
|
||||
const focusEditor = this.notificationsToastsContainer ? isAncestor(document.activeElement, this.notificationsToastsContainer) : false;
|
||||
|
||||
this.removeToasts();
|
||||
|
||||
if (focusGroup) {
|
||||
if (focusEditor) {
|
||||
this.editorGroupService.activeGroup.focus();
|
||||
}
|
||||
}
|
||||
@@ -459,12 +460,12 @@ export class NotificationsToasts extends Themable implements INotificationsToast
|
||||
notificationToasts.push(toast);
|
||||
break;
|
||||
case ToastVisibility.HIDDEN:
|
||||
if (!this.isToastVisible(toast)) {
|
||||
if (!this.isToastInDOM(toast)) {
|
||||
notificationToasts.push(toast);
|
||||
}
|
||||
break;
|
||||
case ToastVisibility.VISIBLE:
|
||||
if (this.isToastVisible(toast)) {
|
||||
if (this.isToastInDOM(toast)) {
|
||||
notificationToasts.push(toast);
|
||||
}
|
||||
break;
|
||||
@@ -530,7 +531,7 @@ export class NotificationsToasts extends Themable implements INotificationsToast
|
||||
|
||||
// In order to measure the client height, the element cannot have display: none
|
||||
toast.container.style.opacity = '0';
|
||||
this.setVisibility(toast, true);
|
||||
this.updateToastVisibility(toast, true);
|
||||
|
||||
heightToGive -= toast.container.offsetHeight;
|
||||
|
||||
@@ -542,7 +543,7 @@ export class NotificationsToasts extends Themable implements INotificationsToast
|
||||
}
|
||||
|
||||
// Hide or show toast based on context
|
||||
this.setVisibility(toast, makeVisible);
|
||||
this.updateToastVisibility(toast, makeVisible);
|
||||
toast.container.style.opacity = '';
|
||||
|
||||
if (makeVisible) {
|
||||
@@ -551,20 +552,24 @@ export class NotificationsToasts extends Themable implements INotificationsToast
|
||||
});
|
||||
}
|
||||
|
||||
private setVisibility(toast: INotificationToast, visible: boolean): void {
|
||||
if (this.isToastVisible(toast) === visible) {
|
||||
private updateToastVisibility(toast: INotificationToast, visible: boolean): void {
|
||||
if (this.isToastInDOM(toast) === visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update visibility in DOM
|
||||
const notificationsToastsContainer = assertIsDefined(this.notificationsToastsContainer);
|
||||
if (visible) {
|
||||
notificationsToastsContainer.appendChild(toast.container);
|
||||
} else {
|
||||
notificationsToastsContainer.removeChild(toast.container);
|
||||
}
|
||||
|
||||
// Update visibility in model
|
||||
toast.item.updateVisibility(visible);
|
||||
}
|
||||
|
||||
private isToastVisible(toast: INotificationToast): boolean {
|
||||
private isToastInDOM(toast: INotificationToast): boolean {
|
||||
return !!toast.container.parentElement;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +62,10 @@
|
||||
|
||||
}
|
||||
|
||||
.monaco-workbench .part.panel > .composite.title > .composite-bar-excess {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.panel > .title > .panel-switcher-container > .monaco-action-bar {
|
||||
line-height: 27px; /* matches panel titles in settings */
|
||||
height: 35px;
|
||||
|
||||
@@ -36,6 +36,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten
|
||||
import { ViewContainer, IViewContainersRegistry, Extensions as ViewContainerExtensions, IViewDescriptorService, IViewDescriptorCollection, ViewContainerLocation } from 'vs/workbench/common/views';
|
||||
import { MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { ViewMenuActions } from 'vs/workbench/browser/parts/views/viewMenuActions';
|
||||
import { IPaneComposite } from 'vs/workbench/common/panecomposite';
|
||||
|
||||
interface ICachedPanel {
|
||||
id: string;
|
||||
@@ -143,7 +144,7 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
|
||||
getDefaultCompositeId: () => this.panelRegistry.getDefaultPanelId(),
|
||||
hidePart: () => this.layoutService.setPanelHidden(true),
|
||||
dndHandler: new CompositeDragAndDrop(this.viewDescriptorService, ViewContainerLocation.Panel,
|
||||
(id: string, focus?: boolean) => this.openPanel(id, focus),
|
||||
(id: string, focus?: boolean) => (<unknown>this.openPanel(id, focus)) as Promise<IPaneComposite | undefined>, // {{SQL CARBON EDIT}} strict-null-check
|
||||
(from: string, to: string) => this.compositeBar.move(from, to),
|
||||
() => this.getPinnedPanels().map(p => p.id)
|
||||
),
|
||||
|
||||
@@ -255,7 +255,10 @@ export abstract class ViewPane extends Pane implements IView {
|
||||
this._onDidFocus.fire();
|
||||
}));
|
||||
this._register(focusTracker.onDidBlur(() => {
|
||||
this.focusedViewContextKey.reset();
|
||||
if (this.focusedViewContextKey.get() === this.id) {
|
||||
this.focusedViewContextKey.reset();
|
||||
}
|
||||
|
||||
this._onDidBlur.fire();
|
||||
}));
|
||||
}
|
||||
@@ -1006,7 +1009,8 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
|
||||
}
|
||||
if (!this.areExtensionsReady) {
|
||||
if (this.visibleViewsCountFromCache === undefined) {
|
||||
return true;
|
||||
// TODO @sbatten fix hack for #91367
|
||||
return this.viewDescriptorService.getViewContainerLocation(this.viewContainer) === ViewContainerLocation.Panel;
|
||||
}
|
||||
// Check in cache so that view do not jump. See #29609
|
||||
return this.visibleViewsCountFromCache === 1;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { INotification, INotificationHandle, INotificationActions, INotificationProgress, NoOpNotification, Severity, NotificationMessage, IPromptChoice, IStatusMessageOptions, NotificationsFilter } from 'vs/platform/notification/common/notification';
|
||||
import { INotification, INotificationHandle, INotificationActions, INotificationProgress, NoOpNotification, Severity, NotificationMessage, IPromptChoice, IStatusMessageOptions, NotificationsFilter, INotificationProgressProperties } from 'vs/platform/notification/common/notification';
|
||||
import { toErrorMessage } from 'vs/base/common/errorMessage';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
@@ -92,6 +92,9 @@ export class NotificationHandle extends Disposable implements INotificationHandl
|
||||
private readonly _onDidClose = this._register(new Emitter<void>());
|
||||
readonly onDidClose = this._onDidClose.event;
|
||||
|
||||
private readonly _onDidChangeVisibility = this._register(new Emitter<boolean>());
|
||||
readonly onDidChangeVisibility = this._onDidChangeVisibility.event;
|
||||
|
||||
constructor(private readonly item: INotificationViewItem, private readonly onClose: (item: INotificationViewItem) => void) {
|
||||
super();
|
||||
|
||||
@@ -99,6 +102,11 @@ export class NotificationHandle extends Disposable implements INotificationHandl
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
|
||||
// Visibility
|
||||
this._register(this.item.onDidChangeVisibility(visible => this._onDidChangeVisibility.fire(visible)));
|
||||
|
||||
// Closing
|
||||
Event.once(this.item.onDidClose)(() => {
|
||||
this._onDidClose.fire();
|
||||
|
||||
@@ -265,6 +273,7 @@ export interface INotificationViewItem {
|
||||
|
||||
readonly onDidChangeExpansion: Event<void>;
|
||||
readonly onDidClose: Event<void>;
|
||||
readonly onDidChangeVisibility: Event<boolean>;
|
||||
readonly onDidChangeLabel: Event<INotificationViewItemLabelChangeEvent>;
|
||||
|
||||
expand(): void;
|
||||
@@ -275,6 +284,8 @@ export interface INotificationViewItem {
|
||||
updateMessage(message: NotificationMessage): void;
|
||||
updateActions(actions?: INotificationActions): void;
|
||||
|
||||
updateVisibility(visible: boolean): void;
|
||||
|
||||
close(): void;
|
||||
|
||||
equals(item: INotificationViewItem): boolean;
|
||||
@@ -398,6 +409,7 @@ export class NotificationViewItem extends Disposable implements INotificationVie
|
||||
private static readonly MAX_MESSAGE_LENGTH = 1000;
|
||||
|
||||
private _expanded: boolean | undefined;
|
||||
private _visible: boolean = false;
|
||||
|
||||
private _actions: INotificationActions | undefined;
|
||||
private _progress: NotificationViewItemProgress | undefined;
|
||||
@@ -411,6 +423,9 @@ export class NotificationViewItem extends Disposable implements INotificationVie
|
||||
private readonly _onDidChangeLabel = this._register(new Emitter<INotificationViewItemLabelChangeEvent>());
|
||||
readonly onDidChangeLabel = this._onDidChangeLabel.event;
|
||||
|
||||
private readonly _onDidChangeVisibility = this._register(new Emitter<boolean>());
|
||||
readonly onDidChangeVisibility = this._onDidChangeVisibility.event;
|
||||
|
||||
static create(notification: INotification, filter: NotificationsFilter = NotificationsFilter.OFF): INotificationViewItem | undefined {
|
||||
if (!notification || !notification.message || isPromiseCanceledError(notification.message)) {
|
||||
return undefined; // we need a message to show
|
||||
@@ -435,7 +450,7 @@ export class NotificationViewItem extends Disposable implements INotificationVie
|
||||
actions = { primary: notification.message.actions };
|
||||
}
|
||||
|
||||
return new NotificationViewItem(severity, notification.sticky, notification.silent || filter === NotificationsFilter.SILENT || (filter === NotificationsFilter.ERROR && notification.severity !== Severity.Error), message, notification.source, actions);
|
||||
return new NotificationViewItem(severity, notification.sticky, notification.silent || filter === NotificationsFilter.SILENT || (filter === NotificationsFilter.ERROR && notification.severity !== Severity.Error), message, notification.source, notification.progress, actions);
|
||||
}
|
||||
|
||||
private static parseNotificationMessage(input: NotificationMessage): INotificationMessage | undefined {
|
||||
@@ -472,13 +487,30 @@ export class NotificationViewItem extends Disposable implements INotificationVie
|
||||
private _silent: boolean | undefined,
|
||||
private _message: INotificationMessage,
|
||||
private _source: string | undefined,
|
||||
progress: INotificationProgressProperties | undefined,
|
||||
actions?: INotificationActions
|
||||
) {
|
||||
super();
|
||||
|
||||
if (progress) {
|
||||
this.setProgress(progress);
|
||||
}
|
||||
|
||||
this.setActions(actions);
|
||||
}
|
||||
|
||||
private setProgress(progress: INotificationProgressProperties): void {
|
||||
if (progress.infinite) {
|
||||
this.progress.infinite();
|
||||
} else if (progress.total) {
|
||||
this.progress.total(progress.total);
|
||||
|
||||
if (progress.worked) {
|
||||
this.progress.worked(progress.worked);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private setActions(actions: INotificationActions = { primary: [], secondary: [] }): void {
|
||||
if (!Array.isArray(actions.primary)) {
|
||||
actions.primary = [];
|
||||
@@ -583,6 +615,14 @@ export class NotificationViewItem extends Disposable implements INotificationVie
|
||||
this._onDidChangeLabel.fire({ kind: NotificationViewItemLabelKind.ACTIONS });
|
||||
}
|
||||
|
||||
updateVisibility(visible: boolean): void {
|
||||
if (this._visible !== visible) {
|
||||
this._visible = visible;
|
||||
|
||||
this._onDidChangeVisibility.fire(visible);
|
||||
}
|
||||
}
|
||||
|
||||
expand(): void {
|
||||
if (this._expanded || !this.canCollapse) {
|
||||
return;
|
||||
|
||||
@@ -17,7 +17,7 @@ import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IKeybindings } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { IAction, IActionViewItem } from 'vs/base/common/actions';
|
||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { flatten } from 'vs/base/common/arrays';
|
||||
import { flatten, mergeSort } from 'vs/base/common/arrays';
|
||||
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
import { SetMap } from 'vs/base/common/collections';
|
||||
|
||||
@@ -53,6 +53,7 @@ export interface IViewContainerDescriptor {
|
||||
|
||||
readonly extensionId?: ExtensionIdentifier;
|
||||
|
||||
readonly rejectAddedViews?: boolean;
|
||||
}
|
||||
|
||||
export interface IViewContainersRegistry {
|
||||
@@ -211,9 +212,16 @@ export interface IViewDescriptorCollection extends IDisposable {
|
||||
readonly allViewDescriptors: IViewDescriptor[];
|
||||
}
|
||||
|
||||
export enum ViewContentPriority {
|
||||
Normal = 0,
|
||||
Low = 1,
|
||||
Lowest = 2
|
||||
}
|
||||
|
||||
export interface IViewContentDescriptor {
|
||||
readonly content: string;
|
||||
readonly when?: ContextKeyExpr | 'default';
|
||||
readonly priority?: ViewContentPriority;
|
||||
|
||||
/**
|
||||
* ordered preconditions for each button in the content
|
||||
@@ -247,6 +255,13 @@ export interface IViewsRegistry {
|
||||
}
|
||||
|
||||
function compareViewContentDescriptors(a: IViewContentDescriptor, b: IViewContentDescriptor): number {
|
||||
const aPriority = a.priority ?? ViewContentPriority.Normal;
|
||||
const bPriority = b.priority ?? ViewContentPriority.Normal;
|
||||
|
||||
if (aPriority !== bPriority) {
|
||||
return aPriority - bPriority;
|
||||
}
|
||||
|
||||
return a.content < b.content ? -1 : 1;
|
||||
}
|
||||
|
||||
@@ -328,8 +343,8 @@ class ViewsRegistry extends Disposable implements IViewsRegistry {
|
||||
|
||||
getViewWelcomeContent(id: string): IViewContentDescriptor[] {
|
||||
const result: IViewContentDescriptor[] = [];
|
||||
result.sort(compareViewContentDescriptors);
|
||||
this._viewWelcomeContents.forEach(id, descriptor => result.push(descriptor));
|
||||
mergeSort(result, compareViewContentDescriptors);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -111,6 +111,7 @@ class BulkEditPreviewContribution {
|
||||
private async _previewEdit(edit: WorkspaceEdit) {
|
||||
this._ctxEnabled.set(true);
|
||||
|
||||
const uxState = this._activeSession?.uxState ?? new UXState(this._panelService, this._editorGroupsService);
|
||||
const view = await getBulkEditPane(this._viewsService);
|
||||
if (!view) {
|
||||
this._ctxEnabled.set(false);
|
||||
@@ -136,9 +137,9 @@ class BulkEditPreviewContribution {
|
||||
let session: PreviewSession;
|
||||
if (this._activeSession) {
|
||||
this._activeSession.cts.dispose(true);
|
||||
session = new PreviewSession(this._activeSession.uxState);
|
||||
session = new PreviewSession(uxState);
|
||||
} else {
|
||||
session = new PreviewSession(new UXState(this._panelService, this._editorGroupsService));
|
||||
session = new PreviewSession(uxState);
|
||||
}
|
||||
this._activeSession = session;
|
||||
|
||||
|
||||
@@ -10,6 +10,13 @@ import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configur
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions';
|
||||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { CoreNavigationCommands } from 'vs/editor/browser/controller/coreCommands';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { CursorColumns } from 'vs/editor/common/controller/cursorCommon';
|
||||
|
||||
export class ToggleColumnSelectionAction extends Action {
|
||||
public static readonly ID = 'editor.action.toggleColumnSelection';
|
||||
@@ -18,26 +25,68 @@ export class ToggleColumnSelectionAction extends Action {
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService,
|
||||
@ICodeEditorService private readonly _codeEditorService: ICodeEditorService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
public run(): Promise<any> {
|
||||
const newValue = !this._configurationService.getValue<boolean>('editor.columnSelection');
|
||||
return this._configurationService.updateValue('editor.columnSelection', newValue, ConfigurationTarget.USER);
|
||||
private _getCodeEditor(): ICodeEditor | null {
|
||||
const codeEditor = this._codeEditorService.getFocusedCodeEditor();
|
||||
if (codeEditor) {
|
||||
return codeEditor;
|
||||
}
|
||||
return this._codeEditorService.getActiveCodeEditor();
|
||||
}
|
||||
|
||||
public async run(): Promise<any> {
|
||||
const oldValue = this._configurationService.getValue<boolean>('editor.columnSelection');
|
||||
const codeEditor = this._getCodeEditor();
|
||||
await this._configurationService.updateValue('editor.columnSelection', !oldValue, ConfigurationTarget.USER);
|
||||
const newValue = this._configurationService.getValue<boolean>('editor.columnSelection');
|
||||
if (!codeEditor || codeEditor !== this._getCodeEditor() || oldValue === newValue || !codeEditor.hasModel()) {
|
||||
return;
|
||||
}
|
||||
const cursors = codeEditor._getCursors();
|
||||
if (codeEditor.getOption(EditorOption.columnSelection)) {
|
||||
const selection = codeEditor.getSelection();
|
||||
const modelSelectionStart = new Position(selection.selectionStartLineNumber, selection.selectionStartColumn);
|
||||
const viewSelectionStart = cursors.context.convertModelPositionToViewPosition(modelSelectionStart);
|
||||
const modelPosition = new Position(selection.positionLineNumber, selection.positionColumn);
|
||||
const viewPosition = cursors.context.convertModelPositionToViewPosition(modelPosition);
|
||||
|
||||
CoreNavigationCommands.MoveTo.runCoreEditorCommand(cursors, {
|
||||
position: modelSelectionStart,
|
||||
viewPosition: viewSelectionStart
|
||||
});
|
||||
const visibleColumn = CursorColumns.visibleColumnFromColumn2(cursors.context.config, cursors.context.viewModel, viewPosition);
|
||||
CoreNavigationCommands.ColumnSelect.runCoreEditorCommand(cursors, {
|
||||
position: modelPosition,
|
||||
viewPosition: viewPosition,
|
||||
doColumnSelect: true,
|
||||
mouseColumn: visibleColumn + 1
|
||||
});
|
||||
} else {
|
||||
const columnSelectData = cursors.getColumnSelectData();
|
||||
const fromViewColumn = CursorColumns.columnFromVisibleColumn2(cursors.context.config, cursors.context.viewModel, columnSelectData.fromViewLineNumber, columnSelectData.fromViewVisualColumn);
|
||||
const fromPosition = cursors.context.convertViewPositionToModelPosition(columnSelectData.fromViewLineNumber, fromViewColumn);
|
||||
const toViewColumn = CursorColumns.columnFromVisibleColumn2(cursors.context.config, cursors.context.viewModel, columnSelectData.toViewLineNumber, columnSelectData.toViewVisualColumn);
|
||||
const toPosition = cursors.context.convertViewPositionToModelPosition(columnSelectData.toViewLineNumber, toViewColumn);
|
||||
|
||||
codeEditor.setSelection(new Selection(fromPosition.lineNumber, fromPosition.column, toPosition.lineNumber, toPosition.column));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const registry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleColumnSelectionAction, ToggleColumnSelectionAction.ID, ToggleColumnSelectionAction.LABEL), 'View: Toggle Column Selection Mode', nls.localize('view', "View"));
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleColumnSelectionAction, ToggleColumnSelectionAction.ID, ToggleColumnSelectionAction.LABEL), 'Toggle Column Selection Mode');
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarSelectionMenu, {
|
||||
group: '3_multi',
|
||||
group: '4_config',
|
||||
command: {
|
||||
id: ToggleColumnSelectionAction.ID,
|
||||
title: nls.localize({ key: 'miColumnSelection', comment: ['&& denotes a mnemonic'] }, "Column &&Selection Mode"),
|
||||
toggled: ContextKeyExpr.equals('config.editor.columnSelection', true)
|
||||
},
|
||||
order: 1.5
|
||||
order: 2
|
||||
});
|
||||
|
||||
@@ -70,7 +70,7 @@ Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).regi
|
||||
const registry = Registry.as<IWorkbenchActionRegistry>(Extensions.WorkbenchActions);
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleMultiCursorModifierAction, ToggleMultiCursorModifierAction.ID, ToggleMultiCursorModifierAction.LABEL), 'Toggle Multi-Cursor Modifier');
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarSelectionMenu, {
|
||||
group: '3_multi',
|
||||
group: '4_config',
|
||||
command: {
|
||||
id: ToggleMultiCursorModifierAction.ID,
|
||||
title: nls.localize('miMultiCursorAlt', "Switch to Alt+Click for Multi-Cursor")
|
||||
@@ -79,7 +79,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarSelectionMenu, {
|
||||
order: 1
|
||||
});
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarSelectionMenu, {
|
||||
group: '3_multi',
|
||||
group: '4_config',
|
||||
command: {
|
||||
id: ToggleMultiCursorModifierAction.ID,
|
||||
title: (
|
||||
|
||||
@@ -20,10 +20,7 @@ import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
|
||||
export class SelectionClipboard extends Disposable implements IEditorContribution {
|
||||
private static readonly SELECTION_LENGTH_LIMIT = 65536;
|
||||
@@ -119,15 +116,7 @@ class PasteSelectionClipboardAction extends EditorAction {
|
||||
id: 'editor.action.selectionClipboardPaste',
|
||||
label: nls.localize('actions.pasteSelectionClipboard', "Paste Selection Clipboard"),
|
||||
alias: 'Paste Selection Clipboard',
|
||||
precondition: EditorContextKeys.writable,
|
||||
kbOpts: {
|
||||
kbExpr: ContextKeyExpr.and(
|
||||
EditorContextKeys.editorTextFocus,
|
||||
ContextKeyExpr.has('config.editor.selectionClipboard')
|
||||
),
|
||||
primary: KeyMod.Shift | KeyCode.Insert,
|
||||
weight: KeybindingWeight.EditorContrib
|
||||
}
|
||||
precondition: EditorContextKeys.writable
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -333,11 +333,7 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ
|
||||
}
|
||||
|
||||
const editorInfo = this._editorInfoStore.get(editor.viewType);
|
||||
if (!editorInfo) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!editorInfo.matches(newResource)) {
|
||||
if (!editorInfo?.matches(newResource)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ export interface ICustomEditorModel extends IWorkingCopy {
|
||||
readonly onWillSave: Event<CustomEditorSaveEvent>;
|
||||
readonly onWillSaveAs: Event<CustomEditorSaveAsEvent>;
|
||||
|
||||
onBackup(f: () => CancelablePromise<boolean>): void;
|
||||
onBackup(f: () => CancelablePromise<void>): void;
|
||||
|
||||
setDirty(dirty: boolean): void;
|
||||
undo(): void;
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace HotExitState {
|
||||
readonly type = Type.Pending;
|
||||
|
||||
constructor(
|
||||
public readonly operation: CancelablePromise<boolean>,
|
||||
public readonly operation: CancelablePromise<void>,
|
||||
) { }
|
||||
}
|
||||
|
||||
@@ -90,9 +90,9 @@ export class CustomEditorModel extends Disposable implements ICustomEditorModel
|
||||
private readonly _onWillSaveAs = this._register(new Emitter<CustomEditorSaveAsEvent>());
|
||||
public readonly onWillSaveAs = this._onWillSaveAs.event;
|
||||
|
||||
private _onBackup: undefined | (() => CancelablePromise<boolean>);
|
||||
private _onBackup: undefined | (() => CancelablePromise<void>);
|
||||
|
||||
public onBackup(f: () => CancelablePromise<boolean>) {
|
||||
public onBackup(f: () => CancelablePromise<void>) {
|
||||
if (this._onBackup) {
|
||||
throw new Error('Backup already implemented');
|
||||
}
|
||||
@@ -182,7 +182,11 @@ export class CustomEditorModel extends Disposable implements ICustomEditorModel
|
||||
this._hotExitState = pendingState;
|
||||
|
||||
try {
|
||||
this._hotExitState = await pendingState.operation ? HotExitState.Allowed : HotExitState.NotAllowed;
|
||||
await pendingState.operation;
|
||||
// Make sure state has not changed in the meantime
|
||||
if (this._hotExitState === pendingState) {
|
||||
this._hotExitState = HotExitState.Allowed;
|
||||
}
|
||||
} catch (e) {
|
||||
// Make sure state has not changed in the meantime
|
||||
if (this._hotExitState === pendingState) {
|
||||
|
||||
@@ -267,7 +267,7 @@ configurationRegistry.registerConfiguration({
|
||||
default: true
|
||||
},
|
||||
'debug.onTaskErrors': {
|
||||
enum: ['debugAnyway', 'showErrors', 'prompt', 'cancel'],
|
||||
enum: ['debugAnyway', 'showErrors', 'prompt', 'abort'],
|
||||
enumDescriptions: [nls.localize('debugAnyway', "Ignore task errors and start debugging."), nls.localize('showErrors', "Show the Problems view and do not start debugging."), nls.localize('prompt', "Prompt user."), nls.localize('cancel', "Cancel debugging.")],
|
||||
description: nls.localize('debug.onTaskErrors', "Controls what to do when errors are encountered after running a preLaunchTask."),
|
||||
default: 'prompt'
|
||||
|
||||
@@ -519,18 +519,17 @@ abstract class AbstractLaunch {
|
||||
if (!config || (!Array.isArray(config.configurations) && !Array.isArray(config.compounds))) {
|
||||
return [];
|
||||
} else {
|
||||
const names: string[] = [];
|
||||
const configurations: (IConfig | ICompound)[] = [];
|
||||
if (config.configurations) {
|
||||
names.push(...config.configurations.filter(cfg => cfg && typeof cfg.name === 'string').map(cfg => cfg.name));
|
||||
configurations.push(...config.configurations.filter(cfg => cfg && typeof cfg.name === 'string'));
|
||||
}
|
||||
if (includeCompounds && config.compounds) {
|
||||
if (config.compounds) {
|
||||
names.push(...config.compounds.filter(compound => typeof compound.name === 'string' && compound.configurations && compound.configurations.length)
|
||||
.map(compound => compound.name));
|
||||
configurations.push(...config.compounds.filter(compound => typeof compound.name === 'string' && compound.configurations && compound.configurations.length));
|
||||
}
|
||||
}
|
||||
|
||||
return names;
|
||||
return getVisibleAndSorted(configurations).map(c => c.name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -209,7 +209,7 @@ export class DebugHoverWidget implements IContentWidget {
|
||||
|
||||
if (!matchingExpression) {
|
||||
const lineContent = model.getLineContent(pos.lineNumber);
|
||||
matchingExpression = lineContent.substring(rng.startColumn - 1, rng.endColumn);
|
||||
matchingExpression = lineContent.substring(rng.startColumn - 1, rng.endColumn - 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ import { RawDebugSession } from 'vs/workbench/contrib/debug/browser/rawDebugSess
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { IWorkspaceFolder, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import { RunOnceScheduler, Queue } from 'vs/base/common/async';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { IHostService } from 'vs/workbench/services/host/browser/host';
|
||||
import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug';
|
||||
@@ -807,62 +807,58 @@ export class DebugSession implements IDebugSession {
|
||||
this._onDidChangeState.fire();
|
||||
}));
|
||||
|
||||
let outpuPromises: Promise<void>[] = [];
|
||||
const outputQueue = new Queue<void>();
|
||||
this.rawListeners.push(this.raw.onDidOutput(async event => {
|
||||
if (!event.body || !this.raw) {
|
||||
return;
|
||||
}
|
||||
|
||||
const outputSeverity = event.body.category === 'stderr' ? severity.Error : event.body.category === 'console' ? severity.Warning : severity.Info;
|
||||
if (event.body.category === 'telemetry') {
|
||||
// only log telemetry events from debug adapter if the debug extension provided the telemetry key
|
||||
// and the user opted in telemetry
|
||||
if (this.raw.customTelemetryService && this.telemetryService.isOptedIn) {
|
||||
// __GDPR__TODO__ We're sending events in the name of the debug extension and we can not ensure that those are declared correctly.
|
||||
this.raw.customTelemetryService.publicLog(event.body.output, event.body.data);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure to append output in the correct order by properly waiting on preivous promises #33822
|
||||
const waitFor = outpuPromises.slice();
|
||||
const source = event.body.source && event.body.line ? {
|
||||
lineNumber: event.body.line,
|
||||
column: event.body.column ? event.body.column : 1,
|
||||
source: this.getSource(event.body.source)
|
||||
} : undefined;
|
||||
|
||||
if (event.body.group === 'start' || event.body.group === 'startCollapsed') {
|
||||
const expanded = event.body.group === 'start';
|
||||
this.repl.startGroup(event.body.output || '', expanded, source);
|
||||
return;
|
||||
}
|
||||
if (event.body.group === 'end') {
|
||||
this.repl.endGroup();
|
||||
if (!event.body.output) {
|
||||
// Only return if the end event does not have additional output in it
|
||||
outputQueue.queue(async () => {
|
||||
if (!event.body || !this.raw) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (event.body.variablesReference) {
|
||||
const container = new ExpressionContainer(this, undefined, event.body.variablesReference, generateUuid());
|
||||
outpuPromises.push(container.getChildren().then(async children => {
|
||||
await Promise.all(waitFor);
|
||||
children.forEach(child => {
|
||||
// Since we can not display multiple trees in a row, we are displaying these variables one after the other (ignoring their names)
|
||||
(<any>child).name = null;
|
||||
this.appendToRepl(child, outputSeverity, source);
|
||||
const outputSeverity = event.body.category === 'stderr' ? severity.Error : event.body.category === 'console' ? severity.Warning : severity.Info;
|
||||
if (event.body.category === 'telemetry') {
|
||||
// only log telemetry events from debug adapter if the debug extension provided the telemetry key
|
||||
// and the user opted in telemetry
|
||||
if (this.raw.customTelemetryService && this.telemetryService.isOptedIn) {
|
||||
// __GDPR__TODO__ We're sending events in the name of the debug extension and we can not ensure that those are declared correctly.
|
||||
this.raw.customTelemetryService.publicLog(event.body.output, event.body.data);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure to append output in the correct order by properly waiting on preivous promises #33822
|
||||
const source = event.body.source && event.body.line ? {
|
||||
lineNumber: event.body.line,
|
||||
column: event.body.column ? event.body.column : 1,
|
||||
source: this.getSource(event.body.source)
|
||||
} : undefined;
|
||||
|
||||
if (event.body.group === 'start' || event.body.group === 'startCollapsed') {
|
||||
const expanded = event.body.group === 'start';
|
||||
this.repl.startGroup(event.body.output || '', expanded, source);
|
||||
return;
|
||||
}
|
||||
if (event.body.group === 'end') {
|
||||
this.repl.endGroup();
|
||||
if (!event.body.output) {
|
||||
// Only return if the end event does not have additional output in it
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (event.body.variablesReference) {
|
||||
const container = new ExpressionContainer(this, undefined, event.body.variablesReference, generateUuid());
|
||||
await container.getChildren().then(children => {
|
||||
children.forEach(child => {
|
||||
// Since we can not display multiple trees in a row, we are displaying these variables one after the other (ignoring their names)
|
||||
(<any>child).name = null;
|
||||
this.appendToRepl(child, outputSeverity, source);
|
||||
});
|
||||
});
|
||||
}));
|
||||
} else if (typeof event.body.output === 'string') {
|
||||
await Promise.all(waitFor);
|
||||
this.appendToRepl(event.body.output, outputSeverity, source);
|
||||
}
|
||||
|
||||
await Promise.all(outpuPromises);
|
||||
outpuPromises = [];
|
||||
} else if (typeof event.body.output === 'string') {
|
||||
this.appendToRepl(event.body.output, outputSeverity, source);
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
this.rawListeners.push(this.raw.onDidBreakpoint(event => {
|
||||
|
||||
@@ -75,35 +75,35 @@ export class StartView extends ViewPane {
|
||||
};
|
||||
this._register(editorService.onDidActiveEditorChange(setContextKey));
|
||||
this._register(this.debugService.getConfigurationManager().onDidRegisterDebugger(setContextKey));
|
||||
this.registerViews();
|
||||
setContextKey();
|
||||
|
||||
const debugKeybinding = this.keybindingService.lookupKeybinding(StartAction.ID);
|
||||
debugKeybindingLabel = debugKeybinding ? ` (${debugKeybinding.getLabel()})` : '';
|
||||
}
|
||||
|
||||
shouldShowWelcome(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
private registerViews(): void {
|
||||
const viewsRegistry = Registry.as<IViewsRegistry>(Extensions.ViewsRegistry);
|
||||
viewsRegistry.registerViewWelcomeContent(StartView.ID, {
|
||||
content: localize('openAFileWhichCanBeDebugged', "[Open a file](command:{0}) which can be debugged or run.", isMacintosh ? OpenFileFolderAction.ID : OpenFileAction.ID),
|
||||
when: CONTEXT_DEBUGGER_INTERESTED_IN_ACTIVE_EDITOR.toNegated()
|
||||
});
|
||||
|
||||
const debugKeybinding = this.keybindingService.lookupKeybinding(StartAction.ID);
|
||||
const debugKeybindingLabel = debugKeybinding ? ` (${debugKeybinding.getLabel()})` : '';
|
||||
viewsRegistry.registerViewWelcomeContent(StartView.ID, {
|
||||
content: localize('runAndDebugAction', "[Run and Debug{0}](command:{1})", debugKeybindingLabel, StartAction.ID),
|
||||
preconditions: [CONTEXT_DEBUGGER_INTERESTED_IN_ACTIVE_EDITOR]
|
||||
});
|
||||
|
||||
viewsRegistry.registerViewWelcomeContent(StartView.ID, {
|
||||
content: localize('customizeRunAndDebug', "To customize Run and Debug [create a launch.json file](command:{0}).", ConfigureAction.ID),
|
||||
when: WorkbenchStateContext.notEqualsTo('empty')
|
||||
});
|
||||
|
||||
viewsRegistry.registerViewWelcomeContent(StartView.ID, {
|
||||
content: localize('customizeRunAndDebugOpenFolder', "To customize Run and Debug, [open a folder](command:{0}) and create a launch.json file.", isMacintosh ? OpenFileFolderAction.ID : OpenFolderAction.ID),
|
||||
when: WorkbenchStateContext.isEqualTo('empty')
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const viewsRegistry = Registry.as<IViewsRegistry>(Extensions.ViewsRegistry);
|
||||
viewsRegistry.registerViewWelcomeContent(StartView.ID, {
|
||||
content: localize('openAFileWhichCanBeDebugged', "[Open a file](command:{0}) which can be debugged or run.", isMacintosh ? OpenFileFolderAction.ID : OpenFileAction.ID),
|
||||
when: CONTEXT_DEBUGGER_INTERESTED_IN_ACTIVE_EDITOR.toNegated()
|
||||
});
|
||||
|
||||
let debugKeybindingLabel = '';
|
||||
viewsRegistry.registerViewWelcomeContent(StartView.ID, {
|
||||
content: localize('runAndDebugAction', "[Run and Debug{0}](command:{1})", debugKeybindingLabel, StartAction.ID),
|
||||
preconditions: [CONTEXT_DEBUGGER_INTERESTED_IN_ACTIVE_EDITOR]
|
||||
});
|
||||
|
||||
viewsRegistry.registerViewWelcomeContent(StartView.ID, {
|
||||
content: localize('customizeRunAndDebug', "To customize Run and Debug [create a launch.json file](command:{0}).", ConfigureAction.ID),
|
||||
when: WorkbenchStateContext.notEqualsTo('empty')
|
||||
});
|
||||
|
||||
viewsRegistry.registerViewWelcomeContent(StartView.ID, {
|
||||
content: localize('customizeRunAndDebugOpenFolder', "To customize Run and Debug, [open a folder](command:{0}) and create a launch.json file.", isMacintosh ? OpenFileFolderAction.ID : OpenFolderAction.ID),
|
||||
when: WorkbenchStateContext.isEqualTo('empty')
|
||||
});
|
||||
|
||||
@@ -87,7 +87,8 @@ Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegis
|
||||
name: localize('extensions', "Extensions"),
|
||||
ctorDescriptor: new SyncDescriptor(ExtensionsViewPaneContainer),
|
||||
icon: 'codicon-extensions',
|
||||
order: 14 // {{SQL CARBON EDIT}}
|
||||
order: 14, // {{SQL CARBON EDIT}}
|
||||
rejectAddedViews: true,
|
||||
}, ViewContainerLocation.Sidebar);
|
||||
|
||||
|
||||
|
||||
@@ -2690,7 +2690,7 @@ export class SystemDisabledWarningAction extends ExtensionAction {
|
||||
if (server) {
|
||||
this.tooltip = localize('Install in other server to enable', "Install the extension on '{0}' to enable.", server.label);
|
||||
} else {
|
||||
this.tooltip = localize('disabled because of extension kind', "This extension cannot be enabled in the remote server.");
|
||||
this.tooltip = localize('disabled because of extension kind', "This extension has defined that it cannot run on the remote server");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -65,21 +65,21 @@ export class ExplorerViewletViewsContribution extends Disposable implements IWor
|
||||
private registerViews(): void {
|
||||
const viewsRegistry = Registry.as<IViewsRegistry>(Extensions.ViewsRegistry);
|
||||
|
||||
viewsRegistry.registerViewWelcomeContent(EmptyView.ID, {
|
||||
this._register(viewsRegistry.registerViewWelcomeContent(EmptyView.ID, {
|
||||
content: localize('noWorkspaceHelp', "You have not yet added a folder to the workspace.\n[Add Folder](command:{0})", AddRootFolderAction.ID),
|
||||
when: WorkbenchStateContext.isEqualTo('workspace')
|
||||
});
|
||||
}));
|
||||
|
||||
const commandId = isMacintosh ? OpenFileFolderAction.ID : OpenFolderAction.ID;
|
||||
viewsRegistry.registerViewWelcomeContent(EmptyView.ID, {
|
||||
this._register(viewsRegistry.registerViewWelcomeContent(EmptyView.ID, {
|
||||
content: localize('remoteNoFolderHelp', "Connected to remote.\n[Open Folder](command:{0})", commandId),
|
||||
when: ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('workspace'), RemoteNameContext.notEqualsTo(''), IsWebContext.toNegated())
|
||||
});
|
||||
}));
|
||||
|
||||
viewsRegistry.registerViewWelcomeContent(EmptyView.ID, {
|
||||
this._register(viewsRegistry.registerViewWelcomeContent(EmptyView.ID, {
|
||||
content: localize('noFolderHelp', "You have not yet opened a folder.\n[Open Folder](command:{0})", commandId),
|
||||
when: ContextKeyExpr.or(ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('workspace'), RemoteNameContext.isEqualTo('')), ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('workspace'), IsWebContext))
|
||||
});
|
||||
}));
|
||||
|
||||
const viewDescriptors = viewsRegistry.getViews(VIEW_CONTAINER);
|
||||
|
||||
|
||||
@@ -55,11 +55,11 @@
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.explorer-viewlet .pane-header .monaco-count-badge.hidden {
|
||||
.pane-header .dirty-count.monaco-count-badge.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.explorer-viewlet .monaco-count-badge {
|
||||
.dirty-count.monaco-count-badge {
|
||||
padding: 1px 6px 2px;
|
||||
margin-left: 6px;
|
||||
min-height: auto;
|
||||
|
||||
@@ -185,7 +185,7 @@ export class OpenEditorsView extends ViewPane {
|
||||
super.renderHeaderTitle(container, this.title);
|
||||
|
||||
const count = dom.append(container, $('.count'));
|
||||
this.dirtyCountElement = dom.append(count, $('.monaco-count-badge'));
|
||||
this.dirtyCountElement = dom.append(count, $('.dirty-count.monaco-count-badge'));
|
||||
|
||||
this._register((attachStylerCallback(this.themeService, { badgeBackground, badgeForeground, contrastBorder }, colors => {
|
||||
const background = colors.badgeBackground ? colors.badgeBackground.toString() : '';
|
||||
|
||||
@@ -856,6 +856,7 @@ export class ResourceDragAndDrop implements ITreeDragAndDrop<TreeElement> {
|
||||
registerThemingParticipant((theme, collector) => {
|
||||
const linkFg = theme.getColor(textLinkForeground);
|
||||
if (linkFg) {
|
||||
collector.addRule(`.markers-panel .markers-panel-container .tree-container .monaco-tl-contents .details-container a.code-link span:hover { color: ${linkFg}; }`);
|
||||
collector.addRule(`.markers-panel .markers-panel-container .tree-container .monaco-tl-contents .details-container a.code-link .marker-code > span:hover { color: ${linkFg}; }`);
|
||||
collector.addRule(`.markers-panel .markers-panel-container .tree-container .monaco-list:focus .monaco-tl-contents .details-container a.code-link .marker-code > span:hover { color: ${linkFg.lighten(.4)}; }`);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -349,7 +349,6 @@ export class MarkersView extends ViewPane implements IMarkerFilterController {
|
||||
private createArialLabelElement(parent: HTMLElement): void {
|
||||
this.ariaLabelElement = dom.append(parent, dom.$(''));
|
||||
this.ariaLabelElement.setAttribute('id', 'markers-panel-arialabel');
|
||||
this.ariaLabelElement.setAttribute('aria-live', 'polite');
|
||||
}
|
||||
|
||||
private createTree(parent: HTMLElement): void {
|
||||
|
||||
@@ -1238,7 +1238,10 @@ export class SettingTreeRenderers {
|
||||
private getActionsForSetting(setting: ISetting): IAction[] {
|
||||
const enableSync = this._userDataSyncEnablementService.isEnabled();
|
||||
return enableSync && !setting.disallowSyncIgnore ?
|
||||
[this._instantiationService.createInstance(SyncSettingAction, setting)] :
|
||||
[
|
||||
new Separator(),
|
||||
this._instantiationService.createInstance(SyncSettingAction, setting)
|
||||
] :
|
||||
[];
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ export interface ISpliceEvent<T> {
|
||||
export class EmptyPane extends ViewPane {
|
||||
|
||||
static readonly ID = 'workbench.scm';
|
||||
static readonly TITLE = localize('scm providers', "Source Control Providers");
|
||||
static readonly TITLE = localize('scm', "Source Control");
|
||||
|
||||
constructor(
|
||||
options: IViewPaneOptions,
|
||||
|
||||
@@ -19,5 +19,8 @@ export const SelectAllSearchEditorMatchesCommandId = 'selectAllSearchEditorMatch
|
||||
export const InSearchEditor = new RawContextKey<boolean>('inSearchEditor', false);
|
||||
|
||||
export const SearchEditorScheme = 'search-editor';
|
||||
export const SearchEditorBodyScheme = 'search-editor-body';
|
||||
|
||||
export const SearchEditorFindMatchClass = 'seaarchEditorFindMatch';
|
||||
|
||||
export const SearchEditorID = 'workbench.editor.searchEditor';
|
||||
|
||||
@@ -9,7 +9,7 @@ import { endsWith } from 'vs/base/common/strings';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ToggleCaseSensitiveKeybinding, ToggleRegexKeybinding, ToggleWholeWordKeybinding } from 'vs/editor/contrib/find/findModel';
|
||||
import { localize } from 'vs/nls';
|
||||
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
|
||||
import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
@@ -20,16 +20,16 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { EditorDescriptor, Extensions as EditorExtensions, IEditorRegistry } from 'vs/workbench/browser/editor';
|
||||
import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions';
|
||||
import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
|
||||
import { Extensions as EditorInputExtensions, IEditorInputFactory, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor';
|
||||
import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput';
|
||||
import { Extensions as EditorInputExtensions, IEditorInputFactory, IEditorInputFactoryRegistry, ActiveEditorContext } from 'vs/workbench/common/editor';
|
||||
import * as SearchConstants from 'vs/workbench/contrib/search/common/constants';
|
||||
import * as SearchEditorConstants from 'vs/workbench/contrib/searchEditor/browser/constants';
|
||||
import { SearchEditor } from 'vs/workbench/contrib/searchEditor/browser/searchEditor';
|
||||
import { OpenResultsInEditorAction, OpenSearchEditorAction, toggleSearchEditorCaseSensitiveCommand, toggleSearchEditorContextLinesCommand, toggleSearchEditorRegexCommand, toggleSearchEditorWholeWordCommand, selectAllSearchEditorMatchesCommand } from 'vs/workbench/contrib/searchEditor/browser/searchEditorActions';
|
||||
import { OpenResultsInEditorAction, OpenSearchEditorAction, toggleSearchEditorCaseSensitiveCommand, toggleSearchEditorContextLinesCommand, toggleSearchEditorRegexCommand, toggleSearchEditorWholeWordCommand, selectAllSearchEditorMatchesCommand, RerunSearchEditorSearchAction } from 'vs/workbench/contrib/searchEditor/browser/searchEditorActions';
|
||||
import { getOrMakeSearchEditorInput, SearchEditorInput } from 'vs/workbench/contrib/searchEditor/browser/searchEditorInput';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { ServicesAccessor } from 'vs/editor/browser/editorExtensions';
|
||||
import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput';
|
||||
|
||||
//#region Editor Descriptior
|
||||
Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
|
||||
@@ -54,10 +54,15 @@ class SearchEditorContribution implements IWorkbenchContribution {
|
||||
) {
|
||||
|
||||
this.editorService.overrideOpenEditor((editor, options, group) => {
|
||||
const resource = editor.resource;
|
||||
if (!resource ||
|
||||
!(endsWith(resource.path, '.code-search') || resource.scheme === SearchEditorConstants.SearchEditorScheme) ||
|
||||
!(editor instanceof FileEditorInput || (resource.scheme === SearchEditorConstants.SearchEditorScheme))) {
|
||||
let resource = editor.resource;
|
||||
if (!resource) { return undefined; }
|
||||
|
||||
if (resource.scheme === SearchEditorConstants.SearchEditorBodyScheme) {
|
||||
resource = resource.with({ scheme: SearchEditorConstants.SearchEditorScheme });
|
||||
}
|
||||
|
||||
if (resource.scheme !== SearchEditorConstants.SearchEditorScheme
|
||||
&& !(endsWith(resource.path, '.code-search') && editor instanceof FileEditorInput)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -154,15 +159,6 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
handler: selectAllSearchEditorMatchesCommand
|
||||
});
|
||||
|
||||
CommandsRegistry.registerCommand(
|
||||
SearchEditorConstants.RerunSearchEditorSearchCommandId,
|
||||
(accessor: ServicesAccessor) => {
|
||||
const activeControl = accessor.get(IEditorService).activeControl;
|
||||
if (activeControl instanceof SearchEditor) {
|
||||
activeControl.triggerSearch({ resetCursor: false });
|
||||
}
|
||||
});
|
||||
|
||||
CommandsRegistry.registerCommand(
|
||||
SearchEditorConstants.CleanSearchEditorStateCommandId,
|
||||
(accessor: ServicesAccessor) => {
|
||||
@@ -186,4 +182,19 @@ registry.registerWorkbenchAction(
|
||||
registry.registerWorkbenchAction(
|
||||
SyncActionDescriptor.create(OpenSearchEditorAction, OpenSearchEditorAction.ID, OpenSearchEditorAction.LABEL),
|
||||
'Search Editor: Open New Search Editor', category);
|
||||
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.create(RerunSearchEditorSearchAction, RerunSearchEditorSearchAction.ID, RerunSearchEditorSearchAction.LABEL,
|
||||
{ mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_R } }, ContextKeyExpr.and(SearchEditorConstants.InSearchEditor)),
|
||||
'Search Editor: Rerun', category);
|
||||
//#endregion
|
||||
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitle, {
|
||||
command: {
|
||||
id: RerunSearchEditorSearchAction.ID,
|
||||
title: RerunSearchEditorSearchAction.LABEL,
|
||||
icon: { id: 'codicon/refresh' },
|
||||
},
|
||||
group: 'navigation',
|
||||
when: ContextKeyExpr.and(ActiveEditorContext.isEqualTo(SearchEditorConstants.SearchEditorID))
|
||||
});
|
||||
|
||||
@@ -35,7 +35,7 @@ import { InputBoxFocusedKey } from 'vs/workbench/contrib/search/common/constants
|
||||
import { ITextQueryBuilderOptions, QueryBuilder } from 'vs/workbench/contrib/search/common/queryBuilder';
|
||||
import { getOutOfWorkspaceEditorResources } from 'vs/workbench/contrib/search/common/search';
|
||||
import { SearchModel } from 'vs/workbench/contrib/search/common/searchModel';
|
||||
import { InSearchEditor, SearchEditorFindMatchClass } from 'vs/workbench/contrib/searchEditor/browser/constants';
|
||||
import { InSearchEditor, SearchEditorFindMatchClass, SearchEditorID } from 'vs/workbench/contrib/searchEditor/browser/constants';
|
||||
import type { SearchConfiguration, SearchEditorInput } from 'vs/workbench/contrib/searchEditor/browser/searchEditorInput';
|
||||
import { extractSearchQuery, serializeSearchConfiguration, serializeSearchResultForEditor } from 'vs/workbench/contrib/searchEditor/browser/searchEditorSerialization';
|
||||
import { IPatternInfo, ISearchConfigurationProperties, ITextQuery } from 'vs/workbench/services/search/common/search';
|
||||
@@ -56,7 +56,7 @@ const FILE_LINE_REGEX = /^(\S.*):$/;
|
||||
type SearchEditorViewState = ICodeEditorViewState & { focused: 'input' | 'editor' };
|
||||
|
||||
export class SearchEditor extends BaseTextEditor {
|
||||
static readonly ID: string = 'workbench.editor.searchEditor';
|
||||
static readonly ID: string = SearchEditorID;
|
||||
|
||||
static readonly SEARCH_EDITOR_VIEW_STATE_PREFERENCE_KEY = 'searchEditorViewState';
|
||||
|
||||
@@ -194,6 +194,7 @@ export class SearchEditor extends BaseTextEditor {
|
||||
const runAgainLink = DOM.append(this.messageBox, DOM.$('a.pointer.prominent.message', {}, localize('runSearch', "Run Search")));
|
||||
this.messageDisposables.push(DOM.addDisposableListener(runAgainLink, DOM.EventType.CLICK, async () => {
|
||||
await this.triggerSearch();
|
||||
this.searchResultEditor.focus();
|
||||
this.toggleRunAgainMessage(false);
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -116,6 +116,24 @@ export class OpenResultsInEditorAction extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
export class RerunSearchEditorSearchAction extends Action {
|
||||
static readonly ID: string = Constants.RerunSearchEditorSearchCommandId;
|
||||
static readonly LABEL = localize('search.rerunSearchInEditor', "Search Again");
|
||||
|
||||
constructor(id: string, label: string,
|
||||
@IEditorService private readonly editorService: IEditorService,
|
||||
) {
|
||||
super(id, label, 'codicon-refresh');
|
||||
}
|
||||
|
||||
async run() {
|
||||
const input = this.editorService.activeEditor;
|
||||
if (input instanceof SearchEditorInput) {
|
||||
(this.editorService.activeControl as SearchEditor).triggerSearch({ resetCursor: false });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const openNewSearchEditor =
|
||||
async (accessor: ServicesAccessor) => {
|
||||
const editorService = accessor.get(IEditorService);
|
||||
|
||||
@@ -18,7 +18,7 @@ import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { EditorInput, GroupIdentifier, IEditorInput, IRevertOptions, ISaveOptions, IMoveResult } from 'vs/workbench/common/editor';
|
||||
import { SearchEditorFindMatchClass, SearchEditorScheme } from 'vs/workbench/contrib/searchEditor/browser/constants';
|
||||
import { SearchEditorFindMatchClass, SearchEditorScheme, SearchEditorBodyScheme } from 'vs/workbench/contrib/searchEditor/browser/constants';
|
||||
import { extractSearchQuery, serializeSearchConfiguration } from 'vs/workbench/contrib/searchEditor/browser/searchEditorSerialization';
|
||||
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
@@ -339,7 +339,7 @@ export const getOrMakeSearchEditorInput = (
|
||||
}
|
||||
}
|
||||
|
||||
const contentsModelURI = uri.with({ scheme: 'search-editor-body' });
|
||||
const contentsModelURI = uri.with({ scheme: SearchEditorBodyScheme });
|
||||
const headerModelURI = uri.with({ scheme: 'search-editor-header' });
|
||||
const contentsModel = modelService.getModel(contentsModelURI) ?? modelService.createModel('', modeService.create('search-result'), contentsModelURI);
|
||||
const headerModel = modelService.getModel(headerModelURI) ?? modelService.createModel('', modeService.create('search-result'), headerModelURI);
|
||||
|
||||
@@ -216,7 +216,7 @@ export const serializeSearchResultForEditor =
|
||||
|
||||
const info = [
|
||||
searchResult.count()
|
||||
? `${filecount} - ${resultcount}`
|
||||
? `${resultcount} - ${filecount}`
|
||||
: localize('noResults', "No Results"),
|
||||
''];
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ import { getTemplates as getTaskTemplates } from 'vs/workbench/contrib/tasks/com
|
||||
import * as TaskConfig from '../common/taskConfiguration';
|
||||
import { TerminalTaskSystem } from './terminalTaskSystem';
|
||||
|
||||
import { IQuickInputService, IQuickPickItem, QuickPickInput } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { IQuickInputService, IQuickPickItem, QuickPickInput, IQuickPick } from 'vs/platform/quickinput/common/quickInput';
|
||||
|
||||
import { TaskDefinitionRegistry } from 'vs/workbench/contrib/tasks/common/taskDefinitionRegistry';
|
||||
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
@@ -80,6 +80,7 @@ import { IPreferencesService } from 'vs/workbench/services/preferences/common/pr
|
||||
import { find } from 'vs/base/common/arrays';
|
||||
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { IViewsService } from 'vs/workbench/common/views';
|
||||
import { ProviderProgressMananger } from 'vs/workbench/contrib/tasks/browser/providerProgressManager';
|
||||
|
||||
const QUICKOPEN_HISTORY_LIMIT_CONFIG = 'task.quickOpen.history';
|
||||
const QUICKOPEN_DETAIL_CONFIG = 'task.quickOpen.detail';
|
||||
@@ -218,6 +219,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
|
||||
private _providers: Map<number, ITaskProvider>;
|
||||
private _providerTypes: Map<number, string>;
|
||||
protected _taskSystemInfos: Map<string, TaskSystemInfo>;
|
||||
private _providerProgressManager: ProviderProgressMananger | undefined;
|
||||
|
||||
protected _workspaceTasksPromise?: Promise<Map<string, WorkspaceFolderTaskResult>>;
|
||||
protected _areJsonTasksSupportedPromise: Promise<boolean> = Promise.resolve(false);
|
||||
@@ -560,6 +562,37 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
|
||||
|
||||
protected abstract versionAndEngineCompatible(filter?: TaskFilter): boolean;
|
||||
|
||||
private tasksAndGroupedTasks(filter?: TaskFilter): { tasks: Promise<Task[]>, grouped: Promise<TaskMap> } {
|
||||
if (!this.versionAndEngineCompatible(filter)) {
|
||||
return { tasks: Promise.resolve<Task[]>([]), grouped: Promise.resolve(new TaskMap()) };
|
||||
}
|
||||
const grouped = this.getGroupedTasks(filter ? filter.type : undefined);
|
||||
const tasks = grouped.then((map) => {
|
||||
if (!filter || !filter.type) {
|
||||
return map.all();
|
||||
}
|
||||
let result: Task[] = [];
|
||||
map.forEach((tasks) => {
|
||||
for (let task of tasks) {
|
||||
if (ContributedTask.is(task) && task.defines.type === filter.type) {
|
||||
result.push(task);
|
||||
} else if (CustomTask.is(task)) {
|
||||
if (task.type === filter.type) {
|
||||
result.push(task);
|
||||
} else {
|
||||
let customizes = task.customizes();
|
||||
if (customizes && customizes.type === filter.type) {
|
||||
result.push(task);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return result;
|
||||
});
|
||||
return { tasks, grouped };
|
||||
}
|
||||
|
||||
public tasks(filter?: TaskFilter): Promise<Task[]> {
|
||||
if (!this.versionAndEngineCompatible(filter)) {
|
||||
return Promise.resolve<Task[]>([]);
|
||||
@@ -703,11 +736,11 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
|
||||
});
|
||||
}
|
||||
|
||||
public run(task: Task | undefined, options?: ProblemMatcherRunOptions, runSource: TaskRunSource = TaskRunSource.System): Promise<ITaskSummary | undefined> {
|
||||
public run(task: Task | undefined, options?: ProblemMatcherRunOptions, runSource: TaskRunSource = TaskRunSource.System, grouped?: Promise<TaskMap>): Promise<ITaskSummary | undefined> {
|
||||
if (!task) {
|
||||
throw new TaskError(Severity.Info, nls.localize('TaskServer.noTask', 'Task to execute is undefined'), TaskErrors.TaskNotFound);
|
||||
}
|
||||
return this.getGroupedTasks().then((grouped) => {
|
||||
return (grouped ?? this.getGroupedTasks()).then((grouped) => {
|
||||
let resolver = this.createResolver(grouped);
|
||||
if (options && options.attachProblemMatcher && this.shouldAttachProblemMatcher(task) && !InMemoryTask.is(task)) {
|
||||
return this.attachProblemMatcher(task).then((toExecute) => {
|
||||
@@ -1345,11 +1378,24 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
|
||||
|
||||
protected abstract getTaskSystem(): ITaskSystem;
|
||||
|
||||
private async provideTasksWithWarning(provider: ITaskProvider, type: string, validTypes: IStringDictionary<boolean>): Promise<TaskSet> {
|
||||
private async provideTasksWithWarning(provider: ITaskProvider, type: string, validTypes: IStringDictionary<boolean>): Promise<TaskSet | undefined> {
|
||||
return new Promise<TaskSet>(async (resolve, reject) => {
|
||||
provider.provideTasks(validTypes).then((value) => {
|
||||
let isDone = false;
|
||||
let disposable: IDisposable | undefined;
|
||||
const providePromise = provider.provideTasks(validTypes);
|
||||
this._providerProgressManager?.addProvider(type, providePromise);
|
||||
disposable = this._providerProgressManager?.canceled.token.onCancellationRequested(() => {
|
||||
if (!isDone) {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
providePromise.then((value) => {
|
||||
isDone = true;
|
||||
disposable?.dispose();
|
||||
resolve(value);
|
||||
}, (e) => {
|
||||
isDone = true;
|
||||
disposable?.dispose();
|
||||
reject(e);
|
||||
});
|
||||
});
|
||||
@@ -1361,10 +1407,11 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
|
||||
TaskDefinitionRegistry.all().forEach(definition => validTypes[definition.taskType] = true);
|
||||
validTypes['shell'] = true;
|
||||
validTypes['process'] = true;
|
||||
this._providerProgressManager = new ProviderProgressMananger();
|
||||
return new Promise<TaskSet[]>(resolve => {
|
||||
let result: TaskSet[] = [];
|
||||
let counter: number = 0;
|
||||
let done = (value: TaskSet) => {
|
||||
let done = (value: TaskSet | undefined) => {
|
||||
if (value) {
|
||||
result.push(value);
|
||||
}
|
||||
@@ -2060,30 +2107,69 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
|
||||
}
|
||||
return entries;
|
||||
});
|
||||
return this.quickInputService.pick(pickEntries, {
|
||||
placeHolder,
|
||||
matchOnDescription: true,
|
||||
onDidTriggerItemButton: context => {
|
||||
let task = context.item.task;
|
||||
this.quickInputService.cancel();
|
||||
if (ContributedTask.is(task)) {
|
||||
this.customize(task, undefined, true);
|
||||
} else if (CustomTask.is(task)) {
|
||||
this.openConfig(task);
|
||||
|
||||
const picker: IQuickPick<TaskQuickPickEntry> = this.quickInputService.createQuickPick();
|
||||
picker.placeholder = placeHolder;
|
||||
picker.matchOnDescription = true;
|
||||
picker.ignoreFocusOut = true;
|
||||
|
||||
picker.onDidTriggerItemButton(context => {
|
||||
let task = context.item.task;
|
||||
this.quickInputService.cancel();
|
||||
if (ContributedTask.is(task)) {
|
||||
this.customize(task, undefined, true);
|
||||
} else if (CustomTask.is(task)) {
|
||||
this.openConfig(task);
|
||||
}
|
||||
});
|
||||
picker.busy = true;
|
||||
const progressManager = this._providerProgressManager;
|
||||
const progressTimeout = setTimeout(() => {
|
||||
if (progressManager) {
|
||||
progressManager.showProgress = (stillProviding, total) => {
|
||||
let message = undefined;
|
||||
if (stillProviding.length > 0) {
|
||||
message = nls.localize('pickProgressManager.description', 'Detecting tasks ({0} of {1}): {2} in progress', total - stillProviding.length, total, stillProviding.join(', '));
|
||||
}
|
||||
picker.description = message;
|
||||
};
|
||||
progressManager.addOnDoneListener(() => {
|
||||
picker.focusOnInput();
|
||||
picker.customButton = false;
|
||||
});
|
||||
if (!progressManager.isDone) {
|
||||
picker.customLabel = nls.localize('taskQuickPick.cancel', "Stop detecting");
|
||||
picker.onDidCustom(() => {
|
||||
this._providerProgressManager?.cancel();
|
||||
});
|
||||
picker.customButton = true;
|
||||
}
|
||||
}
|
||||
}, cancellationToken).then(async (selection) => {
|
||||
if (cancellationToken.isCancellationRequested) {
|
||||
// canceled when there's only one task
|
||||
const task = (await pickEntries)[0];
|
||||
if ((<any>task).task) {
|
||||
selection = <TaskQuickPickEntry>task;
|
||||
}, 1000);
|
||||
pickEntries.then(entries => {
|
||||
clearTimeout(progressTimeout);
|
||||
progressManager?.dispose();
|
||||
picker.busy = false;
|
||||
picker.items = entries;
|
||||
});
|
||||
picker.show();
|
||||
|
||||
return new Promise<TaskQuickPickEntry | undefined | null>(resolve => {
|
||||
this._register(picker.onDidAccept(async () => {
|
||||
let selection = picker.selectedItems ? picker.selectedItems[0] : undefined;
|
||||
if (cancellationToken.isCancellationRequested) {
|
||||
// canceled when there's only one task
|
||||
const task = (await pickEntries)[0];
|
||||
if ((<any>task).task) {
|
||||
selection = <TaskQuickPickEntry>task;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!selection) {
|
||||
return undefined; // {{SQL CARBON EDIT}} strict-null-checks
|
||||
}
|
||||
return selection;
|
||||
picker.dispose();
|
||||
if (!selection) {
|
||||
resolve();
|
||||
}
|
||||
resolve(selection);
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2138,7 +2224,11 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
|
||||
|
||||
private doRunTaskCommand(tasks?: Task[]): void {
|
||||
this.showIgnoredFoldersMessage().then(() => {
|
||||
this.showQuickPick(tasks ? tasks : this.tasks(),
|
||||
let taskResult: { tasks: Promise<Task[]>, grouped: Promise<TaskMap> } | undefined = undefined;
|
||||
if (!tasks) {
|
||||
taskResult = this.tasksAndGroupedTasks();
|
||||
}
|
||||
this.showQuickPick(tasks ? tasks : taskResult!.tasks,
|
||||
nls.localize('TaskService.pickRunTask', 'Select the task to run'),
|
||||
{
|
||||
label: nls.localize('TaskService.noEntryToRun', 'No task to run found. Configure Tasks...'),
|
||||
@@ -2153,7 +2243,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
|
||||
if (task === null) {
|
||||
this.runConfigureTasks();
|
||||
} else {
|
||||
this.run(task, { attachProblemMatcher: true }, TaskRunSource.User).then(undefined, reason => {
|
||||
this.run(task, { attachProblemMatcher: true }, TaskRunSource.User, taskResult?.grouped).then(undefined, reason => {
|
||||
// eat the error, it has already been surfaced to the user and we don't care about it here
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { TaskSet } from 'vs/workbench/contrib/tasks/common/tasks';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
|
||||
export class ProviderProgressMananger extends Disposable {
|
||||
private _onProviderComplete: Emitter<string> = new Emitter();
|
||||
private _stillProviding: Set<string> = new Set();
|
||||
private _totalProviders: number = 0;
|
||||
private _onDone: Emitter<void> = new Emitter();
|
||||
private _isDone: boolean = false;
|
||||
private _showProgress: ((remaining: string[], total: number) => void) | undefined;
|
||||
public canceled: CancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._register(this._onProviderComplete.event(taskType => {
|
||||
this._stillProviding.delete(taskType);
|
||||
if (this._stillProviding.size === 0) {
|
||||
this._isDone = true;
|
||||
this._onDone.fire();
|
||||
}
|
||||
if (this._showProgress) {
|
||||
this._showProgress(Array.from(this._stillProviding), this._totalProviders);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
public addProvider(taskType: string, provider: Promise<TaskSet>) {
|
||||
this._totalProviders++;
|
||||
this._stillProviding.add(taskType);
|
||||
provider.then(() => this._onProviderComplete.fire(taskType));
|
||||
}
|
||||
|
||||
public addOnDoneListener(onDoneListener: () => void) {
|
||||
this._register(this._onDone.event(onDoneListener));
|
||||
}
|
||||
|
||||
set showProgress(progressDisplayFunction: (remaining: string[], total: number) => void) {
|
||||
this._showProgress = progressDisplayFunction;
|
||||
this._showProgress(Array.from(this._stillProviding), this._totalProviders);
|
||||
}
|
||||
|
||||
get isDone(): boolean {
|
||||
return this._isDone;
|
||||
}
|
||||
|
||||
public cancel() {
|
||||
this._isDone = true;
|
||||
if (this._showProgress) {
|
||||
this._showProgress([], 0);
|
||||
}
|
||||
this._onDone.fire();
|
||||
this.canceled.cancel();
|
||||
}
|
||||
}
|
||||
@@ -681,6 +681,7 @@ export namespace RunOnOptions {
|
||||
}
|
||||
|
||||
export namespace RunOptions {
|
||||
const properties: MetaData<Tasks.RunOptions, void>[] = [{ property: 'reevaluateOnRerun' }, { property: 'runOn' }, { property: 'instanceLimit' }];
|
||||
export function fromConfiguration(value: RunOptionsConfig | undefined): Tasks.RunOptions {
|
||||
return {
|
||||
reevaluateOnRerun: value ? value.reevaluateOnRerun : true,
|
||||
@@ -688,6 +689,14 @@ export namespace RunOptions {
|
||||
instanceLimit: value ? value.instanceLimit : 1
|
||||
};
|
||||
}
|
||||
|
||||
export function assignProperties(target: Tasks.RunOptions, source: Tasks.RunOptions | undefined): Tasks.RunOptions {
|
||||
return _assignProperties(target, source, properties)!;
|
||||
}
|
||||
|
||||
export function fillProperties(target: Tasks.RunOptions, source: Tasks.RunOptions | undefined): Tasks.RunOptions {
|
||||
return _fillProperties(target, source, properties)!;
|
||||
}
|
||||
}
|
||||
|
||||
interface ParseContext {
|
||||
@@ -1609,6 +1618,7 @@ namespace CustomTask {
|
||||
result.command.presentation = CommandConfiguration.PresentationOptions.assignProperties(
|
||||
result.command.presentation!, configuredProps.configurationProperties.presentation)!;
|
||||
result.command.options = CommandOptions.assignProperties(result.command.options, configuredProps.configurationProperties.options);
|
||||
result.runOptions = RunOptions.assignProperties(result.runOptions, configuredProps.runOptions);
|
||||
|
||||
let contributedConfigProps: Tasks.ConfigurationProperties = contributedTask.configurationProperties;
|
||||
fillProperty(resultConfigProps, contributedConfigProps, 'group');
|
||||
@@ -1621,6 +1631,7 @@ namespace CustomTask {
|
||||
result.command.presentation = CommandConfiguration.PresentationOptions.fillProperties(
|
||||
result.command.presentation!, contributedConfigProps.presentation)!;
|
||||
result.command.options = CommandOptions.fillProperties(result.command.options, contributedConfigProps.options);
|
||||
result.runOptions = RunOptions.fillProperties(result.runOptions, contributedTask.runOptions);
|
||||
|
||||
if (contributedTask.hasDefinedMatchers === true) {
|
||||
result.hasDefinedMatchers = true;
|
||||
|
||||
@@ -114,7 +114,7 @@ export class TerminalViewPane extends ViewPane {
|
||||
}));
|
||||
|
||||
// Force another layout (first is setContainers) since config has changed
|
||||
this.layoutBody(this._terminalContainer.offsetWidth, this._terminalContainer.offsetHeight);
|
||||
this.layoutBody(this._terminalContainer.offsetHeight, this._terminalContainer.offsetWidth);
|
||||
}
|
||||
|
||||
protected layoutBody(height: number, width: number): void {
|
||||
@@ -321,7 +321,7 @@ export class TerminalViewPane extends ViewPane {
|
||||
}
|
||||
// TODO: Can we support ligatures?
|
||||
// dom.toggleClass(this._parentDomElement, 'enable-ligatures', this._terminalService.configHelper.config.fontLigatures);
|
||||
this.layoutBody(this._parentDomElement.offsetWidth, this._parentDomElement.offsetHeight);
|
||||
this.layoutBody(this._parentDomElement.offsetHeight, this._parentDomElement.offsetWidth);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user