Send text editor properties updates in a single event

This commit is contained in:
Alex Dima
2018-02-26 16:50:13 +01:00
parent ad63cf5a12
commit 6aadb1ad40
5 changed files with 198 additions and 128 deletions

View File

@@ -14,29 +14,132 @@ import { Selection, ISelection } from 'vs/editor/common/core/selection';
import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2';
import { EndOfLine, TextEditorLineNumbersStyle } from 'vs/workbench/api/node/extHostTypes';
import { TextEditorCursorStyle, cursorStyleToString, RenderLineNumbersType } from 'vs/editor/common/config/editorOptions';
import { ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
import { IResolvedTextEditorConfiguration, ISelectionChangeEvent, ITextEditorConfigurationUpdate, TextEditorRevealType, IApplyEditsOptions, IUndoStopOptions } from 'vs/workbench/api/node/extHost.protocol';
import { IResolvedTextEditorConfiguration, ITextEditorConfigurationUpdate, TextEditorRevealType, IApplyEditsOptions, IUndoStopOptions, IEditorPropertiesChangeData } from 'vs/workbench/api/node/extHost.protocol';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { ITextModel, ISingleEditOperation, EndOfLineSequence, IIdentifiedSingleEditOperation, ITextModelUpdateOptions } from 'vs/editor/common/model';
function configurationsEqual(a: IResolvedTextEditorConfiguration, b: IResolvedTextEditorConfiguration) {
if (a && !b || !a && b) {
return false;
}
if (!a && !b) {
return true;
}
return (
a.tabSize === b.tabSize
&& a.insertSpaces === b.insertSpaces
);
}
export interface IFocusTracker {
onGainedFocus(): void;
onLostFocus(): void;
}
export class MainThreadTextEditorProperties {
public static readFromEditor(previousProperties: MainThreadTextEditorProperties, model: ITextModel, codeEditor: ICodeEditor): MainThreadTextEditorProperties {
const selections = MainThreadTextEditorProperties._readSelectionsFromCodeEditor(previousProperties, codeEditor);
const options = MainThreadTextEditorProperties._readOptionsFromCodeEditor(previousProperties, model, codeEditor);
return new MainThreadTextEditorProperties(selections, options);
}
private static _readSelectionsFromCodeEditor(previousProperties: MainThreadTextEditorProperties, codeEditor: ICodeEditor): Selection[] {
if (codeEditor) {
return codeEditor.getSelections();
}
if (previousProperties) {
return previousProperties.selections;
}
return [new Selection(1, 1, 1, 1)];
}
private static _readOptionsFromCodeEditor(previousProperties: MainThreadTextEditorProperties, model: ITextModel, codeEditor: ICodeEditor): IResolvedTextEditorConfiguration {
if (model.isDisposed()) {
// shutdown time
return previousProperties.options;
}
let cursorStyle: TextEditorCursorStyle;
let lineNumbers: TextEditorLineNumbersStyle;
if (codeEditor) {
const codeEditorOpts = codeEditor.getConfiguration();
cursorStyle = codeEditorOpts.viewInfo.cursorStyle;
switch (codeEditorOpts.viewInfo.renderLineNumbers) {
case RenderLineNumbersType.Off:
lineNumbers = TextEditorLineNumbersStyle.Off;
break;
case RenderLineNumbersType.Relative:
lineNumbers = TextEditorLineNumbersStyle.Relative;
break;
default:
lineNumbers = TextEditorLineNumbersStyle.On;
break;
}
} else if (previousProperties) {
cursorStyle = previousProperties.options.cursorStyle;
lineNumbers = previousProperties.options.lineNumbers;
} else {
cursorStyle = TextEditorCursorStyle.Line;
lineNumbers = TextEditorLineNumbersStyle.On;
}
const modelOptions = model.getOptions();
return {
insertSpaces: modelOptions.insertSpaces,
tabSize: modelOptions.tabSize,
cursorStyle: cursorStyle,
lineNumbers: lineNumbers
};
}
constructor(
public readonly selections: Selection[],
public readonly options: IResolvedTextEditorConfiguration
) {
}
public generateDelta(oldProps: MainThreadTextEditorProperties, selectionChangeSource: string): IEditorPropertiesChangeData {
let delta = {
options: null,
selections: null
};
if (!oldProps || !MainThreadTextEditorProperties._selectionsEqual(oldProps.selections, this.selections)) {
delta.selections = {
selections: this.selections,
source: selectionChangeSource
};
}
if (!oldProps || !MainThreadTextEditorProperties._optionsEqual(oldProps.options, this.options)) {
delta.options = this.options;
}
if (delta.selections || delta.options) {
// something changed
return delta;
}
// nothing changed
return null;
}
private static _selectionsEqual(a: Selection[], b: Selection[]): boolean {
if (a.length !== b.length) {
return false;
}
for (let i = 0; i < a.length; i++) {
if (!a[i].equalsSelection(b[i])) {
return false;
}
}
return true;
}
private static _optionsEqual(a: IResolvedTextEditorConfiguration, b: IResolvedTextEditorConfiguration): boolean {
if (a && !b || !a && b) {
return false;
}
if (!a && !b) {
return true;
}
return (
a.tabSize === b.tabSize
&& a.insertSpaces === b.insertSpaces
&& a.cursorStyle === b.cursorStyle
&& a.lineNumbers === b.lineNumbers
);
}
}
/**
* Text Editor that is permanently bound to the same model.
* It can be bound or not to a CodeEditor.
@@ -51,11 +154,8 @@ export class MainThreadTextEditor {
private _focusTracker: IFocusTracker;
private _codeEditorListeners: IDisposable[];
private _lastSelection: Selection[];
private _configuration: IResolvedTextEditorConfiguration;
private _onSelectionChanged: Emitter<ISelectionChangeEvent>;
private _onConfigurationChanged: Emitter<IResolvedTextEditorConfiguration>;
private _properties: MainThreadTextEditorProperties;
private _onPropertiesChanged: Emitter<IEditorPropertiesChangeData>;
constructor(
id: string,
@@ -71,17 +171,16 @@ export class MainThreadTextEditor {
this._modelService = modelService;
this._codeEditorListeners = [];
this._onSelectionChanged = new Emitter<ISelectionChangeEvent>();
this._onConfigurationChanged = new Emitter<IResolvedTextEditorConfiguration>();
this._properties = null;
this._onPropertiesChanged = new Emitter<IEditorPropertiesChangeData>();
this._lastSelection = [new Selection(1, 1, 1, 1)];
this._modelListeners = [];
this._modelListeners.push(this._model.onDidChangeOptions((e) => {
this._setConfiguration(this._readConfiguration(this._model, this._codeEditor));
this._updatePropertiesNow(null);
}));
this.setCodeEditor(codeEditor);
this._setConfiguration(this._readConfiguration(this._model, this._codeEditor));
this._updatePropertiesNow(null);
}
public dispose(): void {
@@ -91,6 +190,21 @@ export class MainThreadTextEditor {
this._codeEditorListeners = dispose(this._codeEditorListeners);
}
private _updatePropertiesNow(selectionChangeSource: string): void {
this._setProperties(
MainThreadTextEditorProperties.readFromEditor(this._properties, this._model, this._codeEditor),
selectionChangeSource
);
}
private _setProperties(newProperties: MainThreadTextEditorProperties, selectionChangeSource: string): void {
const delta = newProperties.generateDelta(this._properties, selectionChangeSource);
this._properties = newProperties;
if (delta) {
this._onPropertiesChanged.fire(delta);
}
}
public getId(): string {
return this._id;
}
@@ -122,27 +236,20 @@ export class MainThreadTextEditor {
this.setCodeEditor(null);
}));
let forwardSelection = (event?: ICursorSelectionChangedEvent) => {
this._lastSelection = this._codeEditor.getSelections();
this._onSelectionChanged.fire({
selections: this._lastSelection,
source: event && event.source
});
};
this._codeEditorListeners.push(this._codeEditor.onDidChangeCursorSelection(forwardSelection));
if (!Selection.selectionsArrEqual(this._lastSelection, this._codeEditor.getSelections())) {
forwardSelection();
}
this._codeEditorListeners.push(this._codeEditor.onDidFocusEditor(() => {
this._focusTracker.onGainedFocus();
}));
this._codeEditorListeners.push(this._codeEditor.onDidBlurEditor(() => {
this._focusTracker.onLostFocus();
}));
this._codeEditorListeners.push(this._codeEditor.onDidChangeConfiguration(() => {
this._setConfiguration(this._readConfiguration(this._model, this._codeEditor));
this._codeEditorListeners.push(this._codeEditor.onDidChangeCursorSelection((e) => {
this._updatePropertiesNow(e.source);
}));
this._setConfiguration(this._readConfiguration(this._model, this._codeEditor));
this._codeEditorListeners.push(this._codeEditor.onDidChangeConfiguration(() => {
this._updatePropertiesNow(null);
}));
this._updatePropertiesNow(null);
}
}
@@ -150,19 +257,12 @@ export class MainThreadTextEditor {
return !!this._codeEditor;
}
public get onSelectionChanged(): Event<ISelectionChangeEvent> {
return this._onSelectionChanged.event;
public getProperties(): MainThreadTextEditorProperties {
return this._properties;
}
public get onConfigurationChanged(): Event<IResolvedTextEditorConfiguration> {
return this._onConfigurationChanged.event;
}
public getSelections(): Selection[] {
if (this._codeEditor) {
return this._codeEditor.getSelections();
}
return this._lastSelection;
public get onPropertiesChanged(): Event<IEditorPropertiesChangeData> {
return this._onPropertiesChanged.event;
}
public setSelections(selections: ISelection[]): void {
@@ -170,11 +270,12 @@ export class MainThreadTextEditor {
this._codeEditor.setSelections(selections);
return;
}
this._lastSelection = selections.map(Selection.liftSelection);
}
public getConfiguration(): IResolvedTextEditorConfiguration {
return this._configuration;
const newSelections = selections.map(Selection.liftSelection);
this._setProperties(
new MainThreadTextEditorProperties(newSelections, this._properties.options),
null
);
}
private _setIndentConfiguration(newConfiguration: ITextEditorConfigurationUpdate): void {
@@ -280,47 +381,6 @@ export class MainThreadTextEditor {
}
}
private _readConfiguration(model: ITextModel, codeEditor: ICodeEditor): IResolvedTextEditorConfiguration {
if (model.isDisposed()) {
// shutdown time
return this._configuration;
}
let cursorStyle = this._configuration ? this._configuration.cursorStyle : TextEditorCursorStyle.Line;
let lineNumbers: TextEditorLineNumbersStyle = this._configuration ? this._configuration.lineNumbers : TextEditorLineNumbersStyle.On;
if (codeEditor) {
let codeEditorOpts = codeEditor.getConfiguration();
cursorStyle = codeEditorOpts.viewInfo.cursorStyle;
switch (codeEditorOpts.viewInfo.renderLineNumbers) {
case RenderLineNumbersType.Off:
lineNumbers = TextEditorLineNumbersStyle.Off;
break;
case RenderLineNumbersType.Relative:
lineNumbers = TextEditorLineNumbersStyle.Relative;
break;
default:
lineNumbers = TextEditorLineNumbersStyle.On;
break;
}
}
let indent = model.getOptions();
return {
insertSpaces: indent.insertSpaces,
tabSize: indent.tabSize,
cursorStyle: cursorStyle,
lineNumbers: lineNumbers
};
}
private _setConfiguration(newConfiguration: IResolvedTextEditorConfiguration): void {
if (configurationsEqual(this._configuration, newConfiguration)) {
return;
}
this._configuration = newConfiguration;
this._onConfigurationChanged.fire(this._configuration);
}
public isFocused(): boolean {
if (this._codeEditor) {
return this._codeEditor.isFocused();