Merge remote-tracking branch 'origin/master' into alex/semantic-exploration
This commit is contained in:
commit
41be9a6142
|
@ -6,7 +6,7 @@ steps:
|
|||
inputs:
|
||||
keyfile: '.yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock'
|
||||
targetfolder: '**/node_modules, !**/node_modules/**/node_modules'
|
||||
vstsFeed: '$(ArtifactFeed)'
|
||||
vstsFeed: 'vscode-build-cache'
|
||||
- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2
|
||||
inputs:
|
||||
versionSpec: "1.x"
|
||||
|
@ -18,7 +18,7 @@ steps:
|
|||
inputs:
|
||||
keyfile: '.yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock'
|
||||
targetfolder: '**/node_modules, !**/node_modules/**/node_modules'
|
||||
vstsFeed: '$(ArtifactFeed)'
|
||||
vstsFeed: 'vscode-build-cache'
|
||||
condition: and(succeeded(), ne(variables['CacheRestored'], 'true'))
|
||||
- script: |
|
||||
yarn electron x64
|
||||
|
|
|
@ -14,7 +14,7 @@ steps:
|
|||
inputs:
|
||||
keyfile: '.yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock'
|
||||
targetfolder: '**/node_modules, !**/node_modules/**/node_modules'
|
||||
vstsFeed: '$(ArtifactFeed)'
|
||||
vstsFeed: 'vscode-build-cache'
|
||||
- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2
|
||||
inputs:
|
||||
versionSpec: "1.x"
|
||||
|
@ -26,7 +26,7 @@ steps:
|
|||
inputs:
|
||||
keyfile: '.yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock'
|
||||
targetfolder: '**/node_modules, !**/node_modules/**/node_modules'
|
||||
vstsFeed: '$(ArtifactFeed)'
|
||||
vstsFeed: 'vscode-build-cache'
|
||||
condition: and(succeeded(), ne(variables['CacheRestored'], 'true'))
|
||||
- script: |
|
||||
yarn electron x64
|
||||
|
|
|
@ -13,7 +13,7 @@ steps:
|
|||
inputs:
|
||||
keyfile: '.yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock'
|
||||
targetfolder: '**/node_modules, !**/node_modules/**/node_modules'
|
||||
vstsFeed: '$(ArtifactFeed)'
|
||||
vstsFeed: 'vscode-build-cache'
|
||||
- powershell: |
|
||||
yarn --frozen-lockfile
|
||||
env:
|
||||
|
@ -24,7 +24,7 @@ steps:
|
|||
inputs:
|
||||
keyfile: '.yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock'
|
||||
targetfolder: '**/node_modules, !**/node_modules/**/node_modules'
|
||||
vstsFeed: '$(ArtifactFeed)'
|
||||
vstsFeed: 'vscode-build-cache'
|
||||
condition: and(succeeded(), ne(variables['CacheRestored'], 'true'))
|
||||
- powershell: |
|
||||
yarn electron
|
||||
|
|
|
@ -303,7 +303,7 @@
|
|||
document.body.classList.remove('loading');
|
||||
});
|
||||
|
||||
image.src = decodeURI(settings.src);
|
||||
image.src = settings.src;
|
||||
|
||||
window.addEventListener('message', e => {
|
||||
switch (e.data.type) {
|
||||
|
|
70
extensions/image-preview/src/binarySizeStatusBarEntry.ts
Normal file
70
extensions/image-preview/src/binarySizeStatusBarEntry.ts
Normal file
|
@ -0,0 +1,70 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { Disposable } from './dispose';
|
||||
import * as nls from 'vscode-nls';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
class BinarySize {
|
||||
static readonly KB = 1024;
|
||||
static readonly MB = BinarySize.KB * BinarySize.KB;
|
||||
static readonly GB = BinarySize.MB * BinarySize.KB;
|
||||
static readonly TB = BinarySize.GB * BinarySize.KB;
|
||||
|
||||
static formatSize(size: number): string {
|
||||
if (size < BinarySize.KB) {
|
||||
return localize('sizeB', "{0}B", size);
|
||||
}
|
||||
|
||||
if (size < BinarySize.MB) {
|
||||
return localize('sizeKB', "{0}KB", (size / BinarySize.KB).toFixed(2));
|
||||
}
|
||||
|
||||
if (size < BinarySize.GB) {
|
||||
return localize('sizeMB', "{0}MB", (size / BinarySize.MB).toFixed(2));
|
||||
}
|
||||
|
||||
if (size < BinarySize.TB) {
|
||||
return localize('sizeGB', "{0}GB", (size / BinarySize.GB).toFixed(2));
|
||||
}
|
||||
|
||||
return localize('sizeTB', "{0}TB", (size / BinarySize.TB).toFixed(2));
|
||||
}
|
||||
}
|
||||
|
||||
export class BinarySizeStatusBarEntry extends Disposable {
|
||||
private readonly _entry: vscode.StatusBarItem;
|
||||
|
||||
private _showingOwner: string | undefined;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._entry = this._register(vscode.window.createStatusBarItem({
|
||||
id: 'imagePreview.binarySize',
|
||||
name: localize('sizeStatusBar.name', "Image Binary Size"),
|
||||
alignment: vscode.StatusBarAlignment.Right,
|
||||
priority: 100,
|
||||
}));
|
||||
}
|
||||
|
||||
public show(owner: string, size: number | undefined) {
|
||||
this._showingOwner = owner;
|
||||
if (typeof size === 'number') {
|
||||
this._entry.text = BinarySize.formatSize(size);
|
||||
this._entry.show();
|
||||
} else {
|
||||
this.hide(owner);
|
||||
}
|
||||
}
|
||||
|
||||
public hide(owner: string) {
|
||||
if (owner === this._showingOwner) {
|
||||
this._entry.hide();
|
||||
this._showingOwner = undefined;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@
|
|||
import * as vscode from 'vscode';
|
||||
import { PreviewManager } from './preview';
|
||||
import { SizeStatusBarEntry } from './sizeStatusBarEntry';
|
||||
import { BinarySizeStatusBarEntry } from './binarySizeStatusBarEntry';
|
||||
import { ZoomStatusBarEntry } from './zoomStatusBarEntry';
|
||||
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
|
@ -14,10 +15,13 @@ export function activate(context: vscode.ExtensionContext) {
|
|||
const sizeStatusBarEntry = new SizeStatusBarEntry();
|
||||
context.subscriptions.push(sizeStatusBarEntry);
|
||||
|
||||
const binarySizeStatusBarEntry = new BinarySizeStatusBarEntry();
|
||||
context.subscriptions.push(binarySizeStatusBarEntry);
|
||||
|
||||
const zoomStatusBarEntry = new ZoomStatusBarEntry();
|
||||
context.subscriptions.push(zoomStatusBarEntry);
|
||||
|
||||
const previewManager = new PreviewManager(extensionRoot, sizeStatusBarEntry, zoomStatusBarEntry);
|
||||
const previewManager = new PreviewManager(extensionRoot, sizeStatusBarEntry, binarySizeStatusBarEntry, zoomStatusBarEntry);
|
||||
|
||||
context.subscriptions.push(vscode.window.registerWebviewEditorProvider(
|
||||
PreviewManager.viewType,
|
||||
|
|
|
@ -8,6 +8,7 @@ import * as nls from 'vscode-nls';
|
|||
import { Disposable } from './dispose';
|
||||
import { SizeStatusBarEntry } from './sizeStatusBarEntry';
|
||||
import { Scale, ZoomStatusBarEntry } from './zoomStatusBarEntry';
|
||||
import { BinarySizeStatusBarEntry } from './binarySizeStatusBarEntry';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
|
@ -22,6 +23,7 @@ export class PreviewManager {
|
|||
constructor(
|
||||
private readonly extensionRoot: vscode.Uri,
|
||||
private readonly sizeStatusBarEntry: SizeStatusBarEntry,
|
||||
private readonly binarySizeStatusBarEntry: BinarySizeStatusBarEntry,
|
||||
private readonly zoomStatusBarEntry: ZoomStatusBarEntry,
|
||||
) { }
|
||||
|
||||
|
@ -29,7 +31,7 @@ export class PreviewManager {
|
|||
resource: vscode.Uri,
|
||||
webviewEditor: vscode.WebviewPanel,
|
||||
): vscode.WebviewEditorCapabilities {
|
||||
const preview = new Preview(this.extensionRoot, resource, webviewEditor, this.sizeStatusBarEntry, this.zoomStatusBarEntry);
|
||||
const preview = new Preview(this.extensionRoot, resource, webviewEditor, this.sizeStatusBarEntry, this.binarySizeStatusBarEntry, this.zoomStatusBarEntry);
|
||||
this._previews.add(preview);
|
||||
this.setActivePreview(preview);
|
||||
|
||||
|
@ -72,6 +74,7 @@ class Preview extends Disposable implements vscode.WebviewEditorEditingCapabilit
|
|||
|
||||
private _previewState = PreviewState.Visible;
|
||||
private _imageSize: string | undefined;
|
||||
private _imageBinarySize: number | undefined;
|
||||
private _imageZoom: Scale | undefined;
|
||||
|
||||
constructor(
|
||||
|
@ -79,6 +82,7 @@ class Preview extends Disposable implements vscode.WebviewEditorEditingCapabilit
|
|||
private readonly resource: vscode.Uri,
|
||||
private readonly webviewEditor: vscode.WebviewPanel,
|
||||
private readonly sizeStatusBarEntry: SizeStatusBarEntry,
|
||||
private readonly binarySizeStatusBarEntry: BinarySizeStatusBarEntry,
|
||||
private readonly zoomStatusBarEntry: ZoomStatusBarEntry,
|
||||
) {
|
||||
super();
|
||||
|
@ -125,6 +129,7 @@ class Preview extends Disposable implements vscode.WebviewEditorEditingCapabilit
|
|||
this._register(webviewEditor.onDidDispose(() => {
|
||||
if (this._previewState === PreviewState.Active) {
|
||||
this.sizeStatusBarEntry.hide(this.id);
|
||||
this.binarySizeStatusBarEntry.hide(this.id);
|
||||
this.zoomStatusBarEntry.hide(this.id);
|
||||
}
|
||||
this._previewState = PreviewState.Disposed;
|
||||
|
@ -142,6 +147,11 @@ class Preview extends Disposable implements vscode.WebviewEditorEditingCapabilit
|
|||
}
|
||||
}));
|
||||
|
||||
vscode.workspace.fs.stat(resource).then(({ size }) => {
|
||||
this._imageBinarySize = size;
|
||||
this.update();
|
||||
});
|
||||
|
||||
this.render();
|
||||
this.update();
|
||||
this.webviewEditor.webview.postMessage({ type: 'setActive', value: this.webviewEditor.active });
|
||||
|
@ -173,10 +183,12 @@ class Preview extends Disposable implements vscode.WebviewEditorEditingCapabilit
|
|||
if (this.webviewEditor.active) {
|
||||
this._previewState = PreviewState.Active;
|
||||
this.sizeStatusBarEntry.show(this.id, this._imageSize || '');
|
||||
this.binarySizeStatusBarEntry.show(this.id, this._imageBinarySize);
|
||||
this.zoomStatusBarEntry.show(this.id, this._imageZoom || 'fit');
|
||||
} else {
|
||||
if (this._previewState === PreviewState.Active) {
|
||||
this.sizeStatusBarEntry.hide(this.id);
|
||||
this.binarySizeStatusBarEntry.hide(this.id);
|
||||
this.zoomStatusBarEntry.hide(this.id);
|
||||
}
|
||||
this._previewState = PreviewState.Visible;
|
||||
|
@ -219,18 +231,18 @@ class Preview extends Disposable implements vscode.WebviewEditorEditingCapabilit
|
|||
private getResourcePath(webviewEditor: vscode.WebviewPanel, resource: vscode.Uri, version: string) {
|
||||
switch (resource.scheme) {
|
||||
case 'data':
|
||||
return encodeURI(resource.toString(true));
|
||||
return resource.toString(true);
|
||||
|
||||
case 'git':
|
||||
// Show blank image
|
||||
return encodeURI('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAEElEQVR42gEFAPr/AP///wAI/AL+Sr4t6gAAAABJRU5ErkJggg==');
|
||||
return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAEElEQVR42gEFAPr/AP///wAI/AL+Sr4t6gAAAABJRU5ErkJggg==';
|
||||
|
||||
default:
|
||||
// Avoid adding cache busting if there is already a query string
|
||||
if (resource.query) {
|
||||
return encodeURI(webviewEditor.webview.asWebviewUri(resource).toString(true));
|
||||
return encodeURI(webviewEditor.webview.asWebviewUri(resource).toString());
|
||||
}
|
||||
return encodeURI(webviewEditor.webview.asWebviewUri(resource).toString(true) + `?version=${version}`);
|
||||
return encodeURI(webviewEditor.webview.asWebviewUri(resource).toString() + `?version=${version}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -248,10 +260,10 @@ class Preview extends Disposable implements vscode.WebviewEditorEditingCapabilit
|
|||
|
||||
async hotExit() { }
|
||||
|
||||
async applyEdits(_edits: any[]) { }
|
||||
|
||||
async undoEdits(edits: any[]) { console.log('undo', edits); }
|
||||
|
||||
async applyEdits(edits: any[]) { console.log('apply', edits); }
|
||||
|
||||
//#endregion
|
||||
|
||||
public test_makeEdit() {
|
||||
|
|
|
@ -36,27 +36,31 @@ function mode2ScriptKind(mode: string): 'TS' | 'TSX' | 'JS' | 'JSX' | undefined
|
|||
return undefined;
|
||||
}
|
||||
|
||||
const enum BufferOperationType { Close, Open, Change }
|
||||
|
||||
class CloseOperation {
|
||||
readonly type = 'close';
|
||||
readonly type = BufferOperationType.Close;
|
||||
constructor(
|
||||
public readonly args: string
|
||||
) { }
|
||||
}
|
||||
|
||||
class OpenOperation {
|
||||
readonly type = 'open';
|
||||
readonly type = BufferOperationType.Open;
|
||||
constructor(
|
||||
public readonly args: Proto.OpenRequestArgs
|
||||
) { }
|
||||
}
|
||||
|
||||
class ChangeOperation {
|
||||
readonly type = 'change';
|
||||
readonly type = BufferOperationType.Change;
|
||||
constructor(
|
||||
public readonly args: Proto.FileCodeEdits
|
||||
) { }
|
||||
}
|
||||
|
||||
type BufferOperation = CloseOperation | OpenOperation | ChangeOperation;
|
||||
|
||||
/**
|
||||
* Manages synchronization of buffers with the TS server.
|
||||
*
|
||||
|
@ -64,7 +68,7 @@ class ChangeOperation {
|
|||
*/
|
||||
class BufferSynchronizer {
|
||||
|
||||
private readonly _pending = new ResourceMap<CloseOperation | OpenOperation | ChangeOperation>();
|
||||
private readonly _pending = new ResourceMap<BufferOperation>();
|
||||
|
||||
constructor(
|
||||
private readonly client: ITypeScriptServiceClient
|
||||
|
@ -72,9 +76,7 @@ class BufferSynchronizer {
|
|||
|
||||
public open(resource: vscode.Uri, args: Proto.OpenRequestArgs) {
|
||||
if (this.supportsBatching) {
|
||||
this.updatePending(resource, pending => {
|
||||
pending.set(resource, new OpenOperation(args));
|
||||
});
|
||||
this.updatePending(resource, new OpenOperation(args));
|
||||
} else {
|
||||
this.client.executeWithoutWaitingForResponse('open', args);
|
||||
}
|
||||
|
@ -82,9 +84,7 @@ class BufferSynchronizer {
|
|||
|
||||
public close(resource: vscode.Uri, filepath: string) {
|
||||
if (this.supportsBatching) {
|
||||
this.updatePending(resource, pending => {
|
||||
pending.set(resource, new CloseOperation(filepath));
|
||||
});
|
||||
this.updatePending(resource, new CloseOperation(filepath));
|
||||
} else {
|
||||
const args: Proto.FileRequestArgs = { file: filepath };
|
||||
this.client.executeWithoutWaitingForResponse('close', args);
|
||||
|
@ -97,16 +97,14 @@ class BufferSynchronizer {
|
|||
}
|
||||
|
||||
if (this.supportsBatching) {
|
||||
this.updatePending(resource, pending => {
|
||||
pending.set(resource, new ChangeOperation({
|
||||
fileName: filepath,
|
||||
textChanges: events.map((change): Proto.CodeEdit => ({
|
||||
newText: change.text,
|
||||
start: typeConverters.Position.toLocation(change.range.start),
|
||||
end: typeConverters.Position.toLocation(change.range.end),
|
||||
})).reverse(), // Send the edits end-of-document to start-of-document order
|
||||
}));
|
||||
});
|
||||
this.updatePending(resource, new ChangeOperation({
|
||||
fileName: filepath,
|
||||
textChanges: events.map((change): Proto.CodeEdit => ({
|
||||
newText: change.text,
|
||||
start: typeConverters.Position.toLocation(change.range.start),
|
||||
end: typeConverters.Position.toLocation(change.range.end),
|
||||
})).reverse(), // Send the edits end-of-document to start-of-document order
|
||||
}));
|
||||
} else {
|
||||
for (const { range, text } of events) {
|
||||
const args: Proto.ChangeRequestArgs = {
|
||||
|
@ -143,9 +141,9 @@ class BufferSynchronizer {
|
|||
const changedFiles: Proto.FileCodeEdits[] = [];
|
||||
for (const change of this._pending.values) {
|
||||
switch (change.type) {
|
||||
case 'change': changedFiles.push(change.args); break;
|
||||
case 'open': openFiles.push(change.args); break;
|
||||
case 'close': closedFiles.push(change.args); break;
|
||||
case BufferOperationType.Change: changedFiles.push(change.args); break;
|
||||
case BufferOperationType.Open: openFiles.push(change.args); break;
|
||||
case BufferOperationType.Close: closedFiles.push(change.args); break;
|
||||
}
|
||||
}
|
||||
this.client.execute('updateOpen', { changedFiles, closedFiles, openFiles }, nulToken, { nonRecoverable: true });
|
||||
|
@ -157,12 +155,23 @@ class BufferSynchronizer {
|
|||
return this.client.apiVersion.gte(API.v340);
|
||||
}
|
||||
|
||||
private updatePending(resource: vscode.Uri, f: (pending: ResourceMap<CloseOperation | OpenOperation | ChangeOperation>) => void): void {
|
||||
private updatePending(resource: vscode.Uri, op: BufferOperation): void {
|
||||
switch (op.type) {
|
||||
case BufferOperationType.Close:
|
||||
const existing = this._pending.get(resource);
|
||||
switch (existing?.type) {
|
||||
case BufferOperationType.Open:
|
||||
this._pending.delete(resource);
|
||||
return; // Open then close. No need to do anything
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (this._pending.has(resource)) {
|
||||
// we saw this file before, make sure we flush before working with it again
|
||||
this.flush();
|
||||
}
|
||||
f(this._pending);
|
||||
this._pending.set(resource, op);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -56,6 +56,7 @@
|
|||
"xterm": "4.3.0-beta17",
|
||||
"xterm-addon-search": "0.4.0-beta4",
|
||||
"xterm-addon-web-links": "0.2.1",
|
||||
"xterm-addon-webgl": "0.4.0-beta6",
|
||||
"yauzl": "^2.9.2",
|
||||
"yazl": "^2.4.3"
|
||||
},
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
"xterm": "4.3.0-beta17",
|
||||
"xterm-addon-search": "0.4.0-beta4",
|
||||
"xterm-addon-web-links": "0.2.1",
|
||||
"xterm-addon-webgl": "0.4.0-beta6",
|
||||
"yauzl": "^2.9.2",
|
||||
"yazl": "^2.4.3"
|
||||
},
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
"vscode-textmate": "^4.3.0",
|
||||
"xterm": "4.3.0-beta17",
|
||||
"xterm-addon-search": "0.4.0-beta4",
|
||||
"xterm-addon-web-links": "0.2.1"
|
||||
"xterm-addon-web-links": "0.2.1",
|
||||
"xterm-addon-webgl": "0.4.0-beta6"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,6 +41,11 @@ xterm-addon-web-links@0.2.1:
|
|||
resolved "https://registry.yarnpkg.com/xterm-addon-web-links/-/xterm-addon-web-links-0.2.1.tgz#6d1f2ce613e09870badf17615e7a1170a31542b2"
|
||||
integrity sha512-2KnHtiq0IG7hfwv3jw2/jQeH1RBk2d5CH4zvgwQe00rLofSJqSfgnJ7gwowxxpGHrpbPr6Lv4AmH/joaNw2+HQ==
|
||||
|
||||
xterm-addon-webgl@0.4.0-beta6:
|
||||
version "0.4.0-beta6"
|
||||
resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.4.0-beta6.tgz#23d152a0467d8b1f96ab3da7ac9a49bfa0b08c98"
|
||||
integrity sha512-gtM8XtRyrNFCtxHBIU3pqTlBeAi5VOoymJAXKQQ7RsHEVJX79OTk1dQ9Q6Ow14+REGwQU/zFECV050jbLTfvrQ==
|
||||
|
||||
xterm@4.3.0-beta17:
|
||||
version "4.3.0-beta17"
|
||||
resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.3.0-beta17.tgz#c038cc00cb5be33d2a5f083255c329d9ed186565"
|
||||
|
|
|
@ -428,6 +428,11 @@ xterm-addon-web-links@0.2.1:
|
|||
resolved "https://registry.yarnpkg.com/xterm-addon-web-links/-/xterm-addon-web-links-0.2.1.tgz#6d1f2ce613e09870badf17615e7a1170a31542b2"
|
||||
integrity sha512-2KnHtiq0IG7hfwv3jw2/jQeH1RBk2d5CH4zvgwQe00rLofSJqSfgnJ7gwowxxpGHrpbPr6Lv4AmH/joaNw2+HQ==
|
||||
|
||||
xterm-addon-webgl@0.4.0-beta6:
|
||||
version "0.4.0-beta6"
|
||||
resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.4.0-beta6.tgz#23d152a0467d8b1f96ab3da7ac9a49bfa0b08c98"
|
||||
integrity sha512-gtM8XtRyrNFCtxHBIU3pqTlBeAi5VOoymJAXKQQ7RsHEVJX79OTk1dQ9Q6Ow14+REGwQU/zFECV050jbLTfvrQ==
|
||||
|
||||
xterm@4.3.0-beta17:
|
||||
version "4.3.0-beta17"
|
||||
resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.3.0-beta17.tgz#c038cc00cb5be33d2a5f083255c329d9ed186565"
|
||||
|
|
|
@ -56,5 +56,5 @@ export const BrowserFeatures = {
|
|||
})(),
|
||||
|
||||
touch: 'ontouchstart' in window || navigator.maxTouchPoints > 0 || window.navigator.msMaxTouchPoints > 0,
|
||||
pointerEvents: browser.isSafari && window.PointerEvent && ('ontouchstart' in window || navigator.maxTouchPoints > 0 || window.navigator.msMaxTouchPoints > 0)
|
||||
pointerEvents: window.PointerEvent && ('ontouchstart' in window || window.navigator.maxTouchPoints > 0 || navigator.maxTouchPoints > 0 || window.navigator.msMaxTouchPoints > 0)
|
||||
};
|
||||
|
|
|
@ -16,6 +16,7 @@ import * as platform from 'vs/base/common/platform';
|
|||
import { coalesce } from 'vs/base/common/arrays';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Schemas, RemoteAuthorities } from 'vs/base/common/network';
|
||||
import { BrowserFeatures } from 'vs/base/browser/canIUse';
|
||||
|
||||
export function clearNode(node: HTMLElement): void {
|
||||
while (node.firstChild) {
|
||||
|
@ -266,6 +267,13 @@ export let addStandardDisposableListener: IAddStandardDisposableListenerSignatur
|
|||
return addDisposableListener(node, type, wrapHandler, useCapture);
|
||||
};
|
||||
|
||||
export function addDisposableGenericMouseDownListner(node: EventTarget, handler: (event: any) => void, useCapture?: boolean): IDisposable {
|
||||
return addDisposableListener(node, platform.isIOS && BrowserFeatures.pointerEvents ? EventType.POINTER_DOWN : EventType.MOUSE_DOWN, handler, useCapture);
|
||||
}
|
||||
|
||||
export function addDisposableGenericMouseUpListner(node: EventTarget, handler: (event: any) => void, useCapture?: boolean): IDisposable {
|
||||
return addDisposableListener(node, platform.isIOS && BrowserFeatures.pointerEvents ? EventType.POINTER_UP : EventType.MOUSE_UP, handler, useCapture);
|
||||
}
|
||||
export function addDisposableNonBubblingMouseOutListener(node: Element, handler: (event: MouseEvent) => void): IDisposable {
|
||||
return addDisposableListener(node, 'mouseout', (e: MouseEvent) => {
|
||||
// Mouse out bubbles, so this is an attempt to ignore faux mouse outs coming from children elements
|
||||
|
@ -443,7 +451,7 @@ export interface DOMEvent {
|
|||
}
|
||||
|
||||
const MINIMUM_TIME_MS = 16;
|
||||
const DEFAULT_EVENT_MERGER: IEventMerger<DOMEvent, DOMEvent> = function (lastEvent: DOMEvent, currentEvent: DOMEvent) {
|
||||
const DEFAULT_EVENT_MERGER: IEventMerger<DOMEvent, DOMEvent> = function (lastEvent: DOMEvent | null, currentEvent: DOMEvent) {
|
||||
return currentEvent;
|
||||
};
|
||||
|
||||
|
@ -492,6 +500,11 @@ export function getClientArea(element: HTMLElement): Dimension {
|
|||
return new Dimension(element.clientWidth, element.clientHeight);
|
||||
}
|
||||
|
||||
// If visual view port exits and it's on mobile, it should be used instead of window innerWidth / innerHeight, or document.body.clientWidth / document.body.clientHeight
|
||||
if (platform.isIOS && (<any>window).visualViewport) {
|
||||
return new Dimension((<any>window).visualViewport.width, (<any>window).visualViewport.height);
|
||||
}
|
||||
|
||||
// Try innerWidth / innerHeight
|
||||
if (window.innerWidth && window.innerHeight) {
|
||||
return new Dimension(window.innerWidth, window.innerHeight);
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { IframeUtils } from 'vs/base/browser/iframe';
|
||||
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
|
@ -16,7 +17,7 @@ export interface IStandardMouseMoveEventData {
|
|||
}
|
||||
|
||||
export interface IEventMerger<R> {
|
||||
(lastEvent: R, currentEvent: MouseEvent): R;
|
||||
(lastEvent: R | null, currentEvent: MouseEvent): R;
|
||||
}
|
||||
|
||||
export interface IMouseMoveCallback<R> {
|
||||
|
@ -27,7 +28,7 @@ export interface IOnStopCallback {
|
|||
(): void;
|
||||
}
|
||||
|
||||
export function standardMouseMoveMerger(lastEvent: IStandardMouseMoveEventData, currentEvent: MouseEvent): IStandardMouseMoveEventData {
|
||||
export function standardMouseMoveMerger(lastEvent: IStandardMouseMoveEventData | null, currentEvent: MouseEvent): IStandardMouseMoveEventData {
|
||||
let ev = new StandardMouseEvent(currentEvent);
|
||||
ev.preventDefault();
|
||||
return {
|
||||
|
@ -85,12 +86,12 @@ export class GlobalMouseMoveMonitor<R> implements IDisposable {
|
|||
this.onStopCallback = onStopCallback;
|
||||
|
||||
let windowChain = IframeUtils.getSameOriginWindowChain();
|
||||
const mouseMove = BrowserFeatures.pointerEvents ? 'pointermove' : 'mousemove';
|
||||
const mouseUp = BrowserFeatures.pointerEvents ? 'pointerup' : 'mouseup';
|
||||
const mouseMove = platform.isIOS && BrowserFeatures.pointerEvents ? 'pointermove' : 'mousemove';
|
||||
const mouseUp = platform.isIOS && BrowserFeatures.pointerEvents ? 'pointerup' : 'mouseup';
|
||||
for (const element of windowChain) {
|
||||
this.hooks.add(dom.addDisposableThrottledListener(element.window.document, mouseMove,
|
||||
(data: R) => this.mouseMoveCallback!(data),
|
||||
(lastEvent: R, currentEvent) => this.mouseMoveEventMerger!(lastEvent, currentEvent as MouseEvent)
|
||||
(lastEvent: R | null, currentEvent) => this.mouseMoveEventMerger!(lastEvent, currentEvent as MouseEvent)
|
||||
));
|
||||
this.hooks.add(dom.addDisposableListener(element.window.document, mouseUp, (e: MouseEvent) => this.stopMonitoring(true)));
|
||||
}
|
||||
|
|
|
@ -50,19 +50,19 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende
|
|||
const _href = function (href: string, isDomUri: boolean): string {
|
||||
const data = markdown.uris && markdown.uris[href];
|
||||
if (!data) {
|
||||
return href;
|
||||
return href; // no uri exists
|
||||
}
|
||||
let uri = URI.revive(data);
|
||||
if (URI.parse(href).toString() === uri.toString()) {
|
||||
return href; // no tranformation performed
|
||||
}
|
||||
if (isDomUri) {
|
||||
uri = DOM.asDomUri(uri);
|
||||
}
|
||||
if (uri.query) {
|
||||
uri = uri.with({ query: _uriMassage(uri.query) });
|
||||
}
|
||||
if (data) {
|
||||
href = uri.toString(true);
|
||||
}
|
||||
return href;
|
||||
return uri.toString();
|
||||
};
|
||||
|
||||
// signal to code-block render that the
|
||||
|
|
|
@ -71,6 +71,7 @@ export class Gesture extends Disposable {
|
|||
|
||||
private dispatched = false;
|
||||
private targets: HTMLElement[];
|
||||
private ignoreTargets: HTMLElement[];
|
||||
private handle: IDisposable | null;
|
||||
|
||||
private activeTouches: { [id: number]: TouchData; };
|
||||
|
@ -81,6 +82,7 @@ export class Gesture extends Disposable {
|
|||
this.activeTouches = {};
|
||||
this.handle = null;
|
||||
this.targets = [];
|
||||
this.ignoreTargets = [];
|
||||
this._register(DomUtils.addDisposableListener(document, 'touchstart', (e: TouchEvent) => this.onTouchStart(e)));
|
||||
this._register(DomUtils.addDisposableListener(document, 'touchend', (e: TouchEvent) => this.onTouchEnd(e)));
|
||||
this._register(DomUtils.addDisposableListener(document, 'touchmove', (e: TouchEvent) => this.onTouchMove(e)));
|
||||
|
@ -103,6 +105,23 @@ export class Gesture extends Disposable {
|
|||
};
|
||||
}
|
||||
|
||||
public static ignoreTarget(element: HTMLElement): IDisposable {
|
||||
if (!Gesture.isTouchDevice()) {
|
||||
return Disposable.None;
|
||||
}
|
||||
if (!Gesture.INSTANCE) {
|
||||
Gesture.INSTANCE = new Gesture();
|
||||
}
|
||||
|
||||
Gesture.INSTANCE.ignoreTargets.push(element);
|
||||
|
||||
return {
|
||||
dispose: () => {
|
||||
Gesture.INSTANCE.ignoreTargets = Gesture.INSTANCE.ignoreTargets.filter(t => t !== element);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@memoize
|
||||
private static isTouchDevice(): boolean {
|
||||
return 'ontouchstart' in window as any || navigator.maxTouchPoints > 0 || window.navigator.msMaxTouchPoints > 0;
|
||||
|
@ -228,6 +247,12 @@ export class Gesture extends Disposable {
|
|||
}
|
||||
|
||||
private dispatchEvent(event: GestureEvent): void {
|
||||
for (let i = 0; i < this.ignoreTargets.length; i++) {
|
||||
if (event.initialTarget instanceof Node && this.ignoreTargets[i].contains(event.initialTarget)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.targets.forEach(target => {
|
||||
if (event.initialTarget instanceof Node && target.contains(event.initialTarget)) {
|
||||
target.dispatchEvent(event);
|
||||
|
|
|
@ -113,6 +113,8 @@ export class Checkbox extends Widget {
|
|||
ev.preventDefault();
|
||||
});
|
||||
|
||||
this.ignoreGesture(this.domNode);
|
||||
|
||||
this.onkeydown(this.domNode, (keyboardEvent) => {
|
||||
if (keyboardEvent.keyCode === KeyCode.Space || keyboardEvent.keyCode === KeyCode.Enter) {
|
||||
this.checked = !this._checked;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
@font-face {
|
||||
font-family: "codicon";
|
||||
src: url("./codicon.ttf?72bd9e6bbf1e48287bcb9a9e4babeb28") format("truetype");
|
||||
src: url("./codicon.ttf?b035097bd976825411d2c57142be0530") format("truetype");
|
||||
}
|
||||
|
||||
.codicon[class*='codicon-'] {
|
||||
|
@ -69,6 +69,9 @@
|
|||
.codicon-eye-watch:before { content: "\ea70" }
|
||||
.codicon-circle-filled:before { content: "\ea71" }
|
||||
.codicon-primitive-dot:before { content: "\ea71" }
|
||||
.codicon-debug-breakpoint:before { content: "\ea71" }
|
||||
.codicon-debug-breakpoint-disabled:before { content: "\ea71" }
|
||||
.codicon-debug-hint:before { content: "\ea71" }
|
||||
.codicon-primitive-square:before { content: "\ea72" }
|
||||
.codicon-edit:before { content: "\ea73" }
|
||||
.codicon-pencil:before { content: "\ea73" }
|
||||
|
@ -162,12 +165,15 @@
|
|||
.codicon-bold:before { content: "\eaa3" }
|
||||
.codicon-book:before { content: "\eaa4" }
|
||||
.codicon-bookmark:before { content: "\eaa5" }
|
||||
.codicon-breakpoint-conditional-unverified:before { content: "\eaa6" }
|
||||
.codicon-breakpoint-conditional:before { content: "\eaa7" }
|
||||
.codicon-breakpoint-data-unverified:before { content: "\eaa8" }
|
||||
.codicon-breakpoint-data:before { content: "\eaa9" }
|
||||
.codicon-breakpoint-log-unverified:before { content: "\eaaa" }
|
||||
.codicon-breakpoint-log:before { content: "\eaab" }
|
||||
.codicon-debug-breakpoint-conditional-unverified:before { content: "\eaa6" }
|
||||
.codicon-debug-breakpoint-conditional:before { content: "\eaa7" }
|
||||
.codicon-debug-breakpoint-conditional-disabled:before { content: "\eaa7" }
|
||||
.codicon-debug-breakpoint-data-unverified:before { content: "\eaa8" }
|
||||
.codicon-debug-breakpoint-data:before { content: "\eaa9" }
|
||||
.codicon-debug-breakpoint-data-disabled:before { content: "\eaa9" }
|
||||
.codicon-debug-breakpoint-log-unverified:before { content: "\eaaa" }
|
||||
.codicon-debug-breakpoint-log:before { content: "\eaab" }
|
||||
.codicon-debug-breakpoint-log-disabled:before { content: "\eaab" }
|
||||
.codicon-briefcase:before { content: "\eaac" }
|
||||
.codicon-broadcast:before { content: "\eaad" }
|
||||
.codicon-browser:before { content: "\eaae" }
|
||||
|
@ -185,6 +191,7 @@
|
|||
.codicon-chrome-minimize:before { content: "\eaba" }
|
||||
.codicon-chrome-restore:before { content: "\eabb" }
|
||||
.codicon-circle-outline:before { content: "\eabc" }
|
||||
.codicon-debug-breakpoint-unverified:before { content: "\eabc" }
|
||||
.codicon-circle-slash:before { content: "\eabd" }
|
||||
.codicon-circuit-board:before { content: "\eabe" }
|
||||
.codicon-clear-all:before { content: "\eabf" }
|
||||
|
@ -198,8 +205,6 @@
|
|||
.codicon-comment-discussion:before { content: "\eac7" }
|
||||
.codicon-compare-changes:before { content: "\eac8" }
|
||||
.codicon-credit-card:before { content: "\eac9" }
|
||||
.codicon-current-and-breakpoint:before { content: "\eaca" }
|
||||
.codicon-current:before { content: "\eacb" }
|
||||
.codicon-dash:before { content: "\eacc" }
|
||||
.codicon-dashboard:before { content: "\eacd" }
|
||||
.codicon-database:before { content: "\eace" }
|
||||
|
@ -387,3 +392,12 @@
|
|||
.codicon-list-selection:before { content: "\eb85" }
|
||||
.codicon-selection:before { content: "\eb85" }
|
||||
.codicon-list-tree:before { content: "\eb86" }
|
||||
.codicon-debug-breakpoint-function-unverified:before { content: "\eb87" }
|
||||
.codicon-debug-breakpoint-function:before { content: "\eb88" }
|
||||
.codicon-debug-breakpoint-function-disabled:before { content: "\eb88" }
|
||||
.codicon-debug-breakpoint-stackframe-active:before { content: "\eb89" }
|
||||
.codicon-debug-breakpoint-stackframe-dot:before { content: "\eb8a" }
|
||||
.codicon-debug-breakpoint-stackframe:before { content: "\eb8b" }
|
||||
.codicon-debug-breakpoint-stackframe-focused:before { content: "\eb8b" }
|
||||
.codicon-debug-breakpoint-unsupported:before { content: "\eb8c" }
|
||||
.codicon-debug-step-back:before { content: "\f101" }
|
||||
|
|
Binary file not shown.
|
@ -5,6 +5,7 @@
|
|||
|
||||
import 'vs/css!./contextview';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { IDisposable, toDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { Range } from 'vs/base/common/range';
|
||||
import { BrowserFeatures } from 'vs/base/browser/canIUse';
|
||||
|
@ -179,7 +180,7 @@ export class ContextView extends Disposable {
|
|||
return;
|
||||
}
|
||||
|
||||
if (this.delegate!.canRelayout === false && !BrowserFeatures.pointerEvents) {
|
||||
if (this.delegate!.canRelayout === false && !(platform.isIOS && BrowserFeatures.pointerEvents)) {
|
||||
this.hide();
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import * as dom from 'vs/base/browser/dom';
|
|||
import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel';
|
||||
import { IMatch } from 'vs/base/common/filters';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { Range } from 'vs/base/common/range';
|
||||
|
||||
export interface IIconLabelCreationOptions {
|
||||
supportHighlights?: boolean;
|
||||
|
@ -24,6 +25,7 @@ export interface IIconLabelValueOptions {
|
|||
matches?: IMatch[];
|
||||
labelEscapeNewLines?: boolean;
|
||||
descriptionMatches?: IMatch[];
|
||||
readonly separator?: string;
|
||||
}
|
||||
|
||||
class FastLabelNode {
|
||||
|
@ -86,9 +88,10 @@ class FastLabelNode {
|
|||
}
|
||||
|
||||
export class IconLabel extends Disposable {
|
||||
|
||||
private domNode: FastLabelNode;
|
||||
private labelDescriptionContainer: FastLabelNode;
|
||||
private labelNode: FastLabelNode | HighlightedLabel;
|
||||
private descriptionContainer: FastLabelNode;
|
||||
private nameNode: Label | LabelWithHighlights;
|
||||
private descriptionNode: FastLabelNode | HighlightedLabel | undefined;
|
||||
private descriptionNodeFactory: () => FastLabelNode | HighlightedLabel;
|
||||
|
||||
|
@ -97,18 +100,21 @@ export class IconLabel extends Disposable {
|
|||
|
||||
this.domNode = this._register(new FastLabelNode(dom.append(container, dom.$('.monaco-icon-label'))));
|
||||
|
||||
this.labelDescriptionContainer = this._register(new FastLabelNode(dom.append(this.domNode.element, dom.$('.monaco-icon-label-description-container'))));
|
||||
const labelContainer = dom.append(this.domNode.element, dom.$('.monaco-icon-label-container'));
|
||||
|
||||
const nameContainer = dom.append(labelContainer, dom.$('span.monaco-icon-name-container'));
|
||||
this.descriptionContainer = this._register(new FastLabelNode(dom.append(labelContainer, dom.$('span.monaco-icon-description-container'))));
|
||||
|
||||
if (options?.supportHighlights) {
|
||||
this.labelNode = new HighlightedLabel(dom.append(this.labelDescriptionContainer.element, dom.$('a.label-name')), !!options.supportCodicons);
|
||||
this.nameNode = new LabelWithHighlights(nameContainer, !!options.supportCodicons);
|
||||
} else {
|
||||
this.labelNode = this._register(new FastLabelNode(dom.append(this.labelDescriptionContainer.element, dom.$('a.label-name'))));
|
||||
this.nameNode = new Label(nameContainer);
|
||||
}
|
||||
|
||||
if (options?.supportDescriptionHighlights) {
|
||||
this.descriptionNodeFactory = () => new HighlightedLabel(dom.append(this.labelDescriptionContainer.element, dom.$('span.label-description')), !!options.supportCodicons);
|
||||
this.descriptionNodeFactory = () => new HighlightedLabel(dom.append(this.descriptionContainer.element, dom.$('span.label-description')), !!options.supportCodicons);
|
||||
} else {
|
||||
this.descriptionNodeFactory = () => this._register(new FastLabelNode(dom.append(this.labelDescriptionContainer.element, dom.$('span.label-description'))));
|
||||
this.descriptionNodeFactory = () => this._register(new FastLabelNode(dom.append(this.descriptionContainer.element, dom.$('span.label-description'))));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -116,7 +122,7 @@ export class IconLabel extends Disposable {
|
|||
return this.domNode.element;
|
||||
}
|
||||
|
||||
setLabel(label: string, description?: string, options?: IIconLabelValueOptions): void {
|
||||
setLabel(label: string | string[], description?: string, options?: IIconLabelValueOptions): void {
|
||||
const classes = ['monaco-icon-label'];
|
||||
if (options) {
|
||||
if (options.extraClasses) {
|
||||
|
@ -131,11 +137,7 @@ export class IconLabel extends Disposable {
|
|||
this.domNode.className = classes.join(' ');
|
||||
this.domNode.title = options?.title || '';
|
||||
|
||||
if (this.labelNode instanceof HighlightedLabel) {
|
||||
this.labelNode.set(label || '', options?.matches, options?.title, options?.labelEscapeNewLines);
|
||||
} else {
|
||||
this.labelNode.textContent = label || '';
|
||||
}
|
||||
this.nameNode.setLabel(label, options);
|
||||
|
||||
if (description || this.descriptionNode) {
|
||||
if (!this.descriptionNode) {
|
||||
|
@ -157,3 +159,110 @@ export class IconLabel extends Disposable {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Label {
|
||||
|
||||
private label: string | string[] | undefined = undefined;
|
||||
private singleLabel: HTMLElement | undefined = undefined;
|
||||
|
||||
constructor(private container: HTMLElement) { }
|
||||
|
||||
setLabel(label: string | string[], options?: IIconLabelValueOptions): void {
|
||||
if (this.label === label) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.label = label;
|
||||
|
||||
if (typeof label === 'string') {
|
||||
if (!this.singleLabel) {
|
||||
this.container.innerHTML = '';
|
||||
dom.removeClass(this.container, 'multiple');
|
||||
this.singleLabel = dom.append(this.container, dom.$('a.label-name'));
|
||||
}
|
||||
|
||||
this.singleLabel.textContent = label;
|
||||
} else {
|
||||
this.container.innerHTML = '';
|
||||
dom.addClass(this.container, 'multiple');
|
||||
this.singleLabel = undefined;
|
||||
|
||||
for (let i = 0; i < label.length; i++) {
|
||||
const l = label[i];
|
||||
|
||||
dom.append(this.container, dom.$('a.label-name', { 'data-icon-label-count': label.length, 'data-icon-label-index': i }, l));
|
||||
|
||||
if (i < label.length - 1) {
|
||||
dom.append(this.container, dom.$('span.label-separator', undefined, options?.separator || '/'));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function splitMatches(labels: string[], separator: string, matches: IMatch[] | undefined): IMatch[][] | undefined {
|
||||
if (!matches) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let labelStart = 0;
|
||||
|
||||
return labels.map(label => {
|
||||
const labelRange = { start: labelStart, end: labelStart + label.length };
|
||||
|
||||
const result = matches
|
||||
.map(match => Range.intersect(labelRange, match))
|
||||
.filter(range => !Range.isEmpty(range))
|
||||
.map(({ start, end }) => ({ start: start - labelStart, end: end - labelStart }));
|
||||
|
||||
labelStart = labelRange.end + separator.length;
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
class LabelWithHighlights {
|
||||
|
||||
private label: string | string[] | undefined = undefined;
|
||||
private singleLabel: HighlightedLabel | undefined = undefined;
|
||||
|
||||
constructor(private container: HTMLElement, private supportCodicons: boolean) { }
|
||||
|
||||
setLabel(label: string | string[], options?: IIconLabelValueOptions): void {
|
||||
if (this.label === label) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.label = label;
|
||||
|
||||
if (typeof label === 'string') {
|
||||
if (!this.singleLabel) {
|
||||
this.container.innerHTML = '';
|
||||
dom.removeClass(this.container, 'multiple');
|
||||
this.singleLabel = new HighlightedLabel(dom.append(this.container, dom.$('a.label-name')), this.supportCodicons);
|
||||
}
|
||||
|
||||
this.singleLabel.set(label, options?.matches, options?.title, options?.labelEscapeNewLines);
|
||||
} else {
|
||||
|
||||
this.container.innerHTML = '';
|
||||
dom.addClass(this.container, 'multiple');
|
||||
this.singleLabel = undefined;
|
||||
|
||||
const separator = options?.separator || '/';
|
||||
const matches = splitMatches(label, separator, options?.matches);
|
||||
|
||||
for (let i = 0; i < label.length; i++) {
|
||||
const l = label[i];
|
||||
const m = matches ? matches[i] : undefined;
|
||||
|
||||
const name = dom.$('a.label-name', { 'data-icon-label-count': label.length, 'data-icon-label-index': i });
|
||||
const highlightedLabel = new HighlightedLabel(dom.append(this.container, name), this.supportCodicons);
|
||||
highlightedLabel.set(l, m, options?.title, options?.labelEscapeNewLines);
|
||||
|
||||
if (i < label.length - 1) {
|
||||
dom.append(name, dom.$('span.label-separator', undefined, separator));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,25 +31,32 @@
|
|||
flex-shrink: 0; /* fix for https://github.com/Microsoft/vscode/issues/13787 */
|
||||
}
|
||||
|
||||
.monaco-icon-label > .monaco-icon-label-description-container {
|
||||
overflow: hidden; /* this causes the label/description to shrink first if decorations are enabled */
|
||||
.monaco-icon-label > .monaco-icon-label-container {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.monaco-icon-label > .monaco-icon-label-description-container > .label-name {
|
||||
.monaco-icon-label > .monaco-icon-label-container > .monaco-icon-name-container > .label-name {
|
||||
color: inherit;
|
||||
white-space: pre; /* enable to show labels that include multiple whitespaces */
|
||||
}
|
||||
|
||||
.monaco-icon-label > .monaco-icon-label-description-container > .label-description {
|
||||
.monaco-icon-label > .monaco-icon-label-container > .monaco-icon-name-container > .label-name > .label-separator {
|
||||
margin: 0 2px;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.monaco-icon-label > .monaco-icon-label-container > .monaco-icon-description-container > .label-description {
|
||||
opacity: .7;
|
||||
margin-left: 0.5em;
|
||||
font-size: 0.9em;
|
||||
white-space: pre; /* enable to show labels that include multiple whitespaces */
|
||||
}
|
||||
|
||||
.monaco-icon-label.italic > .monaco-icon-label-description-container > .label-name,
|
||||
.monaco-icon-label.italic > .monaco-icon-label-description-container > .label-description {
|
||||
.monaco-icon-label.italic > .monaco-icon-label-container > .monaco-icon-name-container > .label-name,
|
||||
.monaco-icon-label.italic > .monaco-icon-description-container > .label-description {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
|
@ -58,7 +65,6 @@
|
|||
font-size: 90%;
|
||||
font-weight: 600;
|
||||
padding: 0 16px 0 5px;
|
||||
margin-left: auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
|
|
@ -220,6 +220,8 @@ export class InputBox extends Widget {
|
|||
});
|
||||
}
|
||||
|
||||
this.ignoreGesture(this.input);
|
||||
|
||||
setTimeout(() => this.updateMirror(), 0);
|
||||
|
||||
// Support actions
|
||||
|
|
|
@ -103,10 +103,11 @@ export const ListDragOverReactions = {
|
|||
|
||||
export interface IListDragAndDrop<T> {
|
||||
getDragURI(element: T): string | null;
|
||||
getDragLabel?(elements: T[]): string | undefined;
|
||||
getDragLabel?(elements: T[], originalEvent: DragEvent): string | undefined;
|
||||
onDragStart?(data: IDragAndDropData, originalEvent: DragEvent): void;
|
||||
onDragOver(data: IDragAndDropData, targetElement: T | undefined, targetIndex: number | undefined, originalEvent: DragEvent): boolean | IListDragOverReaction;
|
||||
drop(data: IDragAndDropData, targetElement: T | undefined, targetIndex: number | undefined, originalEvent: DragEvent): void;
|
||||
onDragEnd?(originalEvent: DragEvent): void;
|
||||
}
|
||||
|
||||
export class ListError extends Error {
|
||||
|
|
|
@ -74,9 +74,10 @@ const DefaultOptions = {
|
|||
horizontalScrolling: false
|
||||
};
|
||||
|
||||
export class ElementsDragAndDropData<T> implements IDragAndDropData {
|
||||
export class ElementsDragAndDropData<T, TContext = void> implements IDragAndDropData {
|
||||
|
||||
readonly elements: T[];
|
||||
context: TContext | undefined;
|
||||
|
||||
constructor(elements: T[]) {
|
||||
this.elements = elements;
|
||||
|
@ -84,7 +85,7 @@ export class ElementsDragAndDropData<T> implements IDragAndDropData {
|
|||
|
||||
update(): void { }
|
||||
|
||||
getData(): any {
|
||||
getData(): T[] {
|
||||
return this.elements;
|
||||
}
|
||||
}
|
||||
|
@ -99,7 +100,7 @@ export class ExternalElementsDragAndDropData<T> implements IDragAndDropData {
|
|||
|
||||
update(): void { }
|
||||
|
||||
getData(): any {
|
||||
getData(): T[] {
|
||||
return this.elements;
|
||||
}
|
||||
}
|
||||
|
@ -766,7 +767,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
|
|||
let label: string | undefined;
|
||||
|
||||
if (this.dnd.getDragLabel) {
|
||||
label = this.dnd.getDragLabel(elements);
|
||||
label = this.dnd.getDragLabel(elements, event);
|
||||
}
|
||||
|
||||
if (typeof label === 'undefined') {
|
||||
|
@ -846,10 +847,6 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
|
|||
feedback = distinct(feedback).filter(i => i >= -1 && i < this.length).sort();
|
||||
feedback = feedback[0] === -1 ? [-1] : feedback;
|
||||
|
||||
if (feedback.length === 0) {
|
||||
throw new Error('Invalid empty feedback list');
|
||||
}
|
||||
|
||||
if (equalsDragFeedback(this.currentDragFeedback, feedback)) {
|
||||
return true;
|
||||
}
|
||||
|
@ -910,12 +907,16 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
|
|||
this.dnd.drop(dragData, event.element, event.index, event.browserEvent);
|
||||
}
|
||||
|
||||
private onDragEnd(): void {
|
||||
private onDragEnd(event: DragEvent): void {
|
||||
this.canDrop = false;
|
||||
this.teardownDragAndDropScrollTopAnimation();
|
||||
this.clearDragOverFeedback();
|
||||
this.currentDragData = undefined;
|
||||
StaticDND.CurrentDragAndDropData = undefined;
|
||||
|
||||
if (this.dnd.onDragEnd) {
|
||||
this.dnd.onDragEnd(event);
|
||||
}
|
||||
}
|
||||
|
||||
private clearDragOverFeedback(): void {
|
||||
|
|
|
@ -1062,9 +1062,9 @@ class ListViewDragAndDrop<T> implements IListViewDragAndDrop<T> {
|
|||
return this.dnd.getDragURI(element);
|
||||
}
|
||||
|
||||
getDragLabel?(elements: T[]): string | undefined {
|
||||
getDragLabel?(elements: T[], originalEvent: DragEvent): string | undefined {
|
||||
if (this.dnd.getDragLabel) {
|
||||
return this.dnd.getDragLabel(elements);
|
||||
return this.dnd.getDragLabel(elements, originalEvent);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
|
@ -1080,6 +1080,12 @@ class ListViewDragAndDrop<T> implements IListViewDragAndDrop<T> {
|
|||
return this.dnd.onDragOver(data, targetElement, targetIndex, originalEvent);
|
||||
}
|
||||
|
||||
onDragEnd(originalEvent: DragEvent): void {
|
||||
if (this.dnd.onDragEnd) {
|
||||
this.dnd.onDragEnd(originalEvent);
|
||||
}
|
||||
}
|
||||
|
||||
drop(data: IDragAndDropData, targetElement: T, targetIndex: number, originalEvent: DragEvent): void {
|
||||
this.dnd.drop(data, targetElement, targetIndex, originalEvent);
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import * as dom from 'vs/base/browser/dom';
|
|||
import * as arrays from 'vs/base/common/arrays';
|
||||
import { ISelectBoxDelegate, ISelectOptionItem, ISelectBoxOptions, ISelectBoxStyles, ISelectData } from 'vs/base/browser/ui/selectBox/selectBox';
|
||||
import { isMacintosh } from 'vs/base/common/platform';
|
||||
import { Gesture, EventType } from 'vs/base/browser/touch';
|
||||
|
||||
export class SelectBoxNative extends Disposable implements ISelectBoxDelegate {
|
||||
|
||||
|
@ -43,6 +44,12 @@ export class SelectBoxNative extends Disposable implements ISelectBoxDelegate {
|
|||
}
|
||||
|
||||
private registerListeners() {
|
||||
this._register(Gesture.addTarget(this.selectElement));
|
||||
[EventType.Tap].forEach(eventType => {
|
||||
this._register(dom.addDisposableListener(this.selectElement, eventType, (e) => {
|
||||
this.selectElement.focus();
|
||||
}));
|
||||
});
|
||||
|
||||
this._register(dom.addStandardDisposableListener(this.selectElement, 'change', (e) => {
|
||||
this.selectElement.title = e.target.value;
|
||||
|
|
|
@ -27,10 +27,24 @@ import { clamp } from 'vs/base/common/numbers';
|
|||
import { ScrollEvent } from 'vs/base/common/scrollable';
|
||||
import { SetMap } from 'vs/base/common/collections';
|
||||
|
||||
class TreeElementsDragAndDropData<T, TFilterData, TContext> extends ElementsDragAndDropData<T, TContext> {
|
||||
|
||||
set context(context: TContext | undefined) {
|
||||
this.data.context = context;
|
||||
}
|
||||
|
||||
get context(): TContext | undefined {
|
||||
return this.data.context;
|
||||
}
|
||||
|
||||
constructor(private data: ElementsDragAndDropData<ITreeNode<T, TFilterData>, TContext>) {
|
||||
super(data.elements.map(node => node.element));
|
||||
}
|
||||
}
|
||||
|
||||
function asTreeDragAndDropData<T, TFilterData>(data: IDragAndDropData): IDragAndDropData {
|
||||
if (data instanceof ElementsDragAndDropData) {
|
||||
const nodes = (data as ElementsDragAndDropData<ITreeNode<T, TFilterData>>).elements;
|
||||
return new ElementsDragAndDropData(nodes.map(node => node.element));
|
||||
return new TreeElementsDragAndDropData(data);
|
||||
}
|
||||
|
||||
return data;
|
||||
|
@ -47,9 +61,9 @@ class TreeNodeListDragAndDrop<T, TFilterData, TRef> implements IListDragAndDrop<
|
|||
return this.dnd.getDragURI(node.element);
|
||||
}
|
||||
|
||||
getDragLabel(nodes: ITreeNode<T, TFilterData>[]): string | undefined {
|
||||
getDragLabel(nodes: ITreeNode<T, TFilterData>[], originalEvent: DragEvent): string | undefined {
|
||||
if (this.dnd.getDragLabel) {
|
||||
return this.dnd.getDragLabel(nodes.map(node => node.element));
|
||||
return this.dnd.getDragLabel(nodes.map(node => node.element), originalEvent);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
|
@ -87,7 +101,7 @@ class TreeNodeListDragAndDrop<T, TFilterData, TRef> implements IListDragAndDrop<
|
|||
}, 500);
|
||||
}
|
||||
|
||||
if (typeof result === 'boolean' || !result.accept || typeof result.bubble === 'undefined') {
|
||||
if (typeof result === 'boolean' || !result.accept || typeof result.bubble === 'undefined' || result.feedback) {
|
||||
if (!raw) {
|
||||
const accept = typeof result === 'boolean' ? result : result.accept;
|
||||
const effect = typeof result === 'boolean' ? undefined : result.effect;
|
||||
|
@ -121,6 +135,12 @@ class TreeNodeListDragAndDrop<T, TFilterData, TRef> implements IListDragAndDrop<
|
|||
|
||||
this.dnd.drop(asTreeDragAndDropData(data), targetNode && targetNode.element, targetIndex, originalEvent);
|
||||
}
|
||||
|
||||
onDragEnd(originalEvent: DragEvent): void {
|
||||
if (this.dnd.onDragEnd) {
|
||||
this.dnd.onDragEnd(originalEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function asListOptions<T, TFilterData, TRef>(modelProvider: () => ITreeModel<T, TFilterData, TRef>, options?: IAbstractTreeOptions<T, TFilterData>): IListOptions<ITreeNode<T, TFilterData>> | undefined {
|
||||
|
|
|
@ -150,10 +150,24 @@ function asTreeContextMenuEvent<TInput, T>(e: ITreeContextMenuEvent<IAsyncDataTr
|
|||
};
|
||||
}
|
||||
|
||||
class AsyncDataTreeElementsDragAndDropData<TInput, T, TContext> extends ElementsDragAndDropData<T, TContext> {
|
||||
|
||||
set context(context: TContext | undefined) {
|
||||
this.data.context = context;
|
||||
}
|
||||
|
||||
get context(): TContext | undefined {
|
||||
return this.data.context;
|
||||
}
|
||||
|
||||
constructor(private data: ElementsDragAndDropData<IAsyncDataTreeNode<TInput, T>, TContext>) {
|
||||
super(data.elements.map(node => node.element as T));
|
||||
}
|
||||
}
|
||||
|
||||
function asAsyncDataTreeDragAndDropData<TInput, T>(data: IDragAndDropData): IDragAndDropData {
|
||||
if (data instanceof ElementsDragAndDropData) {
|
||||
const nodes = (data as ElementsDragAndDropData<IAsyncDataTreeNode<TInput, T>>).elements;
|
||||
return new ElementsDragAndDropData(nodes.map(node => node.element));
|
||||
return new AsyncDataTreeElementsDragAndDropData(data);
|
||||
}
|
||||
|
||||
return data;
|
||||
|
@ -167,9 +181,9 @@ class AsyncDataTreeNodeListDragAndDrop<TInput, T> implements IListDragAndDrop<IA
|
|||
return this.dnd.getDragURI(node.element as T);
|
||||
}
|
||||
|
||||
getDragLabel(nodes: IAsyncDataTreeNode<TInput, T>[]): string | undefined {
|
||||
getDragLabel(nodes: IAsyncDataTreeNode<TInput, T>[], originalEvent: DragEvent): string | undefined {
|
||||
if (this.dnd.getDragLabel) {
|
||||
return this.dnd.getDragLabel(nodes.map(node => node.element as T));
|
||||
return this.dnd.getDragLabel(nodes.map(node => node.element as T), originalEvent);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
|
@ -188,6 +202,12 @@ class AsyncDataTreeNodeListDragAndDrop<TInput, T> implements IListDragAndDrop<IA
|
|||
drop(data: IDragAndDropData, targetNode: IAsyncDataTreeNode<TInput, T> | undefined, targetIndex: number | undefined, originalEvent: DragEvent): void {
|
||||
this.dnd.drop(asAsyncDataTreeDragAndDropData(data), targetNode && targetNode.element as T, targetIndex, originalEvent);
|
||||
}
|
||||
|
||||
onDragEnd(originalEvent: DragEvent): void {
|
||||
if (this.dnd.onDragEnd) {
|
||||
this.dnd.onDragEnd(originalEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function asObjectTreeOptions<TInput, T, TFilterData>(options?: IAsyncDataTreeOptions<T, TFilterData>): IObjectTreeOptions<IAsyncDataTreeNode<TInput, T>, TFilterData> | undefined {
|
||||
|
@ -993,6 +1013,12 @@ class CompressibleAsyncDataTreeRenderer<TInput, T, TFilterData, TTemplateData> i
|
|||
}
|
||||
}
|
||||
|
||||
disposeCompressedElements(node: ITreeNode<ICompressedTreeNode<IAsyncDataTreeNode<TInput, T>>, TFilterData>, index: number, templateData: IDataTreeListTemplateData<TTemplateData>, height: number | undefined): void {
|
||||
if (this.renderer.disposeCompressedElements) {
|
||||
this.renderer.disposeCompressedElements(this.compressibleNodeMapperProvider().map(node) as ITreeNode<ICompressedTreeNode<T>, TFilterData>, index, templateData.templateData, height);
|
||||
}
|
||||
}
|
||||
|
||||
disposeTemplate(templateData: IDataTreeListTemplateData<TTemplateData>): void {
|
||||
this.renderer.disposeTemplate(templateData.templateData);
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import * as dom from 'vs/base/browser/dom';
|
|||
import { IKeyboardEvent, StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { IMouseEvent, StandardMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { Gesture } from 'vs/base/browser/touch';
|
||||
|
||||
export abstract class Widget extends Disposable {
|
||||
|
||||
|
@ -49,4 +50,8 @@ export abstract class Widget extends Disposable {
|
|||
protected onchange(domNode: HTMLElement, listener: (e: Event) => void): void {
|
||||
this._register(dom.addDisposableListener(domNode, dom.EventType.CHANGE, listener));
|
||||
}
|
||||
|
||||
protected ignoreGesture(domNode: HTMLElement): void {
|
||||
Gesture.ignoreTarget(domNode);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,11 +69,11 @@ function validateString(value: string, name: string) {
|
|||
}
|
||||
}
|
||||
|
||||
function isPathSeparator(code: number) {
|
||||
function isPathSeparator(code: number | undefined) {
|
||||
return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH;
|
||||
}
|
||||
|
||||
function isPosixPathSeparator(code: number) {
|
||||
function isPosixPathSeparator(code: number | undefined) {
|
||||
return code === CHAR_FORWARD_SLASH;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ let _isMacintosh = false;
|
|||
let _isLinux = false;
|
||||
let _isNative = false;
|
||||
let _isWeb = false;
|
||||
let _isIOS = false;
|
||||
let _locale: string | undefined = undefined;
|
||||
let _language: string = LANGUAGE_DEFAULT;
|
||||
let _translationsConfigFile: string | undefined = undefined;
|
||||
|
@ -41,6 +42,7 @@ declare const global: any;
|
|||
interface INavigator {
|
||||
userAgent: string;
|
||||
language: string;
|
||||
maxTouchPoints?: number;
|
||||
}
|
||||
declare const navigator: INavigator;
|
||||
declare const self: any;
|
||||
|
@ -52,6 +54,7 @@ if (typeof navigator === 'object' && !isElectronRenderer) {
|
|||
_userAgent = navigator.userAgent;
|
||||
_isWindows = _userAgent.indexOf('Windows') >= 0;
|
||||
_isMacintosh = _userAgent.indexOf('Macintosh') >= 0;
|
||||
_isIOS = _userAgent.indexOf('Macintosh') >= 0 && !!navigator.maxTouchPoints && navigator.maxTouchPoints > 0;
|
||||
_isLinux = _userAgent.indexOf('Linux') >= 0;
|
||||
_isWeb = true;
|
||||
_locale = navigator.language;
|
||||
|
@ -106,6 +109,7 @@ export const isMacintosh = _isMacintosh;
|
|||
export const isLinux = _isLinux;
|
||||
export const isNative = _isNative;
|
||||
export const isWeb = _isWeb;
|
||||
export const isIOS = _isIOS;
|
||||
export const platform = _platform;
|
||||
export const userAgent = _userAgent;
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
'xterm': `${window.location.origin}/static/remote/web/node_modules/xterm/lib/xterm.js`,
|
||||
'xterm-addon-search': `${window.location.origin}/static/remote/web/node_modules/xterm-addon-search/lib/xterm-addon-search.js`,
|
||||
'xterm-addon-web-links': `${window.location.origin}/static/remote/web/node_modules/xterm-addon-web-links/lib/xterm-addon-web-links.js`,
|
||||
'xterm-addon-webgl': `${window.location.origin}/static/remote/web/node_modules/xterm-addon-webgl/lib/xterm-addon-webgl.js`,
|
||||
'semver-umd': `${window.location.origin}/static/remote/web/node_modules/semver-umd/lib/semver-umd.js`,
|
||||
}
|
||||
};
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
'xterm': `${window.location.origin}/static/node_modules/xterm/lib/xterm.js`,
|
||||
'xterm-addon-search': `${window.location.origin}/static/node_modules/xterm-addon-search/lib/xterm-addon-search.js`,
|
||||
'xterm-addon-web-links': `${window.location.origin}/static/node_modules/xterm-addon-web-links/lib/xterm-addon-web-links.js`,
|
||||
'xterm-addon-webgl': `${window.location.origin}/static/node_modules/xterm-addon-webgl/lib/xterm-addon-webgl.js`,
|
||||
'semver-umd': `${window.location.origin}/static/node_modules/semver-umd/lib/semver-umd.js`,
|
||||
}
|
||||
};
|
||||
|
|
|
@ -438,15 +438,8 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
|||
|
||||
// Inject headers when requests are incoming
|
||||
const urls = ['https://marketplace.visualstudio.com/*', 'https://*.vsassets.io/*'];
|
||||
this._win.webContents.session.webRequest.onBeforeSendHeaders({ urls }, (details, cb) => {
|
||||
this.marketplaceHeadersPromise.then(headers => {
|
||||
const requestHeaders = objects.assign(details.requestHeaders, headers) as { [key: string]: string | undefined };
|
||||
if (!this.configurationService.getValue('extensions.disableExperimentalAzureSearch')) {
|
||||
requestHeaders['Cookie'] = `${requestHeaders['Cookie'] ? requestHeaders['Cookie'] + ';' : ''}EnableExternalSearchForVSCode=true`;
|
||||
}
|
||||
cb({ cancel: false, requestHeaders });
|
||||
});
|
||||
});
|
||||
this._win.webContents.session.webRequest.onBeforeSendHeaders({ urls }, (details, cb) =>
|
||||
this.marketplaceHeadersPromise.then(headers => cb({ cancel: false, requestHeaders: objects.assign(details.requestHeaders, headers) as { [key: string]: string | undefined } })));
|
||||
}
|
||||
|
||||
private onWindowError(error: WindowError): void {
|
||||
|
@ -1096,7 +1089,7 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
|||
private createTouchBarGroupSegments(items: ISerializableCommandAction[] = []): ITouchBarSegment[] {
|
||||
const segments: ITouchBarSegment[] = items.map(item => {
|
||||
let icon: NativeImage | undefined;
|
||||
if (item.iconLocation && item.iconLocation.dark.scheme === 'file') {
|
||||
if (item.iconLocation && item.iconLocation?.dark?.scheme === 'file') {
|
||||
icon = nativeImage.createFromPath(URI.revive(item.iconLocation.dark).fsPath);
|
||||
if (icon.isEmpty()) {
|
||||
icon = undefined;
|
||||
|
|
|
@ -27,7 +27,7 @@ import { EditorOption } from 'vs/editor/common/config/editorOptions';
|
|||
* Merges mouse events when mouse move events are throttled
|
||||
*/
|
||||
export function createMouseMoveEventMerger(mouseTargetFactory: MouseTargetFactory | null) {
|
||||
return function (lastEvent: EditorMouseEvent, currentEvent: EditorMouseEvent): EditorMouseEvent {
|
||||
return function (lastEvent: EditorMouseEvent | null, currentEvent: EditorMouseEvent): EditorMouseEvent {
|
||||
let targetIsWidget = false;
|
||||
if (mouseTargetFactory) {
|
||||
targetIsWidget = mouseTargetFactory.mouseTargetIsWidget(currentEvent);
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { EventType, Gesture, GestureEvent } from 'vs/base/browser/touch';
|
||||
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IPointerHandlerHelper, MouseHandler, createMouseMoveEventMerger } from 'vs/editor/browser/controller/mouseHandler';
|
||||
|
@ -18,7 +19,7 @@ interface IThrottledGestureEvent {
|
|||
translationY: number;
|
||||
}
|
||||
|
||||
function gestureChangeEventMerger(lastEvent: IThrottledGestureEvent, currentEvent: MSGestureEvent): IThrottledGestureEvent {
|
||||
function gestureChangeEventMerger(lastEvent: IThrottledGestureEvent | null, currentEvent: MSGestureEvent): IThrottledGestureEvent {
|
||||
const r = {
|
||||
translationY: currentEvent.translationY,
|
||||
translationX: currentEvent.translationX
|
||||
|
@ -53,7 +54,7 @@ class MsPointerHandler extends MouseHandler implements IDisposable {
|
|||
const penGesture = new MSGesture();
|
||||
touchGesture.target = this.viewHelper.linesContentDomNode;
|
||||
penGesture.target = this.viewHelper.linesContentDomNode;
|
||||
this.viewHelper.linesContentDomNode.addEventListener('MSPointerDown', (e: MSPointerEvent) => {
|
||||
this.viewHelper.linesContentDomNode.addEventListener(<any>'MSPointerDown', (e: MSPointerEvent) => {
|
||||
// Circumvent IE11 breaking change in e.pointerType & TypeScript's stale definitions
|
||||
const pointerType = <any>e.pointerType;
|
||||
if (pointerType === ((<any>e).MSPOINTER_TYPE_MOUSE || 'mouse')) {
|
||||
|
@ -67,7 +68,7 @@ class MsPointerHandler extends MouseHandler implements IDisposable {
|
|||
penGesture.addPointer(e.pointerId);
|
||||
}
|
||||
});
|
||||
this._register(dom.addDisposableThrottledListener<IThrottledGestureEvent>(this.viewHelper.linesContentDomNode, 'MSGestureChange', (e) => this._onGestureChange(e), gestureChangeEventMerger));
|
||||
this._register(dom.addDisposableThrottledListener<IThrottledGestureEvent, MSGestureEvent>(this.viewHelper.linesContentDomNode, 'MSGestureChange', (e) => this._onGestureChange(e), gestureChangeEventMerger));
|
||||
this._register(dom.addDisposableListener(this.viewHelper.linesContentDomNode, 'MSGestureTap', (e) => this._onCaptureGestureTap(e), true));
|
||||
}
|
||||
}, 100);
|
||||
|
@ -132,7 +133,7 @@ class StandardPointerHandler extends MouseHandler implements IDisposable {
|
|||
const penGesture = new MSGesture();
|
||||
touchGesture.target = this.viewHelper.linesContentDomNode;
|
||||
penGesture.target = this.viewHelper.linesContentDomNode;
|
||||
this.viewHelper.linesContentDomNode.addEventListener('pointerdown', (e: MSPointerEvent) => {
|
||||
this.viewHelper.linesContentDomNode.addEventListener('pointerdown', (e: PointerEvent) => {
|
||||
const pointerType = <any>e.pointerType;
|
||||
if (pointerType === 'mouse') {
|
||||
this._lastPointerType = 'mouse';
|
||||
|
@ -145,7 +146,7 @@ class StandardPointerHandler extends MouseHandler implements IDisposable {
|
|||
penGesture.addPointer(e.pointerId);
|
||||
}
|
||||
});
|
||||
this._register(dom.addDisposableThrottledListener<IThrottledGestureEvent>(this.viewHelper.linesContentDomNode, 'MSGestureChange', (e) => this._onGestureChange(e), gestureChangeEventMerger));
|
||||
this._register(dom.addDisposableThrottledListener<IThrottledGestureEvent, MSGestureEvent>(this.viewHelper.linesContentDomNode, 'MSGestureChange', (e) => this._onGestureChange(e), gestureChangeEventMerger));
|
||||
this._register(dom.addDisposableListener(this.viewHelper.linesContentDomNode, 'MSGestureTap', (e) => this._onCaptureGestureTap(e), true));
|
||||
}
|
||||
}, 100);
|
||||
|
@ -283,7 +284,7 @@ export class PointerHandler extends Disposable {
|
|||
super();
|
||||
if (window.navigator.msPointerEnabled) {
|
||||
this.handler = this._register(new MsPointerHandler(context, viewController, viewHelper));
|
||||
} else if (((<any>window).PointerEvent && BrowserFeatures.pointerEvents)) {
|
||||
} else if ((platform.isIOS && BrowserFeatures.pointerEvents)) {
|
||||
this.handler = this._register(new PointerEventHandler(context, viewController, viewHelper));
|
||||
} else if ((<any>window).TouchEvent) {
|
||||
this.handler = this._register(new TouchHandler(context, viewController, viewHelper));
|
||||
|
|
|
@ -16,7 +16,7 @@ import * as editorCommon from 'vs/editor/common/editorCommon';
|
|||
import { IIdentifiedSingleEditOperation, IModelDecoration, IModelDeltaDecoration, ITextModel, ICursorStateComputer } from 'vs/editor/common/model';
|
||||
import { IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelLanguageConfigurationChangedEvent, IModelOptionsChangedEvent } from 'vs/editor/common/model/textModelEvents';
|
||||
import { OverviewRulerZone } from 'vs/editor/common/view/overviewZoneManager';
|
||||
import { IEditorWhitespace } from 'vs/editor/common/viewLayout/whitespaceComputer';
|
||||
import { IEditorWhitespace } from 'vs/editor/common/viewLayout/linesLayout';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IDiffComputationResult } from 'vs/editor/common/services/editorWorkerService';
|
||||
|
||||
|
|
|
@ -84,7 +84,7 @@ export class EditorMouseEvent extends StandardMouseEvent {
|
|||
}
|
||||
|
||||
export interface EditorMouseEventMerger {
|
||||
(lastEvent: EditorMouseEvent, currentEvent: EditorMouseEvent): EditorMouseEvent;
|
||||
(lastEvent: EditorMouseEvent | null, currentEvent: EditorMouseEvent): EditorMouseEvent;
|
||||
}
|
||||
|
||||
export class EditorMouseEventFactory {
|
||||
|
@ -124,7 +124,7 @@ export class EditorMouseEventFactory {
|
|||
}
|
||||
|
||||
public onMouseMoveThrottled(target: HTMLElement, callback: (e: EditorMouseEvent) => void, merger: EditorMouseEventMerger, minimumTimeMs: number): IDisposable {
|
||||
const myMerger: dom.IEventMerger<EditorMouseEvent, MouseEvent> = (lastEvent: EditorMouseEvent, currentEvent: MouseEvent): EditorMouseEvent => {
|
||||
const myMerger: dom.IEventMerger<EditorMouseEvent, MouseEvent> = (lastEvent: EditorMouseEvent | null, currentEvent: MouseEvent): EditorMouseEvent => {
|
||||
return merger(lastEvent, this._create(currentEvent));
|
||||
};
|
||||
return dom.addDisposableThrottledListener<EditorMouseEvent, MouseEvent>(target, 'mousemove', callback, myMerger, minimumTimeMs);
|
||||
|
@ -162,7 +162,7 @@ export class EditorPointerEventFactory {
|
|||
}
|
||||
|
||||
public onPointerMoveThrottled(target: HTMLElement, callback: (e: EditorMouseEvent) => void, merger: EditorMouseEventMerger, minimumTimeMs: number): IDisposable {
|
||||
const myMerger: dom.IEventMerger<EditorMouseEvent, MouseEvent> = (lastEvent: EditorMouseEvent, currentEvent: MouseEvent): EditorMouseEvent => {
|
||||
const myMerger: dom.IEventMerger<EditorMouseEvent, MouseEvent> = (lastEvent: EditorMouseEvent | null, currentEvent: MouseEvent): EditorMouseEvent => {
|
||||
return merger(lastEvent, this._create(currentEvent));
|
||||
};
|
||||
return dom.addDisposableThrottledListener<EditorMouseEvent, MouseEvent>(target, 'pointermove', callback, myMerger, minimumTimeMs);
|
||||
|
@ -195,7 +195,7 @@ export class GlobalEditorMouseMoveMonitor extends Disposable {
|
|||
this._globalMouseMoveMonitor.stopMonitoring(true);
|
||||
}, true);
|
||||
|
||||
const myMerger: dom.IEventMerger<EditorMouseEvent, MouseEvent> = (lastEvent: EditorMouseEvent, currentEvent: MouseEvent): EditorMouseEvent => {
|
||||
const myMerger: dom.IEventMerger<EditorMouseEvent, MouseEvent> = (lastEvent: EditorMouseEvent | null, currentEvent: MouseEvent): EditorMouseEvent => {
|
||||
return merger(lastEvent, new EditorMouseEvent(currentEvent, this._editorViewDomNode));
|
||||
};
|
||||
|
||||
|
|
|
@ -307,7 +307,7 @@ export function registerEditorContribution<Services extends BrandedService[]>(id
|
|||
EditorContributionRegistry.INSTANCE.registerEditorContribution(id, ctor);
|
||||
}
|
||||
|
||||
export function registerDiffEditorContribution(id: string, ctor: IDiffEditorContributionCtor): void {
|
||||
export function registerDiffEditorContribution<Services extends BrandedService[]>(id: string, ctor: { new(editor: IDiffEditor, ...services: Services): IEditorContribution }): void {
|
||||
EditorContributionRegistry.INSTANCE.registerDiffEditorContribution(id, ctor);
|
||||
}
|
||||
|
||||
|
@ -363,7 +363,7 @@ class EditorContributionRegistry {
|
|||
return this.editorContributions.slice(0);
|
||||
}
|
||||
|
||||
public registerDiffEditorContribution(id: string, ctor: IDiffEditorContributionCtor): void {
|
||||
public registerDiffEditorContribution<Services extends BrandedService[]>(id: string, ctor: { new(editor: IDiffEditor, ...services: Services): IEditorContribution }): void {
|
||||
this.diffEditorContributions.push({ id, ctor });
|
||||
}
|
||||
|
||||
|
|
|
@ -4,58 +4,132 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { LinkedList } from 'vs/base/common/linkedList';
|
||||
import { parse } from 'vs/base/common/marshalling';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import * as resources from 'vs/base/common/resources';
|
||||
import { equalsIgnoreCase } from 'vs/base/common/strings';
|
||||
import { normalizePath } from 'vs/base/common/resources';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IOpener, IOpenerService, IValidator, IExternalUriResolver, OpenOptions, ResolveExternalUriOptions, IResolvedExternalUri, IExternalOpener } from 'vs/platform/opener/common/opener';
|
||||
import { IOpener, IOpenerService, IValidator, IExternalUriResolver, OpenOptions, ResolveExternalUriOptions, IResolvedExternalUri, IExternalOpener, matchesScheme } from 'vs/platform/opener/common/opener';
|
||||
import { EditorOpenContext } from 'vs/platform/editor/common/editor';
|
||||
|
||||
export class OpenerService extends Disposable implements IOpenerService {
|
||||
|
||||
class CommandOpener implements IOpener {
|
||||
|
||||
constructor(@ICommandService private readonly _commandService: ICommandService) { }
|
||||
|
||||
async open(target: URI | string) {
|
||||
if (!matchesScheme(target, Schemas.command)) {
|
||||
return false;
|
||||
}
|
||||
// run command or bail out if command isn't known
|
||||
if (typeof target === 'string') {
|
||||
target = URI.parse(target);
|
||||
}
|
||||
if (!CommandsRegistry.getCommand(target.path)) {
|
||||
throw new Error(`command '${target.path}' NOT known`);
|
||||
}
|
||||
// execute as command
|
||||
let args: any = [];
|
||||
try {
|
||||
args = parse(target.query);
|
||||
if (!Array.isArray(args)) {
|
||||
args = [args];
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore error
|
||||
}
|
||||
await this._commandService.executeCommand(target.path, ...args);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class EditorOpener implements IOpener {
|
||||
|
||||
constructor(@ICodeEditorService private readonly _editorService: ICodeEditorService) { }
|
||||
|
||||
async open(target: URI | string, options: OpenOptions) {
|
||||
if (typeof target === 'string') {
|
||||
target = URI.parse(target);
|
||||
}
|
||||
let selection: { startLineNumber: number; startColumn: number; } | undefined = undefined;
|
||||
const match = /^L?(\d+)(?:,(\d+))?/.exec(target.fragment);
|
||||
if (match) {
|
||||
// support file:///some/file.js#73,84
|
||||
// support file:///some/file.js#L73
|
||||
selection = {
|
||||
startLineNumber: parseInt(match[1]),
|
||||
startColumn: match[2] ? parseInt(match[2]) : 1
|
||||
};
|
||||
// remove fragment
|
||||
target = target.with({ fragment: '' });
|
||||
}
|
||||
|
||||
if (target.scheme === Schemas.file) {
|
||||
target = normalizePath(target); // workaround for non-normalized paths (https://github.com/Microsoft/vscode/issues/12954)
|
||||
}
|
||||
|
||||
await this._editorService.openCodeEditor(
|
||||
{ resource: target, options: { selection, context: options?.fromUserGesture ? EditorOpenContext.USER : EditorOpenContext.API } },
|
||||
this._editorService.getFocusedCodeEditor(),
|
||||
options?.openToSide
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export class OpenerService implements IOpenerService {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
|
||||
private readonly _openers = new LinkedList<IOpener>();
|
||||
private readonly _validators = new LinkedList<IValidator>();
|
||||
private readonly _resolvers = new LinkedList<IExternalUriResolver>();
|
||||
|
||||
private _externalOpener: IExternalOpener;
|
||||
|
||||
constructor(
|
||||
@ICodeEditorService private readonly _editorService: ICodeEditorService,
|
||||
@ICommandService private readonly _commandService: ICommandService,
|
||||
@ICodeEditorService editorService: ICodeEditorService,
|
||||
@ICommandService commandService: ICommandService,
|
||||
) {
|
||||
super();
|
||||
|
||||
// Default external opener is going through window.open()
|
||||
this._externalOpener = {
|
||||
openExternal: href => {
|
||||
dom.windowOpenNoOpener(href);
|
||||
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
};
|
||||
|
||||
// Default opener: maito, http(s), command, and catch-all-editors
|
||||
this._openers.push({
|
||||
open: async (target: URI | string, options?: OpenOptions) => {
|
||||
if (options?.openExternal || matchesScheme(target, Schemas.mailto) || matchesScheme(target, Schemas.http) || matchesScheme(target, Schemas.https)) {
|
||||
// open externally
|
||||
await this._doOpenExternal(target, options);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
this._openers.push(new CommandOpener(commandService));
|
||||
this._openers.push(new EditorOpener(editorService));
|
||||
}
|
||||
|
||||
registerOpener(opener: IOpener): IDisposable {
|
||||
const remove = this._openers.push(opener);
|
||||
|
||||
const remove = this._openers.unshift(opener);
|
||||
return { dispose: remove };
|
||||
}
|
||||
|
||||
registerValidator(validator: IValidator): IDisposable {
|
||||
const remove = this._validators.push(validator);
|
||||
|
||||
return { dispose: remove };
|
||||
}
|
||||
|
||||
registerExternalUriResolver(resolver: IExternalUriResolver): IDisposable {
|
||||
const remove = this._resolvers.push(resolver);
|
||||
|
||||
return { dispose: remove };
|
||||
}
|
||||
|
||||
|
@ -63,30 +137,24 @@ export class OpenerService extends Disposable implements IOpenerService {
|
|||
this._externalOpener = externalOpener;
|
||||
}
|
||||
|
||||
async open(resource: URI, options?: OpenOptions): Promise<boolean> {
|
||||
|
||||
// no scheme ?!?
|
||||
if (!resource.scheme) {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
async open(target: URI | string, options?: OpenOptions): Promise<boolean> {
|
||||
|
||||
// check with contributed validators
|
||||
for (const validator of this._validators.toArray()) {
|
||||
if (!(await validator.shouldOpen(resource))) {
|
||||
if (!(await validator.shouldOpen(target))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// check with contributed openers
|
||||
for (const opener of this._openers.toArray()) {
|
||||
const handled = await opener.open(resource, options);
|
||||
const handled = await opener.open(target, options);
|
||||
if (handled) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// use default openers
|
||||
return this._doOpen(resource, options);
|
||||
return false;
|
||||
}
|
||||
|
||||
async resolveExternalUri(resource: URI, options?: ResolveExternalUriOptions): Promise<IResolvedExternalUri> {
|
||||
|
@ -100,68 +168,19 @@ export class OpenerService extends Disposable implements IOpenerService {
|
|||
return { resolved: resource, dispose: () => { } };
|
||||
}
|
||||
|
||||
private async _doOpen(resource: URI, options: OpenOptions | undefined): Promise<boolean> {
|
||||
const { scheme, path, query, fragment } = resource;
|
||||
private async _doOpenExternal(resource: URI | string, options: OpenOptions | undefined): Promise<boolean> {
|
||||
|
||||
if (options?.openExternal || equalsIgnoreCase(scheme, Schemas.mailto) || equalsIgnoreCase(scheme, Schemas.http) || equalsIgnoreCase(scheme, Schemas.https)) {
|
||||
// open externally
|
||||
return this._doOpenExternal(resource, options);
|
||||
//todo@joh IExternalUriResolver should support `uri: URI | string`
|
||||
const uri = typeof resource === 'string' ? URI.parse(resource) : resource;
|
||||
const { resolved } = await this.resolveExternalUri(uri, options);
|
||||
|
||||
if (typeof resource === 'string' && uri.toString() === resolved.toString()) {
|
||||
// open the url-string AS IS
|
||||
return this._externalOpener.openExternal(resource);
|
||||
} else {
|
||||
// open URI using the toString(noEncode)+encodeURI-trick
|
||||
return this._externalOpener.openExternal(encodeURI(resolved.toString(true)));
|
||||
}
|
||||
|
||||
if (equalsIgnoreCase(scheme, Schemas.command)) {
|
||||
// run command or bail out if command isn't known
|
||||
if (!CommandsRegistry.getCommand(path)) {
|
||||
throw new Error(`command '${path}' NOT known`);
|
||||
}
|
||||
// execute as command
|
||||
let args: any = [];
|
||||
try {
|
||||
args = parse(query);
|
||||
if (!Array.isArray(args)) {
|
||||
args = [args];
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore error
|
||||
}
|
||||
|
||||
await this._commandService.executeCommand(path, ...args);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// finally open in editor
|
||||
let selection: { startLineNumber: number; startColumn: number; } | undefined = undefined;
|
||||
const match = /^L?(\d+)(?:,(\d+))?/.exec(fragment);
|
||||
if (match) {
|
||||
// support file:///some/file.js#73,84
|
||||
// support file:///some/file.js#L73
|
||||
selection = {
|
||||
startLineNumber: parseInt(match[1]),
|
||||
startColumn: match[2] ? parseInt(match[2]) : 1
|
||||
};
|
||||
// remove fragment
|
||||
resource = resource.with({ fragment: '' });
|
||||
}
|
||||
|
||||
if (resource.scheme === Schemas.file) {
|
||||
resource = resources.normalizePath(resource); // workaround for non-normalized paths (https://github.com/Microsoft/vscode/issues/12954)
|
||||
}
|
||||
|
||||
await this._editorService.openCodeEditor(
|
||||
{ resource, options: { selection, context: options?.fromUserGesture ? EditorOpenContext.USER : EditorOpenContext.API } },
|
||||
this._editorService.getFocusedCodeEditor(),
|
||||
options?.openToSide
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private async _doOpenExternal(resource: URI, options: OpenOptions | undefined): Promise<boolean> {
|
||||
const { resolved } = await this.resolveExternalUri(resource, options);
|
||||
|
||||
// TODO@Jo neither encodeURI nor toString(true) should be needed
|
||||
// once we go with URL and not URI
|
||||
return this._externalOpener.openExternal(encodeURI(resolved.toString(true)));
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
|
|
@ -11,7 +11,7 @@ import { IDisposable } from 'vs/base/common/lifecycle';
|
|||
import { IPointerHandlerHelper } from 'vs/editor/browser/controller/mouseHandler';
|
||||
import { PointerHandler } from 'vs/editor/browser/controller/pointerHandler';
|
||||
import { ITextAreaHandlerHelper, TextAreaHandler } from 'vs/editor/browser/controller/textAreaHandler';
|
||||
import * as editorBrowser from 'vs/editor/browser/editorBrowser';
|
||||
import { IContentWidget, IContentWidgetPosition, IOverlayWidget, IOverlayWidgetPosition, IMouseTarget, IViewZoneChangeAccessor } from 'vs/editor/browser/editorBrowser';
|
||||
import { ICommandDelegate, ViewController } from 'vs/editor/browser/view/viewController';
|
||||
import { ViewOutgoingEvents } from 'vs/editor/browser/view/viewOutgoingEvents';
|
||||
import { ContentViewOverlays, MarginViewOverlays } from 'vs/editor/browser/view/viewOverlays';
|
||||
|
@ -51,17 +51,15 @@ import { EditorOption } from 'vs/editor/common/config/editorOptions';
|
|||
|
||||
|
||||
export interface IContentWidgetData {
|
||||
widget: editorBrowser.IContentWidget;
|
||||
position: editorBrowser.IContentWidgetPosition | null;
|
||||
widget: IContentWidget;
|
||||
position: IContentWidgetPosition | null;
|
||||
}
|
||||
|
||||
export interface IOverlayWidgetData {
|
||||
widget: editorBrowser.IOverlayWidget;
|
||||
position: editorBrowser.IOverlayWidgetPosition | null;
|
||||
widget: IOverlayWidget;
|
||||
position: IOverlayWidgetPosition | null;
|
||||
}
|
||||
|
||||
const invalidFunc = () => { throw new Error(`Invalid change accessor`); };
|
||||
|
||||
export class View extends ViewEventHandler {
|
||||
|
||||
private readonly eventDispatcher: ViewEventDispatcher;
|
||||
|
@ -348,7 +346,7 @@ export class View extends ViewEventHandler {
|
|||
super.dispose();
|
||||
}
|
||||
|
||||
private _renderOnce(callback: () => any): any {
|
||||
private _renderOnce<T>(callback: () => T): T {
|
||||
const r = safeInvokeNoArg(callback);
|
||||
this._scheduleRender();
|
||||
return r;
|
||||
|
@ -458,7 +456,7 @@ export class View extends ViewEventHandler {
|
|||
return visibleRange.left;
|
||||
}
|
||||
|
||||
public getTargetAtClientPoint(clientX: number, clientY: number): editorBrowser.IMouseTarget | null {
|
||||
public getTargetAtClientPoint(clientX: number, clientY: number): IMouseTarget | null {
|
||||
return this.pointerHandler.getTargetAtClientPoint(clientX, clientY);
|
||||
}
|
||||
|
||||
|
@ -466,42 +464,15 @@ export class View extends ViewEventHandler {
|
|||
return new OverviewRuler(this._context, cssClassName);
|
||||
}
|
||||
|
||||
public change(callback: (changeAccessor: editorBrowser.IViewZoneChangeAccessor) => any): boolean {
|
||||
let zonesHaveChanged = false;
|
||||
|
||||
this._renderOnce(() => {
|
||||
const changeAccessor: editorBrowser.IViewZoneChangeAccessor = {
|
||||
addZone: (zone: editorBrowser.IViewZone): string => {
|
||||
zonesHaveChanged = true;
|
||||
return this.viewZones.addZone(zone);
|
||||
},
|
||||
removeZone: (id: string): void => {
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
zonesHaveChanged = this.viewZones.removeZone(id) || zonesHaveChanged;
|
||||
},
|
||||
layoutZone: (id: string): void => {
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
zonesHaveChanged = this.viewZones.layoutZone(id) || zonesHaveChanged;
|
||||
}
|
||||
};
|
||||
|
||||
safeInvoke1Arg(callback, changeAccessor);
|
||||
|
||||
// Invalidate changeAccessor
|
||||
changeAccessor.addZone = invalidFunc;
|
||||
changeAccessor.removeZone = invalidFunc;
|
||||
changeAccessor.layoutZone = invalidFunc;
|
||||
|
||||
public change(callback: (changeAccessor: IViewZoneChangeAccessor) => any): boolean {
|
||||
return this._renderOnce(() => {
|
||||
const zonesHaveChanged = this.viewZones.changeViewZones(callback);
|
||||
if (zonesHaveChanged) {
|
||||
this._context.viewLayout.onHeightMaybeChanged();
|
||||
this._context.privateViewEventBus.emit(new viewEvents.ViewZonesChangedEvent());
|
||||
}
|
||||
return zonesHaveChanged;
|
||||
});
|
||||
return zonesHaveChanged;
|
||||
}
|
||||
|
||||
public render(now: boolean, everything: boolean): void {
|
||||
|
@ -582,10 +553,3 @@ function safeInvokeNoArg(func: Function): any {
|
|||
}
|
||||
}
|
||||
|
||||
function safeInvoke1Arg(func: Function, arg1: any): any {
|
||||
try {
|
||||
return func(arg1);
|
||||
} catch (e) {
|
||||
onUnexpectedError(e);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -177,7 +177,7 @@ export class GlyphMarginOverlay extends DedupOverlay {
|
|||
output[lineIndex] = '';
|
||||
} else {
|
||||
output[lineIndex] = (
|
||||
'<div class="cgmr '
|
||||
'<div class="cgmr codicon '
|
||||
+ classNames.join(' ')
|
||||
+ common
|
||||
);
|
||||
|
|
|
@ -407,7 +407,7 @@ class FastRenderedViewLine implements IRenderedViewLine {
|
|||
*/
|
||||
class RenderedViewLine implements IRenderedViewLine {
|
||||
|
||||
public domNode: FastDomNode<HTMLElement>;
|
||||
public domNode: FastDomNode<HTMLElement> | null;
|
||||
public readonly input: RenderLineInput;
|
||||
|
||||
protected readonly _characterMapping: CharacterMapping;
|
||||
|
@ -420,7 +420,7 @@ class RenderedViewLine implements IRenderedViewLine {
|
|||
*/
|
||||
private readonly _pixelOffsetCache: Int32Array | null;
|
||||
|
||||
constructor(domNode: FastDomNode<HTMLElement>, renderLineInput: RenderLineInput, characterMapping: CharacterMapping, containsRTL: boolean, containsForeignElements: ForeignElementType) {
|
||||
constructor(domNode: FastDomNode<HTMLElement> | null, renderLineInput: RenderLineInput, characterMapping: CharacterMapping, containsRTL: boolean, containsForeignElements: ForeignElementType) {
|
||||
this.domNode = domNode;
|
||||
this.input = renderLineInput;
|
||||
this._characterMapping = characterMapping;
|
||||
|
@ -439,16 +439,19 @@ class RenderedViewLine implements IRenderedViewLine {
|
|||
|
||||
// --- Reading from the DOM methods
|
||||
|
||||
protected _getReadingTarget(): HTMLElement {
|
||||
return <HTMLSpanElement>this.domNode.domNode.firstChild;
|
||||
protected _getReadingTarget(myDomNode: FastDomNode<HTMLElement>): HTMLElement {
|
||||
return <HTMLSpanElement>myDomNode.domNode.firstChild;
|
||||
}
|
||||
|
||||
/**
|
||||
* Width of the line in pixels
|
||||
*/
|
||||
public getWidth(): number {
|
||||
if (!this.domNode) {
|
||||
return 0;
|
||||
}
|
||||
if (this._cachedWidth === -1) {
|
||||
this._cachedWidth = this._getReadingTarget().offsetWidth;
|
||||
this._cachedWidth = this._getReadingTarget(this.domNode).offsetWidth;
|
||||
}
|
||||
return this._cachedWidth;
|
||||
}
|
||||
|
@ -464,14 +467,17 @@ class RenderedViewLine implements IRenderedViewLine {
|
|||
* Visible ranges for a model range
|
||||
*/
|
||||
public getVisibleRangesForRange(startColumn: number, endColumn: number, context: DomReadingContext): HorizontalRange[] | null {
|
||||
if (!this.domNode) {
|
||||
return null;
|
||||
}
|
||||
if (this._pixelOffsetCache !== null) {
|
||||
// the text is LTR
|
||||
const startOffset = this._readPixelOffset(startColumn, context);
|
||||
const startOffset = this._readPixelOffset(this.domNode, startColumn, context);
|
||||
if (startOffset === -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const endOffset = this._readPixelOffset(endColumn, context);
|
||||
const endOffset = this._readPixelOffset(this.domNode, endColumn, context);
|
||||
if (endOffset === -1) {
|
||||
return null;
|
||||
}
|
||||
|
@ -479,23 +485,23 @@ class RenderedViewLine implements IRenderedViewLine {
|
|||
return [new HorizontalRange(startOffset, endOffset - startOffset)];
|
||||
}
|
||||
|
||||
return this._readVisibleRangesForRange(startColumn, endColumn, context);
|
||||
return this._readVisibleRangesForRange(this.domNode, startColumn, endColumn, context);
|
||||
}
|
||||
|
||||
protected _readVisibleRangesForRange(startColumn: number, endColumn: number, context: DomReadingContext): HorizontalRange[] | null {
|
||||
protected _readVisibleRangesForRange(domNode: FastDomNode<HTMLElement>, startColumn: number, endColumn: number, context: DomReadingContext): HorizontalRange[] | null {
|
||||
if (startColumn === endColumn) {
|
||||
const pixelOffset = this._readPixelOffset(startColumn, context);
|
||||
const pixelOffset = this._readPixelOffset(domNode, startColumn, context);
|
||||
if (pixelOffset === -1) {
|
||||
return null;
|
||||
} else {
|
||||
return [new HorizontalRange(pixelOffset, 0)];
|
||||
}
|
||||
} else {
|
||||
return this._readRawVisibleRangesForRange(startColumn, endColumn, context);
|
||||
return this._readRawVisibleRangesForRange(domNode, startColumn, endColumn, context);
|
||||
}
|
||||
}
|
||||
|
||||
protected _readPixelOffset(column: number, context: DomReadingContext): number {
|
||||
protected _readPixelOffset(domNode: FastDomNode<HTMLElement>, column: number, context: DomReadingContext): number {
|
||||
if (this._characterMapping.length === 0) {
|
||||
// This line has no content
|
||||
if (this._containsForeignElements === ForeignElementType.None) {
|
||||
|
@ -520,18 +526,18 @@ class RenderedViewLine implements IRenderedViewLine {
|
|||
return cachedPixelOffset;
|
||||
}
|
||||
|
||||
const result = this._actualReadPixelOffset(column, context);
|
||||
const result = this._actualReadPixelOffset(domNode, column, context);
|
||||
this._pixelOffsetCache[column] = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
return this._actualReadPixelOffset(column, context);
|
||||
return this._actualReadPixelOffset(domNode, column, context);
|
||||
}
|
||||
|
||||
private _actualReadPixelOffset(column: number, context: DomReadingContext): number {
|
||||
private _actualReadPixelOffset(domNode: FastDomNode<HTMLElement>, column: number, context: DomReadingContext): number {
|
||||
if (this._characterMapping.length === 0) {
|
||||
// This line has no content
|
||||
const r = RangeUtil.readHorizontalRanges(this._getReadingTarget(), 0, 0, 0, 0, context.clientRectDeltaLeft, context.endNode);
|
||||
const r = RangeUtil.readHorizontalRanges(this._getReadingTarget(domNode), 0, 0, 0, 0, context.clientRectDeltaLeft, context.endNode);
|
||||
if (!r || r.length === 0) {
|
||||
return -1;
|
||||
}
|
||||
|
@ -547,14 +553,14 @@ class RenderedViewLine implements IRenderedViewLine {
|
|||
const partIndex = CharacterMapping.getPartIndex(partData);
|
||||
const charOffsetInPart = CharacterMapping.getCharIndex(partData);
|
||||
|
||||
const r = RangeUtil.readHorizontalRanges(this._getReadingTarget(), partIndex, charOffsetInPart, partIndex, charOffsetInPart, context.clientRectDeltaLeft, context.endNode);
|
||||
const r = RangeUtil.readHorizontalRanges(this._getReadingTarget(domNode), partIndex, charOffsetInPart, partIndex, charOffsetInPart, context.clientRectDeltaLeft, context.endNode);
|
||||
if (!r || r.length === 0) {
|
||||
return -1;
|
||||
}
|
||||
return r[0].left;
|
||||
}
|
||||
|
||||
private _readRawVisibleRangesForRange(startColumn: number, endColumn: number, context: DomReadingContext): HorizontalRange[] | null {
|
||||
private _readRawVisibleRangesForRange(domNode: FastDomNode<HTMLElement>, startColumn: number, endColumn: number, context: DomReadingContext): HorizontalRange[] | null {
|
||||
|
||||
if (startColumn === 1 && endColumn === this._characterMapping.length) {
|
||||
// This branch helps IE with bidi text & gives a performance boost to other browsers when reading visible ranges for an entire line
|
||||
|
@ -570,7 +576,7 @@ class RenderedViewLine implements IRenderedViewLine {
|
|||
const endPartIndex = CharacterMapping.getPartIndex(endPartData);
|
||||
const endCharOffsetInPart = CharacterMapping.getCharIndex(endPartData);
|
||||
|
||||
return RangeUtil.readHorizontalRanges(this._getReadingTarget(), startPartIndex, startCharOffsetInPart, endPartIndex, endCharOffsetInPart, context.clientRectDeltaLeft, context.endNode);
|
||||
return RangeUtil.readHorizontalRanges(this._getReadingTarget(domNode), startPartIndex, startCharOffsetInPart, endPartIndex, endCharOffsetInPart, context.clientRectDeltaLeft, context.endNode);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -591,8 +597,8 @@ class RenderedViewLine implements IRenderedViewLine {
|
|||
}
|
||||
|
||||
class WebKitRenderedViewLine extends RenderedViewLine {
|
||||
protected _readVisibleRangesForRange(startColumn: number, endColumn: number, context: DomReadingContext): HorizontalRange[] | null {
|
||||
const output = super._readVisibleRangesForRange(startColumn, endColumn, context);
|
||||
protected _readVisibleRangesForRange(domNode: FastDomNode<HTMLElement>, startColumn: number, endColumn: number, context: DomReadingContext): HorizontalRange[] | null {
|
||||
const output = super._readVisibleRangesForRange(domNode, startColumn, endColumn, context);
|
||||
|
||||
if (!output || output.length === 0 || startColumn === endColumn || (startColumn === 1 && endColumn === this._characterMapping.length)) {
|
||||
return output;
|
||||
|
@ -603,7 +609,7 @@ class WebKitRenderedViewLine extends RenderedViewLine {
|
|||
if (!this.input.containsRTL) {
|
||||
// This is an attempt to patch things up
|
||||
// Find position of last column
|
||||
const endPixelOffset = this._readPixelOffset(endColumn, context);
|
||||
const endPixelOffset = this._readPixelOffset(domNode, endColumn, context);
|
||||
if (endPixelOffset !== -1) {
|
||||
const lastRange = output[output.length - 1];
|
||||
if (lastRange.left < endPixelOffset) {
|
||||
|
@ -624,10 +630,10 @@ const createRenderedLine: (domNode: FastDomNode<HTMLElement> | null, renderLineI
|
|||
return createNormalRenderedLine;
|
||||
})();
|
||||
|
||||
function createWebKitRenderedLine(domNode: FastDomNode<HTMLElement>, renderLineInput: RenderLineInput, characterMapping: CharacterMapping, containsRTL: boolean, containsForeignElements: ForeignElementType): RenderedViewLine {
|
||||
function createWebKitRenderedLine(domNode: FastDomNode<HTMLElement> | null, renderLineInput: RenderLineInput, characterMapping: CharacterMapping, containsRTL: boolean, containsForeignElements: ForeignElementType): RenderedViewLine {
|
||||
return new WebKitRenderedViewLine(domNode, renderLineInput, characterMapping, containsRTL, containsForeignElements);
|
||||
}
|
||||
|
||||
function createNormalRenderedLine(domNode: FastDomNode<HTMLElement>, renderLineInput: RenderLineInput, characterMapping: CharacterMapping, containsRTL: boolean, containsForeignElements: ForeignElementType): RenderedViewLine {
|
||||
function createNormalRenderedLine(domNode: FastDomNode<HTMLElement> | null, renderLineInput: RenderLineInput, characterMapping: CharacterMapping, containsRTL: boolean, containsForeignElements: ForeignElementType): RenderedViewLine {
|
||||
return new RenderedViewLine(domNode, renderLineInput, characterMapping, containsRTL, containsForeignElements);
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { IViewZone } from 'vs/editor/browser/editorBrowser';
|
||||
import { IViewZone, IViewZoneChangeAccessor } from 'vs/editor/browser/editorBrowser';
|
||||
import { ViewPart } from 'vs/editor/browser/view/viewPart';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext';
|
||||
|
@ -13,7 +13,7 @@ import { ViewContext } from 'vs/editor/common/view/viewContext';
|
|||
import * as viewEvents from 'vs/editor/common/view/viewEvents';
|
||||
import { IViewWhitespaceViewportData } from 'vs/editor/common/viewModel/viewModel';
|
||||
import { EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
|
||||
import { IWhitespaceChangeAccessor, IEditorWhitespace } from 'vs/editor/common/viewLayout/linesLayout';
|
||||
|
||||
export interface IMyViewZone {
|
||||
whitespaceId: string;
|
||||
|
@ -29,6 +29,8 @@ interface IComputedViewZoneProps {
|
|||
minWidthInPx: number;
|
||||
}
|
||||
|
||||
const invalidFunc = () => { throw new Error(`Invalid change accessor`); };
|
||||
|
||||
export class ViewZones extends ViewPart {
|
||||
|
||||
private _zones: { [id: string]: IMyViewZone; };
|
||||
|
@ -72,20 +74,29 @@ export class ViewZones extends ViewPart {
|
|||
// ---- begin view event handlers
|
||||
|
||||
private _recomputeWhitespacesProps(): boolean {
|
||||
let hadAChange = false;
|
||||
|
||||
const keys = Object.keys(this._zones);
|
||||
for (let i = 0, len = keys.length; i < len; i++) {
|
||||
const id = keys[i];
|
||||
const zone = this._zones[id];
|
||||
const props = this._computeWhitespaceProps(zone.delegate);
|
||||
if (this._context.viewLayout.changeWhitespace(id, props.afterViewLineNumber, props.heightInPx)) {
|
||||
this._safeCallOnComputedHeight(zone.delegate, props.heightInPx);
|
||||
hadAChange = true;
|
||||
}
|
||||
const whitespaces = this._context.viewLayout.getWhitespaces();
|
||||
const oldWhitespaces = new Map<string, IEditorWhitespace>();
|
||||
for (const whitespace of whitespaces) {
|
||||
oldWhitespaces.set(whitespace.id, whitespace);
|
||||
}
|
||||
return this._context.viewLayout.changeWhitespace((whitespaceAccessor: IWhitespaceChangeAccessor) => {
|
||||
let hadAChange = false;
|
||||
|
||||
return hadAChange;
|
||||
const keys = Object.keys(this._zones);
|
||||
for (let i = 0, len = keys.length; i < len; i++) {
|
||||
const id = keys[i];
|
||||
const zone = this._zones[id];
|
||||
const props = this._computeWhitespaceProps(zone.delegate);
|
||||
const oldWhitespace = oldWhitespaces.get(id);
|
||||
if (oldWhitespace && (oldWhitespace.afterLineNumber !== props.afterViewLineNumber || oldWhitespace.height !== props.heightInPx)) {
|
||||
whitespaceAccessor.changeOneWhitespace(id, props.afterViewLineNumber, props.heightInPx);
|
||||
this._safeCallOnComputedHeight(zone.delegate, props.heightInPx);
|
||||
hadAChange = true;
|
||||
}
|
||||
}
|
||||
|
||||
return hadAChange;
|
||||
});
|
||||
}
|
||||
|
||||
public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean {
|
||||
|
@ -138,7 +149,6 @@ export class ViewZones extends ViewPart {
|
|||
return 10000;
|
||||
}
|
||||
|
||||
|
||||
private _computeWhitespaceProps(zone: IViewZone): IComputedViewZoneProps {
|
||||
if (zone.afterLineNumber === 0) {
|
||||
return {
|
||||
|
@ -188,9 +198,44 @@ export class ViewZones extends ViewPart {
|
|||
};
|
||||
}
|
||||
|
||||
public addZone(zone: IViewZone): string {
|
||||
public changeViewZones(callback: (changeAccessor: IViewZoneChangeAccessor) => any): boolean {
|
||||
|
||||
return this._context.viewLayout.changeWhitespace((whitespaceAccessor: IWhitespaceChangeAccessor) => {
|
||||
let zonesHaveChanged = false;
|
||||
|
||||
const changeAccessor: IViewZoneChangeAccessor = {
|
||||
addZone: (zone: IViewZone): string => {
|
||||
zonesHaveChanged = true;
|
||||
return this._addZone(whitespaceAccessor, zone);
|
||||
},
|
||||
removeZone: (id: string): void => {
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
zonesHaveChanged = this._removeZone(whitespaceAccessor, id) || zonesHaveChanged;
|
||||
},
|
||||
layoutZone: (id: string): void => {
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
zonesHaveChanged = this._layoutZone(whitespaceAccessor, id) || zonesHaveChanged;
|
||||
}
|
||||
};
|
||||
|
||||
safeInvoke1Arg(callback, changeAccessor);
|
||||
|
||||
// Invalidate changeAccessor
|
||||
changeAccessor.addZone = invalidFunc;
|
||||
changeAccessor.removeZone = invalidFunc;
|
||||
changeAccessor.layoutZone = invalidFunc;
|
||||
|
||||
return zonesHaveChanged;
|
||||
});
|
||||
}
|
||||
|
||||
private _addZone(whitespaceAccessor: IWhitespaceChangeAccessor, zone: IViewZone): string {
|
||||
const props = this._computeWhitespaceProps(zone);
|
||||
const whitespaceId = this._context.viewLayout.addWhitespace(props.afterViewLineNumber, this._getZoneOrdinal(zone), props.heightInPx, props.minWidthInPx);
|
||||
const whitespaceId = whitespaceAccessor.insertWhitespace(props.afterViewLineNumber, this._getZoneOrdinal(zone), props.heightInPx, props.minWidthInPx);
|
||||
|
||||
const myZone: IMyViewZone = {
|
||||
whitespaceId: whitespaceId,
|
||||
|
@ -224,11 +269,11 @@ export class ViewZones extends ViewPart {
|
|||
return myZone.whitespaceId;
|
||||
}
|
||||
|
||||
public removeZone(id: string): boolean {
|
||||
private _removeZone(whitespaceAccessor: IWhitespaceChangeAccessor, id: string): boolean {
|
||||
if (this._zones.hasOwnProperty(id)) {
|
||||
const zone = this._zones[id];
|
||||
delete this._zones[id];
|
||||
this._context.viewLayout.removeWhitespace(zone.whitespaceId);
|
||||
whitespaceAccessor.removeWhitespace(zone.whitespaceId);
|
||||
|
||||
zone.domNode.removeAttribute('monaco-visible-view-zone');
|
||||
zone.domNode.removeAttribute('monaco-view-zone');
|
||||
|
@ -247,21 +292,20 @@ export class ViewZones extends ViewPart {
|
|||
return false;
|
||||
}
|
||||
|
||||
public layoutZone(id: string): boolean {
|
||||
let changed = false;
|
||||
private _layoutZone(whitespaceAccessor: IWhitespaceChangeAccessor, id: string): boolean {
|
||||
if (this._zones.hasOwnProperty(id)) {
|
||||
const zone = this._zones[id];
|
||||
const props = this._computeWhitespaceProps(zone.delegate);
|
||||
// const newOrdinal = this._getZoneOrdinal(zone.delegate);
|
||||
changed = this._context.viewLayout.changeWhitespace(zone.whitespaceId, props.afterViewLineNumber, props.heightInPx) || changed;
|
||||
whitespaceAccessor.changeOneWhitespace(zone.whitespaceId, props.afterViewLineNumber, props.heightInPx);
|
||||
// TODO@Alex: change `newOrdinal` too
|
||||
|
||||
if (changed) {
|
||||
this._safeCallOnComputedHeight(zone.delegate, props.heightInPx);
|
||||
this.setShouldRender();
|
||||
}
|
||||
this._safeCallOnComputedHeight(zone.delegate, props.heightInPx);
|
||||
this.setShouldRender();
|
||||
|
||||
return true;
|
||||
}
|
||||
return changed;
|
||||
return false;
|
||||
}
|
||||
|
||||
public shouldSuppressMouseDownOnViewZone(id: string): boolean {
|
||||
|
@ -365,3 +409,11 @@ export class ViewZones extends ViewPart {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
function safeInvoke1Arg(func: Function, arg1: any): any {
|
||||
try {
|
||||
return func(arg1);
|
||||
} catch (e) {
|
||||
onUnexpectedError(e);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ import * as modes from 'vs/editor/common/modes';
|
|||
import { editorUnnecessaryCodeBorder, editorUnnecessaryCodeOpacity } from 'vs/editor/common/view/editorColorRegistry';
|
||||
import { editorErrorBorder, editorErrorForeground, editorHintBorder, editorHintForeground, editorInfoBorder, editorInfoForeground, editorWarningBorder, editorWarningForeground, editorForeground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { VerticalRevealType } from 'vs/editor/common/view/viewEvents';
|
||||
import { IEditorWhitespace } from 'vs/editor/common/viewLayout/whitespaceComputer';
|
||||
import { IEditorWhitespace } from 'vs/editor/common/viewLayout/linesLayout';
|
||||
import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
|
|
|
@ -32,7 +32,7 @@ import { IDiffComputationResult, IEditorWorkerService } from 'vs/editor/common/s
|
|||
import { OverviewRulerZone } from 'vs/editor/common/view/overviewZoneManager';
|
||||
import { LineDecoration } from 'vs/editor/common/viewLayout/lineDecorations';
|
||||
import { RenderLineInput, renderViewLine } from 'vs/editor/common/viewLayout/viewLineRenderer';
|
||||
import { IEditorWhitespace } from 'vs/editor/common/viewLayout/whitespaceComputer';
|
||||
import { IEditorWhitespace } from 'vs/editor/common/viewLayout/linesLayout';
|
||||
import { InlineDecoration, InlineDecorationType, ViewLineRenderingData } from 'vs/editor/common/viewModel/viewModel';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
@ -1376,12 +1376,16 @@ abstract class ViewZonesComputer {
|
|||
|
||||
private readonly lineChanges: editorCommon.ILineChange[];
|
||||
private readonly originalForeignVZ: IEditorWhitespace[];
|
||||
private readonly originalLineHeight: number;
|
||||
private readonly modifiedForeignVZ: IEditorWhitespace[];
|
||||
private readonly modifiedLineHeight: number;
|
||||
|
||||
constructor(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[]) {
|
||||
constructor(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], originalLineHeight: number, modifiedForeignVZ: IEditorWhitespace[], modifiedLineHeight: number) {
|
||||
this.lineChanges = lineChanges;
|
||||
this.originalForeignVZ = originalForeignVZ;
|
||||
this.originalLineHeight = originalLineHeight;
|
||||
this.modifiedForeignVZ = modifiedForeignVZ;
|
||||
this.modifiedLineHeight = modifiedLineHeight;
|
||||
}
|
||||
|
||||
public getViewZones(): IEditorsZones {
|
||||
|
@ -1456,7 +1460,7 @@ abstract class ViewZonesComputer {
|
|||
|
||||
stepOriginal.push({
|
||||
afterLineNumber: viewZoneLineNumber,
|
||||
heightInLines: modifiedForeignVZ.current.heightInLines,
|
||||
heightInLines: modifiedForeignVZ.current.height / this.modifiedLineHeight,
|
||||
domNode: null,
|
||||
marginDomNode: marginDomNode
|
||||
});
|
||||
|
@ -1473,7 +1477,7 @@ abstract class ViewZonesComputer {
|
|||
}
|
||||
stepModified.push({
|
||||
afterLineNumber: viewZoneLineNumber,
|
||||
heightInLines: originalForeignVZ.current.heightInLines,
|
||||
heightInLines: originalForeignVZ.current.height / this.originalLineHeight,
|
||||
domNode: null
|
||||
});
|
||||
originalForeignVZ.advance();
|
||||
|
@ -1732,7 +1736,7 @@ class DiffEditorWidgetSideBySide extends DiffEditorWidgetStyle implements IDiffE
|
|||
}
|
||||
|
||||
protected _getViewZones(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorsZones {
|
||||
let c = new SideBySideViewZonesComputer(lineChanges, originalForeignVZ, modifiedForeignVZ);
|
||||
let c = new SideBySideViewZonesComputer(lineChanges, originalForeignVZ, originalEditor.getOption(EditorOption.lineHeight), modifiedForeignVZ, modifiedEditor.getOption(EditorOption.lineHeight));
|
||||
return c.getViewZones();
|
||||
}
|
||||
|
||||
|
@ -1859,8 +1863,8 @@ class DiffEditorWidgetSideBySide extends DiffEditorWidgetStyle implements IDiffE
|
|||
|
||||
class SideBySideViewZonesComputer extends ViewZonesComputer {
|
||||
|
||||
constructor(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[]) {
|
||||
super(lineChanges, originalForeignVZ, modifiedForeignVZ);
|
||||
constructor(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], originalLineHeight: number, modifiedForeignVZ: IEditorWhitespace[], modifiedLineHeight: number) {
|
||||
super(lineChanges, originalForeignVZ, originalLineHeight, modifiedForeignVZ, modifiedLineHeight);
|
||||
}
|
||||
|
||||
protected _createOriginalMarginDomNodeForModifiedForeignViewZoneInAddedRegion(): HTMLDivElement | null {
|
||||
|
@ -2020,7 +2024,7 @@ class InlineViewZonesComputer extends ViewZonesComputer {
|
|||
private readonly renderIndicators: boolean;
|
||||
|
||||
constructor(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor, renderIndicators: boolean) {
|
||||
super(lineChanges, originalForeignVZ, modifiedForeignVZ);
|
||||
super(lineChanges, originalForeignVZ, originalEditor.getOption(EditorOption.lineHeight), modifiedForeignVZ, modifiedEditor.getOption(EditorOption.lineHeight));
|
||||
this.originalModel = originalEditor.getModel()!;
|
||||
this.modifiedEditorOptions = modifiedEditor.getOptions();
|
||||
this.modifiedEditorTabSize = modifiedEditor.getModel()!.getOptions().tabSize;
|
||||
|
|
|
@ -4,44 +4,157 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IPartialViewLinesViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData';
|
||||
import { IEditorWhitespace, WhitespaceComputer } from 'vs/editor/common/viewLayout/whitespaceComputer';
|
||||
import { IViewWhitespaceViewportData } from 'vs/editor/common/viewModel/viewModel';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
|
||||
export interface IEditorWhitespace {
|
||||
readonly id: string;
|
||||
readonly afterLineNumber: number;
|
||||
readonly height: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* An accessor that allows for whtiespace to be added, removed or changed in bulk.
|
||||
*/
|
||||
export interface IWhitespaceChangeAccessor {
|
||||
insertWhitespace(afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): string;
|
||||
changeOneWhitespace(id: string, newAfterLineNumber: number, newHeight: number): void;
|
||||
removeWhitespace(id: string): void;
|
||||
}
|
||||
|
||||
interface IPendingChange { id: string; newAfterLineNumber: number; newHeight: number; }
|
||||
interface IPendingRemove { id: string; }
|
||||
|
||||
class PendingChanges {
|
||||
private _hasPending: boolean;
|
||||
private _inserts: EditorWhitespace[];
|
||||
private _changes: IPendingChange[];
|
||||
private _removes: IPendingRemove[];
|
||||
|
||||
constructor() {
|
||||
this._hasPending = false;
|
||||
this._inserts = [];
|
||||
this._changes = [];
|
||||
this._removes = [];
|
||||
}
|
||||
|
||||
public insert(x: EditorWhitespace): void {
|
||||
this._hasPending = true;
|
||||
this._inserts.push(x);
|
||||
}
|
||||
|
||||
public change(x: IPendingChange): void {
|
||||
this._hasPending = true;
|
||||
this._changes.push(x);
|
||||
}
|
||||
|
||||
public remove(x: IPendingRemove): void {
|
||||
this._hasPending = true;
|
||||
this._removes.push(x);
|
||||
}
|
||||
|
||||
public mustCommit(): boolean {
|
||||
return this._hasPending;
|
||||
}
|
||||
|
||||
public commit(linesLayout: LinesLayout): void {
|
||||
if (!this._hasPending) {
|
||||
return;
|
||||
}
|
||||
|
||||
const inserts = this._inserts;
|
||||
const changes = this._changes;
|
||||
const removes = this._removes;
|
||||
|
||||
this._hasPending = false;
|
||||
this._inserts = [];
|
||||
this._changes = [];
|
||||
this._removes = [];
|
||||
|
||||
linesLayout._commitPendingChanges(inserts, changes, removes);
|
||||
}
|
||||
}
|
||||
|
||||
export class EditorWhitespace implements IEditorWhitespace {
|
||||
public id: string;
|
||||
public afterLineNumber: number;
|
||||
public ordinal: number;
|
||||
public height: number;
|
||||
public minWidth: number;
|
||||
public prefixSum: number;
|
||||
|
||||
constructor(id: string, afterLineNumber: number, ordinal: number, height: number, minWidth: number) {
|
||||
this.id = id;
|
||||
this.afterLineNumber = afterLineNumber;
|
||||
this.ordinal = ordinal;
|
||||
this.height = height;
|
||||
this.minWidth = minWidth;
|
||||
this.prefixSum = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Layouting of objects that take vertical space (by having a height) and push down other objects.
|
||||
*
|
||||
* These objects are basically either text (lines) or spaces between those lines (whitespaces).
|
||||
* This provides commodity operations for working with lines that contain whitespace that pushes lines lower (vertically).
|
||||
* This is written with no knowledge of an editor in mind.
|
||||
*/
|
||||
export class LinesLayout {
|
||||
|
||||
/**
|
||||
* Keep track of the total number of lines.
|
||||
* This is useful for doing binary searches or for doing hit-testing.
|
||||
*/
|
||||
private _lineCount: number;
|
||||
private static INSTANCE_COUNT = 0;
|
||||
|
||||
/**
|
||||
* The height of a line in pixels.
|
||||
*/
|
||||
private readonly _instanceId: string;
|
||||
private readonly _pendingChanges: PendingChanges;
|
||||
private _lastWhitespaceId: number;
|
||||
private _arr: EditorWhitespace[];
|
||||
private _prefixSumValidIndex: number;
|
||||
private _minWidth: number;
|
||||
private _lineCount: number;
|
||||
private _lineHeight: number;
|
||||
|
||||
/**
|
||||
* Contains whitespace information in pixels
|
||||
*/
|
||||
private readonly _whitespaces: WhitespaceComputer;
|
||||
|
||||
constructor(lineCount: number, lineHeight: number) {
|
||||
this._instanceId = strings.singleLetterHash(++LinesLayout.INSTANCE_COUNT);
|
||||
this._pendingChanges = new PendingChanges();
|
||||
this._lastWhitespaceId = 0;
|
||||
this._arr = [];
|
||||
this._prefixSumValidIndex = -1;
|
||||
this._minWidth = -1; /* marker for not being computed */
|
||||
this._lineCount = lineCount;
|
||||
this._lineHeight = lineHeight;
|
||||
this._whitespaces = new WhitespaceComputer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the insertion index for a new value inside a sorted array of values.
|
||||
* If the value is already present in the sorted array, the insertion index will be after the already existing value.
|
||||
*/
|
||||
public static findInsertionIndex(arr: EditorWhitespace[], afterLineNumber: number, ordinal: number): number {
|
||||
let low = 0;
|
||||
let high = arr.length;
|
||||
|
||||
while (low < high) {
|
||||
const mid = ((low + high) >>> 1);
|
||||
|
||||
if (afterLineNumber === arr[mid].afterLineNumber) {
|
||||
if (ordinal < arr[mid].ordinal) {
|
||||
high = mid;
|
||||
} else {
|
||||
low = mid + 1;
|
||||
}
|
||||
} else if (afterLineNumber < arr[mid].afterLineNumber) {
|
||||
high = mid;
|
||||
} else {
|
||||
low = mid + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return low;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the height of a line in pixels.
|
||||
*/
|
||||
public setLineHeight(lineHeight: number): void {
|
||||
this._checkPendingChanges();
|
||||
this._lineHeight = lineHeight;
|
||||
}
|
||||
|
||||
|
@ -51,37 +164,153 @@ export class LinesLayout {
|
|||
* @param lineCount New number of lines.
|
||||
*/
|
||||
public onFlushed(lineCount: number): void {
|
||||
this._checkPendingChanges();
|
||||
this._lineCount = lineCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a new whitespace of a certain height after a line number.
|
||||
* The whitespace has a "sticky" characteristic.
|
||||
* Irrespective of edits above or below `afterLineNumber`, the whitespace will follow the initial line.
|
||||
*
|
||||
* @param afterLineNumber The conceptual position of this whitespace. The whitespace will follow this line as best as possible even when deleting/inserting lines above/below.
|
||||
* @param heightInPx The height of the whitespace, in pixels.
|
||||
* @return An id that can be used later to mutate or delete the whitespace
|
||||
*/
|
||||
public insertWhitespace(afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): string {
|
||||
return this._whitespaces.insertWhitespace(afterLineNumber, ordinal, heightInPx, minWidth);
|
||||
public changeWhitespace<T>(callback: (accessor: IWhitespaceChangeAccessor) => T): T {
|
||||
try {
|
||||
const accessor = {
|
||||
insertWhitespace: (afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): string => {
|
||||
afterLineNumber = afterLineNumber | 0;
|
||||
ordinal = ordinal | 0;
|
||||
heightInPx = heightInPx | 0;
|
||||
minWidth = minWidth | 0;
|
||||
|
||||
const id = this._instanceId + (++this._lastWhitespaceId);
|
||||
this._pendingChanges.insert(new EditorWhitespace(id, afterLineNumber, ordinal, heightInPx, minWidth));
|
||||
return id;
|
||||
},
|
||||
changeOneWhitespace: (id: string, newAfterLineNumber: number, newHeight: number): void => {
|
||||
newAfterLineNumber = newAfterLineNumber | 0;
|
||||
newHeight = newHeight | 0;
|
||||
|
||||
this._pendingChanges.change({ id, newAfterLineNumber, newHeight });
|
||||
},
|
||||
removeWhitespace: (id: string): void => {
|
||||
this._pendingChanges.remove({ id });
|
||||
}
|
||||
};
|
||||
return callback(accessor);
|
||||
} finally {
|
||||
this._pendingChanges.commit(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Change properties associated with a certain whitespace.
|
||||
*/
|
||||
public changeWhitespace(id: string, newAfterLineNumber: number, newHeight: number): boolean {
|
||||
return this._whitespaces.changeWhitespace(id, newAfterLineNumber, newHeight);
|
||||
public _commitPendingChanges(inserts: EditorWhitespace[], changes: IPendingChange[], removes: IPendingRemove[]): void {
|
||||
if (inserts.length > 0 || removes.length > 0) {
|
||||
this._minWidth = -1; /* marker for not being computed */
|
||||
}
|
||||
|
||||
if (inserts.length + changes.length + removes.length <= 1) {
|
||||
// when only one thing happened, handle it "delicately"
|
||||
for (const insert of inserts) {
|
||||
this._insertWhitespace(insert);
|
||||
}
|
||||
for (const change of changes) {
|
||||
this._changeOneWhitespace(change.id, change.newAfterLineNumber, change.newHeight);
|
||||
}
|
||||
for (const remove of removes) {
|
||||
const index = this._findWhitespaceIndex(remove.id);
|
||||
if (index === -1) {
|
||||
continue;
|
||||
}
|
||||
this._removeWhitespace(index);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// simply rebuild the entire datastructure
|
||||
|
||||
const toRemove = new Set<string>();
|
||||
for (const remove of removes) {
|
||||
toRemove.add(remove.id);
|
||||
}
|
||||
|
||||
const toChange = new Map<string, IPendingChange>();
|
||||
for (const change of changes) {
|
||||
toChange.set(change.id, change);
|
||||
}
|
||||
|
||||
const applyRemoveAndChange = (whitespaces: EditorWhitespace[]): EditorWhitespace[] => {
|
||||
let result: EditorWhitespace[] = [];
|
||||
for (const whitespace of whitespaces) {
|
||||
if (toRemove.has(whitespace.id)) {
|
||||
continue;
|
||||
}
|
||||
if (toChange.has(whitespace.id)) {
|
||||
const change = toChange.get(whitespace.id)!;
|
||||
whitespace.afterLineNumber = change.newAfterLineNumber;
|
||||
whitespace.height = change.newHeight;
|
||||
}
|
||||
result.push(whitespace);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
const result = applyRemoveAndChange(this._arr).concat(applyRemoveAndChange(inserts));
|
||||
result.sort((a, b) => {
|
||||
if (a.afterLineNumber === b.afterLineNumber) {
|
||||
return a.ordinal - b.ordinal;
|
||||
}
|
||||
return a.afterLineNumber - b.afterLineNumber;
|
||||
});
|
||||
|
||||
this._arr = result;
|
||||
this._prefixSumValidIndex = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an existing whitespace.
|
||||
*
|
||||
* @param id The whitespace to remove
|
||||
* @return Returns true if the whitespace is found and it is removed.
|
||||
*/
|
||||
public removeWhitespace(id: string): boolean {
|
||||
return this._whitespaces.removeWhitespace(id);
|
||||
private _checkPendingChanges(): void {
|
||||
if (this._pendingChanges.mustCommit()) {
|
||||
console.warn(`Commiting pending changes before change accessor leaves due to read access.`);
|
||||
this._pendingChanges.commit(this);
|
||||
}
|
||||
}
|
||||
|
||||
private _insertWhitespace(whitespace: EditorWhitespace): void {
|
||||
const insertIndex = LinesLayout.findInsertionIndex(this._arr, whitespace.afterLineNumber, whitespace.ordinal);
|
||||
this._arr.splice(insertIndex, 0, whitespace);
|
||||
this._prefixSumValidIndex = Math.min(this._prefixSumValidIndex, insertIndex - 1);
|
||||
}
|
||||
|
||||
private _findWhitespaceIndex(id: string): number {
|
||||
const arr = this._arr;
|
||||
for (let i = 0, len = arr.length; i < len; i++) {
|
||||
if (arr[i].id === id) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private _changeOneWhitespace(id: string, newAfterLineNumber: number, newHeight: number): void {
|
||||
const index = this._findWhitespaceIndex(id);
|
||||
if (index === -1) {
|
||||
return;
|
||||
}
|
||||
if (this._arr[index].height !== newHeight) {
|
||||
this._arr[index].height = newHeight;
|
||||
this._prefixSumValidIndex = Math.min(this._prefixSumValidIndex, index - 1);
|
||||
}
|
||||
if (this._arr[index].afterLineNumber !== newAfterLineNumber) {
|
||||
// `afterLineNumber` changed for this whitespace
|
||||
|
||||
// Record old whitespace
|
||||
const whitespace = this._arr[index];
|
||||
|
||||
// Since changing `afterLineNumber` can trigger a reordering, we're gonna remove this whitespace
|
||||
this._removeWhitespace(index);
|
||||
|
||||
whitespace.afterLineNumber = newAfterLineNumber;
|
||||
|
||||
// And add it again
|
||||
this._insertWhitespace(whitespace);
|
||||
}
|
||||
}
|
||||
|
||||
private _removeWhitespace(removeIndex: number): void {
|
||||
this._arr.splice(removeIndex, 1);
|
||||
this._prefixSumValidIndex = Math.min(this._prefixSumValidIndex, removeIndex - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -91,8 +320,24 @@ export class LinesLayout {
|
|||
* @param toLineNumber The line number at which the deletion ended, inclusive
|
||||
*/
|
||||
public onLinesDeleted(fromLineNumber: number, toLineNumber: number): void {
|
||||
this._checkPendingChanges();
|
||||
fromLineNumber = fromLineNumber | 0;
|
||||
toLineNumber = toLineNumber | 0;
|
||||
|
||||
this._lineCount -= (toLineNumber - fromLineNumber + 1);
|
||||
this._whitespaces.onLinesDeleted(fromLineNumber, toLineNumber);
|
||||
for (let i = 0, len = this._arr.length; i < len; i++) {
|
||||
const afterLineNumber = this._arr[i].afterLineNumber;
|
||||
|
||||
if (fromLineNumber <= afterLineNumber && afterLineNumber <= toLineNumber) {
|
||||
// The line this whitespace was after has been deleted
|
||||
// => move whitespace to before first deleted line
|
||||
this._arr[i].afterLineNumber = fromLineNumber - 1;
|
||||
} else if (afterLineNumber > toLineNumber) {
|
||||
// The line this whitespace was after has been moved up
|
||||
// => move whitespace up
|
||||
this._arr[i].afterLineNumber -= (toLineNumber - fromLineNumber + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -102,8 +347,53 @@ export class LinesLayout {
|
|||
* @param toLineNumber The line number at which the insertion ended, inclusive.
|
||||
*/
|
||||
public onLinesInserted(fromLineNumber: number, toLineNumber: number): void {
|
||||
this._checkPendingChanges();
|
||||
fromLineNumber = fromLineNumber | 0;
|
||||
toLineNumber = toLineNumber | 0;
|
||||
|
||||
this._lineCount += (toLineNumber - fromLineNumber + 1);
|
||||
this._whitespaces.onLinesInserted(fromLineNumber, toLineNumber);
|
||||
for (let i = 0, len = this._arr.length; i < len; i++) {
|
||||
const afterLineNumber = this._arr[i].afterLineNumber;
|
||||
|
||||
if (fromLineNumber <= afterLineNumber) {
|
||||
this._arr[i].afterLineNumber += (toLineNumber - fromLineNumber + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the sum of all the whitespaces.
|
||||
*/
|
||||
public getWhitespacesTotalHeight(): number {
|
||||
this._checkPendingChanges();
|
||||
if (this._arr.length === 0) {
|
||||
return 0;
|
||||
}
|
||||
return this.getWhitespacesAccumulatedHeight(this._arr.length - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the sum of the heights of the whitespaces at [0..index].
|
||||
* This includes the whitespace at `index`.
|
||||
*
|
||||
* @param index The index of the whitespace.
|
||||
* @return The sum of the heights of all whitespaces before the one at `index`, including the one at `index`.
|
||||
*/
|
||||
public getWhitespacesAccumulatedHeight(index: number): number {
|
||||
this._checkPendingChanges();
|
||||
index = index | 0;
|
||||
|
||||
let startIndex = Math.max(0, this._prefixSumValidIndex + 1);
|
||||
if (startIndex === 0) {
|
||||
this._arr[0].prefixSum = this._arr[0].height;
|
||||
startIndex++;
|
||||
}
|
||||
|
||||
for (let i = startIndex; i <= index; i++) {
|
||||
this._arr[i].prefixSum = this._arr[i - 1].prefixSum + this._arr[i].height;
|
||||
}
|
||||
this._prefixSumValidIndex = Math.max(this._prefixSumValidIndex, index);
|
||||
return this._arr[index].prefixSum;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -112,11 +402,81 @@ export class LinesLayout {
|
|||
* @return The sum of heights for all objects.
|
||||
*/
|
||||
public getLinesTotalHeight(): number {
|
||||
let linesHeight = this._lineHeight * this._lineCount;
|
||||
let whitespacesHeight = this._whitespaces.getTotalHeight();
|
||||
this._checkPendingChanges();
|
||||
const linesHeight = this._lineHeight * this._lineCount;
|
||||
const whitespacesHeight = this.getWhitespacesTotalHeight();
|
||||
return linesHeight + whitespacesHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the accumulated height of whitespaces before the given line number.
|
||||
*
|
||||
* @param lineNumber The line number
|
||||
*/
|
||||
public getWhitespaceAccumulatedHeightBeforeLineNumber(lineNumber: number): number {
|
||||
this._checkPendingChanges();
|
||||
lineNumber = lineNumber | 0;
|
||||
|
||||
const lastWhitespaceBeforeLineNumber = this._findLastWhitespaceBeforeLineNumber(lineNumber);
|
||||
|
||||
if (lastWhitespaceBeforeLineNumber === -1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return this.getWhitespacesAccumulatedHeight(lastWhitespaceBeforeLineNumber);
|
||||
}
|
||||
|
||||
private _findLastWhitespaceBeforeLineNumber(lineNumber: number): number {
|
||||
lineNumber = lineNumber | 0;
|
||||
|
||||
// Find the whitespace before line number
|
||||
const arr = this._arr;
|
||||
let low = 0;
|
||||
let high = arr.length - 1;
|
||||
|
||||
while (low <= high) {
|
||||
const delta = (high - low) | 0;
|
||||
const halfDelta = (delta / 2) | 0;
|
||||
const mid = (low + halfDelta) | 0;
|
||||
|
||||
if (arr[mid].afterLineNumber < lineNumber) {
|
||||
if (mid + 1 >= arr.length || arr[mid + 1].afterLineNumber >= lineNumber) {
|
||||
return mid;
|
||||
} else {
|
||||
low = (mid + 1) | 0;
|
||||
}
|
||||
} else {
|
||||
high = (mid - 1) | 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
private _findFirstWhitespaceAfterLineNumber(lineNumber: number): number {
|
||||
lineNumber = lineNumber | 0;
|
||||
|
||||
const lastWhitespaceBeforeLineNumber = this._findLastWhitespaceBeforeLineNumber(lineNumber);
|
||||
const firstWhitespaceAfterLineNumber = lastWhitespaceBeforeLineNumber + 1;
|
||||
|
||||
if (firstWhitespaceAfterLineNumber < this._arr.length) {
|
||||
return firstWhitespaceAfterLineNumber;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the index of the first whitespace which has `afterLineNumber` >= `lineNumber`.
|
||||
* @return The index of the first whitespace with `afterLineNumber` >= `lineNumber` or -1 if no whitespace is found.
|
||||
*/
|
||||
public getFirstWhitespaceIndexAfterLineNumber(lineNumber: number): number {
|
||||
this._checkPendingChanges();
|
||||
lineNumber = lineNumber | 0;
|
||||
|
||||
return this._findFirstWhitespaceAfterLineNumber(lineNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the vertical offset (the sum of heights for all objects above) a certain line number.
|
||||
*
|
||||
|
@ -124,6 +484,7 @@ export class LinesLayout {
|
|||
* @return The sum of heights for all objects above `lineNumber`.
|
||||
*/
|
||||
public getVerticalOffsetForLineNumber(lineNumber: number): number {
|
||||
this._checkPendingChanges();
|
||||
lineNumber = lineNumber | 0;
|
||||
|
||||
let previousLinesHeight: number;
|
||||
|
@ -133,36 +494,40 @@ export class LinesLayout {
|
|||
previousLinesHeight = 0;
|
||||
}
|
||||
|
||||
let previousWhitespacesHeight = this._whitespaces.getAccumulatedHeightBeforeLineNumber(lineNumber);
|
||||
const previousWhitespacesHeight = this.getWhitespaceAccumulatedHeightBeforeLineNumber(lineNumber);
|
||||
|
||||
return previousLinesHeight + previousWhitespacesHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the accumulated height of whitespaces before the given line number.
|
||||
*
|
||||
* @param lineNumber The line number
|
||||
*/
|
||||
public getWhitespaceAccumulatedHeightBeforeLineNumber(lineNumber: number): number {
|
||||
return this._whitespaces.getAccumulatedHeightBeforeLineNumber(lineNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if there is any whitespace in the document.
|
||||
*/
|
||||
public hasWhitespace(): boolean {
|
||||
return this._whitespaces.getCount() > 0;
|
||||
this._checkPendingChanges();
|
||||
return this.getWhitespacesCount() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* The maximum min width for all whitespaces.
|
||||
*/
|
||||
public getWhitespaceMinWidth(): number {
|
||||
return this._whitespaces.getMinWidth();
|
||||
this._checkPendingChanges();
|
||||
if (this._minWidth === -1) {
|
||||
let minWidth = 0;
|
||||
for (let i = 0, len = this._arr.length; i < len; i++) {
|
||||
minWidth = Math.max(minWidth, this._arr[i].minWidth);
|
||||
}
|
||||
this._minWidth = minWidth;
|
||||
}
|
||||
return this._minWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if `verticalOffset` is below all lines.
|
||||
*/
|
||||
public isAfterLines(verticalOffset: number): boolean {
|
||||
let totalHeight = this.getLinesTotalHeight();
|
||||
this._checkPendingChanges();
|
||||
const totalHeight = this.getLinesTotalHeight();
|
||||
return verticalOffset > totalHeight;
|
||||
}
|
||||
|
||||
|
@ -175,6 +540,7 @@ export class LinesLayout {
|
|||
* @return The line number at or after vertical offset `verticalOffset`.
|
||||
*/
|
||||
public getLineNumberAtOrAfterVerticalOffset(verticalOffset: number): number {
|
||||
this._checkPendingChanges();
|
||||
verticalOffset = verticalOffset | 0;
|
||||
|
||||
if (verticalOffset < 0) {
|
||||
|
@ -187,9 +553,9 @@ export class LinesLayout {
|
|||
let maxLineNumber = linesCount;
|
||||
|
||||
while (minLineNumber < maxLineNumber) {
|
||||
let midLineNumber = ((minLineNumber + maxLineNumber) / 2) | 0;
|
||||
const midLineNumber = ((minLineNumber + maxLineNumber) / 2) | 0;
|
||||
|
||||
let midLineNumberVerticalOffset = this.getVerticalOffsetForLineNumber(midLineNumber) | 0;
|
||||
const midLineNumberVerticalOffset = this.getVerticalOffsetForLineNumber(midLineNumber) | 0;
|
||||
|
||||
if (verticalOffset >= midLineNumberVerticalOffset + lineHeight) {
|
||||
// vertical offset is after mid line number
|
||||
|
@ -218,6 +584,7 @@ export class LinesLayout {
|
|||
* @return A structure describing the lines positioned between `verticalOffset1` and `verticalOffset2`.
|
||||
*/
|
||||
public getLinesViewportData(verticalOffset1: number, verticalOffset2: number): IPartialViewLinesViewportData {
|
||||
this._checkPendingChanges();
|
||||
verticalOffset1 = verticalOffset1 | 0;
|
||||
verticalOffset2 = verticalOffset2 | 0;
|
||||
const lineHeight = this._lineHeight;
|
||||
|
@ -230,8 +597,8 @@ export class LinesLayout {
|
|||
let endLineNumber = this._lineCount | 0;
|
||||
|
||||
// Also keep track of what whitespace we've got
|
||||
let whitespaceIndex = this._whitespaces.getFirstWhitespaceIndexAfterLineNumber(startLineNumber) | 0;
|
||||
const whitespaceCount = this._whitespaces.getCount() | 0;
|
||||
let whitespaceIndex = this.getFirstWhitespaceIndexAfterLineNumber(startLineNumber) | 0;
|
||||
const whitespaceCount = this.getWhitespacesCount() | 0;
|
||||
let currentWhitespaceHeight: number;
|
||||
let currentWhitespaceAfterLineNumber: number;
|
||||
|
||||
|
@ -240,8 +607,8 @@ export class LinesLayout {
|
|||
currentWhitespaceAfterLineNumber = endLineNumber + 1;
|
||||
currentWhitespaceHeight = 0;
|
||||
} else {
|
||||
currentWhitespaceAfterLineNumber = this._whitespaces.getAfterLineNumberForWhitespaceIndex(whitespaceIndex) | 0;
|
||||
currentWhitespaceHeight = this._whitespaces.getHeightForWhitespaceIndex(whitespaceIndex) | 0;
|
||||
currentWhitespaceAfterLineNumber = this.getAfterLineNumberForWhitespaceIndex(whitespaceIndex) | 0;
|
||||
currentWhitespaceHeight = this.getHeightForWhitespaceIndex(whitespaceIndex) | 0;
|
||||
}
|
||||
|
||||
let currentVerticalOffset = startLineNumberVerticalOffset;
|
||||
|
@ -258,7 +625,7 @@ export class LinesLayout {
|
|||
currentLineRelativeOffset -= bigNumbersDelta;
|
||||
}
|
||||
|
||||
let linesOffsets: number[] = [];
|
||||
const linesOffsets: number[] = [];
|
||||
|
||||
const verticalCenter = verticalOffset1 + (verticalOffset2 - verticalOffset1) / 2;
|
||||
let centeredLineNumber = -1;
|
||||
|
@ -267,8 +634,8 @@ export class LinesLayout {
|
|||
for (let lineNumber = startLineNumber; lineNumber <= endLineNumber; lineNumber++) {
|
||||
|
||||
if (centeredLineNumber === -1) {
|
||||
let currentLineTop = currentVerticalOffset;
|
||||
let currentLineBottom = currentVerticalOffset + lineHeight;
|
||||
const currentLineTop = currentVerticalOffset;
|
||||
const currentLineBottom = currentVerticalOffset + lineHeight;
|
||||
if ((currentLineTop <= verticalCenter && verticalCenter < currentLineBottom) || currentLineTop > verticalCenter) {
|
||||
centeredLineNumber = lineNumber;
|
||||
}
|
||||
|
@ -291,8 +658,8 @@ export class LinesLayout {
|
|||
if (whitespaceIndex >= whitespaceCount) {
|
||||
currentWhitespaceAfterLineNumber = endLineNumber + 1;
|
||||
} else {
|
||||
currentWhitespaceAfterLineNumber = this._whitespaces.getAfterLineNumberForWhitespaceIndex(whitespaceIndex) | 0;
|
||||
currentWhitespaceHeight = this._whitespaces.getHeightForWhitespaceIndex(whitespaceIndex) | 0;
|
||||
currentWhitespaceAfterLineNumber = this.getAfterLineNumberForWhitespaceIndex(whitespaceIndex) | 0;
|
||||
currentWhitespaceHeight = this.getHeightForWhitespaceIndex(whitespaceIndex) | 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -335,9 +702,10 @@ export class LinesLayout {
|
|||
}
|
||||
|
||||
public getVerticalOffsetForWhitespaceIndex(whitespaceIndex: number): number {
|
||||
this._checkPendingChanges();
|
||||
whitespaceIndex = whitespaceIndex | 0;
|
||||
|
||||
let afterLineNumber = this._whitespaces.getAfterLineNumberForWhitespaceIndex(whitespaceIndex);
|
||||
const afterLineNumber = this.getAfterLineNumberForWhitespaceIndex(whitespaceIndex);
|
||||
|
||||
let previousLinesHeight: number;
|
||||
if (afterLineNumber >= 1) {
|
||||
|
@ -348,7 +716,7 @@ export class LinesLayout {
|
|||
|
||||
let previousWhitespacesHeight: number;
|
||||
if (whitespaceIndex > 0) {
|
||||
previousWhitespacesHeight = this._whitespaces.getAccumulatedHeight(whitespaceIndex - 1);
|
||||
previousWhitespacesHeight = this.getWhitespacesAccumulatedHeight(whitespaceIndex - 1);
|
||||
} else {
|
||||
previousWhitespacesHeight = 0;
|
||||
}
|
||||
|
@ -356,30 +724,28 @@ export class LinesLayout {
|
|||
}
|
||||
|
||||
public getWhitespaceIndexAtOrAfterVerticallOffset(verticalOffset: number): number {
|
||||
this._checkPendingChanges();
|
||||
verticalOffset = verticalOffset | 0;
|
||||
|
||||
let midWhitespaceIndex: number,
|
||||
minWhitespaceIndex = 0,
|
||||
maxWhitespaceIndex = this._whitespaces.getCount() - 1,
|
||||
midWhitespaceVerticalOffset: number,
|
||||
midWhitespaceHeight: number;
|
||||
let minWhitespaceIndex = 0;
|
||||
let maxWhitespaceIndex = this.getWhitespacesCount() - 1;
|
||||
|
||||
if (maxWhitespaceIndex < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Special case: nothing to be found
|
||||
let maxWhitespaceVerticalOffset = this.getVerticalOffsetForWhitespaceIndex(maxWhitespaceIndex);
|
||||
let maxWhitespaceHeight = this._whitespaces.getHeightForWhitespaceIndex(maxWhitespaceIndex);
|
||||
const maxWhitespaceVerticalOffset = this.getVerticalOffsetForWhitespaceIndex(maxWhitespaceIndex);
|
||||
const maxWhitespaceHeight = this.getHeightForWhitespaceIndex(maxWhitespaceIndex);
|
||||
if (verticalOffset >= maxWhitespaceVerticalOffset + maxWhitespaceHeight) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (minWhitespaceIndex < maxWhitespaceIndex) {
|
||||
midWhitespaceIndex = Math.floor((minWhitespaceIndex + maxWhitespaceIndex) / 2);
|
||||
const midWhitespaceIndex = Math.floor((minWhitespaceIndex + maxWhitespaceIndex) / 2);
|
||||
|
||||
midWhitespaceVerticalOffset = this.getVerticalOffsetForWhitespaceIndex(midWhitespaceIndex);
|
||||
midWhitespaceHeight = this._whitespaces.getHeightForWhitespaceIndex(midWhitespaceIndex);
|
||||
const midWhitespaceVerticalOffset = this.getVerticalOffsetForWhitespaceIndex(midWhitespaceIndex);
|
||||
const midWhitespaceHeight = this.getHeightForWhitespaceIndex(midWhitespaceIndex);
|
||||
|
||||
if (verticalOffset >= midWhitespaceVerticalOffset + midWhitespaceHeight) {
|
||||
// vertical offset is after whitespace
|
||||
|
@ -402,27 +768,28 @@ export class LinesLayout {
|
|||
* @return Precisely the whitespace that is layouted at `verticaloffset` or null.
|
||||
*/
|
||||
public getWhitespaceAtVerticalOffset(verticalOffset: number): IViewWhitespaceViewportData | null {
|
||||
this._checkPendingChanges();
|
||||
verticalOffset = verticalOffset | 0;
|
||||
|
||||
let candidateIndex = this.getWhitespaceIndexAtOrAfterVerticallOffset(verticalOffset);
|
||||
const candidateIndex = this.getWhitespaceIndexAtOrAfterVerticallOffset(verticalOffset);
|
||||
|
||||
if (candidateIndex < 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (candidateIndex >= this._whitespaces.getCount()) {
|
||||
if (candidateIndex >= this.getWhitespacesCount()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let candidateTop = this.getVerticalOffsetForWhitespaceIndex(candidateIndex);
|
||||
const candidateTop = this.getVerticalOffsetForWhitespaceIndex(candidateIndex);
|
||||
|
||||
if (candidateTop > verticalOffset) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let candidateHeight = this._whitespaces.getHeightForWhitespaceIndex(candidateIndex);
|
||||
let candidateId = this._whitespaces.getIdForWhitespaceIndex(candidateIndex);
|
||||
let candidateAfterLineNumber = this._whitespaces.getAfterLineNumberForWhitespaceIndex(candidateIndex);
|
||||
const candidateHeight = this.getHeightForWhitespaceIndex(candidateIndex);
|
||||
const candidateId = this.getIdForWhitespaceIndex(candidateIndex);
|
||||
const candidateAfterLineNumber = this.getAfterLineNumberForWhitespaceIndex(candidateIndex);
|
||||
|
||||
return {
|
||||
id: candidateId,
|
||||
|
@ -440,11 +807,12 @@ export class LinesLayout {
|
|||
* @return An array with all the whitespaces in the viewport. If no whitespace is in viewport, the array is empty.
|
||||
*/
|
||||
public getWhitespaceViewportData(verticalOffset1: number, verticalOffset2: number): IViewWhitespaceViewportData[] {
|
||||
this._checkPendingChanges();
|
||||
verticalOffset1 = verticalOffset1 | 0;
|
||||
verticalOffset2 = verticalOffset2 | 0;
|
||||
|
||||
let startIndex = this.getWhitespaceIndexAtOrAfterVerticallOffset(verticalOffset1);
|
||||
let endIndex = this._whitespaces.getCount() - 1;
|
||||
const startIndex = this.getWhitespaceIndexAtOrAfterVerticallOffset(verticalOffset1);
|
||||
const endIndex = this.getWhitespacesCount() - 1;
|
||||
|
||||
if (startIndex < 0) {
|
||||
return [];
|
||||
|
@ -452,15 +820,15 @@ export class LinesLayout {
|
|||
|
||||
let result: IViewWhitespaceViewportData[] = [];
|
||||
for (let i = startIndex; i <= endIndex; i++) {
|
||||
let top = this.getVerticalOffsetForWhitespaceIndex(i);
|
||||
let height = this._whitespaces.getHeightForWhitespaceIndex(i);
|
||||
const top = this.getVerticalOffsetForWhitespaceIndex(i);
|
||||
const height = this.getHeightForWhitespaceIndex(i);
|
||||
if (top >= verticalOffset2) {
|
||||
break;
|
||||
}
|
||||
|
||||
result.push({
|
||||
id: this._whitespaces.getIdForWhitespaceIndex(i),
|
||||
afterLineNumber: this._whitespaces.getAfterLineNumberForWhitespaceIndex(i),
|
||||
id: this.getIdForWhitespaceIndex(i),
|
||||
afterLineNumber: this.getAfterLineNumberForWhitespaceIndex(i),
|
||||
verticalOffset: top,
|
||||
height: height
|
||||
});
|
||||
|
@ -473,6 +841,54 @@ export class LinesLayout {
|
|||
* Get all whitespaces.
|
||||
*/
|
||||
public getWhitespaces(): IEditorWhitespace[] {
|
||||
return this._whitespaces.getWhitespaces(this._lineHeight);
|
||||
this._checkPendingChanges();
|
||||
return this._arr.slice(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of whitespaces.
|
||||
*/
|
||||
public getWhitespacesCount(): number {
|
||||
this._checkPendingChanges();
|
||||
return this._arr.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the `id` for whitespace at index `index`.
|
||||
*
|
||||
* @param index The index of the whitespace.
|
||||
* @return `id` of whitespace at `index`.
|
||||
*/
|
||||
public getIdForWhitespaceIndex(index: number): string {
|
||||
this._checkPendingChanges();
|
||||
index = index | 0;
|
||||
|
||||
return this._arr[index].id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the `afterLineNumber` for whitespace at index `index`.
|
||||
*
|
||||
* @param index The index of the whitespace.
|
||||
* @return `afterLineNumber` of whitespace at `index`.
|
||||
*/
|
||||
public getAfterLineNumberForWhitespaceIndex(index: number): number {
|
||||
this._checkPendingChanges();
|
||||
index = index | 0;
|
||||
|
||||
return this._arr[index].afterLineNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the `height` for whitespace at index `index`.
|
||||
*
|
||||
* @param index The index of the whitespace.
|
||||
* @return `height` of whitespace at `index`.
|
||||
*/
|
||||
public getHeightForWhitespaceIndex(index: number): number {
|
||||
this._checkPendingChanges();
|
||||
index = index | 0;
|
||||
|
||||
return this._arr[index].height;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,9 +8,8 @@ import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
|||
import { IScrollPosition, ScrollEvent, Scrollable, ScrollbarVisibility } from 'vs/base/common/scrollable';
|
||||
import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
import * as editorCommon from 'vs/editor/common/editorCommon';
|
||||
import { LinesLayout } from 'vs/editor/common/viewLayout/linesLayout';
|
||||
import { LinesLayout, IEditorWhitespace, IWhitespaceChangeAccessor } from 'vs/editor/common/viewLayout/linesLayout';
|
||||
import { IPartialViewLinesViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData';
|
||||
import { IEditorWhitespace } from 'vs/editor/common/viewLayout/whitespaceComputer';
|
||||
import { IViewLayout, IViewWhitespaceViewportData, Viewport } from 'vs/editor/common/viewModel/viewModel';
|
||||
|
||||
const SMOOTH_SCROLLING_TIME = 125;
|
||||
|
@ -194,15 +193,8 @@ export class ViewLayout extends Disposable implements IViewLayout {
|
|||
}
|
||||
|
||||
// ---- IVerticalLayoutProvider
|
||||
|
||||
public addWhitespace(afterLineNumber: number, ordinal: number, height: number, minWidth: number): string {
|
||||
return this._linesLayout.insertWhitespace(afterLineNumber, ordinal, height, minWidth);
|
||||
}
|
||||
public changeWhitespace(id: string, newAfterLineNumber: number, newHeight: number): boolean {
|
||||
return this._linesLayout.changeWhitespace(id, newAfterLineNumber, newHeight);
|
||||
}
|
||||
public removeWhitespace(id: string): boolean {
|
||||
return this._linesLayout.removeWhitespace(id);
|
||||
public changeWhitespace<T>(callback: (accessor: IWhitespaceChangeAccessor) => T): T {
|
||||
return this._linesLayout.changeWhitespace(callback);
|
||||
}
|
||||
public getVerticalOffsetForLineNumber(lineNumber: number): number {
|
||||
return this._linesLayout.getVerticalOffsetForLineNumber(lineNumber);
|
||||
|
|
|
@ -1,493 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
|
||||
export interface IEditorWhitespace {
|
||||
readonly id: string;
|
||||
readonly afterLineNumber: number;
|
||||
readonly heightInLines: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represent whitespaces in between lines and provide fast CRUD management methods.
|
||||
* The whitespaces are sorted ascending by `afterLineNumber`.
|
||||
*/
|
||||
export class WhitespaceComputer {
|
||||
|
||||
private static INSTANCE_COUNT = 0;
|
||||
|
||||
private readonly _instanceId: string;
|
||||
|
||||
/**
|
||||
* heights[i] is the height in pixels for whitespace at index i
|
||||
*/
|
||||
private readonly _heights: number[];
|
||||
|
||||
/**
|
||||
* minWidths[i] is the min width in pixels for whitespace at index i
|
||||
*/
|
||||
private readonly _minWidths: number[];
|
||||
|
||||
/**
|
||||
* afterLineNumbers[i] is the line number whitespace at index i is after
|
||||
*/
|
||||
private readonly _afterLineNumbers: number[];
|
||||
|
||||
/**
|
||||
* ordinals[i] is the orinal of the whitespace at index i
|
||||
*/
|
||||
private readonly _ordinals: number[];
|
||||
|
||||
/**
|
||||
* prefixSum[i] = SUM(heights[j]), 1 <= j <= i
|
||||
*/
|
||||
private readonly _prefixSum: number[];
|
||||
|
||||
/**
|
||||
* prefixSum[i], 1 <= i <= prefixSumValidIndex can be trusted
|
||||
*/
|
||||
private _prefixSumValidIndex: number;
|
||||
|
||||
/**
|
||||
* ids[i] is the whitespace id of whitespace at index i
|
||||
*/
|
||||
private readonly _ids: string[];
|
||||
|
||||
/**
|
||||
* index at which a whitespace is positioned (inside heights, afterLineNumbers, prefixSum members)
|
||||
*/
|
||||
private readonly _whitespaceId2Index: {
|
||||
[id: string]: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* last whitespace id issued
|
||||
*/
|
||||
private _lastWhitespaceId: number;
|
||||
|
||||
private _minWidth: number;
|
||||
|
||||
constructor() {
|
||||
this._instanceId = strings.singleLetterHash(++WhitespaceComputer.INSTANCE_COUNT);
|
||||
this._heights = [];
|
||||
this._minWidths = [];
|
||||
this._ids = [];
|
||||
this._afterLineNumbers = [];
|
||||
this._ordinals = [];
|
||||
this._prefixSum = [];
|
||||
this._prefixSumValidIndex = -1;
|
||||
this._whitespaceId2Index = {};
|
||||
this._lastWhitespaceId = 0;
|
||||
this._minWidth = -1; /* marker for not being computed */
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the insertion index for a new value inside a sorted array of values.
|
||||
* If the value is already present in the sorted array, the insertion index will be after the already existing value.
|
||||
*/
|
||||
public static findInsertionIndex(sortedArray: number[], value: number, ordinals: number[], valueOrdinal: number): number {
|
||||
let low = 0;
|
||||
let high = sortedArray.length;
|
||||
|
||||
while (low < high) {
|
||||
let mid = ((low + high) >>> 1);
|
||||
|
||||
if (value === sortedArray[mid]) {
|
||||
if (valueOrdinal < ordinals[mid]) {
|
||||
high = mid;
|
||||
} else {
|
||||
low = mid + 1;
|
||||
}
|
||||
} else if (value < sortedArray[mid]) {
|
||||
high = mid;
|
||||
} else {
|
||||
low = mid + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return low;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a new whitespace of a certain height after a line number.
|
||||
* The whitespace has a "sticky" characteristic.
|
||||
* Irrespective of edits above or below `afterLineNumber`, the whitespace will follow the initial line.
|
||||
*
|
||||
* @param afterLineNumber The conceptual position of this whitespace. The whitespace will follow this line as best as possible even when deleting/inserting lines above/below.
|
||||
* @param heightInPx The height of the whitespace, in pixels.
|
||||
* @return An id that can be used later to mutate or delete the whitespace
|
||||
*/
|
||||
public insertWhitespace(afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): string {
|
||||
afterLineNumber = afterLineNumber | 0;
|
||||
ordinal = ordinal | 0;
|
||||
heightInPx = heightInPx | 0;
|
||||
minWidth = minWidth | 0;
|
||||
|
||||
let id = this._instanceId + (++this._lastWhitespaceId);
|
||||
let insertionIndex = WhitespaceComputer.findInsertionIndex(this._afterLineNumbers, afterLineNumber, this._ordinals, ordinal);
|
||||
this._insertWhitespaceAtIndex(id, insertionIndex, afterLineNumber, ordinal, heightInPx, minWidth);
|
||||
this._minWidth = -1; /* marker for not being computed */
|
||||
return id;
|
||||
}
|
||||
|
||||
private _insertWhitespaceAtIndex(id: string, insertIndex: number, afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): void {
|
||||
insertIndex = insertIndex | 0;
|
||||
afterLineNumber = afterLineNumber | 0;
|
||||
ordinal = ordinal | 0;
|
||||
heightInPx = heightInPx | 0;
|
||||
minWidth = minWidth | 0;
|
||||
|
||||
this._heights.splice(insertIndex, 0, heightInPx);
|
||||
this._minWidths.splice(insertIndex, 0, minWidth);
|
||||
this._ids.splice(insertIndex, 0, id);
|
||||
this._afterLineNumbers.splice(insertIndex, 0, afterLineNumber);
|
||||
this._ordinals.splice(insertIndex, 0, ordinal);
|
||||
this._prefixSum.splice(insertIndex, 0, 0);
|
||||
|
||||
let keys = Object.keys(this._whitespaceId2Index);
|
||||
for (let i = 0, len = keys.length; i < len; i++) {
|
||||
let sid = keys[i];
|
||||
let oldIndex = this._whitespaceId2Index[sid];
|
||||
if (oldIndex >= insertIndex) {
|
||||
this._whitespaceId2Index[sid] = oldIndex + 1;
|
||||
}
|
||||
}
|
||||
|
||||
this._whitespaceId2Index[id] = insertIndex;
|
||||
this._prefixSumValidIndex = Math.min(this._prefixSumValidIndex, insertIndex - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change properties associated with a certain whitespace.
|
||||
*/
|
||||
public changeWhitespace(id: string, newAfterLineNumber: number, newHeight: number): boolean {
|
||||
newAfterLineNumber = newAfterLineNumber | 0;
|
||||
newHeight = newHeight | 0;
|
||||
|
||||
let hasChanges = false;
|
||||
hasChanges = this.changeWhitespaceHeight(id, newHeight) || hasChanges;
|
||||
hasChanges = this.changeWhitespaceAfterLineNumber(id, newAfterLineNumber) || hasChanges;
|
||||
return hasChanges;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the height of an existing whitespace
|
||||
*
|
||||
* @param id The whitespace to change
|
||||
* @param newHeightInPx The new height of the whitespace, in pixels
|
||||
* @return Returns true if the whitespace is found and if the new height is different than the old height
|
||||
*/
|
||||
public changeWhitespaceHeight(id: string, newHeightInPx: number): boolean {
|
||||
newHeightInPx = newHeightInPx | 0;
|
||||
|
||||
if (this._whitespaceId2Index.hasOwnProperty(id)) {
|
||||
let index = this._whitespaceId2Index[id];
|
||||
if (this._heights[index] !== newHeightInPx) {
|
||||
this._heights[index] = newHeightInPx;
|
||||
this._prefixSumValidIndex = Math.min(this._prefixSumValidIndex, index - 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the line number after which an existing whitespace flows.
|
||||
*
|
||||
* @param id The whitespace to change
|
||||
* @param newAfterLineNumber The new line number the whitespace will follow
|
||||
* @return Returns true if the whitespace is found and if the new line number is different than the old line number
|
||||
*/
|
||||
public changeWhitespaceAfterLineNumber(id: string, newAfterLineNumber: number): boolean {
|
||||
newAfterLineNumber = newAfterLineNumber | 0;
|
||||
|
||||
if (this._whitespaceId2Index.hasOwnProperty(id)) {
|
||||
let index = this._whitespaceId2Index[id];
|
||||
if (this._afterLineNumbers[index] !== newAfterLineNumber) {
|
||||
// `afterLineNumber` changed for this whitespace
|
||||
|
||||
// Record old ordinal
|
||||
let ordinal = this._ordinals[index];
|
||||
|
||||
// Record old height
|
||||
let heightInPx = this._heights[index];
|
||||
|
||||
// Record old min width
|
||||
let minWidth = this._minWidths[index];
|
||||
|
||||
// Since changing `afterLineNumber` can trigger a reordering, we're gonna remove this whitespace
|
||||
this.removeWhitespace(id);
|
||||
|
||||
// And add it again
|
||||
let insertionIndex = WhitespaceComputer.findInsertionIndex(this._afterLineNumbers, newAfterLineNumber, this._ordinals, ordinal);
|
||||
this._insertWhitespaceAtIndex(id, insertionIndex, newAfterLineNumber, ordinal, heightInPx, minWidth);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an existing whitespace.
|
||||
*
|
||||
* @param id The whitespace to remove
|
||||
* @return Returns true if the whitespace is found and it is removed.
|
||||
*/
|
||||
public removeWhitespace(id: string): boolean {
|
||||
if (this._whitespaceId2Index.hasOwnProperty(id)) {
|
||||
let index = this._whitespaceId2Index[id];
|
||||
delete this._whitespaceId2Index[id];
|
||||
this._removeWhitespaceAtIndex(index);
|
||||
this._minWidth = -1; /* marker for not being computed */
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private _removeWhitespaceAtIndex(removeIndex: number): void {
|
||||
removeIndex = removeIndex | 0;
|
||||
|
||||
this._heights.splice(removeIndex, 1);
|
||||
this._minWidths.splice(removeIndex, 1);
|
||||
this._ids.splice(removeIndex, 1);
|
||||
this._afterLineNumbers.splice(removeIndex, 1);
|
||||
this._ordinals.splice(removeIndex, 1);
|
||||
this._prefixSum.splice(removeIndex, 1);
|
||||
this._prefixSumValidIndex = Math.min(this._prefixSumValidIndex, removeIndex - 1);
|
||||
|
||||
let keys = Object.keys(this._whitespaceId2Index);
|
||||
for (let i = 0, len = keys.length; i < len; i++) {
|
||||
let sid = keys[i];
|
||||
let oldIndex = this._whitespaceId2Index[sid];
|
||||
if (oldIndex >= removeIndex) {
|
||||
this._whitespaceId2Index[sid] = oldIndex - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the computer that lines have been deleted (a continuous zone of lines).
|
||||
* This gives it a chance to update `afterLineNumber` for whitespaces, giving the "sticky" characteristic.
|
||||
*
|
||||
* @param fromLineNumber The line number at which the deletion started, inclusive
|
||||
* @param toLineNumber The line number at which the deletion ended, inclusive
|
||||
*/
|
||||
public onLinesDeleted(fromLineNumber: number, toLineNumber: number): void {
|
||||
fromLineNumber = fromLineNumber | 0;
|
||||
toLineNumber = toLineNumber | 0;
|
||||
|
||||
for (let i = 0, len = this._afterLineNumbers.length; i < len; i++) {
|
||||
let afterLineNumber = this._afterLineNumbers[i];
|
||||
|
||||
if (fromLineNumber <= afterLineNumber && afterLineNumber <= toLineNumber) {
|
||||
// The line this whitespace was after has been deleted
|
||||
// => move whitespace to before first deleted line
|
||||
this._afterLineNumbers[i] = fromLineNumber - 1;
|
||||
} else if (afterLineNumber > toLineNumber) {
|
||||
// The line this whitespace was after has been moved up
|
||||
// => move whitespace up
|
||||
this._afterLineNumbers[i] -= (toLineNumber - fromLineNumber + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the computer that lines have been inserted (a continuous zone of lines).
|
||||
* This gives it a chance to update `afterLineNumber` for whitespaces, giving the "sticky" characteristic.
|
||||
*
|
||||
* @param fromLineNumber The line number at which the insertion started, inclusive
|
||||
* @param toLineNumber The line number at which the insertion ended, inclusive.
|
||||
*/
|
||||
public onLinesInserted(fromLineNumber: number, toLineNumber: number): void {
|
||||
fromLineNumber = fromLineNumber | 0;
|
||||
toLineNumber = toLineNumber | 0;
|
||||
|
||||
for (let i = 0, len = this._afterLineNumbers.length; i < len; i++) {
|
||||
let afterLineNumber = this._afterLineNumbers[i];
|
||||
|
||||
if (fromLineNumber <= afterLineNumber) {
|
||||
this._afterLineNumbers[i] += (toLineNumber - fromLineNumber + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the sum of all the whitespaces.
|
||||
*/
|
||||
public getTotalHeight(): number {
|
||||
if (this._heights.length === 0) {
|
||||
return 0;
|
||||
}
|
||||
return this.getAccumulatedHeight(this._heights.length - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the sum of the heights of the whitespaces at [0..index].
|
||||
* This includes the whitespace at `index`.
|
||||
*
|
||||
* @param index The index of the whitespace.
|
||||
* @return The sum of the heights of all whitespaces before the one at `index`, including the one at `index`.
|
||||
*/
|
||||
public getAccumulatedHeight(index: number): number {
|
||||
index = index | 0;
|
||||
|
||||
let startIndex = Math.max(0, this._prefixSumValidIndex + 1);
|
||||
if (startIndex === 0) {
|
||||
this._prefixSum[0] = this._heights[0];
|
||||
startIndex++;
|
||||
}
|
||||
|
||||
for (let i = startIndex; i <= index; i++) {
|
||||
this._prefixSum[i] = this._prefixSum[i - 1] + this._heights[i];
|
||||
}
|
||||
this._prefixSumValidIndex = Math.max(this._prefixSumValidIndex, index);
|
||||
return this._prefixSum[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all whitespaces with `afterLineNumber` < `lineNumber` and return the sum of their heights.
|
||||
*
|
||||
* @param lineNumber The line number whitespaces should be before.
|
||||
* @return The sum of the heights of the whitespaces before `lineNumber`.
|
||||
*/
|
||||
public getAccumulatedHeightBeforeLineNumber(lineNumber: number): number {
|
||||
lineNumber = lineNumber | 0;
|
||||
|
||||
let lastWhitespaceBeforeLineNumber = this._findLastWhitespaceBeforeLineNumber(lineNumber);
|
||||
|
||||
if (lastWhitespaceBeforeLineNumber === -1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return this.getAccumulatedHeight(lastWhitespaceBeforeLineNumber);
|
||||
}
|
||||
|
||||
private _findLastWhitespaceBeforeLineNumber(lineNumber: number): number {
|
||||
lineNumber = lineNumber | 0;
|
||||
|
||||
// Find the whitespace before line number
|
||||
let afterLineNumbers = this._afterLineNumbers;
|
||||
let low = 0;
|
||||
let high = afterLineNumbers.length - 1;
|
||||
|
||||
while (low <= high) {
|
||||
let delta = (high - low) | 0;
|
||||
let halfDelta = (delta / 2) | 0;
|
||||
let mid = (low + halfDelta) | 0;
|
||||
|
||||
if (afterLineNumbers[mid] < lineNumber) {
|
||||
if (mid + 1 >= afterLineNumbers.length || afterLineNumbers[mid + 1] >= lineNumber) {
|
||||
return mid;
|
||||
} else {
|
||||
low = (mid + 1) | 0;
|
||||
}
|
||||
} else {
|
||||
high = (mid - 1) | 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
private _findFirstWhitespaceAfterLineNumber(lineNumber: number): number {
|
||||
lineNumber = lineNumber | 0;
|
||||
|
||||
let lastWhitespaceBeforeLineNumber = this._findLastWhitespaceBeforeLineNumber(lineNumber);
|
||||
let firstWhitespaceAfterLineNumber = lastWhitespaceBeforeLineNumber + 1;
|
||||
|
||||
if (firstWhitespaceAfterLineNumber < this._heights.length) {
|
||||
return firstWhitespaceAfterLineNumber;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the index of the first whitespace which has `afterLineNumber` >= `lineNumber`.
|
||||
* @return The index of the first whitespace with `afterLineNumber` >= `lineNumber` or -1 if no whitespace is found.
|
||||
*/
|
||||
public getFirstWhitespaceIndexAfterLineNumber(lineNumber: number): number {
|
||||
lineNumber = lineNumber | 0;
|
||||
|
||||
return this._findFirstWhitespaceAfterLineNumber(lineNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of whitespaces.
|
||||
*/
|
||||
public getCount(): number {
|
||||
return this._heights.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* The maximum min width for all whitespaces.
|
||||
*/
|
||||
public getMinWidth(): number {
|
||||
if (this._minWidth === -1) {
|
||||
let minWidth = 0;
|
||||
for (let i = 0, len = this._minWidths.length; i < len; i++) {
|
||||
minWidth = Math.max(minWidth, this._minWidths[i]);
|
||||
}
|
||||
this._minWidth = minWidth;
|
||||
}
|
||||
return this._minWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the `afterLineNumber` for whitespace at index `index`.
|
||||
*
|
||||
* @param index The index of the whitespace.
|
||||
* @return `afterLineNumber` of whitespace at `index`.
|
||||
*/
|
||||
public getAfterLineNumberForWhitespaceIndex(index: number): number {
|
||||
index = index | 0;
|
||||
|
||||
return this._afterLineNumbers[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the `id` for whitespace at index `index`.
|
||||
*
|
||||
* @param index The index of the whitespace.
|
||||
* @return `id` of whitespace at `index`.
|
||||
*/
|
||||
public getIdForWhitespaceIndex(index: number): string {
|
||||
index = index | 0;
|
||||
|
||||
return this._ids[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the `height` for whitespace at index `index`.
|
||||
*
|
||||
* @param index The index of the whitespace.
|
||||
* @return `height` of whitespace at `index`.
|
||||
*/
|
||||
public getHeightForWhitespaceIndex(index: number): number {
|
||||
index = index | 0;
|
||||
|
||||
return this._heights[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all whitespaces.
|
||||
*/
|
||||
public getWhitespaces(deviceLineHeight: number): IEditorWhitespace[] {
|
||||
deviceLineHeight = deviceLineHeight | 0;
|
||||
|
||||
let result: IEditorWhitespace[] = [];
|
||||
for (let i = 0; i < this._heights.length; i++) {
|
||||
result.push({
|
||||
id: this._ids[i],
|
||||
afterLineNumber: this._afterLineNumbers[i],
|
||||
heightInLines: this._heights[i] / deviceLineHeight
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -13,7 +13,7 @@ import { INewScrollPosition } from 'vs/editor/common/editorCommon';
|
|||
import { EndOfLinePreference, IActiveIndentGuideInfo, IModelDecorationOptions, TextModelResolvedOptions } from 'vs/editor/common/model';
|
||||
import { IViewEventListener } from 'vs/editor/common/view/viewEvents';
|
||||
import { IPartialViewLinesViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData';
|
||||
import { IEditorWhitespace } from 'vs/editor/common/viewLayout/whitespaceComputer';
|
||||
import { IEditorWhitespace, IWhitespaceChangeAccessor } from 'vs/editor/common/viewLayout/linesLayout';
|
||||
import { ITheme } from 'vs/platform/theme/common/themeService';
|
||||
|
||||
export interface IViewWhitespaceViewportData {
|
||||
|
@ -69,20 +69,8 @@ export interface IViewLayout {
|
|||
getWhitespaceAtVerticalOffset(verticalOffset: number): IViewWhitespaceViewportData | null;
|
||||
|
||||
// --------------- Begin vertical whitespace management
|
||||
changeWhitespace<T>(callback: (accessor: IWhitespaceChangeAccessor) => T): T;
|
||||
|
||||
/**
|
||||
* Reserve rendering space.
|
||||
* @return an identifier that can be later used to remove or change the whitespace.
|
||||
*/
|
||||
addWhitespace(afterLineNumber: number, ordinal: number, height: number, minWidth: number): string;
|
||||
/**
|
||||
* Change the properties of a whitespace.
|
||||
*/
|
||||
changeWhitespace(id: string, newAfterLineNumber: number, newHeight: number): boolean;
|
||||
/**
|
||||
* Remove rendering space
|
||||
*/
|
||||
removeWhitespace(id: string): boolean;
|
||||
/**
|
||||
* Get the layout information for whitespaces currently in the viewport
|
||||
*/
|
||||
|
|
|
@ -14,7 +14,6 @@ import { Disposable } from 'vs/base/common/lifecycle';
|
|||
import { ColorPickerModel } from 'vs/editor/contrib/colorPicker/colorPickerModel';
|
||||
import { editorHoverBackground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
import { BrowserFeatures } from 'vs/base/browser/canIUse';
|
||||
|
||||
const $ = dom.$;
|
||||
|
||||
|
@ -151,7 +150,7 @@ class SaturationBox extends Disposable {
|
|||
|
||||
this.layout();
|
||||
|
||||
this._register(dom.addDisposableListener(this.domNode, BrowserFeatures.pointerEvents ? dom.EventType.POINTER_DOWN : dom.EventType.MOUSE_DOWN, e => this.onMouseDown(e)));
|
||||
this._register(dom.addDisposableGenericMouseDownListner(this.domNode, e => this.onMouseDown(e)));
|
||||
this._register(this.model.onDidChangeColor(this.onDidChangeColor, this));
|
||||
this.monitor = null;
|
||||
}
|
||||
|
@ -166,7 +165,7 @@ class SaturationBox extends Disposable {
|
|||
|
||||
this.monitor.startMonitoring(standardMouseMoveMerger, event => this.onDidChangePosition(event.posx - origin.left, event.posy - origin.top), () => null);
|
||||
|
||||
const mouseUpListener = dom.addDisposableListener(document, BrowserFeatures.pointerEvents ? dom.EventType.POINTER_UP : dom.EventType.MOUSE_UP, () => {
|
||||
const mouseUpListener = dom.addDisposableGenericMouseUpListner(document, () => {
|
||||
this._onColorFlushed.fire();
|
||||
mouseUpListener.dispose();
|
||||
if (this.monitor) {
|
||||
|
@ -251,7 +250,7 @@ abstract class Strip extends Disposable {
|
|||
this.slider = dom.append(this.domNode, $('.slider'));
|
||||
this.slider.style.top = `0px`;
|
||||
|
||||
this._register(dom.addDisposableListener(this.domNode, BrowserFeatures.pointerEvents ? dom.EventType.POINTER_DOWN : dom.EventType.MOUSE_DOWN, e => this.onMouseDown(e)));
|
||||
this._register(dom.addDisposableGenericMouseDownListner(this.domNode, e => this.onMouseDown(e)));
|
||||
this.layout();
|
||||
}
|
||||
|
||||
|
@ -273,7 +272,7 @@ abstract class Strip extends Disposable {
|
|||
|
||||
monitor.startMonitoring(standardMouseMoveMerger, event => this.onDidChangeTop(event.posy - origin.top), () => null);
|
||||
|
||||
const mouseUpListener = dom.addDisposableListener(document, BrowserFeatures.pointerEvents ? dom.EventType.POINTER_UP : dom.EventType.MOUSE_UP, () => {
|
||||
const mouseUpListener = dom.addDisposableGenericMouseUpListner(document, () => {
|
||||
this._onColorFlushed.fire();
|
||||
mouseUpListener.dispose();
|
||||
monitor.stopMonitoring(true);
|
||||
|
|
|
@ -8,7 +8,7 @@ import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
|||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { isMacintosh } from 'vs/base/common/platform';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { ICodeEditor, IEditorMouseEvent, IMouseTarget, MouseTargetType } from 'vs/editor/browser/editorBrowser';
|
||||
import { ICodeEditor, IEditorMouseEvent, IMouseTarget, MouseTargetType, IPartialEditorMouseEvent } from 'vs/editor/browser/editorBrowser';
|
||||
import { registerEditorContribution } from 'vs/editor/browser/editorExtensions';
|
||||
import * as editorCommon from 'vs/editor/common/editorCommon';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
|
@ -50,7 +50,7 @@ export class DragAndDropController extends Disposable implements editorCommon.IE
|
|||
this._register(this._editor.onMouseDown((e: IEditorMouseEvent) => this._onEditorMouseDown(e)));
|
||||
this._register(this._editor.onMouseUp((e: IEditorMouseEvent) => this._onEditorMouseUp(e)));
|
||||
this._register(this._editor.onMouseDrag((e: IEditorMouseEvent) => this._onEditorMouseDrag(e)));
|
||||
this._register(this._editor.onMouseDrop((e: IEditorMouseEvent) => this._onEditorMouseDrop(e)));
|
||||
this._register(this._editor.onMouseDrop((e: IPartialEditorMouseEvent) => this._onEditorMouseDrop(e)));
|
||||
this._register(this._editor.onKeyDown((e: IKeyboardEvent) => this.onEditorKeyDown(e)));
|
||||
this._register(this._editor.onKeyUp((e: IKeyboardEvent) => this.onEditorKeyUp(e)));
|
||||
this._register(this._editor.onDidBlurEditorWidget(() => this.onEditorBlur()));
|
||||
|
@ -143,7 +143,7 @@ export class DragAndDropController extends Disposable implements editorCommon.IE
|
|||
}
|
||||
}
|
||||
|
||||
private _onEditorMouseDrop(mouseEvent: IEditorMouseEvent): void {
|
||||
private _onEditorMouseDrop(mouseEvent: IPartialEditorMouseEvent): void {
|
||||
if (mouseEvent.target && (this._hitContent(mouseEvent.target) || this._hitMargin(mouseEvent.target)) && mouseEvent.target.position) {
|
||||
let newCursorPosition = new Position(mouseEvent.target.position.lineNumber, mouseEvent.target.position.column);
|
||||
|
||||
|
|
|
@ -65,7 +65,6 @@
|
|||
|
||||
.monaco-editor .find-widget .monaco-inputbox .input {
|
||||
background-color: transparent;
|
||||
/* Style to compensate for //winjs */
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -423,7 +423,7 @@ export class FoldingController extends Disposable implements IEditorContribution
|
|||
if (iconClicked || isCollapsed) {
|
||||
let toToggle = [region];
|
||||
if (e.event.middleButton || e.event.shiftKey) {
|
||||
toToggle.push(...foldingModel.getRegionsInside(region, r => r.isCollapsed === isCollapsed));
|
||||
toToggle.push(...foldingModel.getRegionsInside(region, (r: FoldingRegion) => r.isCollapsed === isCollapsed));
|
||||
}
|
||||
foldingModel.toggleCollapseState(toToggle);
|
||||
this.reveal({ lineNumber, column: 1 });
|
||||
|
|
|
@ -204,7 +204,7 @@ export class FoldingModel {
|
|||
return null;
|
||||
}
|
||||
|
||||
getRegionsInside(region: FoldingRegion | null, filter?: (r: FoldingRegion, level?: number) => boolean): FoldingRegion[] {
|
||||
getRegionsInside(region: FoldingRegion | null, filter?: RegionFilter | RegionFilterWithLevel): FoldingRegion[] {
|
||||
let result: FoldingRegion[] = [];
|
||||
let index = region ? region.regionIndex + 1 : 0;
|
||||
let endLineNumber = region ? region.endLineNumber : Number.MAX_VALUE;
|
||||
|
@ -229,7 +229,7 @@ export class FoldingModel {
|
|||
for (let i = index, len = this._regions.length; i < len; i++) {
|
||||
let current = this._regions.toRegion(i);
|
||||
if (this._regions.getStartLineNumber(i) < endLineNumber) {
|
||||
if (!filter || filter(current)) {
|
||||
if (!filter || (filter as RegionFilter)(current)) {
|
||||
result.push(current);
|
||||
}
|
||||
} else {
|
||||
|
@ -242,6 +242,10 @@ export class FoldingModel {
|
|||
|
||||
}
|
||||
|
||||
type RegionFilter = (r: FoldingRegion) => boolean;
|
||||
type RegionFilterWithLevel = (r: FoldingRegion, level: number) => boolean;
|
||||
|
||||
|
||||
/**
|
||||
* Collapse or expand the regions at the given locations
|
||||
* @param levels The number of levels. Use 1 to only impact the regions at the location, use Number.MAX_VALUE for all levels.
|
||||
|
|
|
@ -119,8 +119,9 @@ abstract class SymbolNavigationAction extends EditorAction {
|
|||
|
||||
} else {
|
||||
const next = model.firstReference()!;
|
||||
const targetEditor = await this._openReference(editor, editorService, next, this._configuration.openToSide);
|
||||
if (targetEditor && model.references.length > 1 && gotoLocation === 'gotoAndPeek') {
|
||||
const peek = model.references.length > 1 && gotoLocation === 'gotoAndPeek';
|
||||
const targetEditor = await this._openReference(editor, editorService, next, this._configuration.openToSide, !peek);
|
||||
if (peek && targetEditor) {
|
||||
this._openInPeek(targetEditor, model);
|
||||
} else {
|
||||
model.dispose();
|
||||
|
@ -134,7 +135,7 @@ abstract class SymbolNavigationAction extends EditorAction {
|
|||
}
|
||||
}
|
||||
|
||||
private _openReference(editor: ICodeEditor, editorService: ICodeEditorService, reference: Location | LocationLink, sideBySide: boolean): Promise<ICodeEditor | null> {
|
||||
private async _openReference(editor: ICodeEditor, editorService: ICodeEditorService, reference: Location | LocationLink, sideBySide: boolean, highlight: boolean): Promise<ICodeEditor | undefined> {
|
||||
// range is the target-selection-range when we have one
|
||||
// and the the fallback is the 'full' range
|
||||
let range: IRange | undefined = undefined;
|
||||
|
@ -145,13 +146,29 @@ abstract class SymbolNavigationAction extends EditorAction {
|
|||
range = reference.range;
|
||||
}
|
||||
|
||||
return editorService.openCodeEditor({
|
||||
const targetEditor = await editorService.openCodeEditor({
|
||||
resource: reference.uri,
|
||||
options: {
|
||||
selection: Range.collapseToStart(range),
|
||||
revealInCenterIfOutsideViewport: true
|
||||
}
|
||||
}, editor, sideBySide);
|
||||
|
||||
if (!targetEditor) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (highlight) {
|
||||
const modelNow = targetEditor.getModel();
|
||||
const ids = targetEditor.deltaDecorations([], [{ range, options: { className: 'rangeHighlight' } }]);
|
||||
setTimeout(() => {
|
||||
if (targetEditor.getModel() === modelNow) {
|
||||
targetEditor.deltaDecorations(ids, []);
|
||||
}
|
||||
}, 350);
|
||||
}
|
||||
|
||||
return targetEditor;
|
||||
}
|
||||
|
||||
private _openInPeek(target: ICodeEditor, model: ReferencesModel) {
|
||||
|
|
|
@ -207,7 +207,7 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
|
|||
private readonly _themeService: IThemeService,
|
||||
private readonly _keybindingService: IKeybindingService,
|
||||
private readonly _modeService: IModeService,
|
||||
private readonly _openerService: IOpenerService | null = NullOpenerService,
|
||||
private readonly _openerService: IOpenerService = NullOpenerService,
|
||||
) {
|
||||
super(ModesContentHoverWidget.ID, editor);
|
||||
|
||||
|
|
|
@ -97,7 +97,7 @@ export class ModesGlyphHoverWidget extends GlyphHoverWidget {
|
|||
constructor(
|
||||
editor: ICodeEditor,
|
||||
modeService: IModeService,
|
||||
openerService: IOpenerService | null = NullOpenerService,
|
||||
openerService: IOpenerService = NullOpenerService,
|
||||
) {
|
||||
super(ModesGlyphHoverWidget.ID, editor);
|
||||
|
||||
|
|
|
@ -44,17 +44,9 @@ export class Link implements ILink {
|
|||
return this._link.tooltip;
|
||||
}
|
||||
|
||||
resolve(token: CancellationToken): Promise<URI> {
|
||||
async resolve(token: CancellationToken): Promise<URI | string> {
|
||||
if (this._link.url) {
|
||||
try {
|
||||
if (typeof this._link.url === 'string') {
|
||||
return Promise.resolve(URI.parse(this._link.url));
|
||||
} else {
|
||||
return Promise.resolve(this._link.url);
|
||||
}
|
||||
} catch (e) {
|
||||
return Promise.reject(new Error('invalid'));
|
||||
}
|
||||
return this._link.url;
|
||||
}
|
||||
|
||||
if (typeof this._provider.resolveLink === 'function') {
|
||||
|
|
|
@ -7,7 +7,6 @@ import { IMarkdownString } from 'vs/base/common/htmlContent';
|
|||
import { renderMarkdown, MarkdownRenderOptions } from 'vs/base/browser/markdownRenderer';
|
||||
import { IOpenerService, NullOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { tokenizeToString } from 'vs/editor/common/modes/textToHtmlTokenizer';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
|
@ -29,7 +28,7 @@ export class MarkdownRenderer extends Disposable {
|
|||
constructor(
|
||||
private readonly _editor: ICodeEditor,
|
||||
@IModeService private readonly _modeService: IModeService,
|
||||
@optional(IOpenerService) private readonly _openerService: IOpenerService | null = NullOpenerService,
|
||||
@optional(IOpenerService) private readonly _openerService: IOpenerService = NullOpenerService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
@ -64,15 +63,7 @@ export class MarkdownRenderer extends Disposable {
|
|||
codeBlockRenderCallback: () => this._onDidRenderCodeBlock.fire(),
|
||||
actionHandler: {
|
||||
callback: (content) => {
|
||||
let uri: URI | undefined;
|
||||
try {
|
||||
uri = URI.parse(content);
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
if (uri && this._openerService) {
|
||||
this._openerService.open(uri, { fromUserGesture: true }).catch(onUnexpectedError);
|
||||
}
|
||||
this._openerService.open(content, { fromUserGesture: true }).catch(onUnexpectedError);
|
||||
},
|
||||
disposeables
|
||||
}
|
||||
|
|
|
@ -89,7 +89,7 @@ export module StaticServices {
|
|||
|
||||
let _all: LazyStaticService<any>[] = [];
|
||||
|
||||
function define<T>(serviceId: ServiceIdentifier<T>, factory: (overrides: IEditorOverrideServices) => T): LazyStaticService<T> {
|
||||
function define<T>(serviceId: ServiceIdentifier<T>, factory: (overrides: IEditorOverrideServices | undefined) => T): LazyStaticService<T> {
|
||||
let r = new LazyStaticService(serviceId, factory);
|
||||
_all.push(r);
|
||||
return r;
|
||||
|
|
|
@ -8,6 +8,7 @@ import { URI } from 'vs/base/common/uri';
|
|||
import { OpenerService } from 'vs/editor/browser/services/openerService';
|
||||
import { TestCodeEditorService } from 'vs/editor/test/browser/editorTestServices';
|
||||
import { CommandsRegistry, ICommandService, NullCommandService } from 'vs/platform/commands/common/commands';
|
||||
import { matchesScheme } from 'vs/platform/opener/common/opener';
|
||||
|
||||
suite('OpenerService', function () {
|
||||
const editorService = new TestCodeEditorService();
|
||||
|
@ -28,27 +29,27 @@ suite('OpenerService', function () {
|
|||
lastCommand = undefined;
|
||||
});
|
||||
|
||||
test('delegate to editorService, scheme:///fff', function () {
|
||||
test('delegate to editorService, scheme:///fff', async function () {
|
||||
const openerService = new OpenerService(editorService, NullCommandService);
|
||||
openerService.open(URI.parse('another:///somepath'));
|
||||
await openerService.open(URI.parse('another:///somepath'));
|
||||
assert.equal(editorService.lastInput!.options!.selection, undefined);
|
||||
});
|
||||
|
||||
test('delegate to editorService, scheme:///fff#L123', function () {
|
||||
test('delegate to editorService, scheme:///fff#L123', async function () {
|
||||
const openerService = new OpenerService(editorService, NullCommandService);
|
||||
|
||||
openerService.open(URI.parse('file:///somepath#L23'));
|
||||
await openerService.open(URI.parse('file:///somepath#L23'));
|
||||
assert.equal(editorService.lastInput!.options!.selection!.startLineNumber, 23);
|
||||
assert.equal(editorService.lastInput!.options!.selection!.startColumn, 1);
|
||||
assert.equal(editorService.lastInput!.options!.selection!.endLineNumber, undefined);
|
||||
assert.equal(editorService.lastInput!.options!.selection!.endColumn, undefined);
|
||||
assert.equal(editorService.lastInput!.resource.fragment, '');
|
||||
|
||||
openerService.open(URI.parse('another:///somepath#L23'));
|
||||
await openerService.open(URI.parse('another:///somepath#L23'));
|
||||
assert.equal(editorService.lastInput!.options!.selection!.startLineNumber, 23);
|
||||
assert.equal(editorService.lastInput!.options!.selection!.startColumn, 1);
|
||||
|
||||
openerService.open(URI.parse('another:///somepath#L23,45'));
|
||||
await openerService.open(URI.parse('another:///somepath#L23,45'));
|
||||
assert.equal(editorService.lastInput!.options!.selection!.startLineNumber, 23);
|
||||
assert.equal(editorService.lastInput!.options!.selection!.startColumn, 45);
|
||||
assert.equal(editorService.lastInput!.options!.selection!.endLineNumber, undefined);
|
||||
|
@ -56,17 +57,17 @@ suite('OpenerService', function () {
|
|||
assert.equal(editorService.lastInput!.resource.fragment, '');
|
||||
});
|
||||
|
||||
test('delegate to editorService, scheme:///fff#123,123', function () {
|
||||
test('delegate to editorService, scheme:///fff#123,123', async function () {
|
||||
const openerService = new OpenerService(editorService, NullCommandService);
|
||||
|
||||
openerService.open(URI.parse('file:///somepath#23'));
|
||||
await openerService.open(URI.parse('file:///somepath#23'));
|
||||
assert.equal(editorService.lastInput!.options!.selection!.startLineNumber, 23);
|
||||
assert.equal(editorService.lastInput!.options!.selection!.startColumn, 1);
|
||||
assert.equal(editorService.lastInput!.options!.selection!.endLineNumber, undefined);
|
||||
assert.equal(editorService.lastInput!.options!.selection!.endColumn, undefined);
|
||||
assert.equal(editorService.lastInput!.resource.fragment, '');
|
||||
|
||||
openerService.open(URI.parse('file:///somepath#23,45'));
|
||||
await openerService.open(URI.parse('file:///somepath#23,45'));
|
||||
assert.equal(editorService.lastInput!.options!.selection!.startLineNumber, 23);
|
||||
assert.equal(editorService.lastInput!.options!.selection!.startColumn, 45);
|
||||
assert.equal(editorService.lastInput!.options!.selection!.endLineNumber, undefined);
|
||||
|
@ -74,22 +75,22 @@ suite('OpenerService', function () {
|
|||
assert.equal(editorService.lastInput!.resource.fragment, '');
|
||||
});
|
||||
|
||||
test('delegate to commandsService, command:someid', function () {
|
||||
test('delegate to commandsService, command:someid', async function () {
|
||||
const openerService = new OpenerService(editorService, commandService);
|
||||
|
||||
const id = `aCommand${Math.random()}`;
|
||||
CommandsRegistry.registerCommand(id, function () { });
|
||||
|
||||
openerService.open(URI.parse('command:' + id));
|
||||
await openerService.open(URI.parse('command:' + id));
|
||||
assert.equal(lastCommand!.id, id);
|
||||
assert.equal(lastCommand!.args.length, 0);
|
||||
|
||||
openerService.open(URI.parse('command:' + id).with({ query: '123' }));
|
||||
await openerService.open(URI.parse('command:' + id).with({ query: '123' }));
|
||||
assert.equal(lastCommand!.id, id);
|
||||
assert.equal(lastCommand!.args.length, 1);
|
||||
assert.equal(lastCommand!.args[0], '123');
|
||||
|
||||
openerService.open(URI.parse('command:' + id).with({ query: JSON.stringify([12, true]) }));
|
||||
await openerService.open(URI.parse('command:' + id).with({ query: JSON.stringify([12, true]) }));
|
||||
assert.equal(lastCommand!.id, id);
|
||||
assert.equal(lastCommand!.args.length, 2);
|
||||
assert.equal(lastCommand!.args[0], 12);
|
||||
|
@ -199,4 +200,18 @@ suite('OpenerService', function () {
|
|||
assert.equal(v1, 2);
|
||||
assert.equal(v2, 0);
|
||||
});
|
||||
|
||||
test('matchesScheme', function () {
|
||||
assert.ok(matchesScheme('https://microsoft.com', 'https'));
|
||||
assert.ok(matchesScheme('http://microsoft.com', 'http'));
|
||||
assert.ok(matchesScheme('hTTPs://microsoft.com', 'https'));
|
||||
assert.ok(matchesScheme('httP://microsoft.com', 'http'));
|
||||
assert.ok(matchesScheme(URI.parse('https://microsoft.com'), 'https'));
|
||||
assert.ok(matchesScheme(URI.parse('http://microsoft.com'), 'http'));
|
||||
assert.ok(matchesScheme(URI.parse('hTTPs://microsoft.com'), 'https'));
|
||||
assert.ok(matchesScheme(URI.parse('httP://microsoft.com'), 'http'));
|
||||
assert.ok(!matchesScheme(URI.parse('https://microsoft.com'), 'http'));
|
||||
assert.ok(!matchesScheme(URI.parse('htt://microsoft.com'), 'http'));
|
||||
assert.ok(!matchesScheme(URI.parse('z://microsoft.com'), 'http'));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -3,10 +3,28 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as assert from 'assert';
|
||||
import { LinesLayout } from 'vs/editor/common/viewLayout/linesLayout';
|
||||
import { LinesLayout, EditorWhitespace } from 'vs/editor/common/viewLayout/linesLayout';
|
||||
|
||||
suite('Editor ViewLayout - LinesLayout', () => {
|
||||
|
||||
function insertWhitespace(linesLayout: LinesLayout, afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): string {
|
||||
return linesLayout.changeWhitespace((accessor) => {
|
||||
return accessor.insertWhitespace(afterLineNumber, ordinal, heightInPx, minWidth);
|
||||
});
|
||||
}
|
||||
|
||||
function changeOneWhitespace(linesLayout: LinesLayout, id: string, newAfterLineNumber: number, newHeight: number): void {
|
||||
linesLayout.changeWhitespace((accessor) => {
|
||||
accessor.changeOneWhitespace(id, newAfterLineNumber, newHeight);
|
||||
});
|
||||
}
|
||||
|
||||
function removeWhitespace(linesLayout: LinesLayout, id: string): void {
|
||||
linesLayout.changeWhitespace((accessor) => {
|
||||
accessor.removeWhitespace(id);
|
||||
});
|
||||
}
|
||||
|
||||
test('LinesLayout 1', () => {
|
||||
|
||||
// Start off with 10 lines
|
||||
|
@ -39,7 +57,7 @@ suite('Editor ViewLayout - LinesLayout', () => {
|
|||
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(29), 3);
|
||||
|
||||
// Add whitespace of height 5px after 2nd line
|
||||
linesLayout.insertWhitespace(2, 0, 5, 0);
|
||||
insertWhitespace(linesLayout, 2, 0, 5, 0);
|
||||
// lines: [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
|
||||
// whitespace: a(2,5)
|
||||
assert.equal(linesLayout.getLinesTotalHeight(), 105);
|
||||
|
@ -63,8 +81,8 @@ suite('Editor ViewLayout - LinesLayout', () => {
|
|||
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(105), 10);
|
||||
|
||||
// Add two more whitespaces of height 5px
|
||||
linesLayout.insertWhitespace(3, 0, 5, 0);
|
||||
linesLayout.insertWhitespace(4, 0, 5, 0);
|
||||
insertWhitespace(linesLayout, 3, 0, 5, 0);
|
||||
insertWhitespace(linesLayout, 4, 0, 5, 0);
|
||||
// lines: [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
|
||||
// whitespace: a(2,5), b(3, 5), c(4, 5)
|
||||
assert.equal(linesLayout.getLinesTotalHeight(), 115);
|
||||
|
@ -120,7 +138,7 @@ suite('Editor ViewLayout - LinesLayout', () => {
|
|||
|
||||
// Start off with 10 lines and one whitespace after line 2, of height 5
|
||||
let linesLayout = new LinesLayout(10, 1);
|
||||
let a = linesLayout.insertWhitespace(2, 0, 5, 0);
|
||||
let a = insertWhitespace(linesLayout, 2, 0, 5, 0);
|
||||
|
||||
// 10 lines
|
||||
// whitespace: - a(2,5)
|
||||
|
@ -139,7 +157,7 @@ suite('Editor ViewLayout - LinesLayout', () => {
|
|||
// Change whitespace height
|
||||
// 10 lines
|
||||
// whitespace: - a(2,10)
|
||||
linesLayout.changeWhitespace(a, 2, 10);
|
||||
changeOneWhitespace(linesLayout, a, 2, 10);
|
||||
assert.equal(linesLayout.getLinesTotalHeight(), 20);
|
||||
assert.equal(linesLayout.getVerticalOffsetForLineNumber(1), 0);
|
||||
assert.equal(linesLayout.getVerticalOffsetForLineNumber(2), 1);
|
||||
|
@ -155,7 +173,7 @@ suite('Editor ViewLayout - LinesLayout', () => {
|
|||
// Change whitespace position
|
||||
// 10 lines
|
||||
// whitespace: - a(5,10)
|
||||
linesLayout.changeWhitespace(a, 5, 10);
|
||||
changeOneWhitespace(linesLayout, a, 5, 10);
|
||||
assert.equal(linesLayout.getLinesTotalHeight(), 20);
|
||||
assert.equal(linesLayout.getVerticalOffsetForLineNumber(1), 0);
|
||||
assert.equal(linesLayout.getVerticalOffsetForLineNumber(2), 1);
|
||||
|
@ -200,7 +218,7 @@ suite('Editor ViewLayout - LinesLayout', () => {
|
|||
|
||||
// Remove whitespace
|
||||
// 10 lines
|
||||
linesLayout.removeWhitespace(a);
|
||||
removeWhitespace(linesLayout, a);
|
||||
assert.equal(linesLayout.getLinesTotalHeight(), 10);
|
||||
assert.equal(linesLayout.getVerticalOffsetForLineNumber(1), 0);
|
||||
assert.equal(linesLayout.getVerticalOffsetForLineNumber(2), 1);
|
||||
|
@ -216,7 +234,7 @@ suite('Editor ViewLayout - LinesLayout', () => {
|
|||
|
||||
test('LinesLayout getLineNumberAtOrAfterVerticalOffset', () => {
|
||||
let linesLayout = new LinesLayout(10, 1);
|
||||
linesLayout.insertWhitespace(6, 0, 10, 0);
|
||||
insertWhitespace(linesLayout, 6, 0, 10, 0);
|
||||
|
||||
// 10 lines
|
||||
// whitespace: - a(6,10)
|
||||
|
@ -265,7 +283,7 @@ suite('Editor ViewLayout - LinesLayout', () => {
|
|||
|
||||
test('LinesLayout getCenteredLineInViewport', () => {
|
||||
let linesLayout = new LinesLayout(10, 1);
|
||||
linesLayout.insertWhitespace(6, 0, 10, 0);
|
||||
insertWhitespace(linesLayout, 6, 0, 10, 0);
|
||||
|
||||
// 10 lines
|
||||
// whitespace: - a(6,10)
|
||||
|
@ -348,7 +366,7 @@ suite('Editor ViewLayout - LinesLayout', () => {
|
|||
|
||||
test('LinesLayout getLinesViewportData 1', () => {
|
||||
let linesLayout = new LinesLayout(10, 10);
|
||||
linesLayout.insertWhitespace(6, 0, 100, 0);
|
||||
insertWhitespace(linesLayout, 6, 0, 100, 0);
|
||||
|
||||
// 10 lines
|
||||
// whitespace: - a(6,100)
|
||||
|
@ -479,11 +497,10 @@ suite('Editor ViewLayout - LinesLayout', () => {
|
|||
assert.deepEqual(viewportData.relativeVerticalOffset, [160, 170, 180, 190]);
|
||||
});
|
||||
|
||||
|
||||
test('LinesLayout getLinesViewportData 2 & getWhitespaceViewportData', () => {
|
||||
let linesLayout = new LinesLayout(10, 10);
|
||||
let a = linesLayout.insertWhitespace(6, 0, 100, 0);
|
||||
let b = linesLayout.insertWhitespace(7, 0, 50, 0);
|
||||
let a = insertWhitespace(linesLayout, 6, 0, 100, 0);
|
||||
let b = insertWhitespace(linesLayout, 7, 0, 50, 0);
|
||||
|
||||
// 10 lines
|
||||
// whitespace: - a(6,100), b(7, 50)
|
||||
|
@ -553,8 +570,8 @@ suite('Editor ViewLayout - LinesLayout', () => {
|
|||
|
||||
test('LinesLayout getWhitespaceAtVerticalOffset', () => {
|
||||
let linesLayout = new LinesLayout(10, 10);
|
||||
let a = linesLayout.insertWhitespace(6, 0, 100, 0);
|
||||
let b = linesLayout.insertWhitespace(7, 0, 50, 0);
|
||||
let a = insertWhitespace(linesLayout, 6, 0, 100, 0);
|
||||
let b = insertWhitespace(linesLayout, 7, 0, 50, 0);
|
||||
|
||||
let whitespace = linesLayout.getWhitespaceAtVerticalOffset(0);
|
||||
assert.equal(whitespace, null);
|
||||
|
@ -592,4 +609,536 @@ suite('Editor ViewLayout - LinesLayout', () => {
|
|||
whitespace = linesLayout.getWhitespaceAtVerticalOffset(220);
|
||||
assert.equal(whitespace, null);
|
||||
});
|
||||
|
||||
test('LinesLayout', () => {
|
||||
|
||||
const linesLayout = new LinesLayout(100, 20);
|
||||
|
||||
// Insert a whitespace after line number 2, of height 10
|
||||
const a = insertWhitespace(linesLayout, 2, 0, 10, 0);
|
||||
// whitespaces: a(2, 10)
|
||||
assert.equal(linesLayout.getWhitespacesCount(), 1);
|
||||
assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 2);
|
||||
assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 10);
|
||||
assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 10);
|
||||
assert.equal(linesLayout.getWhitespacesTotalHeight(), 10);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 0);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 0);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 10);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 10);
|
||||
|
||||
// Insert a whitespace again after line number 2, of height 20
|
||||
let b = insertWhitespace(linesLayout, 2, 0, 20, 0);
|
||||
// whitespaces: a(2, 10), b(2, 20)
|
||||
assert.equal(linesLayout.getWhitespacesCount(), 2);
|
||||
assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 2);
|
||||
assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 10);
|
||||
assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 2);
|
||||
assert.equal(linesLayout.getHeightForWhitespaceIndex(1), 20);
|
||||
assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 10);
|
||||
assert.equal(linesLayout.getWhitespacesAccumulatedHeight(1), 30);
|
||||
assert.equal(linesLayout.getWhitespacesTotalHeight(), 30);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 0);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 0);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 30);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 30);
|
||||
|
||||
// Change last inserted whitespace height to 30
|
||||
changeOneWhitespace(linesLayout, b, 2, 30);
|
||||
// whitespaces: a(2, 10), b(2, 30)
|
||||
assert.equal(linesLayout.getWhitespacesCount(), 2);
|
||||
assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 2);
|
||||
assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 10);
|
||||
assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 2);
|
||||
assert.equal(linesLayout.getHeightForWhitespaceIndex(1), 30);
|
||||
assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 10);
|
||||
assert.equal(linesLayout.getWhitespacesAccumulatedHeight(1), 40);
|
||||
assert.equal(linesLayout.getWhitespacesTotalHeight(), 40);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 0);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 0);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 40);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 40);
|
||||
|
||||
// Remove last inserted whitespace
|
||||
removeWhitespace(linesLayout, b);
|
||||
// whitespaces: a(2, 10)
|
||||
assert.equal(linesLayout.getWhitespacesCount(), 1);
|
||||
assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 2);
|
||||
assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 10);
|
||||
assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 10);
|
||||
assert.equal(linesLayout.getWhitespacesTotalHeight(), 10);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 0);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 0);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 10);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 10);
|
||||
|
||||
// Add a whitespace before the first line of height 50
|
||||
b = insertWhitespace(linesLayout, 0, 0, 50, 0);
|
||||
// whitespaces: b(0, 50), a(2, 10)
|
||||
assert.equal(linesLayout.getWhitespacesCount(), 2);
|
||||
assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 0);
|
||||
assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 50);
|
||||
assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 2);
|
||||
assert.equal(linesLayout.getHeightForWhitespaceIndex(1), 10);
|
||||
assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 50);
|
||||
assert.equal(linesLayout.getWhitespacesAccumulatedHeight(1), 60);
|
||||
assert.equal(linesLayout.getWhitespacesTotalHeight(), 60);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 50);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 50);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 60);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 60);
|
||||
|
||||
// Add a whitespace after line 4 of height 20
|
||||
insertWhitespace(linesLayout, 4, 0, 20, 0);
|
||||
// whitespaces: b(0, 50), a(2, 10), c(4, 20)
|
||||
assert.equal(linesLayout.getWhitespacesCount(), 3);
|
||||
assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 0);
|
||||
assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 50);
|
||||
assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 2);
|
||||
assert.equal(linesLayout.getHeightForWhitespaceIndex(1), 10);
|
||||
assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(2), 4);
|
||||
assert.equal(linesLayout.getHeightForWhitespaceIndex(2), 20);
|
||||
assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 50);
|
||||
assert.equal(linesLayout.getWhitespacesAccumulatedHeight(1), 60);
|
||||
assert.equal(linesLayout.getWhitespacesAccumulatedHeight(2), 80);
|
||||
assert.equal(linesLayout.getWhitespacesTotalHeight(), 80);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 50);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 50);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 60);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 60);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(5), 80);
|
||||
|
||||
// Add a whitespace after line 3 of height 30
|
||||
insertWhitespace(linesLayout, 3, 0, 30, 0);
|
||||
// whitespaces: b(0, 50), a(2, 10), d(3, 30), c(4, 20)
|
||||
assert.equal(linesLayout.getWhitespacesCount(), 4);
|
||||
assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 0);
|
||||
assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 50);
|
||||
assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 2);
|
||||
assert.equal(linesLayout.getHeightForWhitespaceIndex(1), 10);
|
||||
assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(2), 3);
|
||||
assert.equal(linesLayout.getHeightForWhitespaceIndex(2), 30);
|
||||
assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(3), 4);
|
||||
assert.equal(linesLayout.getHeightForWhitespaceIndex(3), 20);
|
||||
assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 50);
|
||||
assert.equal(linesLayout.getWhitespacesAccumulatedHeight(1), 60);
|
||||
assert.equal(linesLayout.getWhitespacesAccumulatedHeight(2), 90);
|
||||
assert.equal(linesLayout.getWhitespacesAccumulatedHeight(3), 110);
|
||||
assert.equal(linesLayout.getWhitespacesTotalHeight(), 110);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 50);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 50);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 60);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 90);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(5), 110);
|
||||
|
||||
// Change whitespace after line 2 to height of 100
|
||||
changeOneWhitespace(linesLayout, a, 2, 100);
|
||||
// whitespaces: b(0, 50), a(2, 100), d(3, 30), c(4, 20)
|
||||
assert.equal(linesLayout.getWhitespacesCount(), 4);
|
||||
assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 0);
|
||||
assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 50);
|
||||
assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 2);
|
||||
assert.equal(linesLayout.getHeightForWhitespaceIndex(1), 100);
|
||||
assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(2), 3);
|
||||
assert.equal(linesLayout.getHeightForWhitespaceIndex(2), 30);
|
||||
assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(3), 4);
|
||||
assert.equal(linesLayout.getHeightForWhitespaceIndex(3), 20);
|
||||
assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 50);
|
||||
assert.equal(linesLayout.getWhitespacesAccumulatedHeight(1), 150);
|
||||
assert.equal(linesLayout.getWhitespacesAccumulatedHeight(2), 180);
|
||||
assert.equal(linesLayout.getWhitespacesAccumulatedHeight(3), 200);
|
||||
assert.equal(linesLayout.getWhitespacesTotalHeight(), 200);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 50);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 50);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 150);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 180);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(5), 200);
|
||||
|
||||
// Remove whitespace after line 2
|
||||
removeWhitespace(linesLayout, a);
|
||||
// whitespaces: b(0, 50), d(3, 30), c(4, 20)
|
||||
assert.equal(linesLayout.getWhitespacesCount(), 3);
|
||||
assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 0);
|
||||
assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 50);
|
||||
assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 3);
|
||||
assert.equal(linesLayout.getHeightForWhitespaceIndex(1), 30);
|
||||
assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(2), 4);
|
||||
assert.equal(linesLayout.getHeightForWhitespaceIndex(2), 20);
|
||||
assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 50);
|
||||
assert.equal(linesLayout.getWhitespacesAccumulatedHeight(1), 80);
|
||||
assert.equal(linesLayout.getWhitespacesAccumulatedHeight(2), 100);
|
||||
assert.equal(linesLayout.getWhitespacesTotalHeight(), 100);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 50);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 50);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 50);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 80);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(5), 100);
|
||||
|
||||
// Remove whitespace before line 1
|
||||
removeWhitespace(linesLayout, b);
|
||||
// whitespaces: d(3, 30), c(4, 20)
|
||||
assert.equal(linesLayout.getWhitespacesCount(), 2);
|
||||
assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 3);
|
||||
assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 30);
|
||||
assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 4);
|
||||
assert.equal(linesLayout.getHeightForWhitespaceIndex(1), 20);
|
||||
assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 30);
|
||||
assert.equal(linesLayout.getWhitespacesAccumulatedHeight(1), 50);
|
||||
assert.equal(linesLayout.getWhitespacesTotalHeight(), 50);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 0);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 0);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 0);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 30);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(5), 50);
|
||||
|
||||
// Delete line 1
|
||||
linesLayout.onLinesDeleted(1, 1);
|
||||
// whitespaces: d(2, 30), c(3, 20)
|
||||
assert.equal(linesLayout.getWhitespacesCount(), 2);
|
||||
assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 2);
|
||||
assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 30);
|
||||
assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 3);
|
||||
assert.equal(linesLayout.getHeightForWhitespaceIndex(1), 20);
|
||||
assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 30);
|
||||
assert.equal(linesLayout.getWhitespacesAccumulatedHeight(1), 50);
|
||||
assert.equal(linesLayout.getWhitespacesTotalHeight(), 50);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 0);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 0);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 30);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 50);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(5), 50);
|
||||
|
||||
// Insert a line before line 1
|
||||
linesLayout.onLinesInserted(1, 1);
|
||||
// whitespaces: d(3, 30), c(4, 20)
|
||||
assert.equal(linesLayout.getWhitespacesCount(), 2);
|
||||
assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 3);
|
||||
assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 30);
|
||||
assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 4);
|
||||
assert.equal(linesLayout.getHeightForWhitespaceIndex(1), 20);
|
||||
assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 30);
|
||||
assert.equal(linesLayout.getWhitespacesAccumulatedHeight(1), 50);
|
||||
assert.equal(linesLayout.getWhitespacesTotalHeight(), 50);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 0);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 0);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 0);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 30);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(5), 50);
|
||||
|
||||
// Delete line 4
|
||||
linesLayout.onLinesDeleted(4, 4);
|
||||
// whitespaces: d(3, 30), c(3, 20)
|
||||
assert.equal(linesLayout.getWhitespacesCount(), 2);
|
||||
assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 3);
|
||||
assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 30);
|
||||
assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 3);
|
||||
assert.equal(linesLayout.getHeightForWhitespaceIndex(1), 20);
|
||||
assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 30);
|
||||
assert.equal(linesLayout.getWhitespacesAccumulatedHeight(1), 50);
|
||||
assert.equal(linesLayout.getWhitespacesTotalHeight(), 50);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 0);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 0);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 0);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 50);
|
||||
assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(5), 50);
|
||||
});
|
||||
|
||||
test('LinesLayout findInsertionIndex', () => {
|
||||
|
||||
const makeInternalWhitespace = (afterLineNumbers: number[], ordinal: number = 0) => {
|
||||
return afterLineNumbers.map((afterLineNumber) => new EditorWhitespace('', afterLineNumber, ordinal, 0, 0));
|
||||
};
|
||||
|
||||
let arr: EditorWhitespace[];
|
||||
|
||||
arr = makeInternalWhitespace([]);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 0, 0), 0);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 1, 0), 0);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 2, 0), 0);
|
||||
|
||||
arr = makeInternalWhitespace([1]);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 0, 0), 0);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 1, 0), 1);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 2, 0), 1);
|
||||
|
||||
arr = makeInternalWhitespace([1, 3]);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 0, 0), 0);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 1, 0), 1);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 2, 0), 1);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 3, 0), 2);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 4, 0), 2);
|
||||
|
||||
arr = makeInternalWhitespace([1, 3, 5]);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 0, 0), 0);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 1, 0), 1);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 2, 0), 1);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 3, 0), 2);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 4, 0), 2);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 5, 0), 3);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 6, 0), 3);
|
||||
|
||||
arr = makeInternalWhitespace([1, 3, 5], 3);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 0, 0), 0);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 1, 0), 0);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 2, 0), 1);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 3, 0), 1);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 4, 0), 2);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 5, 0), 2);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 6, 0), 3);
|
||||
|
||||
arr = makeInternalWhitespace([1, 3, 5, 7]);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 0, 0), 0);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 1, 0), 1);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 2, 0), 1);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 3, 0), 2);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 4, 0), 2);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 5, 0), 3);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 6, 0), 3);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 7, 0), 4);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 8, 0), 4);
|
||||
|
||||
arr = makeInternalWhitespace([1, 3, 5, 7, 9]);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 0, 0), 0);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 1, 0), 1);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 2, 0), 1);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 3, 0), 2);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 4, 0), 2);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 5, 0), 3);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 6, 0), 3);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 7, 0), 4);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 8, 0), 4);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 9, 0), 5);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 10, 0), 5);
|
||||
|
||||
arr = makeInternalWhitespace([1, 3, 5, 7, 9, 11]);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 0, 0), 0);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 1, 0), 1);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 2, 0), 1);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 3, 0), 2);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 4, 0), 2);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 5, 0), 3);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 6, 0), 3);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 7, 0), 4);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 8, 0), 4);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 9, 0), 5);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 10, 0), 5);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 11, 0), 6);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 12, 0), 6);
|
||||
|
||||
arr = makeInternalWhitespace([1, 3, 5, 7, 9, 11, 13]);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 0, 0), 0);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 1, 0), 1);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 2, 0), 1);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 3, 0), 2);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 4, 0), 2);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 5, 0), 3);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 6, 0), 3);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 7, 0), 4);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 8, 0), 4);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 9, 0), 5);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 10, 0), 5);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 11, 0), 6);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 12, 0), 6);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 13, 0), 7);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 14, 0), 7);
|
||||
|
||||
arr = makeInternalWhitespace([1, 3, 5, 7, 9, 11, 13, 15]);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 0, 0), 0);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 1, 0), 1);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 2, 0), 1);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 3, 0), 2);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 4, 0), 2);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 5, 0), 3);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 6, 0), 3);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 7, 0), 4);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 8, 0), 4);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 9, 0), 5);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 10, 0), 5);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 11, 0), 6);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 12, 0), 6);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 13, 0), 7);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 14, 0), 7);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 15, 0), 8);
|
||||
assert.equal(LinesLayout.findInsertionIndex(arr, 16, 0), 8);
|
||||
});
|
||||
|
||||
test('LinesLayout changeWhitespaceAfterLineNumber & getFirstWhitespaceIndexAfterLineNumber', () => {
|
||||
const linesLayout = new LinesLayout(100, 20);
|
||||
|
||||
const a = insertWhitespace(linesLayout, 0, 0, 1, 0);
|
||||
const b = insertWhitespace(linesLayout, 7, 0, 1, 0);
|
||||
const c = insertWhitespace(linesLayout, 3, 0, 1, 0);
|
||||
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 0
|
||||
assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 0);
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(1), c); // 3
|
||||
assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 3);
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(2), b); // 7
|
||||
assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(2), 7);
|
||||
|
||||
assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(1), 1); // c
|
||||
assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(2), 1); // c
|
||||
assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(3), 1); // c
|
||||
assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(4), 2); // b
|
||||
assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(5), 2); // b
|
||||
assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(6), 2); // b
|
||||
assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(7), 2); // b
|
||||
assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(8), -1); // --
|
||||
|
||||
// Do not really move a
|
||||
changeOneWhitespace(linesLayout, a, 1, 1);
|
||||
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 1
|
||||
assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 1);
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(1), c); // 3
|
||||
assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 3);
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(2), b); // 7
|
||||
assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(2), 7);
|
||||
|
||||
assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(1), 0); // a
|
||||
assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(2), 1); // c
|
||||
assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(3), 1); // c
|
||||
assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(4), 2); // b
|
||||
assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(5), 2); // b
|
||||
assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(6), 2); // b
|
||||
assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(7), 2); // b
|
||||
assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(8), -1); // --
|
||||
|
||||
|
||||
// Do not really move a
|
||||
changeOneWhitespace(linesLayout, a, 2, 1);
|
||||
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 2
|
||||
assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 2);
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(1), c); // 3
|
||||
assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 3);
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(2), b); // 7
|
||||
assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(2), 7);
|
||||
|
||||
assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(1), 0); // a
|
||||
assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(2), 0); // a
|
||||
assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(3), 1); // c
|
||||
assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(4), 2); // b
|
||||
assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(5), 2); // b
|
||||
assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(6), 2); // b
|
||||
assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(7), 2); // b
|
||||
assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(8), -1); // --
|
||||
|
||||
|
||||
// Change a to conflict with c => a gets placed after c
|
||||
changeOneWhitespace(linesLayout, a, 3, 1);
|
||||
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(0), c); // 3
|
||||
assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 3);
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(1), a); // 3
|
||||
assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 3);
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(2), b); // 7
|
||||
assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(2), 7);
|
||||
|
||||
assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(1), 0); // c
|
||||
assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(2), 0); // c
|
||||
assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(3), 0); // c
|
||||
assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(4), 2); // b
|
||||
assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(5), 2); // b
|
||||
assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(6), 2); // b
|
||||
assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(7), 2); // b
|
||||
assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(8), -1); // --
|
||||
|
||||
|
||||
// Make a no-op
|
||||
changeOneWhitespace(linesLayout, c, 3, 1);
|
||||
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(0), c); // 3
|
||||
assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 3);
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(1), a); // 3
|
||||
assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 3);
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(2), b); // 7
|
||||
assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(2), 7);
|
||||
|
||||
assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(1), 0); // c
|
||||
assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(2), 0); // c
|
||||
assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(3), 0); // c
|
||||
assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(4), 2); // b
|
||||
assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(5), 2); // b
|
||||
assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(6), 2); // b
|
||||
assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(7), 2); // b
|
||||
assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(8), -1); // --
|
||||
|
||||
|
||||
|
||||
// Conflict c with b => c gets placed after b
|
||||
changeOneWhitespace(linesLayout, c, 7, 1);
|
||||
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 3
|
||||
assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 3);
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(1), b); // 7
|
||||
assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 7);
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(2), c); // 7
|
||||
assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(2), 7);
|
||||
|
||||
assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(1), 0); // a
|
||||
assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(2), 0); // a
|
||||
assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(3), 0); // a
|
||||
assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(4), 1); // b
|
||||
assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(5), 1); // b
|
||||
assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(6), 1); // b
|
||||
assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(7), 1); // b
|
||||
assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(8), -1); // --
|
||||
});
|
||||
|
||||
test('LinesLayout Bug', () => {
|
||||
const linesLayout = new LinesLayout(100, 20);
|
||||
|
||||
const a = insertWhitespace(linesLayout, 0, 0, 1, 0);
|
||||
const b = insertWhitespace(linesLayout, 7, 0, 1, 0);
|
||||
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 0
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(1), b); // 7
|
||||
|
||||
const c = insertWhitespace(linesLayout, 3, 0, 1, 0);
|
||||
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 0
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(1), c); // 3
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(2), b); // 7
|
||||
|
||||
const d = insertWhitespace(linesLayout, 2, 0, 1, 0);
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 0
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(1), d); // 2
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(2), c); // 3
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(3), b); // 7
|
||||
|
||||
const e = insertWhitespace(linesLayout, 8, 0, 1, 0);
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 0
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(1), d); // 2
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(2), c); // 3
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(3), b); // 7
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(4), e); // 8
|
||||
|
||||
const f = insertWhitespace(linesLayout, 11, 0, 1, 0);
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 0
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(1), d); // 2
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(2), c); // 3
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(3), b); // 7
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(4), e); // 8
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(5), f); // 11
|
||||
|
||||
const g = insertWhitespace(linesLayout, 10, 0, 1, 0);
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 0
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(1), d); // 2
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(2), c); // 3
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(3), b); // 7
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(4), e); // 8
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(5), g); // 10
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(6), f); // 11
|
||||
|
||||
const h = insertWhitespace(linesLayout, 0, 0, 1, 0);
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 0
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(1), h); // 0
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(2), d); // 2
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(3), c); // 3
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(4), b); // 7
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(5), e); // 8
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(6), g); // 10
|
||||
assert.equal(linesLayout.getIdForWhitespaceIndex(7), f); // 11
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,558 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as assert from 'assert';
|
||||
import { WhitespaceComputer } from 'vs/editor/common/viewLayout/whitespaceComputer';
|
||||
|
||||
suite('Editor ViewLayout - WhitespaceComputer', () => {
|
||||
|
||||
test('WhitespaceComputer', () => {
|
||||
|
||||
let whitespaceComputer = new WhitespaceComputer();
|
||||
|
||||
// Insert a whitespace after line number 2, of height 10
|
||||
let a = whitespaceComputer.insertWhitespace(2, 0, 10, 0);
|
||||
// whitespaces: a(2, 10)
|
||||
assert.equal(whitespaceComputer.getCount(), 1);
|
||||
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 2);
|
||||
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 10);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeight(0), 10);
|
||||
assert.equal(whitespaceComputer.getTotalHeight(), 10);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 0);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 0);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 10);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 10);
|
||||
|
||||
// Insert a whitespace again after line number 2, of height 20
|
||||
let b = whitespaceComputer.insertWhitespace(2, 0, 20, 0);
|
||||
// whitespaces: a(2, 10), b(2, 20)
|
||||
assert.equal(whitespaceComputer.getCount(), 2);
|
||||
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 2);
|
||||
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 10);
|
||||
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 2);
|
||||
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(1), 20);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeight(0), 10);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeight(1), 30);
|
||||
assert.equal(whitespaceComputer.getTotalHeight(), 30);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 0);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 0);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 30);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 30);
|
||||
|
||||
// Change last inserted whitespace height to 30
|
||||
whitespaceComputer.changeWhitespaceHeight(b, 30);
|
||||
// whitespaces: a(2, 10), b(2, 30)
|
||||
assert.equal(whitespaceComputer.getCount(), 2);
|
||||
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 2);
|
||||
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 10);
|
||||
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 2);
|
||||
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(1), 30);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeight(0), 10);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeight(1), 40);
|
||||
assert.equal(whitespaceComputer.getTotalHeight(), 40);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 0);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 0);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 40);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 40);
|
||||
|
||||
// Remove last inserted whitespace
|
||||
whitespaceComputer.removeWhitespace(b);
|
||||
// whitespaces: a(2, 10)
|
||||
assert.equal(whitespaceComputer.getCount(), 1);
|
||||
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 2);
|
||||
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 10);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeight(0), 10);
|
||||
assert.equal(whitespaceComputer.getTotalHeight(), 10);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 0);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 0);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 10);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 10);
|
||||
|
||||
// Add a whitespace before the first line of height 50
|
||||
b = whitespaceComputer.insertWhitespace(0, 0, 50, 0);
|
||||
// whitespaces: b(0, 50), a(2, 10)
|
||||
assert.equal(whitespaceComputer.getCount(), 2);
|
||||
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 0);
|
||||
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 50);
|
||||
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 2);
|
||||
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(1), 10);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeight(0), 50);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeight(1), 60);
|
||||
assert.equal(whitespaceComputer.getTotalHeight(), 60);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 50);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 50);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 60);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 60);
|
||||
|
||||
// Add a whitespace after line 4 of height 20
|
||||
whitespaceComputer.insertWhitespace(4, 0, 20, 0);
|
||||
// whitespaces: b(0, 50), a(2, 10), c(4, 20)
|
||||
assert.equal(whitespaceComputer.getCount(), 3);
|
||||
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 0);
|
||||
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 50);
|
||||
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 2);
|
||||
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(1), 10);
|
||||
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(2), 4);
|
||||
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(2), 20);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeight(0), 50);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeight(1), 60);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeight(2), 80);
|
||||
assert.equal(whitespaceComputer.getTotalHeight(), 80);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 50);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 50);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 60);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 60);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(5), 80);
|
||||
|
||||
// Add a whitespace after line 3 of height 30
|
||||
whitespaceComputer.insertWhitespace(3, 0, 30, 0);
|
||||
// whitespaces: b(0, 50), a(2, 10), d(3, 30), c(4, 20)
|
||||
assert.equal(whitespaceComputer.getCount(), 4);
|
||||
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 0);
|
||||
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 50);
|
||||
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 2);
|
||||
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(1), 10);
|
||||
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(2), 3);
|
||||
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(2), 30);
|
||||
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(3), 4);
|
||||
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(3), 20);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeight(0), 50);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeight(1), 60);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeight(2), 90);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeight(3), 110);
|
||||
assert.equal(whitespaceComputer.getTotalHeight(), 110);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 50);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 50);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 60);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 90);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(5), 110);
|
||||
|
||||
// Change whitespace after line 2 to height of 100
|
||||
whitespaceComputer.changeWhitespaceHeight(a, 100);
|
||||
// whitespaces: b(0, 50), a(2, 100), d(3, 30), c(4, 20)
|
||||
assert.equal(whitespaceComputer.getCount(), 4);
|
||||
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 0);
|
||||
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 50);
|
||||
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 2);
|
||||
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(1), 100);
|
||||
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(2), 3);
|
||||
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(2), 30);
|
||||
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(3), 4);
|
||||
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(3), 20);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeight(0), 50);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeight(1), 150);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeight(2), 180);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeight(3), 200);
|
||||
assert.equal(whitespaceComputer.getTotalHeight(), 200);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 50);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 50);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 150);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 180);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(5), 200);
|
||||
|
||||
// Remove whitespace after line 2
|
||||
whitespaceComputer.removeWhitespace(a);
|
||||
// whitespaces: b(0, 50), d(3, 30), c(4, 20)
|
||||
assert.equal(whitespaceComputer.getCount(), 3);
|
||||
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 0);
|
||||
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 50);
|
||||
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 3);
|
||||
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(1), 30);
|
||||
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(2), 4);
|
||||
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(2), 20);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeight(0), 50);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeight(1), 80);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeight(2), 100);
|
||||
assert.equal(whitespaceComputer.getTotalHeight(), 100);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 50);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 50);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 50);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 80);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(5), 100);
|
||||
|
||||
// Remove whitespace before line 1
|
||||
whitespaceComputer.removeWhitespace(b);
|
||||
// whitespaces: d(3, 30), c(4, 20)
|
||||
assert.equal(whitespaceComputer.getCount(), 2);
|
||||
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 3);
|
||||
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 30);
|
||||
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 4);
|
||||
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(1), 20);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeight(0), 30);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeight(1), 50);
|
||||
assert.equal(whitespaceComputer.getTotalHeight(), 50);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 0);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 0);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 0);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 30);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(5), 50);
|
||||
|
||||
// Delete line 1
|
||||
whitespaceComputer.onLinesDeleted(1, 1);
|
||||
// whitespaces: d(2, 30), c(3, 20)
|
||||
assert.equal(whitespaceComputer.getCount(), 2);
|
||||
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 2);
|
||||
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 30);
|
||||
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 3);
|
||||
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(1), 20);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeight(0), 30);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeight(1), 50);
|
||||
assert.equal(whitespaceComputer.getTotalHeight(), 50);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 0);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 0);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 30);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 50);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(5), 50);
|
||||
|
||||
// Insert a line before line 1
|
||||
whitespaceComputer.onLinesInserted(1, 1);
|
||||
// whitespaces: d(3, 30), c(4, 20)
|
||||
assert.equal(whitespaceComputer.getCount(), 2);
|
||||
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 3);
|
||||
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 30);
|
||||
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 4);
|
||||
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(1), 20);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeight(0), 30);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeight(1), 50);
|
||||
assert.equal(whitespaceComputer.getTotalHeight(), 50);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 0);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 0);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 0);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 30);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(5), 50);
|
||||
|
||||
// Delete line 4
|
||||
whitespaceComputer.onLinesDeleted(4, 4);
|
||||
// whitespaces: d(3, 30), c(3, 20)
|
||||
assert.equal(whitespaceComputer.getCount(), 2);
|
||||
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 3);
|
||||
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 30);
|
||||
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 3);
|
||||
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(1), 20);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeight(0), 30);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeight(1), 50);
|
||||
assert.equal(whitespaceComputer.getTotalHeight(), 50);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 0);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 0);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 0);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 50);
|
||||
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(5), 50);
|
||||
});
|
||||
|
||||
test('WhitespaceComputer findInsertionIndex', () => {
|
||||
|
||||
let makeArray = (size: number, fillValue: number) => {
|
||||
let r: number[] = [];
|
||||
for (let i = 0; i < size; i++) {
|
||||
r[i] = fillValue;
|
||||
}
|
||||
return r;
|
||||
};
|
||||
|
||||
let arr: number[];
|
||||
let ordinals: number[];
|
||||
|
||||
arr = [];
|
||||
ordinals = makeArray(arr.length, 0);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 0, ordinals, 0), 0);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 1, ordinals, 0), 0);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 2, ordinals, 0), 0);
|
||||
|
||||
arr = [1];
|
||||
ordinals = makeArray(arr.length, 0);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 0, ordinals, 0), 0);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 1, ordinals, 0), 1);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 2, ordinals, 0), 1);
|
||||
|
||||
arr = [1, 3];
|
||||
ordinals = makeArray(arr.length, 0);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 0, ordinals, 0), 0);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 1, ordinals, 0), 1);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 2, ordinals, 0), 1);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 3, ordinals, 0), 2);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 4, ordinals, 0), 2);
|
||||
|
||||
arr = [1, 3, 5];
|
||||
ordinals = makeArray(arr.length, 0);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 0, ordinals, 0), 0);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 1, ordinals, 0), 1);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 2, ordinals, 0), 1);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 3, ordinals, 0), 2);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 4, ordinals, 0), 2);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 5, ordinals, 0), 3);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 6, ordinals, 0), 3);
|
||||
|
||||
arr = [1, 3, 5];
|
||||
ordinals = makeArray(arr.length, 3);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 0, ordinals, 0), 0);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 1, ordinals, 0), 0);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 2, ordinals, 0), 1);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 3, ordinals, 0), 1);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 4, ordinals, 0), 2);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 5, ordinals, 0), 2);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 6, ordinals, 0), 3);
|
||||
|
||||
arr = [1, 3, 5, 7];
|
||||
ordinals = makeArray(arr.length, 0);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 0, ordinals, 0), 0);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 1, ordinals, 0), 1);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 2, ordinals, 0), 1);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 3, ordinals, 0), 2);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 4, ordinals, 0), 2);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 5, ordinals, 0), 3);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 6, ordinals, 0), 3);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 7, ordinals, 0), 4);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 8, ordinals, 0), 4);
|
||||
|
||||
arr = [1, 3, 5, 7, 9];
|
||||
ordinals = makeArray(arr.length, 0);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 0, ordinals, 0), 0);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 1, ordinals, 0), 1);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 2, ordinals, 0), 1);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 3, ordinals, 0), 2);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 4, ordinals, 0), 2);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 5, ordinals, 0), 3);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 6, ordinals, 0), 3);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 7, ordinals, 0), 4);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 8, ordinals, 0), 4);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 9, ordinals, 0), 5);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 10, ordinals, 0), 5);
|
||||
|
||||
arr = [1, 3, 5, 7, 9, 11];
|
||||
ordinals = makeArray(arr.length, 0);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 0, ordinals, 0), 0);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 1, ordinals, 0), 1);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 2, ordinals, 0), 1);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 3, ordinals, 0), 2);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 4, ordinals, 0), 2);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 5, ordinals, 0), 3);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 6, ordinals, 0), 3);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 7, ordinals, 0), 4);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 8, ordinals, 0), 4);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 9, ordinals, 0), 5);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 10, ordinals, 0), 5);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 11, ordinals, 0), 6);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 12, ordinals, 0), 6);
|
||||
|
||||
arr = [1, 3, 5, 7, 9, 11, 13];
|
||||
ordinals = makeArray(arr.length, 0);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 0, ordinals, 0), 0);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 1, ordinals, 0), 1);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 2, ordinals, 0), 1);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 3, ordinals, 0), 2);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 4, ordinals, 0), 2);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 5, ordinals, 0), 3);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 6, ordinals, 0), 3);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 7, ordinals, 0), 4);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 8, ordinals, 0), 4);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 9, ordinals, 0), 5);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 10, ordinals, 0), 5);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 11, ordinals, 0), 6);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 12, ordinals, 0), 6);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 13, ordinals, 0), 7);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 14, ordinals, 0), 7);
|
||||
|
||||
arr = [1, 3, 5, 7, 9, 11, 13, 15];
|
||||
ordinals = makeArray(arr.length, 0);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 0, ordinals, 0), 0);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 1, ordinals, 0), 1);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 2, ordinals, 0), 1);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 3, ordinals, 0), 2);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 4, ordinals, 0), 2);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 5, ordinals, 0), 3);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 6, ordinals, 0), 3);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 7, ordinals, 0), 4);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 8, ordinals, 0), 4);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 9, ordinals, 0), 5);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 10, ordinals, 0), 5);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 11, ordinals, 0), 6);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 12, ordinals, 0), 6);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 13, ordinals, 0), 7);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 14, ordinals, 0), 7);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 15, ordinals, 0), 8);
|
||||
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 16, ordinals, 0), 8);
|
||||
});
|
||||
|
||||
test('WhitespaceComputer changeWhitespaceAfterLineNumber & getFirstWhitespaceIndexAfterLineNumber', () => {
|
||||
let whitespaceComputer = new WhitespaceComputer();
|
||||
|
||||
let a = whitespaceComputer.insertWhitespace(0, 0, 1, 0);
|
||||
let b = whitespaceComputer.insertWhitespace(7, 0, 1, 0);
|
||||
let c = whitespaceComputer.insertWhitespace(3, 0, 1, 0);
|
||||
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), a); // 0
|
||||
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 0);
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), c); // 3
|
||||
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 3);
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(2), b); // 7
|
||||
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(2), 7);
|
||||
|
||||
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(1), 1); // c
|
||||
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(2), 1); // c
|
||||
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(3), 1); // c
|
||||
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(4), 2); // b
|
||||
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(5), 2); // b
|
||||
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(6), 2); // b
|
||||
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(7), 2); // b
|
||||
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(8), -1); // --
|
||||
|
||||
// Do not really move a
|
||||
whitespaceComputer.changeWhitespaceAfterLineNumber(a, 1);
|
||||
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), a); // 1
|
||||
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 1);
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), c); // 3
|
||||
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 3);
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(2), b); // 7
|
||||
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(2), 7);
|
||||
|
||||
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(1), 0); // a
|
||||
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(2), 1); // c
|
||||
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(3), 1); // c
|
||||
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(4), 2); // b
|
||||
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(5), 2); // b
|
||||
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(6), 2); // b
|
||||
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(7), 2); // b
|
||||
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(8), -1); // --
|
||||
|
||||
|
||||
// Do not really move a
|
||||
whitespaceComputer.changeWhitespaceAfterLineNumber(a, 2);
|
||||
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), a); // 2
|
||||
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 2);
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), c); // 3
|
||||
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 3);
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(2), b); // 7
|
||||
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(2), 7);
|
||||
|
||||
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(1), 0); // a
|
||||
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(2), 0); // a
|
||||
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(3), 1); // c
|
||||
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(4), 2); // b
|
||||
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(5), 2); // b
|
||||
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(6), 2); // b
|
||||
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(7), 2); // b
|
||||
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(8), -1); // --
|
||||
|
||||
|
||||
// Change a to conflict with c => a gets placed after c
|
||||
whitespaceComputer.changeWhitespaceAfterLineNumber(a, 3);
|
||||
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), c); // 3
|
||||
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 3);
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), a); // 3
|
||||
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 3);
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(2), b); // 7
|
||||
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(2), 7);
|
||||
|
||||
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(1), 0); // c
|
||||
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(2), 0); // c
|
||||
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(3), 0); // c
|
||||
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(4), 2); // b
|
||||
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(5), 2); // b
|
||||
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(6), 2); // b
|
||||
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(7), 2); // b
|
||||
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(8), -1); // --
|
||||
|
||||
|
||||
// Make a no-op
|
||||
whitespaceComputer.changeWhitespaceAfterLineNumber(c, 3);
|
||||
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), c); // 3
|
||||
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 3);
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), a); // 3
|
||||
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 3);
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(2), b); // 7
|
||||
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(2), 7);
|
||||
|
||||
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(1), 0); // c
|
||||
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(2), 0); // c
|
||||
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(3), 0); // c
|
||||
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(4), 2); // b
|
||||
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(5), 2); // b
|
||||
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(6), 2); // b
|
||||
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(7), 2); // b
|
||||
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(8), -1); // --
|
||||
|
||||
|
||||
|
||||
// Conflict c with b => c gets placed after b
|
||||
whitespaceComputer.changeWhitespaceAfterLineNumber(c, 7);
|
||||
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), a); // 3
|
||||
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 3);
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), b); // 7
|
||||
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 7);
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(2), c); // 7
|
||||
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(2), 7);
|
||||
|
||||
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(1), 0); // a
|
||||
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(2), 0); // a
|
||||
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(3), 0); // a
|
||||
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(4), 1); // b
|
||||
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(5), 1); // b
|
||||
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(6), 1); // b
|
||||
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(7), 1); // b
|
||||
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(8), -1); // --
|
||||
});
|
||||
|
||||
|
||||
test('WhitespaceComputer Bug', () => {
|
||||
let whitespaceComputer = new WhitespaceComputer();
|
||||
|
||||
let a = whitespaceComputer.insertWhitespace(0, 0, 1, 0);
|
||||
let b = whitespaceComputer.insertWhitespace(7, 0, 1, 0);
|
||||
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), a); // 0
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), b); // 7
|
||||
|
||||
let c = whitespaceComputer.insertWhitespace(3, 0, 1, 0);
|
||||
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), a); // 0
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), c); // 3
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(2), b); // 7
|
||||
|
||||
let d = whitespaceComputer.insertWhitespace(2, 0, 1, 0);
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), a); // 0
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), d); // 2
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(2), c); // 3
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(3), b); // 7
|
||||
|
||||
let e = whitespaceComputer.insertWhitespace(8, 0, 1, 0);
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), a); // 0
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), d); // 2
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(2), c); // 3
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(3), b); // 7
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(4), e); // 8
|
||||
|
||||
let f = whitespaceComputer.insertWhitespace(11, 0, 1, 0);
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), a); // 0
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), d); // 2
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(2), c); // 3
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(3), b); // 7
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(4), e); // 8
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(5), f); // 11
|
||||
|
||||
let g = whitespaceComputer.insertWhitespace(10, 0, 1, 0);
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), a); // 0
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), d); // 2
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(2), c); // 3
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(3), b); // 7
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(4), e); // 8
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(5), g); // 10
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(6), f); // 11
|
||||
|
||||
let h = whitespaceComputer.insertWhitespace(0, 0, 1, 0);
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), a); // 0
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), h); // 0
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(2), d); // 2
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(3), c); // 3
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(4), b); // 7
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(5), e); // 8
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(6), g); // 10
|
||||
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(7), f); // 11
|
||||
});
|
||||
});
|
||||
|
|
@ -236,28 +236,52 @@ export class MenuEntryActionViewItem extends ActionViewItem {
|
|||
_updateItemClass(item: ICommandAction): void {
|
||||
this._itemClassDispose.value = undefined;
|
||||
|
||||
if (item.iconLocation) {
|
||||
let iconClass: string;
|
||||
|
||||
const iconPathMapKey = item.iconLocation.dark.toString();
|
||||
|
||||
if (MenuEntryActionViewItem.ICON_PATH_TO_CSS_RULES.has(iconPathMapKey)) {
|
||||
iconClass = MenuEntryActionViewItem.ICON_PATH_TO_CSS_RULES.get(iconPathMapKey)!;
|
||||
} else {
|
||||
iconClass = ids.nextId();
|
||||
createCSSRule(`.icon.${iconClass}`, `background-image: ${asCSSUrl(item.iconLocation.light || item.iconLocation.dark)}`);
|
||||
createCSSRule(`.vs-dark .icon.${iconClass}, .hc-black .icon.${iconClass}`, `background-image: ${asCSSUrl(item.iconLocation.dark)}`);
|
||||
MenuEntryActionViewItem.ICON_PATH_TO_CSS_RULES.set(iconPathMapKey, iconClass);
|
||||
}
|
||||
// icon class
|
||||
if (item.iconClassName) {
|
||||
let iconClass = item.iconClassName;
|
||||
|
||||
if (this.label) {
|
||||
addClasses(this.label, 'icon', iconClass);
|
||||
addClasses(this.label, 'codicon', iconClass);
|
||||
this._itemClassDispose.value = toDisposable(() => {
|
||||
if (this.label) {
|
||||
removeClasses(this.label, 'icon', iconClass);
|
||||
removeClasses(this.label, 'codicon', iconClass);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// icon path
|
||||
else if (item.iconLocation) {
|
||||
let iconClass: string;
|
||||
|
||||
if (item.iconLocation?.dark?.scheme) {
|
||||
|
||||
const iconPathMapKey = item.iconLocation.dark.toString();
|
||||
|
||||
if (MenuEntryActionViewItem.ICON_PATH_TO_CSS_RULES.has(iconPathMapKey)) {
|
||||
iconClass = MenuEntryActionViewItem.ICON_PATH_TO_CSS_RULES.get(iconPathMapKey)!;
|
||||
} else {
|
||||
iconClass = ids.nextId();
|
||||
createCSSRule(`.icon.${iconClass}`, `background-image: ${asCSSUrl(item.iconLocation.light || item.iconLocation.dark)}`);
|
||||
createCSSRule(`.vs-dark .icon.${iconClass}, .hc-black .icon.${iconClass}`, `background-image: ${asCSSUrl(item.iconLocation.dark)}`);
|
||||
MenuEntryActionViewItem.ICON_PATH_TO_CSS_RULES.set(iconPathMapKey, iconClass);
|
||||
}
|
||||
|
||||
if (this.label) {
|
||||
|
||||
addClasses(this.label, 'icon', iconClass);
|
||||
this._itemClassDispose.value = toDisposable(() => {
|
||||
if (this.label) {
|
||||
removeClasses(this.label, 'icon', iconClass);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,8 @@ export interface ICommandAction {
|
|||
id: string;
|
||||
title: string | ILocalizedString;
|
||||
category?: string | ILocalizedString;
|
||||
iconLocation?: { dark: URI; light?: URI; };
|
||||
iconClassName?: string;
|
||||
iconLocation?: { dark?: URI; light?: URI; };
|
||||
precondition?: ContextKeyExpr;
|
||||
toggled?: ContextKeyExpr;
|
||||
}
|
||||
|
|
|
@ -86,6 +86,7 @@ export interface ParsedArgs {
|
|||
'disable-gpu'?: boolean;
|
||||
'nolazy'?: boolean;
|
||||
'force-device-scale-factor'?: string;
|
||||
'force-renderer-accessibility'?: boolean;
|
||||
}
|
||||
|
||||
export const IEnvironmentService = createDecorator<IEnvironmentService>('environmentService');
|
||||
|
|
|
@ -118,6 +118,7 @@ export const OPTIONS: OptionDescriptions<Required<ParsedArgs>> = {
|
|||
'inspect-brk': { type: 'string' },
|
||||
'nolazy': { type: 'boolean' }, // node inspect
|
||||
'force-device-scale-factor': { type: 'string' },
|
||||
'force-renderer-accessibility': { type: 'boolean' },
|
||||
'_urls': { type: 'string[]' },
|
||||
|
||||
_: { type: 'string[]' } // main arguments
|
||||
|
|
|
@ -217,6 +217,16 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
|||
} else if (semver.gt(existing.manifest.version, manifest.version)) {
|
||||
return this.uninstall(existing, true);
|
||||
}
|
||||
} else {
|
||||
// Remove the extension with same version if it is already uninstalled.
|
||||
// Installing a VSIX extension shall replace the existing extension always.
|
||||
return this.unsetUninstalledAndGetLocal(identifierWithVersion)
|
||||
.then(existing => {
|
||||
if (existing) {
|
||||
return this.removeExtension(existing, 'existing').then(null, e => Promise.reject(new Error(nls.localize('restartCode', "Please restart VS Code before reinstalling {0}.", manifest.displayName || manifest.name))));
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
return undefined;
|
||||
})
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
import { URI } from 'vs/base/common/uri';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { equalsIgnoreCase, startsWithIgnoreCase } from 'vs/base/common/strings';
|
||||
|
||||
export const IOpenerService = createDecorator<IOpenerService>('openerService');
|
||||
|
||||
|
@ -35,8 +36,7 @@ export interface IResolvedExternalUri extends IDisposable {
|
|||
}
|
||||
|
||||
export interface IOpener {
|
||||
open(resource: URI, options?: OpenInternalOptions): Promise<boolean>;
|
||||
open(resource: URI, options?: OpenExternalOptions): Promise<boolean>;
|
||||
open(resource: URI | string, options?: OpenInternalOptions | OpenExternalOptions): Promise<boolean>;
|
||||
}
|
||||
|
||||
export interface IExternalOpener {
|
||||
|
@ -44,7 +44,7 @@ export interface IExternalOpener {
|
|||
}
|
||||
|
||||
export interface IValidator {
|
||||
shouldOpen(resource: URI): Promise<boolean>;
|
||||
shouldOpen(resource: URI | string): Promise<boolean>;
|
||||
}
|
||||
|
||||
export interface IExternalUriResolver {
|
||||
|
@ -83,8 +83,7 @@ export interface IOpenerService {
|
|||
* @param resource A resource
|
||||
* @return A promise that resolves when the opening is done.
|
||||
*/
|
||||
open(resource: URI, options?: OpenInternalOptions): Promise<boolean>;
|
||||
open(resource: URI, options?: OpenExternalOptions): Promise<boolean>;
|
||||
open(resource: URI | string, options?: OpenInternalOptions | OpenExternalOptions): Promise<boolean>;
|
||||
|
||||
/**
|
||||
* Resolve a resource to its external form.
|
||||
|
@ -101,3 +100,11 @@ export const NullOpenerService: IOpenerService = Object.freeze({
|
|||
async open() { return false; },
|
||||
async resolveExternalUri(uri: URI) { return { resolved: uri, dispose() { } }; },
|
||||
});
|
||||
|
||||
export function matchesScheme(target: URI | string, scheme: string) {
|
||||
if (URI.isUri(target)) {
|
||||
return equalsIgnoreCase(target.scheme, scheme);
|
||||
} else {
|
||||
return startsWithIgnoreCase(target, scheme + ':');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -89,8 +89,8 @@ async function connectToRemoteExtensionHostAgent(options: ISimpleConnectionOptio
|
|||
options.host,
|
||||
options.port,
|
||||
`reconnectionToken=${options.reconnectionToken}&reconnection=${options.reconnectionProtocol ? 'true' : 'false'}`,
|
||||
(err: any, socket: ISocket) => {
|
||||
if (err) {
|
||||
(err: any, socket: ISocket | undefined) => {
|
||||
if (err || !socket) {
|
||||
options.logService.error(`${logPrefix} socketFactory.connect() failed. Error:`);
|
||||
options.logService.error(err);
|
||||
e(err);
|
||||
|
|
5
src/vs/vscode.d.ts
vendored
5
src/vs/vscode.d.ts
vendored
|
@ -2009,8 +2009,9 @@ declare module 'vscode' {
|
|||
/**
|
||||
* Base kind for source actions: `source`
|
||||
*
|
||||
* Source code actions apply to the entire file and can be run on save
|
||||
* using `editor.codeActionsOnSave`. They also are shown in `source` context menu.
|
||||
* 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
|
||||
* can be run on save using `editor.codeActionsOnSave` and are also shown in the `source` context menu.
|
||||
*/
|
||||
static readonly Source: CodeActionKind;
|
||||
|
||||
|
|
|
@ -30,7 +30,8 @@ import { ILogService } from 'vs/platform/log/common/log';
|
|||
import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress';
|
||||
import { extHostCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel';
|
||||
import { ISaveParticipant, SaveReason, IResolvedTextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { ISaveParticipant, IResolvedTextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { SaveReason } from 'vs/workbench/common/editor';
|
||||
import { ExtHostContext, ExtHostDocumentSaveParticipantShape, IExtHostContext } from '../common/extHost.protocol';
|
||||
|
||||
export interface ICodeActionsOnSaveOptions {
|
||||
|
|
|
@ -12,7 +12,6 @@ import { URI, UriComponents } from 'vs/base/common/uri';
|
|||
import * as modes from 'vs/editor/common/modes';
|
||||
import { localize } from 'vs/nls';
|
||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
|
@ -21,7 +20,7 @@ import { editorGroupToViewColumn, EditorViewColumn, viewColumnToEditorGroup } fr
|
|||
import { IEditorInput } from 'vs/workbench/common/editor';
|
||||
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
|
||||
import { CustomFileEditorInput } from 'vs/workbench/contrib/customEditor/browser/customEditorInput';
|
||||
import { CustomEditorModel } from 'vs/workbench/contrib/customEditor/browser/customEditorModel';
|
||||
import { ICustomEditorService } from 'vs/workbench/contrib/customEditor/common/customEditor';
|
||||
import { WebviewExtensionDescription } from 'vs/workbench/contrib/webview/browser/webview';
|
||||
import { WebviewInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput';
|
||||
import { ICreateWebViewShowOptions, IWebviewWorkbenchService, WebviewInputOptions } from 'vs/workbench/contrib/webview/browser/webviewWorkbenchService';
|
||||
|
@ -96,14 +95,13 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
|||
private readonly _webviewInputs = new WebviewInputStore();
|
||||
private readonly _revivers = new Map<string, IDisposable>();
|
||||
private readonly _editorProviders = new Map<string, IDisposable>();
|
||||
private readonly _models = new Map<string, CustomEditorModel>();
|
||||
|
||||
constructor(
|
||||
context: extHostProtocol.IExtHostContext,
|
||||
@IExtensionService extensionService: IExtensionService,
|
||||
@ICustomEditorService private readonly _customEditorService: ICustomEditorService,
|
||||
@IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService,
|
||||
@IEditorService private readonly _editorService: IEditorService,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
@IOpenerService private readonly _openerService: IOpenerService,
|
||||
@IProductService private readonly _productService: IProductService,
|
||||
@ITelemetryService private readonly _telemetryService: ITelemetryService,
|
||||
|
@ -273,18 +271,20 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
|||
webviewInput.webview.options = options;
|
||||
webviewInput.webview.extension = extension;
|
||||
|
||||
const model = this._instantiationService.createInstance(CustomEditorModel, webviewInput.getResource());
|
||||
webviewInput.setModel(model);
|
||||
this._models.set(handle, model);
|
||||
|
||||
webviewInput.onDispose(() => {
|
||||
this._models.delete(handle);
|
||||
});
|
||||
const model = await this._customEditorService.models.loadOrCreate(webviewInput.getResource(), webviewInput.viewType);
|
||||
|
||||
model.onUndo(edit => {
|
||||
this._proxy.$undoEdits(handle, [edit]);
|
||||
});
|
||||
|
||||
model.onRedo(edit => {
|
||||
this._proxy.$redoEdits(handle, [edit]);
|
||||
});
|
||||
|
||||
webviewInput.onDispose(() => {
|
||||
this._customEditorService.models.disposeModel(model);
|
||||
});
|
||||
|
||||
try {
|
||||
await this._proxy.$resolveWebviewEditor(
|
||||
webviewInput.getResource(),
|
||||
|
@ -318,7 +318,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
|||
throw new Error('Webview is not a webview editor');
|
||||
}
|
||||
|
||||
const model = this._models.get(handle);
|
||||
const model = this._customEditorService.models.get(webview.getResource(), webview.viewType);
|
||||
if (!model) {
|
||||
throw new Error('Could not find model for webview editor');
|
||||
}
|
||||
|
|
|
@ -42,9 +42,17 @@ export class MainThreadWindow implements MainThreadWindowShape {
|
|||
return Promise.resolve(this.hostService.hasFocus);
|
||||
}
|
||||
|
||||
async $openUri(uriComponents: UriComponents, options: IOpenUriOptions): Promise<boolean> {
|
||||
async $openUri(uriComponents: UriComponents, uriString: string | undefined, options: IOpenUriOptions): Promise<boolean> {
|
||||
const uri = URI.from(uriComponents);
|
||||
return this.openerService.open(uri, { openExternal: true, allowTunneling: options.allowTunneling });
|
||||
let target: URI | string;
|
||||
if (uriString && URI.parse(uriString).toString() === uri.toString()) {
|
||||
// called with string and no transformation happened -> keep string
|
||||
target = uriString;
|
||||
} else {
|
||||
// called with URI or transformed -> use uri
|
||||
target = uri;
|
||||
}
|
||||
return this.openerService.open(target, { openExternal: true, allowTunneling: options.allowTunneling });
|
||||
}
|
||||
|
||||
async $asExternalUri(uriComponents: UriComponents, options: IOpenUriOptions): Promise<UriComponents> {
|
||||
|
|
|
@ -15,7 +15,7 @@ import { IFileMatch, IPatternInfo, ISearchProgressItem, ISearchService } from 'v
|
|||
import { IWorkspaceContextService, WorkbenchState, IWorkspace } from 'vs/platform/workspace/common/workspace';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { ITextQueryBuilderOptions, QueryBuilder } from 'vs/workbench/contrib/search/common/queryBuilder';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing';
|
||||
import { ExtHostContext, ExtHostWorkspaceShape, IExtHostContext, MainContext, MainThreadWorkspaceShape, IWorkspaceData, ITextSearchComplete } from '../common/extHost.protocol';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
|
@ -37,7 +37,7 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
|
|||
extHostContext: IExtHostContext,
|
||||
@ISearchService private readonly _searchService: ISearchService,
|
||||
@IWorkspaceContextService private readonly _contextService: IWorkspaceContextService,
|
||||
@ITextFileService private readonly _textFileService: ITextFileService,
|
||||
@IEditorService private readonly _editorService: IEditorService,
|
||||
@IWorkspaceEditingService private readonly _workspaceEditingService: IWorkspaceEditingService,
|
||||
@INotificationService private readonly _notificationService: INotificationService,
|
||||
@IRequestService private readonly _requestService: IRequestService,
|
||||
|
@ -212,9 +212,7 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
|
|||
// --- save & edit resources ---
|
||||
|
||||
$saveAll(includeUntitled?: boolean): Promise<boolean> {
|
||||
return this._textFileService.saveAll(includeUntitled).then(result => {
|
||||
return result.results.every(each => each.success === true);
|
||||
});
|
||||
return this._editorService.saveAll({ includeUntitled });
|
||||
}
|
||||
|
||||
$resolveProxy(url: string): Promise<string | undefined> {
|
||||
|
|
|
@ -185,8 +185,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
|||
}
|
||||
|
||||
return activeTextEditor.edit((edit: vscode.TextEditorEdit) => {
|
||||
args.unshift(activeTextEditor, edit);
|
||||
callback.apply(thisArg, args);
|
||||
callback.apply(thisArg, [activeTextEditor, edit, ...args]);
|
||||
|
||||
}).then((result) => {
|
||||
if (!result) {
|
||||
|
|
|
@ -45,7 +45,7 @@ import { ITerminalDimensions, IShellLaunchConfig } from 'vs/workbench/contrib/te
|
|||
import { ExtensionActivationError } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { createExtHostContextProxyIdentifier as createExtId, createMainContextProxyIdentifier as createMainId, IRPCProtocol } from 'vs/workbench/services/extensions/common/proxyIdentifier';
|
||||
import * as search from 'vs/workbench/services/search/common/search';
|
||||
import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { SaveReason } from 'vs/workbench/common/editor';
|
||||
import { ExtensionActivationReason } from 'vs/workbench/api/common/extHostExtensionActivator';
|
||||
|
||||
export interface IEnvironment {
|
||||
|
@ -591,6 +591,7 @@ export interface ExtHostWebviewsShape {
|
|||
$deserializeWebviewPanel(newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, state: any, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise<void>;
|
||||
$resolveWebviewEditor(resource: UriComponents, newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise<void>;
|
||||
$undoEdits(handle: WebviewPanelHandle, edits: string[]): void;
|
||||
$redoEdits(handle: WebviewPanelHandle, edits: string[]): void;
|
||||
}
|
||||
|
||||
export interface MainThreadUrlsShape extends IDisposable {
|
||||
|
@ -760,7 +761,7 @@ export interface IOpenUriOptions {
|
|||
|
||||
export interface MainThreadWindowShape extends IDisposable {
|
||||
$getWindowVisibility(): Promise<boolean>;
|
||||
$openUri(uri: UriComponents, options: IOpenUriOptions): Promise<boolean>;
|
||||
$openUri(uri: UriComponents, uriString: string | undefined, options: IOpenUriOptions): Promise<boolean>;
|
||||
$asExternalUri(uri: UriComponents, options: IOpenUriOptions): Promise<UriComponents>;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ import { ExtHostDocumentSaveParticipantShape, MainThreadTextEditorsShape, IResou
|
|||
import { TextEdit } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { Range, TextDocumentSaveReason, EndOfLine } from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
|
||||
import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { SaveReason } from 'vs/workbench/common/editor';
|
||||
import * as vscode from 'vscode';
|
||||
import { LinkedList } from 'vs/base/common/linkedList';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
|
|
|
@ -245,9 +245,9 @@ class OpenNodeModuleFactory implements INodeModuleFactory {
|
|||
return this.callOriginal(target, options);
|
||||
}
|
||||
if (uri.scheme === 'http' || uri.scheme === 'https') {
|
||||
return mainThreadWindow.$openUri(uri, { allowTunneling: true });
|
||||
return mainThreadWindow.$openUri(uri, target, { allowTunneling: true });
|
||||
} else if (uri.scheme === 'mailto' || uri.scheme === this._appUriScheme) {
|
||||
return mainThreadWindow.$openUri(uri, {});
|
||||
return mainThreadWindow.$openUri(uri, target, {});
|
||||
}
|
||||
return this.callOriginal(target, options);
|
||||
};
|
||||
|
|
|
@ -9,7 +9,7 @@ import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShap
|
|||
import { ExtHostConfigProvider } from 'vs/workbench/api/common/extHostConfiguration';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { EXT_HOST_CREATION_DELAY, ITerminalChildProcess, ITerminalDimensions } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { ITerminalChildProcess, ITerminalDimensions, EXT_HOST_CREATION_DELAY } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { TerminalDataBufferer } from 'vs/workbench/contrib/terminal/common/terminalDataBuffering';
|
||||
|
@ -437,22 +437,6 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ
|
|||
}
|
||||
}
|
||||
|
||||
public performTerminalIdAction(id: number, callback: (terminal: ExtHostTerminal) => void): void {
|
||||
// TODO: Use await this._getTerminalByIdEventually(id);
|
||||
let terminal = this._getTerminalById(id);
|
||||
if (terminal) {
|
||||
callback(terminal);
|
||||
} else {
|
||||
// Retry one more time in case the terminal has not yet been initialized.
|
||||
setTimeout(() => {
|
||||
terminal = this._getTerminalById(id);
|
||||
if (terminal) {
|
||||
callback(terminal);
|
||||
}
|
||||
}, EXT_HOST_CREATION_DELAY * 2);
|
||||
}
|
||||
}
|
||||
|
||||
public async $startExtensionTerminal(id: number, initialDimensions: ITerminalDimensionsDto | undefined): Promise<void> {
|
||||
// Make sure the ExtHostTerminal exists so onDidOpenTerminal has fired before we call
|
||||
// Pseudoterminal.start
|
||||
|
@ -550,10 +534,6 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ
|
|||
private _getTerminalByIdEventually(id: number, retries: number = 5): Promise<ExtHostTerminal | undefined> {
|
||||
if (!this._getTerminalPromises[id]) {
|
||||
this._getTerminalPromises[id] = this._createGetTerminalPromise(id, retries);
|
||||
} else {
|
||||
this._getTerminalPromises[id].then(c => {
|
||||
return this._createGetTerminalPromise(id, retries);
|
||||
});
|
||||
}
|
||||
return this._getTerminalPromises[id];
|
||||
}
|
||||
|
@ -571,7 +551,7 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ
|
|||
} else {
|
||||
// This should only be needed immediately after createTerminalRenderer is called as
|
||||
// the ExtHostTerminal has not yet been iniitalized
|
||||
timeout(200).then(() => c(this._createGetTerminalPromise(id, retries - 1)));
|
||||
timeout(EXT_HOST_CREATION_DELAY * 2).then(() => c(this._createGetTerminalPromise(id, retries - 1)));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import { EndOfLineSequence, TrackedRangeStickiness } from 'vs/editor/common/mode
|
|||
import * as vscode from 'vscode';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { ProgressLocation as MainProgressLocation } from 'vs/platform/progress/common/progress';
|
||||
import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { SaveReason } from 'vs/workbench/common/editor';
|
||||
import { IPosition } from 'vs/editor/common/core/position';
|
||||
import * as editorRange from 'vs/editor/common/core/range';
|
||||
import { ISelection } from 'vs/editor/common/core/selection';
|
||||
|
@ -31,7 +31,6 @@ import { LogLevel as _MainLogLevel } from 'vs/platform/log/common/log';
|
|||
import { coalesce, isNonEmptyArray } from 'vs/base/common/arrays';
|
||||
import { RenderLineNumbersType } from 'vs/editor/common/config/editorOptions';
|
||||
|
||||
|
||||
export interface PositionLike {
|
||||
line: number;
|
||||
character: number;
|
||||
|
|
|
@ -252,6 +252,10 @@ export class ExtHostWebviewEditor extends Disposable implements vscode.WebviewPa
|
|||
assertIsDefined(this._capabilities).editingCapability?.undoEdits(edits);
|
||||
}
|
||||
|
||||
_redoEdits(edits: string[]): void {
|
||||
assertIsDefined(this._capabilities).editingCapability?.applyEdits(edits);
|
||||
}
|
||||
|
||||
private assertNotDisposed() {
|
||||
if (this._isDisposed) {
|
||||
throw new Error('Webview is disposed');
|
||||
|
@ -447,6 +451,14 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
|
|||
panel._undoEdits(edits);
|
||||
}
|
||||
|
||||
$redoEdits(handle: WebviewPanelHandle, edits: string[]): void {
|
||||
const panel = this.getWebviewPanel(handle);
|
||||
if (!panel) {
|
||||
return;
|
||||
}
|
||||
panel._redoEdits(edits);
|
||||
}
|
||||
|
||||
private getWebviewPanel(handle: WebviewPanelHandle): ExtHostWebviewEditor | undefined {
|
||||
return this._webviewPanels.get(handle);
|
||||
}
|
||||
|
|
|
@ -39,7 +39,9 @@ export class ExtHostWindow implements ExtHostWindowShape {
|
|||
}
|
||||
|
||||
openUri(stringOrUri: string | URI, options: IOpenUriOptions): Promise<boolean> {
|
||||
let uriAsString: string | undefined;
|
||||
if (typeof stringOrUri === 'string') {
|
||||
uriAsString = stringOrUri;
|
||||
try {
|
||||
stringOrUri = URI.parse(stringOrUri);
|
||||
} catch (e) {
|
||||
|
@ -51,7 +53,7 @@ export class ExtHostWindow implements ExtHostWindowShape {
|
|||
} else if (stringOrUri.scheme === Schemas.command) {
|
||||
return Promise.reject(`Invalid scheme '${stringOrUri.scheme}'`);
|
||||
}
|
||||
return this._proxy.$openUri(stringOrUri, options);
|
||||
return this._proxy.$openUri(stringOrUri, uriAsString, options);
|
||||
}
|
||||
|
||||
async asExternalUri(uri: URI, options: IOpenUriOptions): Promise<URI> {
|
||||
|
|
|
@ -38,6 +38,7 @@ import { ISignService } from 'vs/platform/sign/common/sign';
|
|||
import { IExtHostTerminalService } from 'vs/workbench/api/common/extHostTerminalService';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { IExtHostDebugService } from 'vs/workbench/api/common/extHostDebugService';
|
||||
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||
|
||||
export class ExtHostDebugService implements IExtHostDebugService, ExtHostDebugServiceShape {
|
||||
|
||||
|
@ -114,7 +115,7 @@ export class ExtHostDebugService implements IExtHostDebugService, ExtHostDebugSe
|
|||
|
||||
this._onDidStartDebugSession = new Emitter<vscode.DebugSession>();
|
||||
this._onDidTerminateDebugSession = new Emitter<vscode.DebugSession>();
|
||||
this._onDidChangeActiveDebugSession = new Emitter<vscode.DebugSession>();
|
||||
this._onDidChangeActiveDebugSession = new Emitter<vscode.DebugSession | undefined>();
|
||||
this._onDidReceiveDebugSessionCustomEvent = new Emitter<vscode.DebugSessionCustomEvent>();
|
||||
|
||||
this._debugServiceProxy = extHostRpcService.getProxy(MainContext.MainThreadDebugService);
|
||||
|
@ -511,11 +512,11 @@ export class ExtHostDebugService implements IExtHostDebugService, ExtHostDebugSe
|
|||
}
|
||||
this._debugServiceProxy.$acceptDAError(debugAdapterHandle, err.name, err.message, err.stack);
|
||||
});
|
||||
debugAdapter.onExit((code: number) => {
|
||||
debugAdapter.onExit((code: number | null) => {
|
||||
if (tracker && tracker.onExit) {
|
||||
tracker.onExit(code, undefined);
|
||||
tracker.onExit(withNullAsUndefined(code), undefined);
|
||||
}
|
||||
this._debugServiceProxy.$acceptDAExit(debugAdapterHandle, code, undefined);
|
||||
this._debugServiceProxy.$acceptDAExit(debugAdapterHandle, withNullAsUndefined(code), undefined);
|
||||
});
|
||||
|
||||
if (tracker && tracker.onWillStartSession) {
|
||||
|
|
|
@ -22,7 +22,7 @@ import { IHostService } from 'vs/workbench/services/host/browser/host';
|
|||
import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { IWorkspacesService, hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces';
|
||||
|
||||
export class OpenFileAction extends Action {
|
||||
|
||||
|
@ -213,7 +213,7 @@ export class SaveWorkspaceAsAction extends Action {
|
|||
|
||||
async run(): Promise<any> {
|
||||
const configPathUri = await this.workspaceEditingService.pickNewWorkspacePath();
|
||||
if (configPathUri) {
|
||||
if (configPathUri && hasWorkspaceFileExtension(configPathUri)) {
|
||||
switch (this.contextService.getWorkbenchState()) {
|
||||
case WorkbenchState.EMPTY:
|
||||
case WorkbenchState.FOLDER:
|
||||
|
|
|
@ -8,7 +8,7 @@ import { Disposable } from 'vs/base/common/lifecycle';
|
|||
import { IContextKeyService, IContextKey, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { InputFocusedContext } from 'vs/platform/contextkey/common/contextkeys';
|
||||
import { IWindowsConfiguration } from 'vs/platform/windows/common/windows';
|
||||
import { ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, TEXT_DIFF_EDITOR_ID, SplitEditorsVertically, InEditorZenModeContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorIsSaveableContext, toResource, SideBySideEditor, EditorAreaVisibleContext } from 'vs/workbench/common/editor';
|
||||
import { ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, TEXT_DIFF_EDITOR_ID, SplitEditorsVertically, InEditorZenModeContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorIsSaveableContext, EditorAreaVisibleContext, DirtyWorkingCopiesContext } from 'vs/workbench/common/editor';
|
||||
import { trackFocus, addDisposableListener, EventType } from 'vs/base/browser/dom';
|
||||
import { preferredSideBySideGroupDirection, GroupDirection, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
|
@ -21,8 +21,7 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
|||
import { isMacintosh, isLinux, isWindows, isWeb } from 'vs/base/common/platform';
|
||||
import { PanelPositionContext } from 'vs/workbench/common/panel';
|
||||
import { getRemoteName } from 'vs/platform/remote/common/remoteHosts';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService';
|
||||
|
||||
export const IsMacContext = new RawContextKey<boolean>('isMac', isMacintosh);
|
||||
export const IsLinuxContext = new RawContextKey<boolean>('isLinux', isLinux);
|
||||
|
@ -51,6 +50,8 @@ export const IsFullscreenContext = new RawContextKey<boolean>('isFullscreen', fa
|
|||
export class WorkbenchContextKeysHandler extends Disposable {
|
||||
private inputFocusedContext: IContextKey<boolean>;
|
||||
|
||||
private dirtyWorkingCopiesContext: IContextKey<boolean>;
|
||||
|
||||
private activeEditorContext: IContextKey<string | null>;
|
||||
private activeEditorIsSaveable: IContextKey<boolean>;
|
||||
|
||||
|
@ -75,19 +76,18 @@ export class WorkbenchContextKeysHandler extends Disposable {
|
|||
private panelPositionContext: IContextKey<string>;
|
||||
|
||||
constructor(
|
||||
@IContextKeyService private contextKeyService: IContextKeyService,
|
||||
@IWorkspaceContextService private contextService: IWorkspaceContextService,
|
||||
@IConfigurationService private configurationService: IConfigurationService,
|
||||
@IWorkbenchEnvironmentService private environmentService: IWorkbenchEnvironmentService,
|
||||
@IEditorService private editorService: IEditorService,
|
||||
@IEditorGroupsService private editorGroupService: IEditorGroupsService,
|
||||
@IWorkbenchLayoutService private layoutService: IWorkbenchLayoutService,
|
||||
@IViewletService private viewletService: IViewletService,
|
||||
@IFileService private fileService: IFileService
|
||||
@IContextKeyService private readonly contextKeyService: IContextKeyService,
|
||||
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
|
||||
@IEditorService private readonly editorService: IEditorService,
|
||||
@IEditorGroupsService private readonly editorGroupService: IEditorGroupsService,
|
||||
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
|
||||
@IViewletService private readonly viewletService: IViewletService,
|
||||
@IWorkingCopyService private readonly workingCopyService: IWorkingCopyService
|
||||
) {
|
||||
super();
|
||||
|
||||
|
||||
// Platform
|
||||
IsMacContext.bindTo(this.contextKeyService);
|
||||
IsLinuxContext.bindTo(this.contextKeyService);
|
||||
|
@ -116,6 +116,9 @@ export class WorkbenchContextKeysHandler extends Disposable {
|
|||
this.activeEditorGroupLast = ActiveEditorGroupLastContext.bindTo(this.contextKeyService);
|
||||
this.multipleEditorGroupsContext = MultipleEditorGroupsContext.bindTo(this.contextKeyService);
|
||||
|
||||
// Working Copies
|
||||
this.dirtyWorkingCopiesContext = DirtyWorkingCopiesContext.bindTo(this.contextKeyService);
|
||||
|
||||
// Inputs
|
||||
this.inputFocusedContext = InputFocusedContext.bindTo(this.contextKeyService);
|
||||
|
||||
|
@ -183,6 +186,8 @@ export class WorkbenchContextKeysHandler extends Disposable {
|
|||
this._register(this.viewletService.onDidViewletOpen(() => this.updateSideBarContextKeys()));
|
||||
|
||||
this._register(this.layoutService.onPartVisibilityChange(() => this.editorAreaVisibleContext.set(this.layoutService.isVisible(Parts.EDITOR_PART))));
|
||||
|
||||
this._register(this.workingCopyService.onDidChangeDirty(w => this.dirtyWorkingCopiesContext.set(w.isDirty() || this.workingCopyService.hasDirty)));
|
||||
}
|
||||
|
||||
private updateEditorContextKeys(): void {
|
||||
|
@ -217,10 +222,7 @@ export class WorkbenchContextKeysHandler extends Disposable {
|
|||
|
||||
if (activeControl) {
|
||||
this.activeEditorContext.set(activeControl.getId());
|
||||
|
||||
const resource = toResource(activeControl.input, { supportSideBySide: SideBySideEditor.MASTER });
|
||||
const canSave = resource ? this.fileService.canHandleResource(resource) || resource.scheme === Schemas.untitled : false;
|
||||
this.activeEditorIsSaveable.set(canSave);
|
||||
this.activeEditorIsSaveable.set(!activeControl.input.isReadonly());
|
||||
} else {
|
||||
this.activeEditorContext.reset();
|
||||
this.activeEditorIsSaveable.reset();
|
||||
|
|
|
@ -28,7 +28,7 @@ import { withNullAsUndefined } from 'vs/base/common/types';
|
|||
|
||||
export interface IResourceLabelProps {
|
||||
resource?: URI;
|
||||
name?: string;
|
||||
name?: string | string[];
|
||||
description?: string;
|
||||
}
|
||||
|
||||
|
@ -41,6 +41,7 @@ export interface IResourceLabelOptions extends IIconLabelValueOptions {
|
|||
export interface IFileLabelOptions extends IResourceLabelOptions {
|
||||
hideLabel?: boolean;
|
||||
hidePath?: boolean;
|
||||
readonly parentCount?: number;
|
||||
}
|
||||
|
||||
export interface IResourceLabel extends IDisposable {
|
||||
|
@ -442,7 +443,8 @@ class ResourceLabelWidget extends IconLabel {
|
|||
title: '',
|
||||
italic: this.options && this.options.italic,
|
||||
matches: this.options && this.options.matches,
|
||||
extraClasses: []
|
||||
extraClasses: [],
|
||||
separator: this.options?.separator
|
||||
};
|
||||
|
||||
const resource = this.label.resource;
|
||||
|
|
|
@ -44,6 +44,10 @@ body.web {
|
|||
position: fixed; /* prevent bounce effect */
|
||||
}
|
||||
|
||||
.monaco-workbench.web {
|
||||
touch-action: initial; /* reenable touch events on workbench */
|
||||
}
|
||||
|
||||
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
|
||||
|
||||
.monaco-workbench {
|
||||
|
|
|
@ -22,7 +22,7 @@ import { ActivityAction, ActivityActionViewItem, ICompositeBar, ICompositeBarCol
|
|||
import { ViewletDescriptor } from 'vs/workbench/browser/viewlet';
|
||||
import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions';
|
||||
import { IActivity } from 'vs/workbench/common/activity';
|
||||
import { ACTIVITY_BAR_FOREGROUND, ACTIVITY_BAR_ACTIVE_BORDER, ACTIVITY_BAR_ACTIVE_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
import { ACTIVITY_BAR_FOREGROUND, ACTIVITY_BAR_ACTIVE_BORDER, ACTIVITY_BAR_ACTIVE_FOCUS_BORDER, ACTIVITY_BAR_ACTIVE_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
import { IActivityBarService } from 'vs/workbench/services/activityBar/browser/activityBarService';
|
||||
import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
|
@ -297,6 +297,20 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
|
|||
`);
|
||||
}
|
||||
|
||||
const activeFocusBorderColor = theme.getColor(ACTIVITY_BAR_ACTIVE_FOCUS_BORDER);
|
||||
if (activeFocusBorderColor) {
|
||||
collector.addRule(`
|
||||
.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.checked:focus::before {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.checked:focus .active-item-indicator:before {
|
||||
visibility: visible;
|
||||
border-left-color: ${activeFocusBorderColor};
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
||||
const activeBackgroundColor = theme.getColor(ACTIVITY_BAR_ACTIVE_BACKGROUND);
|
||||
if (activeBackgroundColor) {
|
||||
collector.addRule(`
|
||||
|
|
|
@ -6,14 +6,15 @@
|
|||
.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item {
|
||||
display: block;
|
||||
position: relative;
|
||||
padding: 5px 0;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-label {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
height: 40px;
|
||||
height: 48px;
|
||||
margin-right: 0;
|
||||
box-sizing: border-box;
|
||||
|
||||
|
@ -35,11 +36,10 @@
|
|||
.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item:focus:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 9px;
|
||||
height: 32px;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
top: 5px;
|
||||
height: 40px;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
width: 0;
|
||||
border-left: 2px solid;
|
||||
}
|
||||
|
@ -50,7 +50,7 @@
|
|||
}
|
||||
|
||||
.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.checked:focus .active-item-indicator:before {
|
||||
border-left: none; /* don't show active border + focus at the same time, focus takes priority */
|
||||
visibility: hidden; /* don't show active border + focus at the same time, focus takes priority */
|
||||
}
|
||||
|
||||
/* Hides active elements in high contrast mode */
|
||||
|
@ -62,20 +62,14 @@
|
|||
border-left: none !important; /* no focus feedback when using mouse */
|
||||
}
|
||||
|
||||
.monaco-workbench .activitybar.left > .content :not(.monaco-menu) > .monaco-action-bar .action-item:focus:before,
|
||||
.monaco-workbench .activitybar.left > .content :not(.monaco-menu) > .monaco-action-bar .action-item.checked .active-item-indicator:before{
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.monaco-workbench .activitybar.left > .content :not(.monaco-menu) > .monaco-action-bar .action-item:focus:before {
|
||||
left: 1px;
|
||||
}
|
||||
|
||||
.monaco-workbench .activitybar.right > .content :not(.monaco-menu) > .monaco-action-bar .action-item.checked .active-item-indicator:before,
|
||||
.monaco-workbench .activitybar.right > .content :not(.monaco-menu) > .monaco-action-bar .action-item:focus:before {
|
||||
right: 1px;
|
||||
}
|
||||
|
||||
.monaco-workbench .activitybar.right > .content :not(.monaco-menu) > .monaco-action-bar .action-item.checked .active-item-indicator:before {
|
||||
right: 2px;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
/* Hides outline on HC as focus is handled by border */
|
||||
|
@ -88,16 +82,18 @@
|
|||
.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .badge {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
top: 5px;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
margin: auto;
|
||||
left: 0;
|
||||
overflow: hidden;
|
||||
width: 50px;
|
||||
height: 40px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .badge .badge-content {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
top: 24px;
|
||||
right: 8px;
|
||||
font-size: 9px;
|
||||
font-weight: 600;
|
||||
|
@ -113,7 +109,7 @@
|
|||
|
||||
.monaco-workbench .activitybar.right > .content :not(.monaco-menu) > .monaco-action-bar .action-label:not(.codicon) {
|
||||
margin-left: 0;
|
||||
padding: 0 50px 0 0;
|
||||
padding: 0 48px 0 0;
|
||||
background-position: calc(100% - 9px) center;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,6 @@ import { IResourceInput } from 'vs/platform/editor/common/editor';
|
|||
import { IHistoryService } from 'vs/workbench/services/history/common/history';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { CLOSE_EDITOR_COMMAND_ID, NAVIGATE_ALL_EDITORS_GROUP_PREFIX, MOVE_ACTIVE_EDITOR_COMMAND_ID, NAVIGATE_IN_ACTIVE_GROUP_PREFIX, ActiveEditorMoveArguments, SPLIT_EDITOR_LEFT, SPLIT_EDITOR_RIGHT, SPLIT_EDITOR_UP, SPLIT_EDITOR_DOWN, splitEditor, LAYOUT_EDITOR_GROUPS_COMMAND_ID, mergeAllGroups } from 'vs/workbench/browser/parts/editor/editorCommands';
|
||||
import { IEditorGroupsService, IEditorGroup, GroupsArrangement, EditorsOrder, GroupLocation, GroupDirection, preferredSideBySideGroupDirection, IFindGroupScope, GroupOrientation, EditorGroupLayout, GroupsOrder } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService';
|
||||
|
@ -507,7 +506,7 @@ export class CloseOneEditorAction extends Action {
|
|||
|
||||
// Close specific editor in group
|
||||
if (typeof editorIndex === 'number') {
|
||||
const editorAtIndex = group.getEditor(editorIndex);
|
||||
const editorAtIndex = group.getEditorByIndex(editorIndex);
|
||||
if (editorAtIndex) {
|
||||
return group.closeEditor(editorAtIndex);
|
||||
}
|
||||
|
@ -598,10 +597,10 @@ export abstract class BaseCloseAllAction extends Action {
|
|||
id: string,
|
||||
label: string,
|
||||
clazz: string | undefined,
|
||||
private textFileService: ITextFileService,
|
||||
private workingCopyService: IWorkingCopyService,
|
||||
private fileDialogService: IFileDialogService,
|
||||
protected editorGroupService: IEditorGroupsService
|
||||
protected editorGroupService: IEditorGroupsService,
|
||||
private editorService: IEditorService
|
||||
) {
|
||||
super(id, label, clazz);
|
||||
}
|
||||
|
@ -647,11 +646,10 @@ export abstract class BaseCloseAllAction extends Action {
|
|||
|
||||
let saveOrRevert: boolean;
|
||||
if (confirm === ConfirmResult.DONT_SAVE) {
|
||||
await this.textFileService.revertAll(undefined, { soft: true });
|
||||
await this.editorService.revertAll({ soft: true });
|
||||
saveOrRevert = true;
|
||||
} else {
|
||||
const res = await this.textFileService.saveAll(true);
|
||||
saveOrRevert = res.results.every(r => !!r.success);
|
||||
saveOrRevert = await this.editorService.saveAll({ includeUntitled: true });
|
||||
}
|
||||
|
||||
if (saveOrRevert) {
|
||||
|
@ -670,12 +668,12 @@ export class CloseAllEditorsAction extends BaseCloseAllAction {
|
|||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@ITextFileService textFileService: ITextFileService,
|
||||
@IWorkingCopyService workingCopyService: IWorkingCopyService,
|
||||
@IFileDialogService fileDialogService: IFileDialogService,
|
||||
@IEditorGroupsService editorGroupService: IEditorGroupsService
|
||||
@IEditorGroupsService editorGroupService: IEditorGroupsService,
|
||||
@IEditorService editorService: IEditorService
|
||||
) {
|
||||
super(id, label, 'codicon-close-all', textFileService, workingCopyService, fileDialogService, editorGroupService);
|
||||
super(id, label, 'codicon-close-all', workingCopyService, fileDialogService, editorGroupService, editorService);
|
||||
}
|
||||
|
||||
protected doCloseAll(): Promise<any> {
|
||||
|
@ -691,12 +689,12 @@ export class CloseAllEditorGroupsAction extends BaseCloseAllAction {
|
|||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@ITextFileService textFileService: ITextFileService,
|
||||
@IWorkingCopyService workingCopyService: IWorkingCopyService,
|
||||
@IFileDialogService fileDialogService: IFileDialogService,
|
||||
@IEditorGroupsService editorGroupService: IEditorGroupsService
|
||||
@IEditorGroupsService editorGroupService: IEditorGroupsService,
|
||||
@IEditorService editorService: IEditorService
|
||||
) {
|
||||
super(id, label, undefined, textFileService, workingCopyService, fileDialogService, editorGroupService);
|
||||
super(id, label, undefined, workingCopyService, fileDialogService, editorGroupService, editorService);
|
||||
}
|
||||
|
||||
protected async doCloseAll(): Promise<any> {
|
||||
|
|
|
@ -318,7 +318,7 @@ function registerOpenEditorAtIndexCommands(): void {
|
|||
const editorService = accessor.get(IEditorService);
|
||||
const activeControl = editorService.activeControl;
|
||||
if (activeControl) {
|
||||
const editor = activeControl.group.getEditor(editorIndex);
|
||||
const editor = activeControl.group.getEditorByIndex(editorIndex);
|
||||
if (editor) {
|
||||
editorService.openEditor(editor);
|
||||
}
|
||||
|
@ -448,7 +448,7 @@ export function splitEditor(editorGroupService: IEditorGroupsService, direction:
|
|||
// Split editor (if it can be split)
|
||||
let editorToCopy: IEditorInput | undefined;
|
||||
if (context && typeof context.editorIndex === 'number') {
|
||||
editorToCopy = sourceGroup.getEditor(context.editorIndex);
|
||||
editorToCopy = sourceGroup.getEditorByIndex(context.editorIndex);
|
||||
} else {
|
||||
editorToCopy = types.withNullAsUndefined(sourceGroup.activeEditor);
|
||||
}
|
||||
|
@ -548,7 +548,7 @@ function registerCloseEditorCommands() {
|
|||
if (group) {
|
||||
const editors = coalesce(contexts
|
||||
.filter(context => context.groupId === groupId)
|
||||
.map(context => typeof context.editorIndex === 'number' ? group.getEditor(context.editorIndex) : group.activeEditor));
|
||||
.map(context => typeof context.editorIndex === 'number' ? group.getEditorByIndex(context.editorIndex) : group.activeEditor));
|
||||
|
||||
return group.closeEditors(editors);
|
||||
}
|
||||
|
@ -603,7 +603,7 @@ function registerCloseEditorCommands() {
|
|||
if (group) {
|
||||
const editors = contexts
|
||||
.filter(context => context.groupId === groupId)
|
||||
.map(context => typeof context.editorIndex === 'number' ? group.getEditor(context.editorIndex) : group.activeEditor);
|
||||
.map(context => typeof context.editorIndex === 'number' ? group.getEditorByIndex(context.editorIndex) : group.activeEditor);
|
||||
const editorsToClose = group.editors.filter(e => editors.indexOf(e) === -1);
|
||||
|
||||
if (group.activeEditor) {
|
||||
|
@ -715,7 +715,7 @@ function resolveCommandsContext(editorGroupService: IEditorGroupsService, contex
|
|||
|
||||
// Resolve from context
|
||||
let group = context && typeof context.groupId === 'number' ? editorGroupService.getGroup(context.groupId) : undefined;
|
||||
let editor = group && context && typeof context.editorIndex === 'number' ? types.withNullAsUndefined(group.getEditor(context.editorIndex)) : undefined;
|
||||
let editor = group && context && typeof context.editorIndex === 'number' ? types.withNullAsUndefined(group.getEditorByIndex(context.editorIndex)) : undefined;
|
||||
let control = group ? group.activeControl : undefined;
|
||||
|
||||
// Fallback to active group as needed
|
||||
|
|
|
@ -518,11 +518,10 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
|||
editorsToClose.push(editor.master, editor.details);
|
||||
}
|
||||
|
||||
// Close the editor when it is no longer open in any group including diff editors
|
||||
// Dispose the editor when it is no longer open in any group including diff editors
|
||||
editorsToClose.forEach(editorToClose => {
|
||||
const resource = editorToClose ? editorToClose.getResource() : undefined; // prefer resource to not close right-hand side editors of a diff editor
|
||||
if (!this.accessor.groups.some(groupView => groupView.group.contains(resource || editorToClose))) {
|
||||
editorToClose.close();
|
||||
if (!this.accessor.groups.some(groupView => groupView.group.contains(editorToClose, true /* include side by side editor master & details */))) {
|
||||
editorToClose.dispose();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -761,8 +760,8 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
|||
return this.editors;
|
||||
}
|
||||
|
||||
getEditor(index: number): EditorInput | undefined {
|
||||
return this._group.getEditor(index);
|
||||
getEditorByIndex(index: number): EditorInput | undefined {
|
||||
return this._group.getEditorByIndex(index);
|
||||
}
|
||||
|
||||
getIndexOfEditor(editor: EditorInput): number {
|
||||
|
@ -1121,7 +1120,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
|||
}
|
||||
|
||||
// Check for dirty and veto
|
||||
const veto = await this.handleDirty([editor]);
|
||||
const veto = await this.handleDirtyClosing([editor]);
|
||||
if (veto) {
|
||||
return;
|
||||
}
|
||||
|
@ -1232,7 +1231,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
|||
this._group.closeEditor(editor);
|
||||
}
|
||||
|
||||
private async handleDirty(editors: EditorInput[]): Promise<boolean /* veto */> {
|
||||
private async handleDirtyClosing(editors: EditorInput[]): Promise<boolean /* veto */> {
|
||||
if (!editors.length) {
|
||||
return false; // no veto
|
||||
}
|
||||
|
@ -1241,13 +1240,13 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
|||
|
||||
// To prevent multiple confirmation dialogs from showing up one after the other
|
||||
// we check if a pending confirmation is currently showing and if so, join that
|
||||
let handleDirtyPromise = this.mapEditorToPendingConfirmation.get(editor);
|
||||
if (!handleDirtyPromise) {
|
||||
handleDirtyPromise = this.doHandleDirty(editor);
|
||||
this.mapEditorToPendingConfirmation.set(editor, handleDirtyPromise);
|
||||
let handleDirtyClosingPromise = this.mapEditorToPendingConfirmation.get(editor);
|
||||
if (!handleDirtyClosingPromise) {
|
||||
handleDirtyClosingPromise = this.doHandleDirtyClosing(editor);
|
||||
this.mapEditorToPendingConfirmation.set(editor, handleDirtyClosingPromise);
|
||||
}
|
||||
|
||||
const veto = await handleDirtyPromise;
|
||||
const veto = await handleDirtyClosingPromise;
|
||||
|
||||
// Make sure to remove from our map of cached pending confirmations
|
||||
this.mapEditorToPendingConfirmation.delete(editor);
|
||||
|
@ -1258,16 +1257,40 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
|||
}
|
||||
|
||||
// Otherwise continue with the remainders
|
||||
return this.handleDirty(editors);
|
||||
return this.handleDirtyClosing(editors);
|
||||
}
|
||||
|
||||
private async doHandleDirty(editor: EditorInput): Promise<boolean /* veto */> {
|
||||
if (
|
||||
!editor.isDirty() || // editor must be dirty
|
||||
this.accessor.groups.some(groupView => groupView !== this && groupView.group.contains(editor, true /* support side by side */)) || // editor is opened in other group
|
||||
editor instanceof SideBySideEditorInput && this.isOpened(editor.master) // side by side editor master is still opened
|
||||
) {
|
||||
private async doHandleDirtyClosing(editor: EditorInput): Promise<boolean /* veto */> {
|
||||
if (!editor.isDirty()) {
|
||||
return false; // editor must be dirty
|
||||
}
|
||||
|
||||
if (editor instanceof SideBySideEditorInput && this.isOpened(editor.master)) {
|
||||
return false; // master-side of editor is still opened somewhere else
|
||||
}
|
||||
|
||||
// Note: we explicitly decide to ask for confirm if closing a normal editor even
|
||||
// if it is opened in a side-by-side editor in the group. This decision is made
|
||||
// because it may be less obvious that one side of a side by side editor is dirty
|
||||
// and can still be changed.
|
||||
|
||||
if (this.accessor.groups.some(groupView => {
|
||||
if (groupView === this) {
|
||||
return false; // skip this group to avoid false assumptions about the editor being opened still
|
||||
}
|
||||
|
||||
const otherGroup = groupView.group;
|
||||
if (otherGroup.contains(editor)) {
|
||||
return true; // exact editor still opened
|
||||
}
|
||||
|
||||
if (editor instanceof SideBySideEditorInput && otherGroup.contains(editor.master)) {
|
||||
return true; // master side of side by side editor still opened
|
||||
}
|
||||
|
||||
return false;
|
||||
})) {
|
||||
return false; // editor is still editable somewhere else
|
||||
}
|
||||
|
||||
// Switch to editor that we want to handle and confirm to save/revert
|
||||
|
@ -1287,7 +1310,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
|||
// Otherwise, handle accordingly
|
||||
switch (res) {
|
||||
case ConfirmResult.SAVE:
|
||||
const result = await editor.save();
|
||||
const result = await editor.save(this._group.id);
|
||||
|
||||
return !result;
|
||||
case ConfirmResult.DONT_SAVE:
|
||||
|
@ -1324,7 +1347,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
|||
const editors = this.getEditorsToClose(args);
|
||||
|
||||
// Check for dirty and veto
|
||||
const veto = await this.handleDirty(editors.slice(0));
|
||||
const veto = await this.handleDirtyClosing(editors.slice(0));
|
||||
if (veto) {
|
||||
return;
|
||||
}
|
||||
|
@ -1403,7 +1426,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
|||
|
||||
// Check for dirty and veto
|
||||
const editors = this._group.getEditors(true);
|
||||
const veto = await this.handleDirty(editors.slice(0));
|
||||
const veto = await this.handleDirtyClosing(editors.slice(0));
|
||||
if (veto) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -501,7 +501,7 @@ export class TabsTitleControl extends TitleControl {
|
|||
}
|
||||
|
||||
// Open tabs editor
|
||||
const input = this.group.getEditor(index);
|
||||
const input = this.group.getEditorByIndex(index);
|
||||
if (input) {
|
||||
this.group.openEditor(input);
|
||||
}
|
||||
|
@ -512,7 +512,7 @@ export class TabsTitleControl extends TitleControl {
|
|||
const showContextMenu = (e: Event) => {
|
||||
EventHelper.stop(e);
|
||||
|
||||
const input = this.group.getEditor(index);
|
||||
const input = this.group.getEditorByIndex(index);
|
||||
if (input) {
|
||||
this.onContextMenu(input, e, tab);
|
||||
}
|
||||
|
@ -562,7 +562,7 @@ export class TabsTitleControl extends TitleControl {
|
|||
// Run action on Enter/Space
|
||||
if (event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) {
|
||||
handled = true;
|
||||
const input = this.group.getEditor(index);
|
||||
const input = this.group.getEditorByIndex(index);
|
||||
if (input) {
|
||||
this.group.openEditor(input);
|
||||
}
|
||||
|
@ -581,7 +581,7 @@ export class TabsTitleControl extends TitleControl {
|
|||
targetIndex = this.group.count - 1;
|
||||
}
|
||||
|
||||
const target = this.group.getEditor(targetIndex);
|
||||
const target = this.group.getEditorByIndex(targetIndex);
|
||||
if (target) {
|
||||
handled = true;
|
||||
this.group.openEditor(target, { preserveFocus: true });
|
||||
|
@ -603,7 +603,7 @@ export class TabsTitleControl extends TitleControl {
|
|||
disposables.add(addDisposableListener(tab, EventType.DBLCLICK, (e: MouseEvent) => {
|
||||
EventHelper.stop(e);
|
||||
|
||||
const editor = this.group.getEditor(index);
|
||||
const editor = this.group.getEditorByIndex(index);
|
||||
if (editor && this.group.isPinned(editor)) {
|
||||
this.accessor.arrangeGroups(GroupsArrangement.TOGGLE, this.group);
|
||||
} else {
|
||||
|
@ -615,7 +615,7 @@ export class TabsTitleControl extends TitleControl {
|
|||
disposables.add(addDisposableListener(tab, EventType.CONTEXT_MENU, (e: Event) => {
|
||||
EventHelper.stop(e, true);
|
||||
|
||||
const input = this.group.getEditor(index);
|
||||
const input = this.group.getEditorByIndex(index);
|
||||
if (input) {
|
||||
this.onContextMenu(input, e, tab);
|
||||
}
|
||||
|
@ -623,7 +623,7 @@ export class TabsTitleControl extends TitleControl {
|
|||
|
||||
// Drag support
|
||||
disposables.add(addDisposableListener(tab, EventType.DRAG_START, (e: DragEvent) => {
|
||||
const editor = this.group.getEditor(index);
|
||||
const editor = this.group.getEditorByIndex(index);
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
|
@ -669,7 +669,7 @@ export class TabsTitleControl extends TitleControl {
|
|||
const data = this.editorTransfer.getData(DraggedEditorIdentifier.prototype);
|
||||
if (Array.isArray(data)) {
|
||||
const localDraggedEditor = data[0].identifier;
|
||||
if (localDraggedEditor.editor === this.group.getEditor(index) && localDraggedEditor.groupId === this.group.id) {
|
||||
if (localDraggedEditor.editor === this.group.getEditorByIndex(index) && localDraggedEditor.groupId === this.group.id) {
|
||||
if (e.dataTransfer) {
|
||||
e.dataTransfer.dropEffect = 'none';
|
||||
}
|
||||
|
@ -739,7 +739,7 @@ export class TabsTitleControl extends TitleControl {
|
|||
|
||||
private updateDropFeedback(element: HTMLElement, isDND: boolean, index?: number): void {
|
||||
const isTab = (typeof index === 'number');
|
||||
const editor = typeof index === 'number' ? this.group.getEditor(index) : undefined;
|
||||
const editor = typeof index === 'number' ? this.group.getEditorByIndex(index) : undefined;
|
||||
const isActiveTab = isTab && !!editor && this.group.isActive(editor);
|
||||
|
||||
// Background
|
||||
|
|
|
@ -6,17 +6,17 @@
|
|||
import { localize } from 'vs/nls';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { distinct, deepClone, assign } from 'vs/base/common/objects';
|
||||
import { isObject, assertIsDefined } from 'vs/base/common/types';
|
||||
import { isObject, assertIsDefined, withNullAsUndefined } from 'vs/base/common/types';
|
||||
import { Dimension } from 'vs/base/browser/dom';
|
||||
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
|
||||
import { EditorInput, EditorOptions, IEditorMemento, ITextEditor } from 'vs/workbench/common/editor';
|
||||
import { EditorInput, EditorOptions, IEditorMemento, ITextEditor, SaveReason } from 'vs/workbench/common/editor';
|
||||
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
|
||||
import { IEditorViewState, IEditor } from 'vs/editor/common/editorCommon';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { ITextFileService, SaveReason } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
|
||||
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
|
||||
import { isDiffEditor, isCodeEditor, getCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
|
@ -248,6 +248,15 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor {
|
|||
this.editorMemento.saveEditorState(this.group, resource, editorViewState);
|
||||
}
|
||||
|
||||
getViewState(): IEditorViewState | undefined {
|
||||
const resource = this.input?.getResource();
|
||||
if (resource) {
|
||||
return withNullAsUndefined(this.retrieveTextEditorViewState(resource));
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
protected retrieveTextEditorViewState(resource: URI): IEditorViewState | null {
|
||||
const control = this.getControl();
|
||||
if (!isCodeEditor(control)) {
|
||||
|
|
|
@ -127,7 +127,7 @@
|
|||
cursor: pointer;
|
||||
min-width: 110px;
|
||||
min-height: 18px;
|
||||
padding: 2px 8px;
|
||||
padding: 2px 23px 2px 8px;
|
||||
}
|
||||
|
||||
/* Rotate icons when panel is on right */
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue