Merge branch 'master' into misolori/icon-font-breakpoints

This commit is contained in:
Miguel Solorio 2019-11-15 10:33:44 -08:00 committed by GitHub
commit 7766a5d0f0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
198 changed files with 2780 additions and 2504 deletions

6
.vscode/tasks.json vendored
View file

@ -33,15 +33,15 @@
},
{
"type": "npm",
"script": "strict-initialization-watch",
"label": "TS - Strict Initialization",
"script": "strict-function-types-watch",
"label": "TS - Strict Function Types",
"isBackground": true,
"presentation": {
"reveal": "never"
},
"problemMatcher": {
"base": "$tsc-watch",
"owner": "typescript-strict-initialization",
"owner": "typescript-function-types",
"applyTo": "allDocuments"
}
},

View file

@ -4,23 +4,11 @@
*--------------------------------------------------------------------------------------------*/
import { getLocation, parse, visit } from 'jsonc-parser';
import * as path from 'path';
import * as vscode from 'vscode';
import * as nls from 'vscode-nls';
import { SettingsDocument } from './settingsDocumentHelper';
const localize = nls.loadMessageBundle();
const fadedDecoration = vscode.window.createTextEditorDecorationType({
light: {
color: '#757575'
},
dark: {
color: '#878787'
}
});
let pendingLaunchJsonDecoration: NodeJS.Timer;
export function activate(context: vscode.ExtensionContext): void {
//settings.json suggestions
context.subscriptions.push(registerSettingsCompletions());
@ -33,18 +21,6 @@ export function activate(context: vscode.ExtensionContext): void {
// task.json variable suggestions
context.subscriptions.push(registerVariableCompletions('**/tasks.json'));
// launch.json decorations
context.subscriptions.push(vscode.window.onDidChangeActiveTextEditor(editor => updateLaunchJsonDecorations(editor), null, context.subscriptions));
context.subscriptions.push(vscode.workspace.onDidChangeTextDocument(event => {
if (vscode.window.activeTextEditor && event.document === vscode.window.activeTextEditor.document) {
if (pendingLaunchJsonDecoration) {
clearTimeout(pendingLaunchJsonDecoration);
}
pendingLaunchJsonDecoration = setTimeout(() => updateLaunchJsonDecorations(vscode.window.activeTextEditor), 1000);
}
}, null, context.subscriptions));
updateLaunchJsonDecorations(vscode.window.activeTextEditor);
}
function registerSettingsCompletions(): vscode.Disposable {
@ -153,39 +129,6 @@ function provideInstalledExtensionProposals(extensionsContent: IExtensionsConten
return undefined;
}
function updateLaunchJsonDecorations(editor: vscode.TextEditor | undefined): void {
if (!editor || path.basename(editor.document.fileName) !== 'launch.json') {
return;
}
const ranges: vscode.Range[] = [];
let addPropertyAndValue = false;
let depthInArray = 0;
visit(editor.document.getText(), {
onObjectProperty: (property, offset, length) => {
// Decorate attributes which are unlikely to be edited by the user.
// Only decorate "configurations" if it is not inside an array (compounds have a configurations property which should not be decorated).
addPropertyAndValue = property === 'version' || property === 'type' || property === 'request' || property === 'compounds' || (property === 'configurations' && depthInArray === 0);
if (addPropertyAndValue) {
ranges.push(new vscode.Range(editor.document.positionAt(offset), editor.document.positionAt(offset + length)));
}
},
onLiteralValue: (_value, offset, length) => {
if (addPropertyAndValue) {
ranges.push(new vscode.Range(editor.document.positionAt(offset), editor.document.positionAt(offset + length)));
}
},
onArrayBegin: (_offset: number, _length: number) => {
depthInArray++;
},
onArrayEnd: (_offset: number, _length: number) => {
depthInArray--;
}
});
editor.setDecorations(fadedDecoration, ranges);
}
vscode.languages.registerDocumentSymbolProvider({ pattern: '**/launch.json', language: 'jsonc' }, {
provideDocumentSymbols(document: vscode.TextDocument, _token: vscode.CancellationToken): vscode.ProviderResult<vscode.SymbolInformation[]> {
const result: vscode.SymbolInformation[] = [];

View file

@ -9,7 +9,7 @@
},
"main": "./out/cssServerMain",
"dependencies": {
"vscode-css-languageservice": "^4.0.3-next.19",
"vscode-css-languageservice": "^4.0.3-next.20",
"vscode-languageserver": "^6.0.0-next.3"
},
"devDependencies": {

View file

@ -781,10 +781,10 @@ supports-color@^5.3.0:
dependencies:
has-flag "^3.0.0"
vscode-css-languageservice@^4.0.3-next.19:
version "4.0.3-next.19"
resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-4.0.3-next.19.tgz#b7dc58fb8d1968877724e163b6ef20b26c0b5ff6"
integrity sha512-wWSo2MZvd8aEI9lf/Asy0MP0Ao6xAzhBeEWxGcF+Ms/zcV4lDRkyFCNzwDt0OgWRzsBNaEBXfjeWLq9rNXkREA==
vscode-css-languageservice@^4.0.3-next.20:
version "4.0.3-next.20"
resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-4.0.3-next.20.tgz#d0c4933c33e356617ccfaeb6b7144be196b8b3d9"
integrity sha512-9j32ppmJauPg9XYieAS6alCGazTaeEi8nhSAb11le5qfFNWep4Ngz7dNc/UMlt6hB7BESRyk2B8MM/KTlnsNBw==
dependencies:
vscode-languageserver-textdocument "^1.0.0-next.4"
vscode-languageserver-types "^3.15.0-next.6"

View file

@ -9,7 +9,7 @@
},
"main": "./out/htmlServerMain",
"dependencies": {
"vscode-css-languageservice": "^4.0.3-next.19",
"vscode-css-languageservice": "^4.0.3-next.20",
"vscode-html-languageservice": "^3.0.4-next.8",
"vscode-languageserver": "^6.0.0-next.3",
"vscode-nls": "^4.1.1",

View file

@ -611,10 +611,10 @@ supports-color@^5.3.0:
dependencies:
has-flag "^3.0.0"
vscode-css-languageservice@^4.0.3-next.19:
version "4.0.3-next.19"
resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-4.0.3-next.19.tgz#b7dc58fb8d1968877724e163b6ef20b26c0b5ff6"
integrity sha512-wWSo2MZvd8aEI9lf/Asy0MP0Ao6xAzhBeEWxGcF+Ms/zcV4lDRkyFCNzwDt0OgWRzsBNaEBXfjeWLq9rNXkREA==
vscode-css-languageservice@^4.0.3-next.20:
version "4.0.3-next.20"
resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-4.0.3-next.20.tgz#d0c4933c33e356617ccfaeb6b7144be196b8b3d9"
integrity sha512-9j32ppmJauPg9XYieAS6alCGazTaeEi8nhSAb11le5qfFNWep4Ngz7dNc/UMlt6hB7BESRyk2B8MM/KTlnsNBw==
dependencies:
vscode-languageserver-textdocument "^1.0.0-next.4"
vscode-languageserver-types "^3.15.0-next.6"

View file

@ -54,9 +54,11 @@ function registerMarkdownLanguageFeatures(
{ language: 'markdown', scheme: 'untitled' }
];
const charPattern = '(\\p{Alphabetic}|\\p{Number}|\\p{Nonspacing_Mark})';
return vscode.Disposable.from(
vscode.languages.setLanguageConfiguration('markdown', {
wordPattern: new RegExp('(\\p{Alphabetic}|\\p{Number}|\\p{Nonspacing_Mark})+', 'ug'),
wordPattern: new RegExp(`${charPattern}((${charPattern}|[_])?${charPattern})*`, 'ug'),
}),
vscode.languages.registerDocumentSymbolProvider(selector, symbolProvider),
vscode.languages.registerDocumentLinkProvider(selector, new LinkProvider()),

View file

@ -401,7 +401,10 @@ export default class BufferSyncSupport extends Disposable {
this.pendingGetErr?.cancel();
this.pendingDiagnostics.clear();
this.synchronizer.reset();
}
public reinitialize(): void {
this.reset();
for (const buffer of this.syncedBuffers.allBuffers) {
buffer.open();
}

View file

@ -82,6 +82,10 @@ class TypeScriptAutoFixProvider implements vscode.CodeActionProvider {
return undefined;
}
const { edit, fixedDiagnostics } = autoFixResponse;
if (!edit.size) {
return undefined;
}
const codeAction = new vscode.CodeAction(
localize('autoFix.label', 'Auto fix'),
TypeScriptAutoFixProvider.kind);
@ -105,11 +109,11 @@ class TypeScriptAutoFixProvider implements vscode.CodeActionProvider {
};
const response = await this.client.execute('getCodeFixes', args, token);
if (response.type !== 'response' || !response.body || response.body.length > 1) {
return undefined;
continue;
}
const fix = response.body[0];
if (new Set<string>(['fixClassIncorrectlyImplementsInterface', 'spelling']).has(fix.fixName)) {
if (['fixClassIncorrectlyImplementsInterface', 'spelling'].includes(fix.fixName)) {
typeConverters.WorkspaceEdit.withFileCodeEdits(edit, this.client, fix.changes);
fixedDiagnostics.push(diagnostic);
}

View file

@ -182,8 +182,6 @@ export default class TypeScriptServiceClientHost extends Disposable {
private populateService(): void {
this.fileConfigurationManager.reset();
this.client.bufferSyncSupport.reset();
this.client.bufferSyncSupport.requestAllDiagnostics();
// See https://github.com/Microsoft/TypeScript/issues/5530
vscode.workspace.saveAll(false).then(() => {

View file

@ -465,6 +465,8 @@ export default class TypeScriptServiceClient extends Disposable implements IType
}
private serviceStarted(resendModels: boolean): void {
this.bufferSyncSupport.reset();
const configureOptions: Proto.ConfigureRequestArguments = {
hostInfo: 'vscode',
preferences: {
@ -476,6 +478,8 @@ export default class TypeScriptServiceClient extends Disposable implements IType
this.setCompilerOptionsForInferredProjects(this._configuration);
if (resendModels) {
this._onResendModelsRequested.fire();
this.bufferSyncSupport.reinitialize();
this.bufferSyncSupport.requestAllDiagnostics();
}
// Reconfigure any plugins

View file

@ -25,7 +25,7 @@
"smoketest": "cd test/smoke && node test/index.js",
"download-builtin-extensions": "node build/lib/builtInExtensions.js",
"monaco-compile-check": "tsc -p src/tsconfig.monaco.json --noEmit",
"strict-initialization-watch": "tsc --watch -p src/tsconfig.json --noEmit --strictPropertyInitialization",
"strict-function-types-watch": "tsc --watch -p src/tsconfig.json --noEmit --strictFunctionTypes",
"update-distro": "node build/npm/update-distro.js",
"web": "node scripts/code-web.js"
},
@ -45,7 +45,7 @@
"onigasm-umd": "^2.2.4",
"semver-umd": "^5.5.3",
"spdlog": "^0.11.1",
"sudo-prompt": "9.1.0",
"sudo-prompt": "9.1.1",
"v8-inspect-profiler": "^0.0.20",
"vscode-minimist": "^1.2.2",
"vscode-nsfw": "1.2.8",

View file

@ -55,5 +55,6 @@ export const BrowserFeatures = {
return KeyboardSupport.None;
})(),
touch: 'ontouchstart' in window || navigator.maxTouchPoints > 0 || window.navigator.msMaxTouchPoints > 0
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)
};

View file

@ -281,6 +281,21 @@ export function addDisposableNonBubblingMouseOutListener(node: Element, handler:
});
}
export function addDisposableNonBubblingPointerOutListener(node: Element, handler: (event: MouseEvent) => void): IDisposable {
return addDisposableListener(node, 'pointerout', (e: MouseEvent) => {
// Mouse out bubbles, so this is an attempt to ignore faux mouse outs coming from children elements
let toElement: Node | null = <Node>(e.relatedTarget || e.target);
while (toElement && toElement !== node) {
toElement = toElement.parentNode;
}
if (toElement === node) {
return;
}
handler(e);
});
}
interface IRequestAnimationFrame {
(callback: (time: number) => void): number;
}
@ -428,7 +443,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;
};
@ -852,6 +867,8 @@ export const EventType = {
MOUSE_OUT: 'mouseout',
MOUSE_ENTER: 'mouseenter',
MOUSE_LEAVE: 'mouseleave',
POINTER_UP: 'pointerup',
POINTER_DOWN: 'pointerdown',
CONTEXT_MENU: 'contextmenu',
WHEEL: 'wheel',
// Keyboard

View file

@ -7,6 +7,7 @@ import * as dom from 'vs/base/browser/dom';
import { IframeUtils } from 'vs/base/browser/iframe';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { BrowserFeatures } from 'vs/base/browser/canIUse';
export interface IStandardMouseMoveEventData {
leftButton: boolean;
@ -15,7 +16,7 @@ export interface IStandardMouseMoveEventData {
}
export interface IEventMerger<R> {
(lastEvent: R, currentEvent: MouseEvent): R;
(lastEvent: R | null, currentEvent: MouseEvent): R;
}
export interface IMouseMoveCallback<R> {
@ -26,7 +27,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 {
@ -38,10 +39,10 @@ export function standardMouseMoveMerger(lastEvent: IStandardMouseMoveEventData,
export class GlobalMouseMoveMonitor<R> implements IDisposable {
private readonly hooks = new DisposableStore();
private mouseMoveEventMerger: IEventMerger<R> | null = null;
private mouseMoveCallback: IMouseMoveCallback<R> | null = null;
private onStopCallback: IOnStopCallback | null = null;
protected readonly hooks = new DisposableStore();
protected mouseMoveEventMerger: IEventMerger<R> | null = null;
protected mouseMoveCallback: IMouseMoveCallback<R> | null = null;
protected onStopCallback: IOnStopCallback | null = null;
public dispose(): void {
this.stopMonitoring(false);
@ -84,12 +85,14 @@ 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';
for (const element of windowChain) {
this.hooks.add(dom.addDisposableThrottledListener(element.window.document, 'mousemove',
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)));
this.hooks.add(dom.addDisposableListener(element.window.document, mouseUp, (e: MouseEvent) => this.stopMonitoring(true)));
}
if (IframeUtils.hasDifferentOriginAncestor()) {

View file

@ -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

View file

@ -7,6 +7,7 @@ import 'vs/css!./contextview';
import * as DOM from 'vs/base/browser/dom';
import { IDisposable, toDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle';
import { Range } from 'vs/base/common/range';
import { BrowserFeatures } from 'vs/base/browser/canIUse';
export interface IAnchor {
x: number;
@ -178,7 +179,7 @@ export class ContextView extends Disposable {
return;
}
if (this.delegate!.canRelayout === false) {
if (this.delegate!.canRelayout === false && !BrowserFeatures.pointerEvents) {
this.hide();
return;
}

View file

@ -543,7 +543,7 @@ export function fuzzyScore(pattern: string, patternLow: string, patternStart: nu
const patternLen = pattern.length > _maxLen ? _maxLen : pattern.length;
const wordLen = word.length > _maxLen ? _maxLen : word.length;
if (patternStart >= patternLen || wordStart >= wordLen || patternLen > wordLen) {
if (patternStart >= patternLen || wordStart >= wordLen || (patternLen - patternStart) > (wordLen - wordStart)) {
return undefined;
}

View file

@ -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;
}

View file

@ -498,4 +498,9 @@ suite('Filters', () => {
fuzzyScore
);
});
test('"Go to Symbol" with the exact method name doesn\'t work as expected #84787', function () {
const match = fuzzyScore(':get', ':get', 1, 'get', 'get', 0, true);
assert.ok(Boolean(match));
});
});

View file

@ -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 {

View file

@ -26,8 +26,8 @@ import { EditorOption } from 'vs/editor/common/config/editorOptions';
/**
* Merges mouse events when mouse move events are throttled
*/
function createMouseMoveEventMerger(mouseTargetFactory: MouseTargetFactory | null) {
return function (lastEvent: EditorMouseEvent, currentEvent: EditorMouseEvent): EditorMouseEvent {
export function createMouseMoveEventMerger(mouseTargetFactory: MouseTargetFactory | null) {
return function (lastEvent: EditorMouseEvent | null, currentEvent: EditorMouseEvent): EditorMouseEvent {
let targetIsWidget = false;
if (mouseTargetFactory) {
targetIsWidget = mouseTargetFactory.mouseTargetIsWidget(currentEvent);
@ -71,8 +71,7 @@ export class MouseHandler extends ViewEventHandler {
protected viewHelper: IPointerHandlerHelper;
protected mouseTargetFactory: MouseTargetFactory;
private readonly _asyncFocus: RunOnceScheduler;
private readonly _mouseDownOperation: MouseDownOperation;
protected readonly _mouseDownOperation: MouseDownOperation;
private lastMouseLeaveTime: number;
constructor(context: ViewContext, viewController: ViewController, viewHelper: IPointerHandlerHelper) {
@ -179,7 +178,7 @@ export class MouseHandler extends ViewEventHandler {
});
}
private _onMouseMove(e: EditorMouseEvent): void {
public _onMouseMove(e: EditorMouseEvent): void {
if (this._mouseDownOperation.isActive()) {
// In selection/drag operation
return;
@ -196,7 +195,7 @@ export class MouseHandler extends ViewEventHandler {
});
}
private _onMouseLeave(e: EditorMouseEvent): void {
public _onMouseLeave(e: EditorMouseEvent): void {
this.lastMouseLeaveTime = (new Date()).getTime();
this.viewController.emitMouseLeave({
event: e,

View file

@ -6,18 +6,19 @@
import * as dom from 'vs/base/browser/dom';
import { EventType, Gesture, GestureEvent } from 'vs/base/browser/touch';
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
import { IPointerHandlerHelper, MouseHandler } from 'vs/editor/browser/controller/mouseHandler';
import { IPointerHandlerHelper, MouseHandler, createMouseMoveEventMerger } from 'vs/editor/browser/controller/mouseHandler';
import { IMouseTarget } from 'vs/editor/browser/editorBrowser';
import { EditorMouseEvent } from 'vs/editor/browser/editorDom';
import { EditorMouseEvent, EditorPointerEventFactory } from 'vs/editor/browser/editorDom';
import { ViewController } from 'vs/editor/browser/view/viewController';
import { ViewContext } from 'vs/editor/common/view/viewContext';
import { BrowserFeatures } from 'vs/base/browser/canIUse';
interface IThrottledGestureEvent {
translationX: number;
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
@ -52,7 +53,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')) {
@ -66,7 +67,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);
@ -131,7 +132,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';
@ -144,7 +145,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);
@ -185,6 +186,67 @@ class StandardPointerHandler extends MouseHandler implements IDisposable {
}
}
/**
* Currently only tested on iOS 13/ iPadOS.
*/
export class PointerEventHandler extends MouseHandler {
private _lastPointerType: string;
constructor(context: ViewContext, viewController: ViewController, viewHelper: IPointerHandlerHelper) {
super(context, viewController, viewHelper);
this._register(Gesture.addTarget(this.viewHelper.linesContentDomNode));
this._register(dom.addDisposableListener(this.viewHelper.linesContentDomNode, EventType.Tap, (e) => this.onTap(e)));
this._register(dom.addDisposableListener(this.viewHelper.linesContentDomNode, EventType.Change, (e) => this.onChange(e)));
this._register(dom.addDisposableListener(this.viewHelper.linesContentDomNode, EventType.Contextmenu, (e: MouseEvent) => this._onContextMenu(new EditorMouseEvent(e, this.viewHelper.viewDomNode), false)));
this._lastPointerType = 'mouse';
this.viewHelper.linesContentDomNode.addEventListener('pointerdown', (e: any) => {
const pointerType = <any>e.pointerType;
if (pointerType === 'mouse') {
this._lastPointerType = 'mouse';
return;
} else if (pointerType === 'touch') {
this._lastPointerType = 'touch';
} else {
this._lastPointerType = 'pen';
}
});
// PonterEvents
const pointerEvents = new EditorPointerEventFactory(this.viewHelper.viewDomNode);
this._register(pointerEvents.onPointerMoveThrottled(this.viewHelper.viewDomNode,
(e) => this._onMouseMove(e),
createMouseMoveEventMerger(this.mouseTargetFactory), MouseHandler.MOUSE_MOVE_MINIMUM_TIME));
this._register(pointerEvents.onPointerUp(this.viewHelper.viewDomNode, (e) => this._onMouseUp(e)));
this._register(pointerEvents.onPointerLeave(this.viewHelper.viewDomNode, (e) => this._onMouseLeave(e)));
this._register(pointerEvents.onPointerDown(this.viewHelper.viewDomNode, (e) => this._onMouseDown(e)));
}
private onTap(event: GestureEvent): void {
event.preventDefault();
this.viewHelper.focusTextArea();
const target = this._createMouseTarget(new EditorMouseEvent(event, this.viewHelper.viewDomNode), false);
if (target.position) {
this.viewController.moveTo(target.position);
}
}
private onChange(e: GestureEvent): void {
if (this._lastPointerType === 'touch') {
this._context.viewLayout.deltaScrollNow(-e.translationX, -e.translationY);
}
}
public _onMouseDown(e: EditorMouseEvent): void {
if (this._lastPointerType !== 'touch') {
super._onMouseDown(e);
}
}
}
class TouchHandler extends MouseHandler {
constructor(context: ViewContext, viewController: ViewController, viewHelper: IPointerHandlerHelper) {
@ -221,6 +283,8 @@ 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)) {
this.handler = this._register(new PointerEventHandler(context, viewController, viewHelper));
} else if ((<any>window).TouchEvent) {
this.handler = this._register(new TouchHandler(context, viewController, viewHelper));
} else if (window.navigator.pointerEnabled || (<any>window).PointerEvent) {

View file

@ -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';

View file

@ -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,17 +124,55 @@ 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);
}
}
export class EditorPointerEventFactory {
private readonly _editorViewDomNode: HTMLElement;
constructor(editorViewDomNode: HTMLElement) {
this._editorViewDomNode = editorViewDomNode;
}
private _create(e: MouseEvent): EditorMouseEvent {
return new EditorMouseEvent(e, this._editorViewDomNode);
}
public onPointerUp(target: HTMLElement, callback: (e: EditorMouseEvent) => void): IDisposable {
return dom.addDisposableListener(target, 'pointerup', (e: MouseEvent) => {
callback(this._create(e));
});
}
public onPointerDown(target: HTMLElement, callback: (e: EditorMouseEvent) => void): IDisposable {
return dom.addDisposableListener(target, 'pointerdown', (e: MouseEvent) => {
callback(this._create(e));
});
}
public onPointerLeave(target: HTMLElement, callback: (e: EditorMouseEvent) => void): IDisposable {
return dom.addDisposableNonBubblingPointerOutListener(target, (e: MouseEvent) => {
callback(this._create(e));
});
}
public onPointerMoveThrottled(target: HTMLElement, callback: (e: EditorMouseEvent) => void, merger: EditorMouseEventMerger, minimumTimeMs: number): IDisposable {
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);
}
}
export class GlobalEditorMouseMoveMonitor extends Disposable {
private readonly _editorViewDomNode: HTMLElement;
private readonly _globalMouseMoveMonitor: GlobalMouseMoveMonitor<EditorMouseEvent>;
protected readonly _globalMouseMoveMonitor: GlobalMouseMoveMonitor<EditorMouseEvent>;
private _keydownListener: IDisposable | null;
constructor(editorViewDomNode: HTMLElement) {
@ -157,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));
};

View file

@ -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 });
}

View file

@ -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() {

View file

@ -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);
}
}

View file

@ -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);
}

View file

@ -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);
}
}

View file

@ -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';

View file

@ -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;

View file

@ -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;
}
}

View file

@ -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);

View file

@ -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;
}
}

View file

@ -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
*/

View file

@ -5,6 +5,7 @@
import * as assert from 'assert';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { assertType } from 'vs/base/common/types';
import { URI } from 'vs/base/common/uri';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { Selection } from 'vs/editor/common/core/selection';
@ -55,8 +56,10 @@ suite('CodeActionModel', () => {
const contextKeys = new MockContextKeyService();
const model = disposables.add(new CodeActionModel(editor, markerService, contextKeys, undefined));
disposables.add(model.onDidChangeState((e: CodeActionsState.Triggered) => {
assert.equal(e.trigger.type, 'auto');
disposables.add(model.onDidChangeState((e: CodeActionsState.State) => {
assertType(e.type === CodeActionsState.Type.Triggered);
assert.strictEqual(e.trigger.type, 'auto');
assert.ok(e.actions);
e.actions.then(fixes => {
@ -94,7 +97,9 @@ suite('CodeActionModel', () => {
return new Promise((resolve, reject) => {
const contextKeys = new MockContextKeyService();
const model = disposables.add(new CodeActionModel(editor, markerService, contextKeys, undefined));
disposables.add(model.onDidChangeState((e: CodeActionsState.Triggered) => {
disposables.add(model.onDidChangeState((e: CodeActionsState.State) => {
assertType(e.type === CodeActionsState.Type.Triggered);
assert.equal(e.trigger.type, 'auto');
assert.ok(e.actions);
e.actions.then(fixes => {
@ -130,7 +135,9 @@ suite('CodeActionModel', () => {
await new Promise(resolve => {
const contextKeys = new MockContextKeyService();
const model = disposables.add(new CodeActionModel(editor, markerService, contextKeys, undefined));
disposables.add(model.onDidChangeState((e: CodeActionsState.Triggered) => {
disposables.add(model.onDidChangeState((e: CodeActionsState.State) => {
assertType(e.type === CodeActionsState.Type.Triggered);
assert.equal(e.trigger.type, 'auto');
const selection = <Selection>e.rangeOrSelection;
assert.deepEqual(selection.selectionStartLineNumber, 1);
@ -153,7 +160,9 @@ suite('CodeActionModel', () => {
let triggerCount = 0;
const contextKeys = new MockContextKeyService();
const model = disposables.add(new CodeActionModel(editor, markerService, contextKeys, undefined));
disposables.add(model.onDidChangeState((e: CodeActionsState.Triggered) => {
disposables.add(model.onDidChangeState((e: CodeActionsState.State) => {
assertType(e.type === CodeActionsState.Type.Triggered);
assert.equal(e.trigger.type, 'auto');
++triggerCount;

View file

@ -18,6 +18,8 @@ import { ICommandService } from 'vs/platform/commands/common/commands';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { ICodeLensCache } from 'vs/editor/contrib/codelens/codeLensCache';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
import { createStyleSheet } from 'vs/base/browser/dom';
import { hash } from 'vs/base/common/hash';
export class CodeLensContribution implements editorCommon.IEditorContribution {
@ -27,6 +29,8 @@ export class CodeLensContribution implements editorCommon.IEditorContribution {
private readonly _globalToDispose = new DisposableStore();
private readonly _localToDispose = new DisposableStore();
private readonly _styleElement: HTMLStyleElement;
private readonly _styleClassName: string;
private _lenses: CodeLensWidget[] = [];
private _currentFindCodeLensSymbolsPromise: CancelablePromise<CodeLensModel> | undefined;
private _oldCodeLensModels = new DisposableStore();
@ -53,7 +57,16 @@ export class CodeLensContribution implements editorCommon.IEditorContribution {
}
}));
this._globalToDispose.add(CodeLensProviderRegistry.onDidChange(this._onModelChange, this));
this._globalToDispose.add(this._editor.onDidChangeConfiguration(e => {
if (e.hasChanged(EditorOption.fontInfo)) {
this._updateLensStyle();
}
}));
this._onModelChange();
this._styleClassName = hash(this._editor.getId()).toString(16);
this._styleElement = createStyleSheet();
this._updateLensStyle();
}
dispose(): void {
@ -63,6 +76,15 @@ export class CodeLensContribution implements editorCommon.IEditorContribution {
dispose(this._currentCodeLensModel);
}
private _updateLensStyle(): void {
const options = this._editor.getOptions();
const fontInfo = options.get(EditorOption.fontInfo);
const lineHeight = options.get(EditorOption.lineHeight);
const newStyle = `.monaco-editor .codelens-decoration.${this._styleClassName} { height: ${Math.round(lineHeight * 1.1)}px; line-height: ${lineHeight}px; font-size: ${Math.round(fontInfo.fontSize * 0.9)}px; padding-right: ${Math.round(fontInfo.fontSize * 0.45)}px;}`;
this._styleElement.innerHTML = newStyle;
}
private _localDispose(): void {
if (this._currentFindCodeLensSymbolsPromise) {
this._currentFindCodeLensSymbolsPromise.cancel();
@ -200,13 +222,6 @@ export class CodeLensContribution implements editorCommon.IEditorContribution {
this._disposeAllLenses(undefined, undefined);
}
}));
this._localToDispose.add(this._editor.onDidChangeConfiguration(e => {
if (e.hasChanged(EditorOption.fontInfo)) {
for (const lens of this._lenses) {
lens.updateHeight();
}
}
}));
this._localToDispose.add(this._editor.onMouseUp(e => {
if (e.target.type === editorBrowser.MouseTargetType.CONTENT_WIDGET && e.target.element && e.target.element.tagName === 'A') {
for (const lens of this._lenses) {
@ -222,8 +237,10 @@ export class CodeLensContribution implements editorCommon.IEditorContribution {
}
private _disposeAllLenses(decChangeAccessor: IModelDecorationsChangeAccessor | undefined, viewZoneChangeAccessor: editorBrowser.IViewZoneChangeAccessor | undefined): void {
let helper = new CodeLensHelper();
this._lenses.forEach((lens) => lens.dispose(helper, viewZoneChangeAccessor));
const helper = new CodeLensHelper();
for (const lens of this._lenses) {
lens.dispose(helper, viewZoneChangeAccessor);
}
if (decChangeAccessor) {
helper.commit(decChangeAccessor);
}
@ -276,7 +293,7 @@ export class CodeLensContribution implements editorCommon.IEditorContribution {
groupsIndex++;
codeLensIndex++;
} else {
this._lenses.splice(codeLensIndex, 0, new CodeLensWidget(groups[groupsIndex], this._editor, helper, viewZoneAccessor, () => this._detectVisibleLenses && this._detectVisibleLenses.schedule()));
this._lenses.splice(codeLensIndex, 0, new CodeLensWidget(groups[groupsIndex], this._editor, this._styleClassName, helper, viewZoneAccessor, () => this._detectVisibleLenses && this._detectVisibleLenses.schedule()));
codeLensIndex++;
groupsIndex++;
}
@ -290,7 +307,7 @@ export class CodeLensContribution implements editorCommon.IEditorContribution {
// Create extra symbols
while (groupsIndex < groups.length) {
this._lenses.push(new CodeLensWidget(groups[groupsIndex], this._editor, helper, viewZoneAccessor, () => this._detectVisibleLenses && this._detectVisibleLenses.schedule()));
this._lenses.push(new CodeLensWidget(groups[groupsIndex], this._editor, this._styleClassName, helper, viewZoneAccessor, () => this._detectVisibleLenses && this._detectVisibleLenses.schedule()));
groupsIndex++;
}

View file

@ -27,10 +27,6 @@
cursor: pointer;
}
.monaco-editor .codelens-decoration.invisible-cl {
opacity: 0;
}
@keyframes fadein {
0% { opacity: 0; visibility: visible;}
100% { opacity: 1; }

View file

@ -15,7 +15,6 @@ import { editorCodeLensForeground } from 'vs/editor/common/view/editorColorRegis
import { CodeLensItem } from 'vs/editor/contrib/codelens/codelens';
import { editorActiveLinkForeground } from 'vs/platform/theme/common/colorRegistry';
import { registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
class CodeLensViewZone implements editorBrowser.IViewZone {
@ -61,33 +60,24 @@ class CodeLensContentWidget implements editorBrowser.IContentWidget {
private readonly _commands = new Map<string, Command>();
private _widgetPosition?: editorBrowser.IContentWidgetPosition;
private _isEmpty: boolean = true;
constructor(
editor: editorBrowser.ICodeEditor,
className: string,
symbolRange: Range,
lenses: Array<CodeLens | undefined | null>
) {
this._id = 'codeLensWidget' + (++CodeLensContentWidget._idPool);
this._editor = editor;
this._id = (CodeLensContentWidget._idPool++).toString();
this.setSymbolRange(symbolRange);
this._domNode = document.createElement('span');
this._domNode.className = 'codelens-decoration';
this.updateHeight();
this._domNode.className = `codelens-decoration ${className}`;
this.withCommands(lenses, false);
}
updateHeight(): void {
const options = this._editor.getOptions();
const fontInfo = options.get(EditorOption.fontInfo);
const lineHeight = options.get(EditorOption.lineHeight);
this._domNode.style.height = `${Math.round(lineHeight * 1.1)}px`;
this._domNode.style.lineHeight = `${lineHeight}px`;
this._domNode.style.fontSize = `${Math.round(fontInfo.fontSize * 0.9)}px`;
this._domNode.style.paddingRight = `${Math.round(fontInfo.fontSize * 0.45)}px`;
}
withCommands(lenses: Array<CodeLens | undefined | null>, animate: boolean): void {
this._commands.clear();
@ -119,12 +109,15 @@ class CodeLensContentWidget implements editorBrowser.IContentWidget {
} else {
// symbols and commands
const wasEmpty = this._domNode.innerHTML === '' || this._domNode.innerHTML === '&nbsp;';
this._domNode.innerHTML = innerHtml || '&nbsp;';
if (!innerHtml) {
innerHtml = '&nbsp;';
}
this._domNode.innerHTML = innerHtml;
this._editor.layoutContentWidget(this);
if (wasEmpty && animate) {
if (this._isEmpty && animate) {
dom.addClass(this._domNode, 'fadein');
}
this._isEmpty = false;
}
}
@ -208,6 +201,7 @@ export class CodeLensWidget {
constructor(
data: CodeLensItem[],
editor: editorBrowser.ICodeEditor,
className: string,
helper: CodeLensHelper,
viewZoneChangeAccessor: editorBrowser.IViewZoneChangeAccessor,
updateCallback: Function
@ -236,7 +230,7 @@ export class CodeLensWidget {
});
if (range) {
this._contentWidget = new CodeLensContentWidget(editor, range, lenses);
this._contentWidget = new CodeLensContentWidget(editor, className, range, lenses);
this._viewZone = new CodeLensViewZone(range.startLineNumber - 1, updateCallback);
this._viewZoneId = viewZoneChangeAccessor.addZone(this._viewZone);
@ -306,10 +300,6 @@ export class CodeLensWidget {
}
}
updateHeight(): void {
this._contentWidget.updateHeight();
}
getCommand(link: HTMLLinkElement): Command | undefined {
return this._contentWidget.getCommand(link);
}

View file

@ -14,6 +14,7 @@ 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.$;
@ -150,7 +151,7 @@ class SaturationBox extends Disposable {
this.layout();
this._register(dom.addDisposableListener(this.domNode, dom.EventType.MOUSE_DOWN, e => this.onMouseDown(e)));
this._register(dom.addDisposableListener(this.domNode, BrowserFeatures.pointerEvents ? dom.EventType.POINTER_DOWN : dom.EventType.MOUSE_DOWN, e => this.onMouseDown(e)));
this._register(this.model.onDidChangeColor(this.onDidChangeColor, this));
this.monitor = null;
}
@ -165,7 +166,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, dom.EventType.MOUSE_UP, () => {
const mouseUpListener = dom.addDisposableListener(document, BrowserFeatures.pointerEvents ? dom.EventType.POINTER_UP : dom.EventType.MOUSE_UP, () => {
this._onColorFlushed.fire();
mouseUpListener.dispose();
if (this.monitor) {
@ -250,7 +251,7 @@ abstract class Strip extends Disposable {
this.slider = dom.append(this.domNode, $('.slider'));
this.slider.style.top = `0px`;
this._register(dom.addDisposableListener(this.domNode, dom.EventType.MOUSE_DOWN, e => this.onMouseDown(e)));
this._register(dom.addDisposableListener(this.domNode, BrowserFeatures.pointerEvents ? dom.EventType.POINTER_DOWN : dom.EventType.MOUSE_DOWN, e => this.onMouseDown(e)));
this.layout();
}
@ -272,7 +273,7 @@ abstract class Strip extends Disposable {
monitor.startMonitoring(standardMouseMoveMerger, event => this.onDidChangeTop(event.posy - origin.top), () => null);
const mouseUpListener = dom.addDisposableListener(document, dom.EventType.MOUSE_UP, () => {
const mouseUpListener = dom.addDisposableListener(document, BrowserFeatures.pointerEvents ? dom.EventType.POINTER_UP : dom.EventType.MOUSE_UP, () => {
this._onColorFlushed.fire();
mouseUpListener.dispose();
monitor.stopMonitoring(true);

View file

@ -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);

View file

@ -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 });

View file

@ -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.

View file

@ -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) {

View file

@ -227,6 +227,7 @@ export class ReferenceWidget extends peekView.PeekViewWidget {
this.setModel(undefined);
this._callOnDispose.dispose();
this._disposeOnNewModel.dispose();
this._preview.setModel(null); // drop all view-zones, workaround for https://github.com/microsoft/vscode/issues/84726
dispose(this._preview);
dispose(this._previewNotAvailableMessage);
dispose(this._tree);

View file

@ -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);

View file

@ -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);

View file

@ -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') {

View file

@ -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
}

View file

@ -21,7 +21,11 @@
margin-left: 0.5em;
}
.monaco-editor .peekview-widget .head .peekview-title .meta::before {
.monaco-editor .peekview-widget .head .peekview-title .meta {
white-space: nowrap;
}
.monaco-editor .peekview-widget .head .peekview-title .meta:not(:empty)::before {
content: '-';
padding: 0 0.3em;
}

View file

@ -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;

View file

@ -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'));
});
});

View file

@ -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
});
});

View file

@ -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
});
});

View file

@ -5,7 +5,7 @@
import { Action } from 'vs/base/common/actions';
import { SyncDescriptor0, createSyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { IConstructorSignature2, createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IConstructorSignature2, createDecorator, BrandedService } from 'vs/platform/instantiation/common/instantiation';
import { IKeybindings, KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { ICommandService, ICommandHandler, CommandsRegistry } from 'vs/platform/commands/common/commands';
@ -296,7 +296,13 @@ export class SyncActionDescriptor {
private readonly _keybindingContext: ContextKeyExpr | undefined;
private readonly _keybindingWeight: number | undefined;
constructor(ctor: IConstructorSignature2<string, string, Action>,
public static create<Services extends BrandedService[]>(ctor: { new(id: string, label: string, ...services: Services): Action },
id: string, label: string | undefined, keybindings?: IKeybindings, keybindingContext?: ContextKeyExpr, keybindingWeight?: number
): SyncActionDescriptor {
return new SyncActionDescriptor(ctor as IConstructorSignature2<string, string, Action>, id, label, keybindings, keybindingContext, keybindingWeight);
}
private constructor(ctor: IConstructorSignature2<string, string, Action>,
id: string, label: string | undefined, keybindings?: IKeybindings, keybindingContext?: ContextKeyExpr, keybindingWeight?: number
) {
this._id = id;

View file

@ -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');

View file

@ -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

View file

@ -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;
})

View file

@ -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 + ':');
}
}

View file

@ -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);

View file

@ -446,7 +446,7 @@ export class MainThreadComments extends Disposable implements MainThreadComments
private registerPanel(commentsPanelAlreadyConstructed: boolean) {
if (!commentsPanelAlreadyConstructed) {
Registry.as<PanelRegistry>(PanelExtensions.Panels).registerPanel(new PanelDescriptor(
Registry.as<PanelRegistry>(PanelExtensions.Panels).registerPanel(PanelDescriptor.create(
CommentsPanel,
COMMENTS_PANEL_ID,
COMMENTS_PANEL_TITLE,

View file

@ -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/services/workingCopy/common/workingCopyService';
import { ExtHostContext, ExtHostDocumentSaveParticipantShape, IExtHostContext } from '../common/extHost.protocol';
export interface ICodeActionsOnSaveOptions {

View file

@ -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> {

View file

@ -335,7 +335,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
super(id, `${id}.state`, true, configurationService, layoutService, telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService);
}
}
const viewletDescriptor = new ViewletDescriptor(
const viewletDescriptor = ViewletDescriptor.create(
CustomViewlet,
id,
title,
@ -359,7 +359,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
}
const registry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
registry.registerWorkbenchAction(
new SyncActionDescriptor(OpenCustomViewletAction, id, localize('showViewlet', "Show {0}", title)),
SyncActionDescriptor.create(OpenCustomViewletAction, id, localize('showViewlet', "Show {0}", title)),
`View: Show ${title}`,
localize('view', "View")
);

View file

@ -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) {

View file

@ -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/services/workingCopy/common/workingCopyService';
import { ExtensionActivationReason } from 'vs/workbench/api/common/extHostExtensionActivator';
export interface IEnvironment {
@ -759,7 +759,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>;
}

View file

@ -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/services/workingCopy/common/workingCopyService';
import * as vscode from 'vscode';
import { LinkedList } from 'vs/base/common/linkedList';
import { ILogService } from 'vs/platform/log/common/log';

View file

@ -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);
};

View file

@ -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/services/workingCopy/common/workingCopyService';
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;

View file

@ -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> {

View file

@ -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) {

View file

@ -214,9 +214,9 @@ class LogStorageAction extends Action {
const developerCategory = nls.localize('developer', "Developer");
const registry = Registry.as<IWorkbenchActionRegistry>(Extensions.WorkbenchActions);
registry.registerWorkbenchAction(new SyncActionDescriptor(InspectContextKeysAction, InspectContextKeysAction.ID, InspectContextKeysAction.LABEL), 'Developer: Inspect Context Keys', developerCategory);
registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleScreencastModeAction, ToggleScreencastModeAction.ID, ToggleScreencastModeAction.LABEL), 'Developer: Toggle Screencast Mode', developerCategory);
registry.registerWorkbenchAction(new SyncActionDescriptor(LogStorageAction, LogStorageAction.ID, LogStorageAction.LABEL), 'Developer: Log Storage Database Contents', developerCategory);
registry.registerWorkbenchAction(SyncActionDescriptor.create(InspectContextKeysAction, InspectContextKeysAction.ID, InspectContextKeysAction.LABEL), 'Developer: Inspect Context Keys', developerCategory);
registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleScreencastModeAction, ToggleScreencastModeAction.ID, ToggleScreencastModeAction.LABEL), 'Developer: Toggle Screencast Mode', developerCategory);
registry.registerWorkbenchAction(SyncActionDescriptor.create(LogStorageAction, LogStorageAction.ID, LogStorageAction.LABEL), 'Developer: Log Storage Database Contents', developerCategory);
// Screencast Mode
const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);

View file

@ -248,39 +248,39 @@ const registry = Registry.as<IWorkbenchActionRegistry>(Extensions.WorkbenchActio
const helpCategory = nls.localize('help', "Help");
if (KeybindingsReferenceAction.AVAILABLE) {
registry.registerWorkbenchAction(new SyncActionDescriptor(KeybindingsReferenceAction, KeybindingsReferenceAction.ID, KeybindingsReferenceAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_R) }), 'Help: Keyboard Shortcuts Reference', helpCategory);
registry.registerWorkbenchAction(SyncActionDescriptor.create(KeybindingsReferenceAction, KeybindingsReferenceAction.ID, KeybindingsReferenceAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_R) }), 'Help: Keyboard Shortcuts Reference', helpCategory);
}
if (OpenDocumentationUrlAction.AVAILABLE) {
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenDocumentationUrlAction, OpenDocumentationUrlAction.ID, OpenDocumentationUrlAction.LABEL), 'Help: Documentation', helpCategory);
registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenDocumentationUrlAction, OpenDocumentationUrlAction.ID, OpenDocumentationUrlAction.LABEL), 'Help: Documentation', helpCategory);
}
if (OpenIntroductoryVideosUrlAction.AVAILABLE) {
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenIntroductoryVideosUrlAction, OpenIntroductoryVideosUrlAction.ID, OpenIntroductoryVideosUrlAction.LABEL), 'Help: Introductory Videos', helpCategory);
registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenIntroductoryVideosUrlAction, OpenIntroductoryVideosUrlAction.ID, OpenIntroductoryVideosUrlAction.LABEL), 'Help: Introductory Videos', helpCategory);
}
if (OpenTipsAndTricksUrlAction.AVAILABLE) {
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenTipsAndTricksUrlAction, OpenTipsAndTricksUrlAction.ID, OpenTipsAndTricksUrlAction.LABEL), 'Help: Tips and Tricks', helpCategory);
registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenTipsAndTricksUrlAction, OpenTipsAndTricksUrlAction.ID, OpenTipsAndTricksUrlAction.LABEL), 'Help: Tips and Tricks', helpCategory);
}
if (OpenNewsletterSignupUrlAction.AVAILABLE) {
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenNewsletterSignupUrlAction, OpenNewsletterSignupUrlAction.ID, OpenNewsletterSignupUrlAction.LABEL), 'Help: Tips and Tricks', helpCategory);
registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenNewsletterSignupUrlAction, OpenNewsletterSignupUrlAction.ID, OpenNewsletterSignupUrlAction.LABEL), 'Help: Tips and Tricks', helpCategory);
}
if (OpenTwitterUrlAction.AVAILABLE) {
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenTwitterUrlAction, OpenTwitterUrlAction.ID, OpenTwitterUrlAction.LABEL), 'Help: Join Us on Twitter', helpCategory);
registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenTwitterUrlAction, OpenTwitterUrlAction.ID, OpenTwitterUrlAction.LABEL), 'Help: Join Us on Twitter', helpCategory);
}
if (OpenRequestFeatureUrlAction.AVAILABLE) {
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenRequestFeatureUrlAction, OpenRequestFeatureUrlAction.ID, OpenRequestFeatureUrlAction.LABEL), 'Help: Search Feature Requests', helpCategory);
registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenRequestFeatureUrlAction, OpenRequestFeatureUrlAction.ID, OpenRequestFeatureUrlAction.LABEL), 'Help: Search Feature Requests', helpCategory);
}
if (OpenLicenseUrlAction.AVAILABLE) {
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenLicenseUrlAction, OpenLicenseUrlAction.ID, OpenLicenseUrlAction.LABEL), 'Help: View License', helpCategory);
registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenLicenseUrlAction, OpenLicenseUrlAction.ID, OpenLicenseUrlAction.LABEL), 'Help: View License', helpCategory);
}
if (OpenPrivacyStatementUrlAction.AVAILABE) {
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenPrivacyStatementUrlAction, OpenPrivacyStatementUrlAction.ID, OpenPrivacyStatementUrlAction.LABEL), 'Help: Privacy Statement', helpCategory);
registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenPrivacyStatementUrlAction, OpenPrivacyStatementUrlAction.ID, OpenPrivacyStatementUrlAction.LABEL), 'Help: Privacy Statement', helpCategory);
}
// --- Menu Registration

View file

@ -56,7 +56,7 @@ export class ToggleActivityBarVisibilityAction extends Action {
}
}
registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleActivityBarVisibilityAction, ToggleActivityBarVisibilityAction.ID, ToggleActivityBarVisibilityAction.LABEL), 'View: Toggle Activity Bar Visibility', viewCategory);
registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleActivityBarVisibilityAction, ToggleActivityBarVisibilityAction.ID, ToggleActivityBarVisibilityAction.LABEL), 'View: Toggle Activity Bar Visibility', viewCategory);
MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, {
group: '2_workbench_layout',
@ -91,7 +91,7 @@ class ToggleCenteredLayout extends Action {
}
}
registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleCenteredLayout, ToggleCenteredLayout.ID, ToggleCenteredLayout.LABEL), 'View: Toggle Centered Layout', viewCategory);
registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleCenteredLayout, ToggleCenteredLayout.ID, ToggleCenteredLayout.LABEL), 'View: Toggle Centered Layout', viewCategory);
MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, {
group: '1_toggle_view',
@ -143,7 +143,7 @@ export class ToggleEditorLayoutAction extends Action {
}
const group = viewCategory;
registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleEditorLayoutAction, ToggleEditorLayoutAction.ID, ToggleEditorLayoutAction.LABEL, { primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_0, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_0 } }), 'View: Toggle Vertical/Horizontal Editor Layout', group);
registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleEditorLayoutAction, ToggleEditorLayoutAction.ID, ToggleEditorLayoutAction.LABEL, { primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_0, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_0 } }), 'View: Toggle Vertical/Horizontal Editor Layout', group);
MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, {
group: 'z_flip',
@ -186,7 +186,7 @@ export class ToggleSidebarPositionAction extends Action {
}
}
registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleSidebarPositionAction, ToggleSidebarPositionAction.ID, ToggleSidebarPositionAction.LABEL), 'View: Toggle Side Bar Position', viewCategory);
registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleSidebarPositionAction, ToggleSidebarPositionAction.ID, ToggleSidebarPositionAction.LABEL), 'View: Toggle Side Bar Position', viewCategory);
MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, {
group: '3_workbench_layout_move',
@ -231,7 +231,7 @@ export class ToggleEditorVisibilityAction extends Action {
}
}
registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleEditorVisibilityAction, ToggleEditorVisibilityAction.ID, ToggleEditorVisibilityAction.LABEL), 'View: Toggle Editor Area Visibility', viewCategory);
registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleEditorVisibilityAction, ToggleEditorVisibilityAction.ID, ToggleEditorVisibilityAction.LABEL), 'View: Toggle Editor Area Visibility', viewCategory);
MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, {
group: '2_workbench_layout',
@ -266,7 +266,7 @@ export class ToggleSidebarVisibilityAction extends Action {
}
}
registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleSidebarVisibilityAction, ToggleSidebarVisibilityAction.ID, ToggleSidebarVisibilityAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_B }), 'View: Toggle Side Bar Visibility', viewCategory);
registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleSidebarVisibilityAction, ToggleSidebarVisibilityAction.ID, ToggleSidebarVisibilityAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_B }), 'View: Toggle Side Bar Visibility', viewCategory);
MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, {
group: '2_appearance',
@ -313,7 +313,7 @@ export class ToggleStatusbarVisibilityAction extends Action {
}
}
registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleStatusbarVisibilityAction, ToggleStatusbarVisibilityAction.ID, ToggleStatusbarVisibilityAction.LABEL), 'View: Toggle Status Bar Visibility', viewCategory);
registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleStatusbarVisibilityAction, ToggleStatusbarVisibilityAction.ID, ToggleStatusbarVisibilityAction.LABEL), 'View: Toggle Status Bar Visibility', viewCategory);
MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, {
group: '2_workbench_layout',
@ -350,7 +350,7 @@ class ToggleTabsVisibilityAction extends Action {
}
}
registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleTabsVisibilityAction, ToggleTabsVisibilityAction.ID, ToggleTabsVisibilityAction.LABEL, {
registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleTabsVisibilityAction, ToggleTabsVisibilityAction.ID, ToggleTabsVisibilityAction.LABEL, {
primary: undefined,
mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_W, },
linux: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_W, }
@ -379,7 +379,7 @@ class ToggleZenMode extends Action {
}
}
registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleZenMode, ToggleZenMode.ID, ToggleZenMode.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_Z) }), 'View: Toggle Zen Mode', viewCategory);
registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleZenMode, ToggleZenMode.ID, ToggleZenMode.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_Z) }), 'View: Toggle Zen Mode', viewCategory);
MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, {
group: '1_toggle_view',
@ -442,7 +442,7 @@ export class ToggleMenuBarAction extends Action {
}
if (isWindows || isLinux || isWeb) {
registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleMenuBarAction, ToggleMenuBarAction.ID, ToggleMenuBarAction.LABEL), 'View: Toggle Menu Bar', viewCategory);
registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleMenuBarAction, ToggleMenuBarAction.ID, ToggleMenuBarAction.LABEL), 'View: Toggle Menu Bar', viewCategory);
}
MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, {
@ -529,5 +529,5 @@ export class DecreaseViewSizeAction extends BaseResizeViewAction {
}
}
registry.registerWorkbenchAction(new SyncActionDescriptor(IncreaseViewSizeAction, IncreaseViewSizeAction.ID, IncreaseViewSizeAction.LABEL, undefined), 'View: Increase Current View Size', viewCategory);
registry.registerWorkbenchAction(new SyncActionDescriptor(DecreaseViewSizeAction, DecreaseViewSizeAction.ID, DecreaseViewSizeAction.LABEL, undefined), 'View: Decrease Current View Size', viewCategory);
registry.registerWorkbenchAction(SyncActionDescriptor.create(IncreaseViewSizeAction, IncreaseViewSizeAction.ID, IncreaseViewSizeAction.LABEL, undefined), 'View: Increase Current View Size', viewCategory);
registry.registerWorkbenchAction(SyncActionDescriptor.create(DecreaseViewSizeAction, DecreaseViewSizeAction.ID, DecreaseViewSizeAction.LABEL, undefined), 'View: Decrease Current View Size', viewCategory);

View file

@ -280,7 +280,7 @@ class NavigateDownAction extends BaseNavigationAction {
const registry = Registry.as<IWorkbenchActionRegistry>(Extensions.WorkbenchActions);
const viewCategory = nls.localize('view', "View");
registry.registerWorkbenchAction(new SyncActionDescriptor(NavigateUpAction, NavigateUpAction.ID, NavigateUpAction.LABEL, undefined), 'View: Navigate to the View Above', viewCategory);
registry.registerWorkbenchAction(new SyncActionDescriptor(NavigateDownAction, NavigateDownAction.ID, NavigateDownAction.LABEL, undefined), 'View: Navigate to the View Below', viewCategory);
registry.registerWorkbenchAction(new SyncActionDescriptor(NavigateLeftAction, NavigateLeftAction.ID, NavigateLeftAction.LABEL, undefined), 'View: Navigate to the View on the Left', viewCategory);
registry.registerWorkbenchAction(new SyncActionDescriptor(NavigateRightAction, NavigateRightAction.ID, NavigateRightAction.LABEL, undefined), 'View: Navigate to the View on the Right', viewCategory);
registry.registerWorkbenchAction(SyncActionDescriptor.create(NavigateUpAction, NavigateUpAction.ID, NavigateUpAction.LABEL, undefined), 'View: Navigate to the View Above', viewCategory);
registry.registerWorkbenchAction(SyncActionDescriptor.create(NavigateDownAction, NavigateDownAction.ID, NavigateDownAction.LABEL, undefined), 'View: Navigate to the View Below', viewCategory);
registry.registerWorkbenchAction(SyncActionDescriptor.create(NavigateLeftAction, NavigateLeftAction.ID, NavigateLeftAction.LABEL, undefined), 'View: Navigate to the View on the Left', viewCategory);
registry.registerWorkbenchAction(SyncActionDescriptor.create(NavigateRightAction, NavigateRightAction.ID, NavigateRightAction.LABEL, undefined), 'View: Navigate to the View on the Right', viewCategory);

View file

@ -269,18 +269,18 @@ const registry = Registry.as<IWorkbenchActionRegistry>(Extensions.WorkbenchActio
// --- Actions Registration
const fileCategory = nls.localize('file', "File");
registry.registerWorkbenchAction(new SyncActionDescriptor(NewWindowAction, NewWindowAction.ID, NewWindowAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_N }), 'New Window');
registry.registerWorkbenchAction(new SyncActionDescriptor(QuickOpenRecentAction, QuickOpenRecentAction.ID, QuickOpenRecentAction.LABEL), 'File: Quick Open Recent...', fileCategory);
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenRecentAction, OpenRecentAction.ID, OpenRecentAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_R, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_R } }), 'File: Open Recent...', fileCategory);
registry.registerWorkbenchAction(SyncActionDescriptor.create(NewWindowAction, NewWindowAction.ID, NewWindowAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_N }), 'New Window');
registry.registerWorkbenchAction(SyncActionDescriptor.create(QuickOpenRecentAction, QuickOpenRecentAction.ID, QuickOpenRecentAction.LABEL), 'File: Quick Open Recent...', fileCategory);
registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenRecentAction, OpenRecentAction.ID, OpenRecentAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_R, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_R } }), 'File: Open Recent...', fileCategory);
const viewCategory = nls.localize('view', "View");
registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleFullScreenAction, ToggleFullScreenAction.ID, ToggleFullScreenAction.LABEL, { primary: KeyCode.F11, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_F } }), 'View: Toggle Full Screen', viewCategory);
registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleFullScreenAction, ToggleFullScreenAction.ID, ToggleFullScreenAction.LABEL, { primary: KeyCode.F11, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_F } }), 'View: Toggle Full Screen', viewCategory);
const developerCategory = nls.localize('developer', "Developer");
registry.registerWorkbenchAction(new SyncActionDescriptor(ReloadWindowAction, ReloadWindowAction.ID, ReloadWindowAction.LABEL), 'Developer: Reload Window', developerCategory);
registry.registerWorkbenchAction(SyncActionDescriptor.create(ReloadWindowAction, ReloadWindowAction.ID, ReloadWindowAction.LABEL), 'Developer: Reload Window', developerCategory);
const helpCategory = nls.localize('help', "Help");
registry.registerWorkbenchAction(new SyncActionDescriptor(ShowAboutDialogAction, ShowAboutDialogAction.ID, ShowAboutDialogAction.LABEL), `Help: About`, helpCategory);
registry.registerWorkbenchAction(SyncActionDescriptor.create(ShowAboutDialogAction, ShowAboutDialogAction.ID, ShowAboutDialogAction.LABEL), `Help: About`, helpCategory);
// --- Commands/Keybindings Registration

View file

@ -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:
@ -259,11 +259,11 @@ export class DuplicateWorkspaceInNewWindowAction extends Action {
const registry = Registry.as<IWorkbenchActionRegistry>(Extensions.WorkbenchActions);
const workspacesCategory = nls.localize('workspaces', "Workspaces");
registry.registerWorkbenchAction(new SyncActionDescriptor(AddRootFolderAction, AddRootFolderAction.ID, AddRootFolderAction.LABEL), 'Workspaces: Add Folder to Workspace...', workspacesCategory);
registry.registerWorkbenchAction(new SyncActionDescriptor(GlobalRemoveRootFolderAction, GlobalRemoveRootFolderAction.ID, GlobalRemoveRootFolderAction.LABEL), 'Workspaces: Remove Folder from Workspace...', workspacesCategory);
registry.registerWorkbenchAction(new SyncActionDescriptor(CloseWorkspaceAction, CloseWorkspaceAction.ID, CloseWorkspaceAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_F) }), 'Workspaces: Close Workspace', workspacesCategory);
registry.registerWorkbenchAction(new SyncActionDescriptor(SaveWorkspaceAsAction, SaveWorkspaceAsAction.ID, SaveWorkspaceAsAction.LABEL), 'Workspaces: Save Workspace As...', workspacesCategory);
registry.registerWorkbenchAction(new SyncActionDescriptor(DuplicateWorkspaceInNewWindowAction, DuplicateWorkspaceInNewWindowAction.ID, DuplicateWorkspaceInNewWindowAction.LABEL), 'Workspaces: Duplicate Workspace in New Window', workspacesCategory);
registry.registerWorkbenchAction(SyncActionDescriptor.create(AddRootFolderAction, AddRootFolderAction.ID, AddRootFolderAction.LABEL), 'Workspaces: Add Folder to Workspace...', workspacesCategory);
registry.registerWorkbenchAction(SyncActionDescriptor.create(GlobalRemoveRootFolderAction, GlobalRemoveRootFolderAction.ID, GlobalRemoveRootFolderAction.LABEL), 'Workspaces: Remove Folder from Workspace...', workspacesCategory);
registry.registerWorkbenchAction(SyncActionDescriptor.create(CloseWorkspaceAction, CloseWorkspaceAction.ID, CloseWorkspaceAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_F) }), 'Workspaces: Close Workspace', workspacesCategory);
registry.registerWorkbenchAction(SyncActionDescriptor.create(SaveWorkspaceAsAction, SaveWorkspaceAsAction.ID, SaveWorkspaceAsAction.LABEL), 'Workspaces: Save Workspace As...', workspacesCategory);
registry.registerWorkbenchAction(SyncActionDescriptor.create(DuplicateWorkspaceInNewWindowAction, DuplicateWorkspaceInNewWindowAction.ID, DuplicateWorkspaceInNewWindowAction.LABEL), 'Workspaces: Duplicate Workspace in New Window', workspacesCategory);
// --- Menu Registration

View file

@ -7,7 +7,7 @@ import { EditorInput } from 'vs/workbench/common/editor';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { Registry } from 'vs/platform/registry/common/platform';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { IConstructorSignature0, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IConstructorSignature0, IInstantiationService, BrandedService } from 'vs/platform/instantiation/common/instantiation';
import { find } from 'vs/base/common/arrays';
export interface IEditorDescriptor {
@ -54,6 +54,14 @@ export interface IEditorRegistry {
*/
export class EditorDescriptor implements IEditorDescriptor {
public static create<Services extends BrandedService[]>(
ctor: { new(...services: Services): BaseEditor },
id: string,
name: string
): EditorDescriptor {
return new EditorDescriptor(ctor as IConstructorSignature0<BaseEditor>, id, name);
}
constructor(
private readonly ctor: IConstructorSignature0<BaseEditor>,
private readonly id: string,

View file

@ -9,7 +9,7 @@ import { Composite, CompositeDescriptor, CompositeRegistry } from 'vs/workbench/
import { Action } from 'vs/base/common/actions';
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService';
import { IConstructorSignature0 } from 'vs/platform/instantiation/common/instantiation';
import { IConstructorSignature0, BrandedService } from 'vs/platform/instantiation/common/instantiation';
import { isAncestor } from 'vs/base/browser/dom';
import { assertIsDefined } from 'vs/base/common/types';
@ -20,7 +20,11 @@ export abstract class Panel extends Composite implements IPanel { }
*/
export class PanelDescriptor extends CompositeDescriptor<Panel> {
constructor(ctor: IConstructorSignature0<Panel>, id: string, name: string, cssClass?: string, order?: number, _commandId?: string) {
public static create<Services extends BrandedService[]>(ctor: { new(...services: Services): Panel }, id: string, name: string, cssClass?: string, order?: number, _commandId?: string): PanelDescriptor {
return new PanelDescriptor(ctor as IConstructorSignature0<Panel>, id, name, cssClass, order, _commandId);
}
private constructor(ctor: IConstructorSignature0<Panel>, id: string, name: string, cssClass?: string, order?: number, _commandId?: string) {
super(ctor, id, name, cssClass, order, _commandId);
}
}

View file

@ -267,8 +267,8 @@ export class NextSideBarViewAction extends SwitchSideBarViewAction {
}
const registry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
registry.registerWorkbenchAction(new SyncActionDescriptor(PreviousSideBarViewAction, PreviousSideBarViewAction.ID, PreviousSideBarViewAction.LABEL), 'View: Previous Side Bar View', nls.localize('view', "View"));
registry.registerWorkbenchAction(new SyncActionDescriptor(NextSideBarViewAction, NextSideBarViewAction.ID, NextSideBarViewAction.LABEL), 'View: Next Side Bar View', nls.localize('view', "View"));
registry.registerWorkbenchAction(SyncActionDescriptor.create(PreviousSideBarViewAction, PreviousSideBarViewAction.ID, PreviousSideBarViewAction.LABEL), 'View: Previous Side Bar View', nls.localize('view', "View"));
registry.registerWorkbenchAction(SyncActionDescriptor.create(NextSideBarViewAction, NextSideBarViewAction.ID, NextSideBarViewAction.LABEL), 'View: Next Side Bar View', nls.localize('view', "View"));
registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {

View file

@ -57,7 +57,7 @@ import { IFilesConfigurationService } from 'vs/workbench/services/filesConfigura
// Register String Editor
Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
new EditorDescriptor(
EditorDescriptor.create(
TextResourceEditor,
TextResourceEditor.ID,
nls.localize('textEditor', "Text Editor"),
@ -70,7 +70,7 @@ Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
// Register Text Diff Editor
Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
new EditorDescriptor(
EditorDescriptor.create(
TextDiffEditor,
TextDiffEditor.ID,
nls.localize('textDiffEditor', "Text Diff Editor")
@ -82,7 +82,7 @@ Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
// Register Binary Resource Diff Editor
Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
new EditorDescriptor(
EditorDescriptor.create(
BinaryResourceDiffEditor,
BinaryResourceDiffEditor.ID,
nls.localize('binaryDiffEditor', "Binary Diff Editor")
@ -93,7 +93,7 @@ Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
);
Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
new EditorDescriptor(
EditorDescriptor.create(
SideBySideEditor,
SideBySideEditor.ID,
nls.localize('sideBySideEditor', "Side by Side Editor")
@ -226,11 +226,11 @@ Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).regi
// Register Status Actions
const registry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
registry.registerWorkbenchAction(new SyncActionDescriptor(ChangeModeAction, ChangeModeAction.ID, ChangeModeAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_M) }), 'Change Language Mode');
registry.registerWorkbenchAction(new SyncActionDescriptor(ChangeEOLAction, ChangeEOLAction.ID, ChangeEOLAction.LABEL), 'Change End of Line Sequence');
registry.registerWorkbenchAction(SyncActionDescriptor.create(ChangeModeAction, ChangeModeAction.ID, ChangeModeAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_M) }), 'Change Language Mode');
registry.registerWorkbenchAction(SyncActionDescriptor.create(ChangeEOLAction, ChangeEOLAction.ID, ChangeEOLAction.LABEL), 'Change End of Line Sequence');
if (Object.keys(SUPPORTED_ENCODINGS).length > 1) {
registry.registerWorkbenchAction(new SyncActionDescriptor(ChangeEncodingAction, ChangeEncodingAction.ID, ChangeEncodingAction.LABEL), 'Change File Encoding');
registry.registerWorkbenchAction(SyncActionDescriptor.create(ChangeEncodingAction, ChangeEncodingAction.ID, ChangeEncodingAction.LABEL), 'Change File Encoding');
}
export class QuickOpenActionContributor extends ActionBarContributor {
@ -279,7 +279,7 @@ const editorPickerContextKey = 'inEditorsPicker';
const editorPickerContext = ContextKeyExpr.and(inQuickOpenContext, ContextKeyExpr.has(editorPickerContextKey));
Registry.as<IQuickOpenRegistry>(QuickOpenExtensions.Quickopen).registerQuickOpenHandler(
new QuickOpenHandlerDescriptor(
QuickOpenHandlerDescriptor.create(
ActiveEditorGroupPicker,
ActiveEditorGroupPicker.ID,
editorCommands.NAVIGATE_IN_ACTIVE_GROUP_PREFIX,
@ -295,7 +295,7 @@ Registry.as<IQuickOpenRegistry>(QuickOpenExtensions.Quickopen).registerQuickOpen
);
Registry.as<IQuickOpenRegistry>(QuickOpenExtensions.Quickopen).registerQuickOpenHandler(
new QuickOpenHandlerDescriptor(
QuickOpenHandlerDescriptor.create(
AllEditorsPicker,
AllEditorsPicker.ID,
editorCommands.NAVIGATE_ALL_EDITORS_GROUP_PREFIX,
@ -312,84 +312,84 @@ Registry.as<IQuickOpenRegistry>(QuickOpenExtensions.Quickopen).registerQuickOpen
// Register Editor Actions
const category = nls.localize('view', "View");
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenNextEditorInGroup, OpenNextEditorInGroup.ID, OpenNextEditorInGroup.LABEL), 'View: Open Next Editor in Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenPreviousEditorInGroup, OpenPreviousEditorInGroup.ID, OpenPreviousEditorInGroup.LABEL), 'View: Open Previous Editor in Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenLastEditorInGroup, OpenLastEditorInGroup.ID, OpenLastEditorInGroup.LABEL, { primary: KeyMod.Alt | KeyCode.KEY_0, secondary: [KeyMod.CtrlCmd | KeyCode.KEY_9], mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_0, secondary: [KeyMod.CtrlCmd | KeyCode.KEY_9] } }), 'View: Open Last Editor in Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenFirstEditorInGroup, OpenFirstEditorInGroup.ID, OpenFirstEditorInGroup.LABEL), 'View: Open First Editor in Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenNextRecentlyUsedEditorAction, OpenNextRecentlyUsedEditorAction.ID, OpenNextRecentlyUsedEditorAction.LABEL), 'View: Open Next Recently Used Editor', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenPreviousRecentlyUsedEditorAction, OpenPreviousRecentlyUsedEditorAction.ID, OpenPreviousRecentlyUsedEditorAction.LABEL), 'View: Open Previous Recently Used Editor', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(ShowAllEditorsAction, ShowAllEditorsAction.ID, ShowAllEditorsAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_P), mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Tab } }), 'View: Show All Editors', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(ShowEditorsInActiveGroupAction, ShowEditorsInActiveGroupAction.ID, ShowEditorsInActiveGroupAction.LABEL), 'View: Show Editors in Active Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenNextEditor, OpenNextEditor.ID, OpenNextEditor.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.PageDown, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.RightArrow, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_CLOSE_SQUARE_BRACKET] } }), 'View: Open Next Editor', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenPreviousEditor, OpenPreviousEditor.ID, OpenPreviousEditor.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.PageUp, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.LeftArrow, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_OPEN_SQUARE_BRACKET] } }), 'View: Open Previous Editor', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(ReopenClosedEditorAction, ReopenClosedEditorAction.ID, ReopenClosedEditorAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_T }), 'View: Reopen Closed Editor', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(ClearRecentFilesAction, ClearRecentFilesAction.ID, ClearRecentFilesAction.LABEL), 'File: Clear Recently Opened', nls.localize('file', "File"));
registry.registerWorkbenchAction(new SyncActionDescriptor(CloseAllEditorsAction, CloseAllEditorsAction.ID, CloseAllEditorsAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_W) }), 'View: Close All Editors', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(CloseAllEditorGroupsAction, CloseAllEditorGroupsAction.ID, CloseAllEditorGroupsAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_W) }), 'View: Close All Editor Groups', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(CloseLeftEditorsInGroupAction, CloseLeftEditorsInGroupAction.ID, CloseLeftEditorsInGroupAction.LABEL), 'View: Close Editors to the Left in Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(CloseEditorsInOtherGroupsAction, CloseEditorsInOtherGroupsAction.ID, CloseEditorsInOtherGroupsAction.LABEL), 'View: Close Editors in Other Groups', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(CloseEditorInAllGroupsAction, CloseEditorInAllGroupsAction.ID, CloseEditorInAllGroupsAction.LABEL), 'View: Close Editor in All Groups', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(SplitEditorAction, SplitEditorAction.ID, SplitEditorAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.US_BACKSLASH }), 'View: Split Editor', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(SplitEditorOrthogonalAction, SplitEditorOrthogonalAction.ID, SplitEditorOrthogonalAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.US_BACKSLASH) }), 'View: Split Editor Orthogonal', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(SplitEditorLeftAction, SplitEditorLeftAction.ID, SplitEditorLeftAction.LABEL), 'View: Split Editor Left', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(SplitEditorRightAction, SplitEditorRightAction.ID, SplitEditorRightAction.LABEL), 'View: Split Editor Right', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(SplitEditorUpAction, SplitEditorUpAction.ID, SplitEditorUpAction.LABEL), 'Split Editor Up', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(SplitEditorDownAction, SplitEditorDownAction.ID, SplitEditorDownAction.LABEL), 'View: Split Editor Down', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(JoinTwoGroupsAction, JoinTwoGroupsAction.ID, JoinTwoGroupsAction.LABEL), 'View: Join Editor Group with Next Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(JoinAllGroupsAction, JoinAllGroupsAction.ID, JoinAllGroupsAction.LABEL), 'View: Join All Editor Groups', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(NavigateBetweenGroupsAction, NavigateBetweenGroupsAction.ID, NavigateBetweenGroupsAction.LABEL), 'View: Navigate Between Editor Groups', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(ResetGroupSizesAction, ResetGroupSizesAction.ID, ResetGroupSizesAction.LABEL), 'View: Reset Editor Group Sizes', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleGroupSizesAction, ToggleGroupSizesAction.ID, ToggleGroupSizesAction.LABEL), 'View: Toggle Editor Group Sizes', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(MaximizeGroupAction, MaximizeGroupAction.ID, MaximizeGroupAction.LABEL), 'View: Maximize Editor Group and Hide Side Bar', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(MinimizeOtherGroupsAction, MinimizeOtherGroupsAction.ID, MinimizeOtherGroupsAction.LABEL), 'View: Maximize Editor Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorLeftInGroupAction, MoveEditorLeftInGroupAction.ID, MoveEditorLeftInGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.PageUp, mac: { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.LeftArrow) } }), 'View: Move Editor Left', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorRightInGroupAction, MoveEditorRightInGroupAction.ID, MoveEditorRightInGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.PageDown, mac: { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.RightArrow) } }), 'View: Move Editor Right', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveGroupLeftAction, MoveGroupLeftAction.ID, MoveGroupLeftAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.LeftArrow) }), 'View: Move Editor Group Left', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveGroupRightAction, MoveGroupRightAction.ID, MoveGroupRightAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.RightArrow) }), 'View: Move Editor Group Right', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveGroupUpAction, MoveGroupUpAction.ID, MoveGroupUpAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.UpArrow) }), 'View: Move Editor Group Up', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveGroupDownAction, MoveGroupDownAction.ID, MoveGroupDownAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.DownArrow) }), 'View: Move Editor Group Down', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorToPreviousGroupAction, MoveEditorToPreviousGroupAction.ID, MoveEditorToPreviousGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.LeftArrow, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.LeftArrow } }), 'View: Move Editor into Previous Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorToNextGroupAction, MoveEditorToNextGroupAction.ID, MoveEditorToNextGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.RightArrow, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.RightArrow } }), 'View: Move Editor into Next Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorToFirstGroupAction, MoveEditorToFirstGroupAction.ID, MoveEditorToFirstGroupAction.LABEL, { primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_1, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_1 } }), 'View: Move Editor into First Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorToLastGroupAction, MoveEditorToLastGroupAction.ID, MoveEditorToLastGroupAction.LABEL, { primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_9, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_9 } }), 'View: Move Editor into Last Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorToLeftGroupAction, MoveEditorToLeftGroupAction.ID, MoveEditorToLeftGroupAction.LABEL), 'View: Move Editor into Left Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorToRightGroupAction, MoveEditorToRightGroupAction.ID, MoveEditorToRightGroupAction.LABEL), 'View: Move Editor into Right Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorToAboveGroupAction, MoveEditorToAboveGroupAction.ID, MoveEditorToAboveGroupAction.LABEL), 'View: Move Editor into Above Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorToBelowGroupAction, MoveEditorToBelowGroupAction.ID, MoveEditorToBelowGroupAction.LABEL), 'View: Move Editor into Below Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusActiveGroupAction, FocusActiveGroupAction.ID, FocusActiveGroupAction.LABEL), 'View: Focus Active Editor Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusFirstGroupAction, FocusFirstGroupAction.ID, FocusFirstGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_1 }), 'View: Focus First Editor Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusLastGroupAction, FocusLastGroupAction.ID, FocusLastGroupAction.LABEL), 'View: Focus Last Editor Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusPreviousGroup, FocusPreviousGroup.ID, FocusPreviousGroup.LABEL), 'View: Focus Previous Editor Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusNextGroup, FocusNextGroup.ID, FocusNextGroup.LABEL), 'View: Focus Next Editor Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusLeftGroup, FocusLeftGroup.ID, FocusLeftGroup.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.LeftArrow) }), 'View: Focus Left Editor Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusRightGroup, FocusRightGroup.ID, FocusRightGroup.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.RightArrow) }), 'View: Focus Right Editor Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusAboveGroup, FocusAboveGroup.ID, FocusAboveGroup.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.UpArrow) }), 'View: Focus Above Editor Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusBelowGroup, FocusBelowGroup.ID, FocusBelowGroup.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.DownArrow) }), 'View: Focus Below Editor Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(NewEditorGroupLeftAction, NewEditorGroupLeftAction.ID, NewEditorGroupLeftAction.LABEL), 'View: New Editor Group to the Left', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(NewEditorGroupRightAction, NewEditorGroupRightAction.ID, NewEditorGroupRightAction.LABEL), 'View: New Editor Group to the Right', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(NewEditorGroupAboveAction, NewEditorGroupAboveAction.ID, NewEditorGroupAboveAction.LABEL), 'View: New Editor Group Above', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(NewEditorGroupBelowAction, NewEditorGroupBelowAction.ID, NewEditorGroupBelowAction.LABEL), 'View: New Editor Group Below', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(NavigateForwardAction, NavigateForwardAction.ID, NavigateForwardAction.LABEL, { primary: 0, win: { primary: KeyMod.Alt | KeyCode.RightArrow }, mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.US_MINUS }, linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_MINUS } }), 'Go Forward');
registry.registerWorkbenchAction(new SyncActionDescriptor(NavigateBackwardsAction, NavigateBackwardsAction.ID, NavigateBackwardsAction.LABEL, { primary: 0, win: { primary: KeyMod.Alt | KeyCode.LeftArrow }, mac: { primary: KeyMod.WinCtrl | KeyCode.US_MINUS }, linux: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.US_MINUS } }), 'Go Back');
registry.registerWorkbenchAction(new SyncActionDescriptor(NavigateToLastEditLocationAction, NavigateToLastEditLocationAction.ID, NavigateToLastEditLocationAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_Q) }), 'Go to Last Edit Location');
registry.registerWorkbenchAction(new SyncActionDescriptor(NavigateLastAction, NavigateLastAction.ID, NavigateLastAction.LABEL), 'Go Last');
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenPreviousEditorFromHistoryAction, OpenPreviousEditorFromHistoryAction.ID, OpenPreviousEditorFromHistoryAction.LABEL), 'Open Previous Editor from History');
registry.registerWorkbenchAction(new SyncActionDescriptor(ClearEditorHistoryAction, ClearEditorHistoryAction.ID, ClearEditorHistoryAction.LABEL), 'Clear Editor History');
registry.registerWorkbenchAction(new SyncActionDescriptor(RevertAndCloseEditorAction, RevertAndCloseEditorAction.ID, RevertAndCloseEditorAction.LABEL), 'View: Revert and Close Editor', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(EditorLayoutSingleAction, EditorLayoutSingleAction.ID, EditorLayoutSingleAction.LABEL), 'View: Single Column Editor Layout', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(EditorLayoutTwoColumnsAction, EditorLayoutTwoColumnsAction.ID, EditorLayoutTwoColumnsAction.LABEL), 'View: Two Columns Editor Layout', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(EditorLayoutThreeColumnsAction, EditorLayoutThreeColumnsAction.ID, EditorLayoutThreeColumnsAction.LABEL), 'View: Three Columns Editor Layout', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(EditorLayoutTwoRowsAction, EditorLayoutTwoRowsAction.ID, EditorLayoutTwoRowsAction.LABEL), 'View: Two Rows Editor Layout', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(EditorLayoutThreeRowsAction, EditorLayoutThreeRowsAction.ID, EditorLayoutThreeRowsAction.LABEL), 'View: Three Rows Editor Layout', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(EditorLayoutTwoByTwoGridAction, EditorLayoutTwoByTwoGridAction.ID, EditorLayoutTwoByTwoGridAction.LABEL), 'View: Grid Editor Layout (2x2)', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(EditorLayoutTwoRowsRightAction, EditorLayoutTwoRowsRightAction.ID, EditorLayoutTwoRowsRightAction.LABEL), 'View: Two Rows Right Editor Layout', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(EditorLayoutTwoColumnsBottomAction, EditorLayoutTwoColumnsBottomAction.ID, EditorLayoutTwoColumnsBottomAction.LABEL), 'View: Two Columns Bottom Editor Layout', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenNextEditorInGroup, OpenNextEditorInGroup.ID, OpenNextEditorInGroup.LABEL), 'View: Open Next Editor in Group', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenPreviousEditorInGroup, OpenPreviousEditorInGroup.ID, OpenPreviousEditorInGroup.LABEL), 'View: Open Previous Editor in Group', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenLastEditorInGroup, OpenLastEditorInGroup.ID, OpenLastEditorInGroup.LABEL, { primary: KeyMod.Alt | KeyCode.KEY_0, secondary: [KeyMod.CtrlCmd | KeyCode.KEY_9], mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_0, secondary: [KeyMod.CtrlCmd | KeyCode.KEY_9] } }), 'View: Open Last Editor in Group', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenFirstEditorInGroup, OpenFirstEditorInGroup.ID, OpenFirstEditorInGroup.LABEL), 'View: Open First Editor in Group', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenNextRecentlyUsedEditorAction, OpenNextRecentlyUsedEditorAction.ID, OpenNextRecentlyUsedEditorAction.LABEL), 'View: Open Next Recently Used Editor', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenPreviousRecentlyUsedEditorAction, OpenPreviousRecentlyUsedEditorAction.ID, OpenPreviousRecentlyUsedEditorAction.LABEL), 'View: Open Previous Recently Used Editor', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(ShowAllEditorsAction, ShowAllEditorsAction.ID, ShowAllEditorsAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_P), mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Tab } }), 'View: Show All Editors', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(ShowEditorsInActiveGroupAction, ShowEditorsInActiveGroupAction.ID, ShowEditorsInActiveGroupAction.LABEL), 'View: Show Editors in Active Group', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenNextEditor, OpenNextEditor.ID, OpenNextEditor.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.PageDown, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.RightArrow, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_CLOSE_SQUARE_BRACKET] } }), 'View: Open Next Editor', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenPreviousEditor, OpenPreviousEditor.ID, OpenPreviousEditor.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.PageUp, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.LeftArrow, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_OPEN_SQUARE_BRACKET] } }), 'View: Open Previous Editor', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(ReopenClosedEditorAction, ReopenClosedEditorAction.ID, ReopenClosedEditorAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_T }), 'View: Reopen Closed Editor', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(ClearRecentFilesAction, ClearRecentFilesAction.ID, ClearRecentFilesAction.LABEL), 'File: Clear Recently Opened', nls.localize('file', "File"));
registry.registerWorkbenchAction(SyncActionDescriptor.create(CloseAllEditorsAction, CloseAllEditorsAction.ID, CloseAllEditorsAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_W) }), 'View: Close All Editors', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(CloseAllEditorGroupsAction, CloseAllEditorGroupsAction.ID, CloseAllEditorGroupsAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_W) }), 'View: Close All Editor Groups', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(CloseLeftEditorsInGroupAction, CloseLeftEditorsInGroupAction.ID, CloseLeftEditorsInGroupAction.LABEL), 'View: Close Editors to the Left in Group', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(CloseEditorsInOtherGroupsAction, CloseEditorsInOtherGroupsAction.ID, CloseEditorsInOtherGroupsAction.LABEL), 'View: Close Editors in Other Groups', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(CloseEditorInAllGroupsAction, CloseEditorInAllGroupsAction.ID, CloseEditorInAllGroupsAction.LABEL), 'View: Close Editor in All Groups', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(SplitEditorAction, SplitEditorAction.ID, SplitEditorAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.US_BACKSLASH }), 'View: Split Editor', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(SplitEditorOrthogonalAction, SplitEditorOrthogonalAction.ID, SplitEditorOrthogonalAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.US_BACKSLASH) }), 'View: Split Editor Orthogonal', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(SplitEditorLeftAction, SplitEditorLeftAction.ID, SplitEditorLeftAction.LABEL), 'View: Split Editor Left', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(SplitEditorRightAction, SplitEditorRightAction.ID, SplitEditorRightAction.LABEL), 'View: Split Editor Right', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(SplitEditorUpAction, SplitEditorUpAction.ID, SplitEditorUpAction.LABEL), 'Split Editor Up', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(SplitEditorDownAction, SplitEditorDownAction.ID, SplitEditorDownAction.LABEL), 'View: Split Editor Down', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(JoinTwoGroupsAction, JoinTwoGroupsAction.ID, JoinTwoGroupsAction.LABEL), 'View: Join Editor Group with Next Group', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(JoinAllGroupsAction, JoinAllGroupsAction.ID, JoinAllGroupsAction.LABEL), 'View: Join All Editor Groups', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(NavigateBetweenGroupsAction, NavigateBetweenGroupsAction.ID, NavigateBetweenGroupsAction.LABEL), 'View: Navigate Between Editor Groups', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(ResetGroupSizesAction, ResetGroupSizesAction.ID, ResetGroupSizesAction.LABEL), 'View: Reset Editor Group Sizes', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleGroupSizesAction, ToggleGroupSizesAction.ID, ToggleGroupSizesAction.LABEL), 'View: Toggle Editor Group Sizes', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(MaximizeGroupAction, MaximizeGroupAction.ID, MaximizeGroupAction.LABEL), 'View: Maximize Editor Group and Hide Side Bar', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(MinimizeOtherGroupsAction, MinimizeOtherGroupsAction.ID, MinimizeOtherGroupsAction.LABEL), 'View: Maximize Editor Group', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(MoveEditorLeftInGroupAction, MoveEditorLeftInGroupAction.ID, MoveEditorLeftInGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.PageUp, mac: { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.LeftArrow) } }), 'View: Move Editor Left', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(MoveEditorRightInGroupAction, MoveEditorRightInGroupAction.ID, MoveEditorRightInGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.PageDown, mac: { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.RightArrow) } }), 'View: Move Editor Right', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(MoveGroupLeftAction, MoveGroupLeftAction.ID, MoveGroupLeftAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.LeftArrow) }), 'View: Move Editor Group Left', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(MoveGroupRightAction, MoveGroupRightAction.ID, MoveGroupRightAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.RightArrow) }), 'View: Move Editor Group Right', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(MoveGroupUpAction, MoveGroupUpAction.ID, MoveGroupUpAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.UpArrow) }), 'View: Move Editor Group Up', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(MoveGroupDownAction, MoveGroupDownAction.ID, MoveGroupDownAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.DownArrow) }), 'View: Move Editor Group Down', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(MoveEditorToPreviousGroupAction, MoveEditorToPreviousGroupAction.ID, MoveEditorToPreviousGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.LeftArrow, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.LeftArrow } }), 'View: Move Editor into Previous Group', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(MoveEditorToNextGroupAction, MoveEditorToNextGroupAction.ID, MoveEditorToNextGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.RightArrow, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.RightArrow } }), 'View: Move Editor into Next Group', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(MoveEditorToFirstGroupAction, MoveEditorToFirstGroupAction.ID, MoveEditorToFirstGroupAction.LABEL, { primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_1, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_1 } }), 'View: Move Editor into First Group', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(MoveEditorToLastGroupAction, MoveEditorToLastGroupAction.ID, MoveEditorToLastGroupAction.LABEL, { primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_9, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_9 } }), 'View: Move Editor into Last Group', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(MoveEditorToLeftGroupAction, MoveEditorToLeftGroupAction.ID, MoveEditorToLeftGroupAction.LABEL), 'View: Move Editor into Left Group', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(MoveEditorToRightGroupAction, MoveEditorToRightGroupAction.ID, MoveEditorToRightGroupAction.LABEL), 'View: Move Editor into Right Group', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(MoveEditorToAboveGroupAction, MoveEditorToAboveGroupAction.ID, MoveEditorToAboveGroupAction.LABEL), 'View: Move Editor into Above Group', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(MoveEditorToBelowGroupAction, MoveEditorToBelowGroupAction.ID, MoveEditorToBelowGroupAction.LABEL), 'View: Move Editor into Below Group', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(FocusActiveGroupAction, FocusActiveGroupAction.ID, FocusActiveGroupAction.LABEL), 'View: Focus Active Editor Group', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(FocusFirstGroupAction, FocusFirstGroupAction.ID, FocusFirstGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_1 }), 'View: Focus First Editor Group', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(FocusLastGroupAction, FocusLastGroupAction.ID, FocusLastGroupAction.LABEL), 'View: Focus Last Editor Group', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(FocusPreviousGroup, FocusPreviousGroup.ID, FocusPreviousGroup.LABEL), 'View: Focus Previous Editor Group', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(FocusNextGroup, FocusNextGroup.ID, FocusNextGroup.LABEL), 'View: Focus Next Editor Group', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(FocusLeftGroup, FocusLeftGroup.ID, FocusLeftGroup.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.LeftArrow) }), 'View: Focus Left Editor Group', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(FocusRightGroup, FocusRightGroup.ID, FocusRightGroup.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.RightArrow) }), 'View: Focus Right Editor Group', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(FocusAboveGroup, FocusAboveGroup.ID, FocusAboveGroup.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.UpArrow) }), 'View: Focus Above Editor Group', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(FocusBelowGroup, FocusBelowGroup.ID, FocusBelowGroup.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.DownArrow) }), 'View: Focus Below Editor Group', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(NewEditorGroupLeftAction, NewEditorGroupLeftAction.ID, NewEditorGroupLeftAction.LABEL), 'View: New Editor Group to the Left', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(NewEditorGroupRightAction, NewEditorGroupRightAction.ID, NewEditorGroupRightAction.LABEL), 'View: New Editor Group to the Right', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(NewEditorGroupAboveAction, NewEditorGroupAboveAction.ID, NewEditorGroupAboveAction.LABEL), 'View: New Editor Group Above', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(NewEditorGroupBelowAction, NewEditorGroupBelowAction.ID, NewEditorGroupBelowAction.LABEL), 'View: New Editor Group Below', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(NavigateForwardAction, NavigateForwardAction.ID, NavigateForwardAction.LABEL, { primary: 0, win: { primary: KeyMod.Alt | KeyCode.RightArrow }, mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.US_MINUS }, linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_MINUS } }), 'Go Forward');
registry.registerWorkbenchAction(SyncActionDescriptor.create(NavigateBackwardsAction, NavigateBackwardsAction.ID, NavigateBackwardsAction.LABEL, { primary: 0, win: { primary: KeyMod.Alt | KeyCode.LeftArrow }, mac: { primary: KeyMod.WinCtrl | KeyCode.US_MINUS }, linux: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.US_MINUS } }), 'Go Back');
registry.registerWorkbenchAction(SyncActionDescriptor.create(NavigateToLastEditLocationAction, NavigateToLastEditLocationAction.ID, NavigateToLastEditLocationAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_Q) }), 'Go to Last Edit Location');
registry.registerWorkbenchAction(SyncActionDescriptor.create(NavigateLastAction, NavigateLastAction.ID, NavigateLastAction.LABEL), 'Go Last');
registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenPreviousEditorFromHistoryAction, OpenPreviousEditorFromHistoryAction.ID, OpenPreviousEditorFromHistoryAction.LABEL), 'Open Previous Editor from History');
registry.registerWorkbenchAction(SyncActionDescriptor.create(ClearEditorHistoryAction, ClearEditorHistoryAction.ID, ClearEditorHistoryAction.LABEL), 'Clear Editor History');
registry.registerWorkbenchAction(SyncActionDescriptor.create(RevertAndCloseEditorAction, RevertAndCloseEditorAction.ID, RevertAndCloseEditorAction.LABEL), 'View: Revert and Close Editor', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(EditorLayoutSingleAction, EditorLayoutSingleAction.ID, EditorLayoutSingleAction.LABEL), 'View: Single Column Editor Layout', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(EditorLayoutTwoColumnsAction, EditorLayoutTwoColumnsAction.ID, EditorLayoutTwoColumnsAction.LABEL), 'View: Two Columns Editor Layout', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(EditorLayoutThreeColumnsAction, EditorLayoutThreeColumnsAction.ID, EditorLayoutThreeColumnsAction.LABEL), 'View: Three Columns Editor Layout', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(EditorLayoutTwoRowsAction, EditorLayoutTwoRowsAction.ID, EditorLayoutTwoRowsAction.LABEL), 'View: Two Rows Editor Layout', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(EditorLayoutThreeRowsAction, EditorLayoutThreeRowsAction.ID, EditorLayoutThreeRowsAction.LABEL), 'View: Three Rows Editor Layout', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(EditorLayoutTwoByTwoGridAction, EditorLayoutTwoByTwoGridAction.ID, EditorLayoutTwoByTwoGridAction.LABEL), 'View: Grid Editor Layout (2x2)', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(EditorLayoutTwoRowsRightAction, EditorLayoutTwoRowsRightAction.ID, EditorLayoutTwoRowsRightAction.LABEL), 'View: Two Rows Right Editor Layout', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(EditorLayoutTwoColumnsBottomAction, EditorLayoutTwoColumnsBottomAction.ID, EditorLayoutTwoColumnsBottomAction.LABEL), 'View: Two Columns Bottom Editor Layout', category);
// Register Editor Picker Actions including quick navigate support
const openNextEditorKeybinding = { primary: KeyMod.CtrlCmd | KeyCode.Tab, mac: { primary: KeyMod.WinCtrl | KeyCode.Tab } };
const openPreviousEditorKeybinding = { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Tab, mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.Tab } };
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenNextRecentlyUsedEditorInGroupAction, OpenNextRecentlyUsedEditorInGroupAction.ID, OpenNextRecentlyUsedEditorInGroupAction.LABEL, openNextEditorKeybinding), 'View: Open Next Recently Used Editor in Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenPreviousRecentlyUsedEditorInGroupAction, OpenPreviousRecentlyUsedEditorInGroupAction.ID, OpenPreviousRecentlyUsedEditorInGroupAction.LABEL, openPreviousEditorKeybinding), 'View: Open Previous Recently Used Editor in Group', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenNextRecentlyUsedEditorInGroupAction, OpenNextRecentlyUsedEditorInGroupAction.ID, OpenNextRecentlyUsedEditorInGroupAction.LABEL, openNextEditorKeybinding), 'View: Open Next Recently Used Editor in Group', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenPreviousRecentlyUsedEditorInGroupAction, OpenPreviousRecentlyUsedEditorInGroupAction.ID, OpenPreviousRecentlyUsedEditorInGroupAction.LABEL, openPreviousEditorKeybinding), 'View: Open Previous Recently Used Editor in Group', category);
const quickOpenNavigateNextInEditorPickerId = 'workbench.action.quickOpenNavigateNextInEditorPicker';
KeybindingsRegistry.registerCommandAndKeybindingRule({

View file

@ -507,7 +507,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);
}

View file

@ -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

View file

@ -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
@ -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;
}

View file

@ -48,6 +48,21 @@
overflow: hidden;
}
.monaco-workbench.windows .part.editor > .content .editor-group-container > .title,
.monaco-workbench.linux .part.editor > .content .editor-group-container > .title,
.monaco-workbench.web .part.editor > .content .editor-group-container > .title {
/*
* Explicitly put the part onto its own layer to help Chrome to
* render the content with LCD-anti-aliasing. By partioning the
* workbench into multiple layers, we can ensure that a bad
* behaving part is not making another part fallback to greyscale
* rendering.
*
* macOS: does not render LCD-anti-aliased.
*/
transform: translate3d(0px, 0px, 0px);
}
.monaco-workbench .part.editor > .content .editor-group-container > .title:not(.tabs) {
display: flex; /* when tabs are not shown, use flex layout */
flex-wrap: nowrap;

View file

@ -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

View file

@ -16,7 +16,8 @@ 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 { SaveReason } from 'vs/workbench/services/workingCopy/common/workingCopyService';
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';

View file

@ -243,14 +243,14 @@ export class NextPanelViewAction extends SwitchPanelViewAction {
}
const actionRegistry = Registry.as<IWorkbenchActionRegistry>(WorkbenchExtensions.WorkbenchActions);
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(TogglePanelAction, TogglePanelAction.ID, TogglePanelAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_J }), 'View: Toggle Panel', nls.localize('view', "View"));
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusPanelAction, FocusPanelAction.ID, FocusPanelAction.LABEL), 'View: Focus into Panel', nls.localize('view', "View"));
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleMaximizedPanelAction, ToggleMaximizedPanelAction.ID, ToggleMaximizedPanelAction.LABEL), 'View: Toggle Maximized Panel', nls.localize('view', "View"));
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ClosePanelAction, ClosePanelAction.ID, ClosePanelAction.LABEL), 'View: Close Panel', nls.localize('view', "View"));
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(TogglePanelPositionAction, TogglePanelPositionAction.ID, TogglePanelPositionAction.LABEL), 'View: Toggle Panel Position', nls.localize('view', "View"));
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleMaximizedPanelAction, ToggleMaximizedPanelAction.ID, undefined), 'View: Toggle Panel Position', nls.localize('view', "View"));
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(PreviousPanelViewAction, PreviousPanelViewAction.ID, PreviousPanelViewAction.LABEL), 'View: Previous Panel View', nls.localize('view', "View"));
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(NextPanelViewAction, NextPanelViewAction.ID, NextPanelViewAction.LABEL), 'View: Next Panel View', nls.localize('view', "View"));
actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(TogglePanelAction, TogglePanelAction.ID, TogglePanelAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_J }), 'View: Toggle Panel', nls.localize('view', "View"));
actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(FocusPanelAction, FocusPanelAction.ID, FocusPanelAction.LABEL), 'View: Focus into Panel', nls.localize('view', "View"));
actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleMaximizedPanelAction, ToggleMaximizedPanelAction.ID, ToggleMaximizedPanelAction.LABEL), 'View: Toggle Maximized Panel', nls.localize('view', "View"));
actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ClosePanelAction, ClosePanelAction.ID, ClosePanelAction.LABEL), 'View: Close Panel', nls.localize('view', "View"));
actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(TogglePanelPositionAction, TogglePanelPositionAction.ID, TogglePanelPositionAction.LABEL), 'View: Toggle Panel Position', nls.localize('view', "View"));
actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleMaximizedPanelAction, ToggleMaximizedPanelAction.ID, undefined), 'View: Toggle Panel Position', nls.localize('view', "View"));
actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(PreviousPanelViewAction, PreviousPanelViewAction.ID, PreviousPanelViewAction.LABEL), 'View: Previous Panel View', nls.localize('view', "View"));
actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(NextPanelViewAction, NextPanelViewAction.ID, NextPanelViewAction.LABEL), 'View: Next Panel View', nls.localize('view', "View"));
MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, {
group: '2_workbench_layout',

View file

@ -14,4 +14,4 @@ import { inQuickOpenContext } from 'vs/workbench/browser/parts/quickopen/quickop
KeybindingsRegistry.registerCommandAndKeybindingRule(QuickPickManyToggle);
const registry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
registry.registerWorkbenchAction(new SyncActionDescriptor(BackAction, BackAction.ID, BackAction.LABEL, { primary: 0, win: { primary: KeyMod.Alt | KeyCode.LeftArrow }, mac: { primary: KeyMod.WinCtrl | KeyCode.US_MINUS }, linux: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.US_MINUS } }, inQuickOpenContext, KeybindingWeight.WorkbenchContrib + 50), 'Back');
registry.registerWorkbenchAction(SyncActionDescriptor.create(BackAction, BackAction.ID, BackAction.LABEL, { primary: 0, win: { primary: KeyMod.Alt | KeyCode.LeftArrow }, mac: { primary: KeyMod.WinCtrl | KeyCode.US_MINUS }, linux: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.US_MINUS } }, inQuickOpenContext, KeybindingWeight.WorkbenchContrib + 50), 'Back');

View file

@ -69,11 +69,11 @@ MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
command: { id: QUICKOPEN_ACTION_ID, title: { value: QUICKOPEN_ACION_LABEL, original: 'Go to File...' } }
});
registry.registerWorkbenchAction(new SyncActionDescriptor(QuickOpenSelectNextAction, QuickOpenSelectNextAction.ID, QuickOpenSelectNextAction.LABEL, { primary: 0, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_N } }, inQuickOpenContext, KeybindingWeight.WorkbenchContrib + 50), 'Select Next in Quick Open');
registry.registerWorkbenchAction(new SyncActionDescriptor(QuickOpenSelectPreviousAction, QuickOpenSelectPreviousAction.ID, QuickOpenSelectPreviousAction.LABEL, { primary: 0, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_P } }, inQuickOpenContext, KeybindingWeight.WorkbenchContrib + 50), 'Select Previous in Quick Open');
registry.registerWorkbenchAction(new SyncActionDescriptor(QuickOpenNavigateNextAction, QuickOpenNavigateNextAction.ID, QuickOpenNavigateNextAction.LABEL), 'Navigate Next in Quick Open');
registry.registerWorkbenchAction(new SyncActionDescriptor(QuickOpenNavigatePreviousAction, QuickOpenNavigatePreviousAction.ID, QuickOpenNavigatePreviousAction.LABEL), 'Navigate Previous in Quick Open');
registry.registerWorkbenchAction(new SyncActionDescriptor(RemoveFromEditorHistoryAction, RemoveFromEditorHistoryAction.ID, RemoveFromEditorHistoryAction.LABEL), 'Remove From History');
registry.registerWorkbenchAction(SyncActionDescriptor.create(QuickOpenSelectNextAction, QuickOpenSelectNextAction.ID, QuickOpenSelectNextAction.LABEL, { primary: 0, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_N } }, inQuickOpenContext, KeybindingWeight.WorkbenchContrib + 50), 'Select Next in Quick Open');
registry.registerWorkbenchAction(SyncActionDescriptor.create(QuickOpenSelectPreviousAction, QuickOpenSelectPreviousAction.ID, QuickOpenSelectPreviousAction.LABEL, { primary: 0, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_P } }, inQuickOpenContext, KeybindingWeight.WorkbenchContrib + 50), 'Select Previous in Quick Open');
registry.registerWorkbenchAction(SyncActionDescriptor.create(QuickOpenNavigateNextAction, QuickOpenNavigateNextAction.ID, QuickOpenNavigateNextAction.LABEL), 'Navigate Next in Quick Open');
registry.registerWorkbenchAction(SyncActionDescriptor.create(QuickOpenNavigatePreviousAction, QuickOpenNavigatePreviousAction.ID, QuickOpenNavigatePreviousAction.LABEL), 'Navigate Previous in Quick Open');
registry.registerWorkbenchAction(SyncActionDescriptor.create(RemoveFromEditorHistoryAction, RemoveFromEditorHistoryAction.ID, RemoveFromEditorHistoryAction.LABEL), 'Remove From History');
const quickOpenNavigateNextInFilePickerId = 'workbench.action.quickOpenNavigateNextInFilePicker';
KeybindingsRegistry.registerCommandAndKeybindingRule({
@ -98,4 +98,4 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
primary: globalQuickOpenKeybinding.mac.primary | KeyMod.Shift,
secondary: undefined
}
});
});

View file

@ -320,7 +320,7 @@ class FocusSideBarAction extends Action {
}
const registry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusSideBarAction, FocusSideBarAction.ID, FocusSideBarAction.LABEL, {
registry.registerWorkbenchAction(SyncActionDescriptor.create(FocusSideBarAction, FocusSideBarAction.ID, FocusSideBarAction.LABEL, {
primary: KeyMod.CtrlCmd | KeyCode.KEY_0
}), 'View: Focus into Side Bar', nls.localize('viewCategory', "View"));

View file

@ -32,6 +32,8 @@
* macOS: does not render LCD-anti-aliased.
*/
transform: translate3d(0px, 0px, 0px);
position: relative;
z-index: 1;
}
.monaco-workbench .part.titlebar > .titlebar-drag-region {
@ -118,6 +120,7 @@
height: 100%;
width: 138px;
margin-left: auto;
transform: translate3d(0px, 0px, 0px);
}
.monaco-workbench.fullscreen .part.titlebar > .window-controls-container {

View file

@ -42,6 +42,7 @@ import { IElectronService } from 'vs/platform/electron/node/electron';
import { optional } from 'vs/platform/instantiation/common/instantiation';
// tslint:disable-next-line: import-patterns layering TODO@sbatten
import { IElectronEnvironmentService } from 'vs/workbench/services/electron/electron-browser/electronEnvironmentService';
import { BrowserFeatures } from 'vs/base/browser/canIUse';
export abstract class MenubarControl extends Disposable {
@ -673,7 +674,7 @@ export class CustomMenubarControl extends MenubarControl {
}
this._register(DOM.addDisposableListener(window, DOM.EventType.RESIZE, () => {
if (this.menubar) {
if (this.menubar && !BrowserFeatures.pointerEvents) {
this.menubar.blur();
}
}));

View file

@ -350,22 +350,26 @@ export abstract class FilterViewContainerViewlet extends ViewContainerViewlet {
}));
this._register(this.viewsModel.onDidChangeActiveViews((viewDescriptors) => {
viewDescriptors.forEach(descriptor => {
let filterOnValue = this.getFilterOn(descriptor);
if (!filterOnValue) {
return;
}
if (!this.allViews.has(filterOnValue)) {
this.allViews.set(filterOnValue, new Map());
}
this.allViews.get(filterOnValue)!.set(descriptor.id, descriptor);
if (filterOnValue !== this.filterValue) {
this.viewsModel.setVisible(descriptor.id, false);
}
});
this.updateAllViews(viewDescriptors);
}));
}
private updateAllViews(viewDescriptors: IViewDescriptor[]) {
viewDescriptors.forEach(descriptor => {
let filterOnValue = this.getFilterOn(descriptor);
if (!filterOnValue) {
return;
}
if (!this.allViews.has(filterOnValue)) {
this.allViews.set(filterOnValue, new Map());
}
this.allViews.get(filterOnValue)!.set(descriptor.id, descriptor);
if (filterOnValue !== this.filterValue) {
this.viewsModel.setVisible(descriptor.id, false);
}
});
}
protected addConstantViewDescriptors(constantViewDescriptors: IViewDescriptor[]) {
constantViewDescriptors.forEach(viewDescriptor => this.constantViewDescriptors.set(viewDescriptor.id, viewDescriptor));
}
@ -415,6 +419,10 @@ export abstract class FilterViewContainerViewlet extends ViewContainerViewlet {
}
onDidAddViews(added: IAddedViewDescriptorRef[]): ViewletPanel[] {
// Check that allViews is ready
if (this.allViews.size === 0) {
this.updateAllViews(this.viewsModel.viewDescriptors);
}
const panels: ViewletPanel[] = super.onDidAddViews(added);
for (let i = 0; i < added.length; i++) {
if (this.constantViewDescriptors.has(added[i].viewDescriptor.id)) {

View file

@ -15,7 +15,7 @@ import { QuickOpenEntry, QuickOpenEntryGroup } from 'vs/base/parts/quickopen/bro
import { EditorOptions, EditorInput, IEditorInput } from 'vs/workbench/common/editor';
import { IResourceInput, IEditorOptions } from 'vs/platform/editor/common/editor';
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
import { IConstructorSignature0, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IConstructorSignature0, IInstantiationService, BrandedService } from 'vs/platform/instantiation/common/instantiation';
import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService';
import { CancellationToken } from 'vs/base/common/cancellation';
@ -136,9 +136,13 @@ export class QuickOpenHandlerDescriptor {
private id: string;
private ctor: IConstructorSignature0<QuickOpenHandler>;
constructor(ctor: IConstructorSignature0<QuickOpenHandler>, id: string, prefix: string, contextKey: string | undefined, description: string, instantProgress?: boolean);
constructor(ctor: IConstructorSignature0<QuickOpenHandler>, id: string, prefix: string, contextKey: string | undefined, helpEntries: QuickOpenHandlerHelpEntry[], instantProgress?: boolean);
constructor(ctor: IConstructorSignature0<QuickOpenHandler>, id: string, prefix: string, contextKey: string | undefined, param: string | QuickOpenHandlerHelpEntry[], instantProgress: boolean = false) {
public static create<Services extends BrandedService[]>(ctor: { new(...services: Services): QuickOpenHandler }, id: string, prefix: string, contextKey: string | undefined, description: string, instantProgress?: boolean): QuickOpenHandlerDescriptor;
public static create<Services extends BrandedService[]>(ctor: { new(...services: Services): QuickOpenHandler }, id: string, prefix: string, contextKey: string | undefined, helpEntries: QuickOpenHandlerHelpEntry[], instantProgress?: boolean): QuickOpenHandlerDescriptor;
public static create<Services extends BrandedService[]>(ctor: { new(...services: Services): QuickOpenHandler }, id: string, prefix: string, contextKey: string | undefined, param: string | QuickOpenHandlerHelpEntry[], instantProgress: boolean = false): QuickOpenHandlerDescriptor {
return new QuickOpenHandlerDescriptor(ctor as IConstructorSignature0<QuickOpenHandler>, id, prefix, contextKey, param, instantProgress);
}
private constructor(ctor: IConstructorSignature0<QuickOpenHandler>, id: string, prefix: string, contextKey: string | undefined, param: string | QuickOpenHandlerHelpEntry[], instantProgress: boolean = false) {
this.ctor = ctor;
this.id = id;
this.prefix = prefix;

View file

@ -175,6 +175,9 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
// allow to select text in monaco editor instances.
if (isSafari) {
collector.addRule(`
body.web {
touch-action: none;
}
.monaco-workbench .monaco-editor .view-lines {
user-select: text;
-webkit-user-select: text;

View file

@ -10,7 +10,7 @@ import { Action, IAction } from 'vs/base/common/actions';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { IViewlet } from 'vs/workbench/common/viewlet';
import { Composite, CompositeDescriptor, CompositeRegistry } from 'vs/workbench/browser/composite';
import { IConstructorSignature0 } from 'vs/platform/instantiation/common/instantiation';
import { IConstructorSignature0, BrandedService } from 'vs/platform/instantiation/common/instantiation';
import { ToggleSidebarVisibilityAction, ToggleSidebarPositionAction } from 'vs/workbench/browser/actions/layoutActions';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService';
@ -56,7 +56,18 @@ export abstract class Viewlet extends Composite implements IViewlet {
*/
export class ViewletDescriptor extends CompositeDescriptor<Viewlet> {
constructor(
public static create<Services extends BrandedService[]>(
ctor: { new(...services: Services): Viewlet },
id: string,
name: string,
cssClass?: string,
order?: number,
iconUrl?: URI
): ViewletDescriptor {
return new ViewletDescriptor(ctor as IConstructorSignature0<Viewlet>, id, name, cssClass, order, iconUrl);
}
private constructor(
ctor: IConstructorSignature0<Viewlet>,
id: string,
name: string,

View file

@ -10,7 +10,7 @@ import { URI } from 'vs/base/common/uri';
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
import { IEditor as ICodeEditor, IEditorViewState, ScrollType, IDiffEditor } from 'vs/editor/common/editorCommon';
import { IEditorModel, IEditorOptions, ITextEditorOptions, IBaseResourceInput, IResourceInput, EditorActivation, EditorOpenContext } from 'vs/platform/editor/common/editor';
import { IInstantiationService, IConstructorSignature0, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { IInstantiationService, IConstructorSignature0, ServicesAccessor, BrandedService } from 'vs/platform/instantiation/common/instantiation';
import { RawContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { Registry } from 'vs/platform/registry/common/platform';
import { ITextModel } from 'vs/editor/common/model';
@ -20,6 +20,7 @@ import { ActionRunner, IAction } from 'vs/base/common/actions';
import { IFileService } from 'vs/platform/files/common/files';
import { IPathData } from 'vs/platform/windows/common/windows';
import { coalesce, firstOrDefault } from 'vs/base/common/arrays';
import { ISaveOptions, IRevertOptions } from 'vs/workbench/services/workingCopy/common/workingCopyService';
export const ActiveEditorContext = new RawContextKey<string | null>('activeEditor', null);
export const ActiveEditorIsSaveableContext = new RawContextKey<boolean>('activeEditorIsSaveable', false);
@ -175,7 +176,7 @@ export interface IEditorInputFactoryRegistry {
* @param editorInputId the identifier of the editor input
* @param factory the editor input factory for serialization/deserialization
*/
registerEditorInputFactory(editorInputId: string, ctor: IConstructorSignature0<IEditorInputFactory>): void;
registerEditorInputFactory<Services extends BrandedService[]>(editorInputId: string, ctor: { new(...Services: Services): IEditorInputFactory }): void;
/**
* Returns the editor input factory for the given editor input.
@ -261,25 +262,12 @@ export const enum Verbosity {
LONG
}
export interface IRevertOptions {
/**
* Forces to load the contents of the editor again even if the editor is not dirty.
*/
force?: boolean;
/**
* A soft revert will clear dirty state of an editor but will not attempt to load it.
*/
soft?: boolean;
}
export interface IEditorInput extends IDisposable {
/**
* Triggered when this input is disposed.
*/
onDispose: Event<void>;
readonly onDispose: Event<void>;
/**
* Returns the associated resource of this input.
@ -316,6 +304,11 @@ export interface IEditorInput extends IDisposable {
*/
isDirty(): boolean;
/**
* Saves the editor if it is dirty.
*/
save(options?: ISaveOptions): Promise<boolean>;
/**
* Reverts this input.
*/
@ -333,14 +326,14 @@ export interface IEditorInput extends IDisposable {
*/
export abstract class EditorInput extends Disposable implements IEditorInput {
protected readonly _onDidChangeDirty: Emitter<void> = this._register(new Emitter<void>());
readonly onDidChangeDirty: Event<void> = this._onDidChangeDirty.event;
protected readonly _onDidChangeDirty = this._register(new Emitter<void>());
readonly onDidChangeDirty = this._onDidChangeDirty.event;
protected readonly _onDidChangeLabel: Emitter<void> = this._register(new Emitter<void>());
readonly onDidChangeLabel: Event<void> = this._onDidChangeLabel.event;
protected readonly _onDidChangeLabel = this._register(new Emitter<void>());
readonly onDidChangeLabel = this._onDidChangeLabel.event;
private readonly _onDispose: Emitter<void> = this._register(new Emitter<void>());
readonly onDispose: Event<void> = this._onDispose.event;
private readonly _onDispose = this._register(new Emitter<void>());
readonly onDispose = this._onDispose.event;
private disposed: boolean = false;
@ -418,7 +411,7 @@ export abstract class EditorInput extends Disposable implements IEditorInput {
/**
* Saves the editor if it is dirty. Subclasses return a promise with a boolean indicating the success of the operation.
*/
save(): Promise<boolean> {
save(options?: ISaveOptions): Promise<boolean> {
return Promise.resolve(true);
}
@ -429,13 +422,6 @@ export abstract class EditorInput extends Disposable implements IEditorInput {
return Promise.resolve(true);
}
/**
* Called when this input is no longer opened in any editor. Subclasses can free resources as needed.
*/
close(): void {
this.dispose();
}
/**
* Subclasses can set this to false if it does not make sense to split the editor input.
*/
@ -560,12 +546,12 @@ export class SideBySideEditorInput extends EditorInput {
return this.master.isDirty();
}
save(): Promise<boolean> {
return this.master.save();
save(options?: ISaveOptions): Promise<boolean> {
return this.master.save(options);
}
revert(): Promise<boolean> {
return this.master.revert();
revert(options?: IRevertOptions): Promise<boolean> {
return this.master.revert(options);
}
getTelemetryDescriptor(): { [key: string]: unknown } {
@ -640,8 +626,8 @@ export interface ITextEditorModel extends IEditorModel {
*/
export class EditorModel extends Disposable implements IEditorModel {
private readonly _onDispose: Emitter<void> = this._register(new Emitter<void>());
readonly onDispose: Event<void> = this._onDispose.event;
private readonly _onDispose = this._register(new Emitter<void>());
readonly onDispose = this._onDispose.event;
/**
* Causes this model to load returning a promise when loading is completed.

View file

@ -4,13 +4,11 @@
*--------------------------------------------------------------------------------------------*/
import { Event, Emitter } from 'vs/base/common/event';
import { Extensions, IEditorInputFactoryRegistry, EditorInput, toResource, IEditorIdentifier, IEditorCloseEvent, GroupIdentifier, SideBySideEditorInput, CloseDirection, IEditorInput, SideBySideEditor } from 'vs/workbench/common/editor';
import { URI } from 'vs/base/common/uri';
import { Extensions, IEditorInputFactoryRegistry, EditorInput, IEditorIdentifier, IEditorCloseEvent, GroupIdentifier, CloseDirection, IEditorInput, SideBySideEditorInput } from 'vs/workbench/common/editor';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
import { dispose, Disposable, DisposableStore } from 'vs/base/common/lifecycle';
import { Registry } from 'vs/platform/registry/common/platform';
import { ResourceMap } from 'vs/base/common/map';
import { coalesce } from 'vs/base/common/arrays';
const EditorOpenPositioning = {
@ -60,31 +58,31 @@ export class EditorGroup extends Disposable {
//#region events
private readonly _onDidEditorActivate = this._register(new Emitter<EditorInput>());
readonly onDidEditorActivate: Event<EditorInput> = this._onDidEditorActivate.event;
readonly onDidEditorActivate = this._onDidEditorActivate.event;
private readonly _onDidEditorOpen = this._register(new Emitter<EditorInput>());
readonly onDidEditorOpen: Event<EditorInput> = this._onDidEditorOpen.event;
readonly onDidEditorOpen = this._onDidEditorOpen.event;
private readonly _onDidEditorClose = this._register(new Emitter<EditorCloseEvent>());
readonly onDidEditorClose: Event<EditorCloseEvent> = this._onDidEditorClose.event;
readonly onDidEditorClose = this._onDidEditorClose.event;
private readonly _onDidEditorDispose = this._register(new Emitter<EditorInput>());
readonly onDidEditorDispose: Event<EditorInput> = this._onDidEditorDispose.event;
readonly onDidEditorDispose = this._onDidEditorDispose.event;
private readonly _onDidEditorBecomeDirty = this._register(new Emitter<EditorInput>());
readonly onDidEditorBecomeDirty: Event<EditorInput> = this._onDidEditorBecomeDirty.event;
readonly onDidEditorBecomeDirty = this._onDidEditorBecomeDirty.event;
private readonly _onDidEditorLabelChange = this._register(new Emitter<EditorInput>());
readonly onDidEditorLabelChange: Event<EditorInput> = this._onDidEditorLabelChange.event;
readonly onDidEditorLabelChange = this._onDidEditorLabelChange.event;
private readonly _onDidEditorMove = this._register(new Emitter<EditorInput>());
readonly onDidEditorMove: Event<EditorInput> = this._onDidEditorMove.event;
readonly onDidEditorMove = this._onDidEditorMove.event;
private readonly _onDidEditorPin = this._register(new Emitter<EditorInput>());
readonly onDidEditorPin: Event<EditorInput> = this._onDidEditorPin.event;
readonly onDidEditorPin = this._onDidEditorPin.event;
private readonly _onDidEditorUnpin = this._register(new Emitter<EditorInput>());
readonly onDidEditorUnpin: Event<EditorInput> = this._onDidEditorUnpin.event;
readonly onDidEditorUnpin = this._onDidEditorUnpin.event;
//#endregion
@ -93,7 +91,6 @@ export class EditorGroup extends Disposable {
private editors: EditorInput[] = [];
private mru: EditorInput[] = [];
private mapResourceToEditorCount: ResourceMap<number> = new ResourceMap<number>();
private preview: EditorInput | null = null; // editor in preview state
private active: EditorInput | null = null; // editor in active state
@ -135,26 +132,8 @@ export class EditorGroup extends Disposable {
return mru ? this.mru.slice(0) : this.editors.slice(0);
}
getEditor(index: number): EditorInput | undefined;
getEditor(resource: URI): EditorInput | undefined;
getEditor(arg1: number | URI): EditorInput | undefined {
if (typeof arg1 === 'number') {
return this.editors[arg1];
}
const resource: URI = arg1;
if (!this.contains(resource)) {
return undefined; // fast check for resource opened or not
}
for (const editor of this.editors) {
const editorResource = toResource(editor, { supportSideBySide: SideBySideEditor.MASTER });
if (editorResource?.toString() === resource.toString()) {
return editor;
}
}
return undefined;
getEditorByIndex(index: number): EditorInput | undefined {
return this.editors[index];
}
get activeEditor(): EditorInput | null {
@ -509,7 +488,6 @@ export class EditorGroup extends Disposable {
// Add
if (!del && editor) {
this.mru.push(editor); // make it LRU editor
this.updateResourceMap(editor, false /* add */); // add new to resource map
}
// Remove / Replace
@ -519,41 +497,11 @@ export class EditorGroup extends Disposable {
// Remove
if (del && !editor) {
this.mru.splice(indexInMRU, 1); // remove from MRU
this.updateResourceMap(editorToDeleteOrReplace, true /* delete */); // remove from resource map
}
// Replace
else if (del && editor) {
this.mru.splice(indexInMRU, 1, editor); // replace MRU at location
this.updateResourceMap(editor, false /* add */); // add new to resource map
this.updateResourceMap(editorToDeleteOrReplace, true /* delete */); // remove replaced from resource map
}
}
}
private updateResourceMap(editor: EditorInput, remove: boolean): void {
const resource = toResource(editor, { supportSideBySide: SideBySideEditor.MASTER });
if (resource) {
// It is possible to have the same resource opened twice (once as normal input and once as diff input)
// So we need to do ref counting on the resource to provide the correct picture
const counter = this.mapResourceToEditorCount.get(resource) || 0;
// Add
let newCounter: number;
if (!remove) {
newCounter = counter + 1;
}
// Delete
else {
newCounter = counter - 1;
}
if (newCounter > 0) {
this.mapResourceToEditorCount.set(resource, newCounter);
} else {
this.mapResourceToEditorCount.delete(resource);
}
}
}
@ -572,28 +520,20 @@ export class EditorGroup extends Disposable {
return -1;
}
contains(editorOrResource: EditorInput | URI): boolean;
contains(editor: EditorInput, supportSideBySide?: boolean): boolean;
contains(editorOrResource: EditorInput | URI, supportSideBySide?: boolean): boolean {
if (editorOrResource instanceof EditorInput) {
const index = this.indexOf(editorOrResource);
if (index >= 0) {
contains(candidate: EditorInput, searchInSideBySideEditors?: boolean): boolean {
for (const editor of this.editors) {
if (this.matches(editor, candidate)) {
return true;
}
if (supportSideBySide && editorOrResource instanceof SideBySideEditorInput) {
const index = this.indexOf(editorOrResource.master);
if (index >= 0) {
if (searchInSideBySideEditors && editor instanceof SideBySideEditorInput) {
if (this.matches(editor.master, candidate) || this.matches(editor.details, candidate)) {
return true;
}
}
return false;
}
const counter = this.mapResourceToEditorCount.get(editorOrResource);
return typeof counter === 'number' && counter > 0;
return false;
}
private setMostRecentlyUsed(editor: EditorInput): void {
@ -619,7 +559,6 @@ export class EditorGroup extends Disposable {
const group = this.instantiationService.createInstance(EditorGroup, undefined);
group.editors = this.editors.slice(0);
group.mru = this.mru.slice(0);
group.mapResourceToEditorCount = this.mapResourceToEditorCount.clone();
group.preview = this.preview;
group.active = this.active;
group.editorOpenPositioning = this.editorOpenPositioning;
@ -678,7 +617,6 @@ export class EditorGroup extends Disposable {
const editor = factory.deserialize(this.instantiationService, e.value);
if (editor) {
this.registerEditorListeners(editor);
this.updateResourceMap(editor, false /* add */);
}
return editor;

Some files were not shown because too many files have changed in this diff Show more