EditorScroll: Get rid of cursor -> view -> cursor recursion

This commit is contained in:
Alex Dima 2017-04-21 00:35:48 +03:00
parent 17b8819f9b
commit 39608c5cb2
13 changed files with 68 additions and 160 deletions

View file

@ -50,7 +50,6 @@ import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'
import { EditorScrollbar } from 'vs/editor/browser/viewParts/editorScrollbar/editorScrollbar';
import { Minimap } from 'vs/editor/browser/viewParts/minimap/minimap';
import * as viewEvents from 'vs/editor/common/view/viewEvents';
import { CursorMovePosition } from "vs/editor/common/controller/oneCursor";
import { IEditorWhitespace } from "vs/editor/common/viewLayout/whitespaceComputer";
export interface IContentWidgetData {
@ -427,21 +426,9 @@ export class View extends ViewEventHandler {
return false;
}
public onScrollRequest(e: viewEvents.ViewScrollRequestEvent): boolean {
let currentScrollTop = this.layoutProvider.getScrollTop();
let newScrollTop = currentScrollTop + e.deltaLines * this._context.configuration.editor.lineHeight;
this.layoutProvider.setScrollPosition({
scrollTop: newScrollTop
scrollTop: e.desiredScrollTop
});
return e.revealCursor ? this.revealCursor() : false;
}
public onScrollRequest2(e: viewEvents.ViewScrollRequestEvent2): boolean {
this.layoutProvider.setScrollPosition({
scrollTop: e.request.desiredScrollTop
});
return false;
}
private revealCursor(): boolean {
this.triggerCursorHandler('revealCursor', editorCommon.Handler.CursorMove, { to: CursorMovePosition.ViewPortIfOutside });
return false;
}

View file

@ -175,9 +175,6 @@ export class EditorScrollbar extends ViewPart {
public onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean {
return true;
}
public onScrollRequest(e: viewEvents.ViewScrollRequestEvent): boolean {
return false;
}
public onTokensChanged(e: viewEvents.ViewTokensChangedEvent): boolean {
return false;
}

View file

@ -778,6 +778,9 @@ export abstract class CommonCodeEditor extends Disposable implements editorCommo
let viewModelHelper: IViewModelHelper = {
viewModel: this.viewModel,
coordinatesConverter: this.viewModel.coordinatesConverter,
getScrollTop: (): number => {
return this.getScrollTop();
},
getCompletelyVisibleViewRange: (): Range => {
return this._getCompletelyVisibleViewRange();
},

View file

@ -25,7 +25,7 @@ import { ColumnSelection, IColumnSelectResult } from 'vs/editor/common/controlle
import { DeleteOperations } from 'vs/editor/common/controller/cursorDeleteOperations';
import { TypeOperations } from 'vs/editor/common/controller/cursorTypeOperations';
import { TextModelEventType, ModelRawContentChangedEvent, RawContentChangedType } from 'vs/editor/common/model/textModelEvents';
import { CursorEventType, CursorChangeReason, ICursorPositionChangedEvent, VerticalRevealType, ICursorSelectionChangedEvent, ICursorRevealRangeEvent, ICursorScrollRequestEvent, CursorScrollTopRequest } from "vs/editor/common/controller/cursorEvents";
import { CursorEventType, CursorChangeReason, ICursorPositionChangedEvent, VerticalRevealType, ICursorSelectionChangedEvent, ICursorRevealRangeEvent, CursorScrollRequest } from "vs/editor/common/controller/cursorEvents";
import { ICommandHandlerDescription } from "vs/platform/commands/common/commands";
import * as types from 'vs/base/common/types';
@ -1610,6 +1610,25 @@ export class Cursor extends Disposable {
private _doEditorScroll(args: EditorScroll.ParsedArguments, ctx: IMultipleCursorOperationContext): boolean {
const desiredScrollTop = this._computeDesiredScrollTop(args);
if (args.revealCursor) {
// must ensure cursor is in new visible range
const desiredVisibleViewRange = this.context.getCompletelyVisibleViewRangeAtScrollTop(desiredScrollTop);
const r = OneCursorOp.findPositionInViewportIfOutside(this.context, this.cursors.getPrimaryCursor(), desiredVisibleViewRange, false);
this.cursors.setStates([r], false);
}
this._eventEmitter.emit(CursorEventType.CursorScrollRequest, new CursorScrollRequest(
desiredScrollTop
));
ctx.shouldReveal = false;
return true;
}
private _computeDesiredScrollTop(args: EditorScroll.ParsedArguments): number {
if (args.unit === EditorScroll.Unit.Line) {
// scrolling by model lines
const visibleModelRange = this.context.getCompletelyVisibleModelRange();
@ -1624,43 +1643,19 @@ export class Cursor extends Disposable {
}
const desiredTopViewPosition = this.context.convertModelPositionToViewPosition(new Position(desiredTopModelLineNumber, 1));
const desiredScrollTop = this.context.getVerticalOffsetForViewLine(desiredTopViewPosition.lineNumber);
if (args.revealCursor) {
// must ensure cursor is in new visible range
const desiredVisibleViewRange = this.context.getCompletelyVisibleViewRangeAtScrollTop(desiredScrollTop);
const r = OneCursorOp.findPositionInViewportIfOutside(this.context, this.cursors.getPrimaryCursor(), desiredVisibleViewRange, false);
this.cursors.setStates([r], false);
}
this._eventEmitter.emit(CursorEventType.CursorScrollRequest2, new CursorScrollTopRequest(
desiredScrollTop
));
ctx.shouldReveal = false;
return true;
return this.context.getVerticalOffsetForViewLine(desiredTopViewPosition.lineNumber);
}
let up = args.direction === EditorScroll.Direction.Up;
let noOfLines = args.value;
switch (args.unit) {
case EditorScroll.Unit.Page:
noOfLines = this.context.config.pageSize * noOfLines;
break;
case EditorScroll.Unit.HalfPage:
noOfLines = Math.round(this.context.config.pageSize / 2) * noOfLines;
break;
let noOfLines: number;
if (args.unit === EditorScroll.Unit.Page) {
noOfLines = this.context.config.pageSize * args.value;
} else if (args.unit === EditorScroll.Unit.HalfPage) {
noOfLines = Math.round(this.context.config.pageSize / 2) * args.value;
} else {
noOfLines = args.value;
}
this.emitCursorScrollRequest((up ? -1 : 1) * noOfLines, args.revealCursor);
return true;
}
private emitCursorScrollRequest(deltaLines: number, revealCursor: boolean): void {
var e: ICursorScrollRequestEvent = {
deltaLines,
revealCursor
};
this._eventEmitter.emit(CursorEventType.CursorScrollRequest, e);
const deltaLines = (args.direction === EditorScroll.Direction.Up ? -1 : 1) * noOfLines;
return this.context.getScrollTop() + deltaLines * this.context.config.lineHeight;
}
private _undo(ctx: IMultipleCursorOperationContext): boolean {

View file

@ -28,6 +28,7 @@ export class CursorConfiguration {
public readonly insertSpaces: boolean;
public readonly oneIndent: string;
public readonly pageSize: number;
public readonly lineHeight: number;
public readonly useTabStops: boolean;
public readonly wordSeparators: string;
public readonly autoClosingBrackets: boolean;
@ -42,6 +43,7 @@ export class CursorConfiguration {
|| e.wordSeparators
|| e.autoClosingBrackets
|| e.useTabStops
|| e.lineHeight
);
}
@ -57,6 +59,7 @@ export class CursorConfiguration {
this.insertSpaces = modelOptions.insertSpaces;
this.oneIndent = oneIndent;
this.pageSize = Math.floor(c.layoutInfo.height / c.fontInfo.lineHeight) - 2;
this.lineHeight = c.lineHeight;
this.useTabStops = c.useTabStops;
this.wordSeparators = c.wordSeparators;
this.autoClosingBrackets = c.autoClosingBrackets;

View file

@ -17,7 +17,6 @@ export const CursorEventType = {
CursorSelectionChanged: 'selectionChanged',
CursorRevealRange: 'revealRange',
CursorScrollRequest: 'scrollRequest',
CursorScrollRequest2: 'scrollRequest2',
};
/**
@ -150,23 +149,7 @@ export interface ICursorRevealRangeEvent {
/**
* @internal
*/
export interface ICursorScrollRequestEvent {
readonly deltaLines: number;
readonly revealCursor: boolean;
}
/**
* @internal
*/
export const enum CursorScrollRequestType {
Absolute = 1
}
/**
* @internal
*/
export class CursorScrollTopRequest {
public readonly type = CursorScrollRequestType.Absolute;
export class CursorScrollRequest {
public readonly desiredScrollTop: number;
@ -174,8 +157,3 @@ export class CursorScrollTopRequest {
this.desiredScrollTop = desiredScrollTop;
}
}
/**
* @internal
*/
export type CursorScrollRequest = CursorScrollTopRequest;

View file

@ -178,6 +178,8 @@ export interface IViewModelHelper {
viewModel: ICursorSimpleModel;
getScrollTop(): number;
getCompletelyVisibleViewRange(): Range;
getCompletelyVisibleViewRangeAtScrollTop(scrollTop: number): Range;
@ -231,6 +233,10 @@ export class CursorContext {
return this._coordinatesConverter.convertModelRangeToViewRange(modelRange);
}
public getScrollTop(): number {
return this._viewModelHelper.getScrollTop();
}
public getCompletelyVisibleViewRange(): Range {
return this._viewModelHelper.getCompletelyVisibleViewRange();
}
@ -252,43 +258,6 @@ export class CursorContext {
public getVerticalOffsetForViewLine(viewLineNumber: number): number {
return this._viewModelHelper.getVerticalOffsetForViewLineNumber(viewLineNumber);
}
public getRangeToRevealModelLinesBeforeViewPortTop(noOfLinesBeforeTop: number): Range {
let visibleModelRange = this.getCompletelyVisibleModelRange();
let startLineNumber: number;
if (this.model.getLineMinColumn(visibleModelRange.startLineNumber) !== visibleModelRange.startColumn) {
// Start line is partially visible by wrapping so reveal start line
startLineNumber = visibleModelRange.startLineNumber;
} else {
// Reveal previous line
startLineNumber = visibleModelRange.startLineNumber - 1;
}
startLineNumber -= (noOfLinesBeforeTop - 1);
startLineNumber = this.model.validateRange({ startLineNumber, startColumn: 1, endLineNumber: startLineNumber, endColumn: 1 }).startLineNumber;
let startColumn = this.model.getLineMinColumn(startLineNumber);
let endColumn = this.model.getLineMaxColumn(visibleModelRange.startLineNumber);
return new Range(startLineNumber, startColumn, startLineNumber, endColumn);
}
public getRangeToRevealModelLinesAfterViewPortBottom(noOfLinesAfterBottom: number): Range {
let visibleModelRange = this.getCompletelyVisibleModelRange();
// Last line in the view port is not considered revealed because scroll bar would cover it
// Hence consider last line to reveal in the range
let startLineNumber = visibleModelRange.endLineNumber + (noOfLinesAfterBottom - 1);
startLineNumber = this.model.validateRange({ startLineNumber, startColumn: 1, endLineNumber: startLineNumber, endColumn: 1 }).startLineNumber;
let startColumn = this.model.getLineMinColumn(startLineNumber);
let endColumn = this.model.getLineMaxColumn(startLineNumber);
return new Range(startLineNumber, startColumn, startLineNumber, endColumn);
}
public isLastLineVisibleInViewPort(): boolean {
return this.viewModel.getLineCount() <= this.getCompletelyVisibleViewRange().getEndPosition().lineNumber;
}
}
export interface IOneCursorState {

View file

@ -9,7 +9,7 @@ import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import { ScrollEvent } from 'vs/base/common/scrollable';
import { IViewConfigurationChangedEvent, IConfigurationChangedEvent } from "vs/editor/common/config/editorOptions";
import { VerticalRevealType, CursorScrollRequest } from "vs/editor/common/controller/cursorEvents";
import { VerticalRevealType } from "vs/editor/common/controller/cursorEvents";
export const enum ViewEventType {
ViewConfigurationChanged = 1,
@ -25,10 +25,9 @@ export const enum ViewEventType {
ViewRevealRangeRequest = 11,
ViewScrollChanged = 12,
ViewScrollRequest = 13,
ViewScrollRequest2 = 14,
ViewTokensChanged = 15,
ViewTokensColorsChanged = 16,
ViewZonesChanged = 17,
ViewTokensChanged = 14,
ViewTokensColorsChanged = 15,
ViewZonesChanged = 16,
}
export class ViewConfigurationChangedEvent {
@ -244,23 +243,10 @@ export class ViewScrollRequestEvent {
public readonly type = ViewEventType.ViewScrollRequest;
public readonly deltaLines: number;
public readonly revealCursor: boolean;
public readonly desiredScrollTop: number;
constructor(deltaLines: number, revealCursor: boolean) {
this.deltaLines = deltaLines;
this.revealCursor = revealCursor;
}
}
export class ViewScrollRequestEvent2 {
public readonly type = ViewEventType.ViewScrollRequest2;
public readonly request: CursorScrollRequest;
constructor(request: CursorScrollRequest) {
this.request = request;
constructor(desiredScrollTop: number) {
this.desiredScrollTop = desiredScrollTop;
}
}
@ -316,7 +302,6 @@ export type ViewEvent = (
| ViewRevealRangeRequestEvent
| ViewScrollChangedEvent
| ViewScrollRequestEvent
| ViewScrollRequestEvent2
| ViewTokensChangedEvent
| ViewTokensColorsChangedEvent
| ViewZonesChangedEvent

View file

@ -198,8 +198,15 @@ export class LayoutProvider extends Disposable implements IViewLayout {
return this._linesLayout.getLinesViewportData(visibleBox.top, visibleBox.top + visibleBox.height);
}
public getLinesViewportDataAtScrollTop(scrollTop: number): IPartialViewLinesViewportData {
const visibleBox = this.getCurrentViewport();
return this._linesLayout.getLinesViewportData(scrollTop, scrollTop + visibleBox.height);
// do some minimal validations on scrollTop
const scrollState = this._scrollable.getState();
if (scrollTop + scrollState.height > scrollState.scrollHeight) {
scrollTop = scrollState.scrollHeight - scrollState.height;
}
if (scrollTop < 0) {
scrollTop = 0;
}
return this._linesLayout.getLinesViewportData(scrollTop, scrollTop + scrollState.height);
}
public getWhitespaceViewportData(): IViewWhitespaceViewportData[] {
const visibleBox = this.getCurrentViewport();

View file

@ -70,10 +70,7 @@ export class ViewEventHandler extends Disposable {
public onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean {
return false;
}
public onScrollRequest(e: viewEvents.ViewScrollRequestEvent): boolean {
return false;
}
public onScrollRequest2(e: viewEvents.ViewScrollRequestEvent2): boolean {
public onScrollRequest(e: viewEvents.ViewScrollRequestEvent): boolean { // TODO@cursor
return false;
}
public onTokensChanged(e: viewEvents.ViewTokensChangedEvent): boolean {
@ -175,12 +172,6 @@ export class ViewEventHandler extends Disposable {
}
break;
case viewEvents.ViewEventType.ViewScrollRequest2:
if (this.onScrollRequest2(e)) {
shouldRender = true;
}
break;
case viewEvents.ViewEventType.ViewTokensChanged:
if (this.onTokensChanged(e)) {
shouldRender = true;

View file

@ -9,7 +9,7 @@ import { Position } from 'vs/editor/common/core/position';
import { ICoordinatesConverter, ViewEventsCollector } from 'vs/editor/common/viewModel/viewModel';
import { Selection } from 'vs/editor/common/core/selection';
import * as viewEvents from 'vs/editor/common/view/viewEvents';
import { ICursorRevealRangeEvent, ICursorScrollRequestEvent, CursorScrollRequest } from "vs/editor/common/controller/cursorEvents";
import { ICursorRevealRangeEvent, CursorScrollRequest } from "vs/editor/common/controller/cursorEvents";
export interface ICursorPositionChangedEvent {
readonly position: Position;
@ -111,11 +111,7 @@ export class ViewModelCursors {
}
}
public onCursorScrollRequest(eventsCollector: ViewEventsCollector, e: ICursorScrollRequestEvent): void {
eventsCollector.emit(new viewEvents.ViewScrollRequestEvent(e.deltaLines, e.revealCursor));
}
public onCursorScrollRequest2(eventsCollector: ViewEventsCollector, e: CursorScrollRequest): void {
eventsCollector.emit(new viewEvents.ViewScrollRequestEvent2(e));
public onCursorScrollRequest(eventsCollector: ViewEventsCollector, e: CursorScrollRequest): void {
eventsCollector.emit(new viewEvents.ViewScrollRequestEvent(e.desiredScrollTop));
}
}

View file

@ -22,7 +22,7 @@ import * as errors from 'vs/base/common/errors';
import { MinimapTokensColorTracker } from 'vs/editor/common/view/minimapCharRenderer';
import * as textModelEvents from 'vs/editor/common/model/textModelEvents';
import { WrappingIndent, IConfigurationChangedEvent } from "vs/editor/common/config/editorOptions";
import { CursorEventType, ICursorPositionChangedEvent, VerticalRevealType, ICursorSelectionChangedEvent, ICursorRevealRangeEvent, ICursorScrollRequestEvent, CursorScrollRequest } from "vs/editor/common/controller/cursorEvents";
import { CursorEventType, ICursorPositionChangedEvent, VerticalRevealType, ICursorSelectionChangedEvent, ICursorRevealRangeEvent, CursorScrollRequest } from "vs/editor/common/controller/cursorEvents";
import { Cursor } from "vs/editor/common/controller/cursor";
const ConfigurationChanged = 'configurationChanged';
@ -398,13 +398,8 @@ export class ViewModel extends Disposable implements IViewModel {
break;
}
case CursorEventType.CursorScrollRequest: {
const e = <ICursorScrollRequestEvent>data;
this.cursors.onCursorScrollRequest(eventsCollector, e);
break;
}
case CursorEventType.CursorScrollRequest2: {
const e = <CursorScrollRequest>data;
this.cursors.onCursorScrollRequest2(eventsCollector, e);
this.cursors.onCursorScrollRequest(eventsCollector, e);
break;
}
case ConfigurationChanged: {

View file

@ -51,6 +51,8 @@ export function viewModelHelper(model: IModel): IViewModelHelper {
},
},
getScrollTop: (): number => 0,
getCompletelyVisibleViewRange: () => null,
getCompletelyVisibleViewRangeAtScrollTop: (scrollTop: number) => null,