8000 Implement virtual space by x17jiri · Pull Request #228680 · microsoft/vscode · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Implement virtual space #228680

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 29 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
8000 e3d3ec4
Implement virtual space
x17jiri Sep 15, 2024
0720dc0
fix hover
x17jiri Sep 27, 2024
31f59da
fix move lines command
x17jiri Oct 18, 2024
2fd5f23
Merge branch 'main' into virtual-space-4
alexdima Oct 29, 2024
0fbb47e
Merge branch 'main' into virtual-space-4
alexdima Nov 6, 2024
ff83138
Redesign
x17jiri Nov 25, 2024
e0be1ab
correction of move right
x17jiri Nov 25, 2024
9043922
fix rendering of selection when the first line starts on virtual whit…
x17jiri Nov 25, 2024
b5223b2
fix status bar position
x17jiri Nov 25, 2024
9959ecb
disable in simple widgets
x17jiri Nov 25, 2024
5b06a0a
preserve selections during "format document"
x17jiri Nov 27, 2024
6dc9422
make going out of selection with up/down more consistent
x17jiri Nov 27, 2024
fb1eba0
fix right arrow at the end of the line if there is copilot hint
x17jiri Nov 27, 2024
63e60d9
small refactorings and readability improvements
x17jiri Nov 28, 2024
51f5f8e
1. fix scrolling when line wrapping is on
x17jiri Nov 28, 2024
c51b95a
Merge branch 'main' into virtual-space-4
x17jiri Nov 30, 2024
b971651
Fix code that materializes virtual space to handle null
x17jiri Nov 29, 2024
c547751
more updates to disable virtual space on wrapped lines except the las…
x17jiri Nov 29, 2024
55f3c0a
fix unit tests
x17jiri Nov 30, 2024
ec9cd2a
Merge branch 'main' into virtual-space-4
x17jiri Dec 4, 2024
b76cef8
fix up/down to handle wrapped lines with long inlay hints
x17jiri Dec 4, 2024
ef1e5cb
improve conversion model -> view
x17jiri Dec 5, 2024
b909c09
improve mouse column calculation
x17jiri Dec 5, 2024
da7a022
restore virtual space positions during undo
x17jiri Dec 5, 2024
aa5f87b
unit tests
x17jiri Dec 6, 2024
b351bcb
fix overtype
x17jiri Dec 6, 2024
94c3a5b
Merge branch 'main' into virtual-space-4
x17jiri Dec 7, 2024
00c552d
Merge branch 'main' into virtual-space-4
x17jiri Dec 14, 2024
e245f9d
Simplify the PR, breaks the status bar indication for now.
alexdima Jan 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions src/vs/editor/browser/controller/mouseHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,7 @@ class MouseDownOperation extends Disposable {
&& e.detail < 2 // only single click on a selection can work
&& !this._isActive // the mouse is not down yet
&& !this._currentSelection.isEmpty() // we don't drag single cursor
&& (position.type === MouseTargetType.CONTENT_TEXT) // single click on text
&& (position.type === MouseTargetType.CONTENT_TEXT || position.type === MouseTargetType.CONTENT_EMPTY) // single click on text
&& position.position && this._currentSelection.containsPosition(position.position) // single click on a selection
) {
this._mouseState.isDragAndDrop = true;
Expand Down Expand Up @@ -529,12 +529,12 @@ class MouseDownOperation extends Disposable {
if (viewZoneData) {
const newPosition = this._helpPositionJumpOverViewZone(viewZoneData);
if (newPosition) {
return MouseTarget.createOutsideEditor(mouseColumn, newPosition, 'above', outsideDistance);
return MouseTarget.createOutsideEditor(mouseColumn, newPosition, 0, 'above', outsideDistance);
}
}

const aboveLineNumber = viewLayout.getLineNumberAtVerticalOffset(verticalOffset);
return MouseTarget.createOutsideEditor(mouseColumn, new Position(aboveLineNumber, 1), 'above', outsideDistance);
return MouseTarget.createOutsideEditor(mouseColumn, new Position(aboveLineNumber, 1), 0, 'above', outsideDistance);
}

if (e.posy > editorContent.y + editorContent.height) {
Expand All @@ -544,24 +544,24 @@ class MouseDownOperation extends Disposable {
if (viewZoneData) {
const newPosition = this._helpPositionJumpOverViewZone(viewZoneData);
if (newPosition) {
return MouseTarget.createOutsideEditor(mouseColumn, newPosition, 'below', outsideDistance);
return MouseTarget.createOutsideEditor(mouseColumn, newPosition, 0, 'below', outsideDistance);
}
}

const belowLineNumber = viewLayout.getLineNumberAtVerticalOffset(verticalOffset);
return MouseTarget.createOutsideEditor(mouseColumn, new Position(belowLineNumber, model.getLineMaxColumn(belowLineNumber)), 'below', outsideDistance);
return MouseTarget.createOutsideEditor(mouseColumn, new Position(belowLineNumber, model.getLineMaxColumn(belowLineNumber)), 0, 'below', outsideDistance);
}

const possibleLineNumber = viewLayout.getLineNumberAtVerticalOffset(viewLayout.getCurrentScrollTop() + e.relativePos.y);

if (e.posx < editorContent.x) {
const outsideDistance = editorContent.x - e.posx;
return MouseTarget.createOutsideEditor(mouseColumn, new Position(possibleLineNumber, 1), 'left', outsideDistance);
return MouseTarget.createOutsideEditor(mouseColumn, new Position(possibleLineNumber, 1), 0, 'left', outsideDistance);
}

if (e.posx > editorContent.x + editorContent.width) {
const outsideDistance = e.posx - editorContent.x - editorContent.width;
return MouseTarget.createOutsideEditor(mouseColumn, new Position(possibleLineNumber, model.getLineMaxColumn(possibleLineNumber)), 'right', outsideDistance);
return MouseTarget.createOutsideEditor(mouseColumn, new Position(possibleLineNumber, model.getLineMaxColumn(possibleLineNumber)), 0, 'right', outsideDistance);
}

return null;
Expand All @@ -582,7 +582,7 @@ class MouseDownOperation extends Disposable {
if (t.type === MouseTargetType.CONTENT_VIEW_ZONE || t.type === MouseTargetType.GUTTER_VIEW_ZONE) {
const newPosition = this._helpPositionJumpOverViewZone(t.detail);
if (newPosition) {
return MouseTarget.createViewZone(t.type, t.element, t.mouseColumn, newPosition, t.detail);
return MouseTarget.createViewZone(t.type, t.element, t.mouseColumn, newPosition, 0, t.detail);
}
}

Expand Down Expand Up @@ -748,9 +748,9 @@ class TopBottomDragScrollingOperation extends Disposable {
}
if (!mouseTarget.position || mouseTarget.position.lineNumber !== edgeLineNumber) {
if (this._position.outsidePosition === 'above') {
mouseTarget = MouseTarget.createOutsideEditor(this._position.mouseColumn, new Position(edgeLineNumber, 1), 'above', this._position.outsideDistance);
mouseTarget = MouseTarget.createOutsideEditor(this._position.mouseColumn, new Position(edgeLineNumber, 1), 0, 'above', this._position.outsideDistance);
} else {
mouseTarget = MouseTarget.createOutsideEditor(this._position.mouseColumn, new Position(edgeLineNumber, this._context.viewModel.getLineMaxColumn(edgeLineNumber)), 'below', this._position.outsideDistance);
mouseTarget = MouseTarget.createOutsideEditor(this._position.mouseColumn, new Position(edgeLineNumber, this._context.viewModel.getLineMaxColumn(edgeLineNumber)), 0, 'below', this._position.outsideDistance);
}
}

Expand Down
118 changes: 79 additions & 39 deletions src/vs/editor/browser/controller/mouseTarget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,35 +78,35 @@ export class MouseTarget {
}
return range ?? null;
}
public static createUnknown(element: HTMLElement | null, mouseColumn: number, position: Position | null): IMouseTargetUnknown {
return { type: MouseTargetType.UNKNOWN, element, mouseColumn, position, range: this._deduceRage(position) };
public static createUnknown(element: HTMLElement | null, mouseColumn: number, position: Position | null, leftoverVisibleColumns: number): IMouseTargetUnknown {
return { type: MouseTargetType.UNKNOWN, element, mouseColumn, position, range: this._deduceRage(position), leftoverVisibleColumns };
}
public static createTextarea(element: HTMLElement | null, mouseColumn: number): IMouseTargetTextarea {
return { type: MouseTargetType.TEXTAREA, element, mouseColumn, position: null, range: null };
return { type: MouseTargetType.TEXTAREA, element, mouseColumn, position: null, leftoverVisibleColumns: 0, range: null };
}
public static createMargin(type: MouseTargetType.GUTTER_GLYPH_MARGIN | MouseTargetType.GUTTER_LINE_NUMBERS | MouseTargetType.GUTTER_LINE_DECORATIONS, element: HTMLElement | null, mouseColumn: number, position: Position, range: EditorRange, detail: IMouseTargetMarginData): IMouseTargetMargin {
return { type, element, mouseColumn, position, range, detail };
public static createMargin(type: MouseTargetType.GUTTER_GLYPH_MARGIN | MouseTargetType.GUTTER_LINE_NUMBERS | MouseTargetType.GUTTER_LINE_DECORATIONS, element: HTMLElement | null, mouseColumn: number, position: Position, leftoverVisibleColumns: number, range: EditorRange, detail: IMouseTargetMarginData): IMouseTargetMargin {
return { type, element, mouseColumn, position, leftoverVisibleColumns, range, detail };
}
public static createViewZone(type: MouseTargetType.GUTTER_VIEW_ZONE | MouseTargetType.CONTENT_VIEW_ZONE, element: HTMLElement | null, mouseColumn: number, position: Position, detail: IMouseTargetViewZoneData): IMouseTargetViewZone {
return { type, element, mouseColumn, position, range: this._deduceRage(position), detail };
public static createViewZone(type: MouseTargetType.GUTTER_VIEW_ZONE | MouseTargetType.CONTENT_VIEW_ZONE, element: HTMLElement | null, mouseColumn: number, position: Position, leftoverVisibleColumns: number, detail: IMouseTargetViewZoneData): IMouseTargetViewZone {
return { type, element, mouseColumn, position, leftoverVisibleColumns, range: this._deduceRage(position), detail };
}
public static createContentText(element: HTMLElement | null, mouseColumn: number, position: Position, range: EditorRange | null, detail: IMouseTargetContentTextData): IMouseTargetContentText {
return { type: MouseTargetType.CONTENT_TEXT, element, mouseColumn, position, range: this._deduceRage(position, range), detail };
public static createContentText(element: HTMLElement | null, mouseColumn: number, position: Position, leftoverVisibleColumns: number, range: EditorRange | null, detail: IMouseTargetContentTextData): IMouseTargetContentText {
return { type: MouseTargetType.CONTENT_TEXT, element, mouseColumn, position, leftoverVisibleColumns, range: this._deduceRage(position, range), detail };
}
public static createContentEmpty(element: HTMLElement | null, mouseColumn: number, position: Position, detail: IMouseTargetContentEmptyData): IMouseTargetContentEmpty {
return { type: MouseTargetType.CONTENT_EMPTY, element, mouseColumn, position, range: this._deduceRage(position), detail };
public static createContentEmpty(element: HTMLElement | null, mouseColumn: number, position: Position, leftoverVisibleColumns: number, detail: IMouseTargetContentEmptyData): IMouseTargetContentEmpty {
return { type: MouseTargetType.CONTENT_EMPTY, element, mouseColumn, position, leftoverVisibleColumns, range: this._deduceRage(position), detail };
}
public static createContentWidget(element: HTMLElement | null, mouseColumn: number, detail: string): IMouseTargetContentWidget {
return { type: MouseTargetType.CONTENT_WIDGET, element, mouseColumn, position: null, range: null, detail };
return { type: MouseTargetType.CONTENT_WIDGET, element, mouseColumn, position: null, leftoverVisibleColumns: 0, range: null, detail };
}
public static createScrollbar(element: HTMLElement | null, mouseColumn: number, position: Position): IMouseTargetScrollbar {
return { type: MouseTargetType.SCROLLBAR, element, mouseColumn, position, range: this._deduceRage(position) };
public static createScrollbar(element: HTMLElement | null, mouseColumn: number, position: Position, leftoverVisibleColumns: number): IMouseTargetScrollbar {
return { type: MouseTargetType.SCROLLBAR, element, mouseColumn, position, leftoverVisibleColumns, range: this._deduceRage(position) };
}
public static createOverlayWidget(element: HTMLElement | null, mouseColumn: number, detail: string): IMouseTargetOverlayWidget {
return { type: MouseTargetType.OVERLAY_WIDGET, element, mouseColumn, position: null, range: null, detail };
return { type: MouseTargetType.OVERLAY_WIDGET, element, mouseColumn, position: null, leftoverVisibleColumns: 0, range: null, detail };
}
public static createOutsideEditor(mouseColumn: number, position: Position, outsidePosition: 'above' | 'below' | 'left' | 'right', outsideDistance: number): IMouseTargetOutsideEditor {
return { type: MouseTargetType.OUTSIDE_EDITOR, element: null, mouseColumn, position, range: this._deduceRage(position), outsidePosition, outsideDistance };
public static createOutsideEditor(mouseColumn: number, position: Position, leftoverVisibleColumns: number, outsidePosition: 'above' | 'below' | 'left' | 'right', outsideDistance: number): IMouseTargetOutsideEditor {
return { type: MouseTargetType.OUTSIDE_EDITOR, element: null, mouseColumn, position, leftoverVisibleColumns, range: this._deduceRage(position), outsidePosition, outsideDistance };
}

private static _typeToString(type: MouseTargetType): string {
Expand Down Expand Up @@ -242,7 +242,8 @@ export class HitTestContext {
public readonly viewLinesGpu: ViewLinesGpu | undefined;
public readonly lineHeight: number;
public readonly stickyTabStops: boolean;
public readonly typicalHalfwidthCharacterWidth: number;
public readonly virtualSpaces: boolean;
public readonly spaceWidth: number;
public readonly lastRenderData: PointerHandlerLastRenderData;

private readonly _context: ViewContext;
Expand All @@ -256,7 +257,8 @@ export class HitTestContext {
this.viewLinesGpu = viewHelper.viewLinesGpu;
this.lineHeight = options.get(EditorOption.lineHeight);
this.stickyTabStops = options.get(EditorOption.stickyTabStops);
this.typicalHalfwidthCharacterWidth = options.get(EditorOption.fontInfo).typicalHalfwidthCharacterWidth;
this.virtualSpaces = options.get(EditorOption.virtualSpace);
this.spaceWidth = options.get(EditorOption.fontInfo).spaceWidth;
this.lastRenderData = lastRenderData;
this._context = context;
this._viewHelper = viewHelper;
Expand Down Expand Up @@ -394,8 +396,6 @@ abstract class BareHitTestRequest {
public readonly isInContentArea: boolean;
public readonly mouseContentHorizontalOffset: number;

protected readonly mouseColumn: number;

constructor(ctx: HitTestContext, editorPos: EditorPagePosition, pos: PageCoordinates, relativePos: CoordinatesRelativeToEditor) {
this.editorPos = editorPos;
this.pos = pos;
Expand All @@ -405,7 +405,6 @@ abstract class BareHitTestRequest {
this.mouseContentHorizontalOffset = ctx.getCurrentScrollLeft() + this.relativePos.x - ctx.layoutInfo.contentLeft;
this.isInMarginArea = (this.relativePos.x < ctx.layoutInfo.contentLeft && this.relativePos.x >= ctx.layoutInfo.glyphMarginLeft);
this.isInContentArea = !this.isInMarginArea;
this.mouseColumn = Math.max(0, MouseTargetFactory._getMouseColumn(this.mouseContentHorizontalOffset, ctx.typicalHalfwidthCharacterWidth));
}
}

Expand Down Expand Up @@ -458,40 +457,81 @@ class HitTestRequest extends BareHitTestRequest {
this._useHitTestTarget = true;
}

private _getMouseColumn(position: Position | null = null): number {
if (position && position.column < this._ctx.viewModel.getLineMaxColumn(position.lineNumber)) {
// Most likely, the line contains foreign decorations...
return CursorColumns.visibleColumnFromColumn(this._ctx.viewModel.getLineContent(position.lineNumber), position.column, this._ctx.viewModel.model.getOptions().tabSize) + 1;
private _fixPositions(position: Position): { fixedPosition: Position; leftoverVisibleColumns: number; mouseColumn: number };
private _fixPositions(): { fixedPosition: null; leftoverVisibleColumns: number; mouseColumn: number };
private _fixPositions(position: Position | null): { fixedPosition: Position | null; leftoverVisibleColumns: number; mouseColumn: number };
private _fixPositions(position: Position | null = null): { fixedPosition: Position | null; leftoverVisibleColumns: number; mouseColumn: number } {
if (position) {
const lineNumber = position.lineNumber;
const maxColumn = this._ctx.viewModel.getLineMaxColumn(lineNumber);
const column = Math.min(position.column, maxColumn);
const visibleColumn = 1 + CursorColumns.visibleColumnFromColumn(this._ctx.viewModel.getLineContent(lineNumber), column, this._ctx.viewModel.model.getOptions().tabSize);
if (column < maxColumn) {
return { fixedPosition: position, leftoverVisibleColumns: 0, mouseColumn: visibleColumn };
}

const visibleRange = this._ctx.visibleRangeForPosition(lineNumber, maxColumn);
if (visibleRange !== null) {
const lineWidth = visibleRange.originalLeft;
const spaceWidth = this._ctx.spaceWidth;
const offset = this.mouseContentHorizontalOffset - lineWidth;
const leftoverMouseColumns = Math.max(0, MouseTargetFactory._getMouseColumn(offset, spaceWidth) - 1);
const virtualSpace = leftoverMouseColumns > 0
&& this._ctx.virtualSpaces
// If we are on wrapped line, it's the last view line
&& this._ctx.viewModel.normalizePosition(new Position(lineNumber, maxColumn), PositionAffinity.Right).lineNumber === lineNumber;
const leftoverVisibleColumns = virtualSpace ? leftoverMouseColumns : 0;
return {
fixedPosition: new Position(lineNumber, maxColumn + leftoverVisibleColumns),
leftoverVisibleColumns,
mouseColumn: visibleColumn + leftoverMouseColumns,
};
}
}
return this.mouseColumn;

// fallback
return {
fixedPosition: position,
leftoverVisibleColumns: 0,
mouseColumn: Math.max(0, MouseTargetFactory._getMouseColumn(this.mouseContentHorizontalOffset, this._ctx.spaceWidth)),
};
}

public fulfillUnknown(position: Position | null = null): IMouseTargetUnknown {
return MouseTarget.createUnknown(this.target, this._getMouseColumn(position), position);
const { fixedPosition, leftoverVisibleColumns, mouseColumn } = this._fixPositions(position);
return MouseTarget.createUnknown(this.target, mouseColumn, fixedPosition, leftoverVisibleColumns);
}
public fulfillTextarea(): IMouseTargetTextarea {
return MouseTarget.createTextarea(this.target, this._getMouseColumn());
const { mouseColumn } = this._fixPositions();
return MouseTarget.createTextarea(this.target, mouseColumn);
}
public fulfillMargin(type: MouseTargetType.GUTTER_GLYPH_MARGIN | MouseTargetType.GUTTER_LINE_NUMBERS | MouseTargetType.GUTTER_LINE_DECORATIONS, position: Position, range: EditorRange, detail: IMouseTargetMarginData): IMouseTargetMargin {
return MouseTarget.createMargin(type, this.target, this._getMouseColumn(position), position, range, detail);
const { fixedPosition, leftoverVisibleColumns, mouseColumn } = this._fixPositions(position);
return MouseTarget.createMargin(type, this.target, mouseColumn, fixedPosition, leftoverVisibleColumns, range, detail);
}
public fulfillViewZone(type: MouseTargetType.GUTTER_VIEW_ZONE | MouseTargetType.CONTENT_VIEW_ZONE, position: Position, detail: IMouseTargetViewZoneData): IMouseTargetViewZone {
return MouseTarget.createViewZone(type, this.target, this._getMouseColumn(position), position, detail);
const { fixedPosition, leftoverVisibleColumns, mouseColumn } = this._fixPositions(position);
return MouseTarget.createViewZone(type, this.target, mouseColumn, fixedPosition, leftoverVisibleColumns, detail);
}
public fulfillContentText(position: Position, range: EditorRange | null, detail: IMouseTargetContentTextData): IMouseTargetContentText {
return MouseTarget.createContentText(this.target, this._getMouseColumn(position), position, range, detail);
const { fixedPosition, leftoverVisibleColumns, mouseColumn } = this._fixPositions(position);
return MouseTarget.createContentText(this.target, mouseColumn, fixedPosition, leftoverVisibleColumns, range, detail);
}
public fulfillContentEmpty(position: Position, detail: IMouseTargetContentEmptyData): IMouseTargetContentEmpty {
return MouseTarget.createContentEmpty(this.target, this._getMouseColumn(position), position, detail);
const { fixedPosition, leftoverVisibleColumns, mouseColumn } = this._fixPositions(position);
return MouseTarget.createContentEmpty(this.target, mouseColumn, fixedPosition, leftoverVisibleColumns, detail);
}
public fulfillContentWidget(detail: string): IMouseTargetContentWidget {
return MouseTarget.createContentWidget(this.target, this._getMouseColumn(), detail);
const { mouseColumn } = this._fixPositions();
return MouseTarget.createContentWidget(this.target, mouseColumn, detail);
}
public fulfillScrollbar(position: Position): IMouseTargetScrollbar {
return MouseTarget.createScrollbar(this.target, this._getMouseColumn(position), position);
const { fixedPosition, leftoverVisibleColumns, mouseColumn } = this._fixPositions(position);
return MouseTarget.createScrollbar(this.target, mouseColumn, fixedPosition, leftoverVisibleColumns);
}
public fulfillOverlayWidget(detail: string): IMouseTargetOverlayWidget {
return MouseTarget.createOverlayWidget(this.target, this._getMouseColumn(), detail);
const { mouseColumn } = this._fixPositions();
return MouseTarget.createOverlayWidget(this.target, mouseColumn, detail);
}
}

Expand Down Expand Up @@ -842,14 +882,14 @@ export class MouseTargetFactory {
const options = this._context.configuration.options;
const layoutInfo = options.get(EditorOption.layoutInfo);
const mouseContentHorizontalOffset = this._context.viewLayout.getCurrentScrollLeft() + relativePos.x - layoutInfo.contentLeft;
return MouseTargetFactory._getMouseColumn(mouseContentHorizontalOffset, options.get(EditorOption.fontInfo).typicalHalfwidthCharacterWidth);
return MouseTargetFactory._getMouseColumn(mouseContentHorizontalOffset, options.get(EditorOption.fontInfo).spaceWidth);
}

public static _getMouseColumn(mouseContentHorizontalOffset: number, typicalHalfwidthCharacterWidth: number): number {
public static _getMouseColumn(mouseContentHorizontalOffset: number, columnWidth: number): number {
if (mouseContentHorizontalOffset < 0) {
return 1;
}
const chars = Math.round(mouseContentHorizontalOffset / typicalHalfwidthCharacterWidth);
const chars = Math.round(mouseContentHorizontalOffset / columnWidth);
return (chars + 1);
}

Expand Down
Loading
0