Skip to content

Commit 9237465

Browse files
committed
Added support for Virtual Space
1 parent 1c728e8 commit 9237465

17 files changed

+263
-109
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,4 @@ dist
4545
.github/prompts/*.local.prompt.md
4646
.agents/skills/.local/
4747
.github/skills/.local/
48+
.antigravity/

src/vs/editor/browser/coreCommands.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,7 @@ export namespace CoreNavigationCommands {
374374
export interface MoveCommandOptions extends BaseCommandOptions {
375375
position: IPosition;
376376
viewPosition?: IPosition;
377+
leftoverVisibleColumns?: number;
377378
revealType: NavigationCommandRevealType;
378379
}
379380

@@ -395,7 +396,7 @@ export namespace CoreNavigationCommands {
395396
args.source,
396397
CursorChangeReason.Explicit,
397398
[
398-
CursorMoveCommands.moveTo(viewModel, viewModel.getPrimaryCursorState(), this._inSelectionMode, args.position, args.viewPosition)
399+
CursorMoveCommands.moveTo(viewModel, viewModel.getPrimaryCursorState(), this._inSelectionMode, args.position, args.viewPosition, args.leftoverVisibleColumns)
399400
]
400401
);
401402
if (cursorStateChanged && args.revealType !== NavigationCommandRevealType.None) {

src/vs/editor/browser/view/viewController.ts

Lines changed: 48 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import * as platform from '../../../base/common/platform.js';
1717
import { StandardTokenType } from '../../common/encodedTokenAttributes.js';
1818
import { ITextModel } from '../../common/model.js';
1919
import { containsRTL } from '../../../base/common/strings.js';
20+
import { CursorColumns } from '../../common/core/cursorColumns.js';
2021

2122
export interface IMouseDispatchData {
2223
position: Position;
@@ -286,7 +287,7 @@ export class ViewController {
286287
} else {
287288
// Do multi-cursor operations only when purely alt is pressed
288289
if (data.inSelectionMode) {
289-
this._lastCursorMoveToSelect(data.position, data.revealType);
290+
this._lastCursorMoveToSelect(data.position, data.mouseColumn, data.revealType);
290291
} else {
291292
this._createCursor(data.position, false);
292293
}
@@ -300,32 +301,63 @@ export class ViewController {
300301
if (columnSelection) {
301302
this._columnSelect(data.position, data.mouseColumn, true);
302303
} else {
303-
this._moveToSelect(data.position, data.revealType);
304+
this._moveToSelect(data.position, data.mouseColumn, data.revealType);
304305
}
305306
}
306307
} else {
307-
this.moveTo(data.position, data.revealType);
308+
this.moveTo(data.position, data.mouseColumn, data.revealType);
308309
}
309310
}
310311
}
311312
}
312313

313-
private _usualArgs(viewPosition: Position, revealType: NavigationCommandRevealType): CoreNavigationCommands.MoveCommandOptions {
314+
private _usualArgs(viewPosition: Position, revealType: NavigationCommandRevealType): CoreNavigationCommands.MoveCommandOptions;
315+
private _usualArgs(viewPosition: Position, mouseColumn: number, revealType: NavigationCommandRevealType): CoreNavigationCommands.MoveCommandOptions;
316+
private _usualArgs(viewPosition: Position, arg2: number | NavigationCommandRevealType, arg3?: NavigationCommandRevealType): CoreNavigationCommands.MoveCommandOptions {
314317
viewPosition = this._validateViewColumn(viewPosition);
318+
let mouseColumn: number;
319+
let revealType: NavigationCommandRevealType;
320+
if (arg3 !== undefined) {
321+
mouseColumn = arg2 as number;
322+
revealType = arg3;
323+
} else {
324+
mouseColumn = CursorColumns.visibleColumnFromColumn(this.viewModel.getLineContent(viewPosition.lineNumber), viewPosition.column, this.viewModel.model.getOptions().tabSize) + 1;
325+
revealType = arg2 as NavigationCommandRevealType;
326+
}
327+
328+
let leftoverVisibleColumns = 0;
329+
if (this.configuration.options.get(EditorOption.virtualSpace)) {
330+
const maxColumn = this.viewModel.getLineMaxColumn(viewPosition.lineNumber);
331+
const maxVisibleColumn = CursorColumns.visibleColumnFromColumn(this.viewModel.getLineContent(viewPosition.lineNumber), maxColumn, this.viewModel.model.getOptions().tabSize);
332+
leftoverVisibleColumns = Math.max(0, (mouseColumn - 1) - maxVisibleColumn);
333+
}
315334
return {
316335
source: 'mouse',
317336
position: this._convertViewToModelPosition(viewPosition),
318337
viewPosition,
338+
leftoverVisibleColumns,
319339
revealType
320340
};
321341
}
322342

323-
public moveTo(viewPosition: Position, revealType: NavigationCommandRevealType): void {
324-
CoreNavigationCommands.MoveTo.runCoreEditorCommand(this.viewModel, this._usualArgs(viewPosition, revealType));
343+
public moveTo(viewPosition: Position, revealType: NavigationCommandRevealType): void;
344+
public moveTo(viewPosition: Position, mouseColumn: number, revealType: NavigationCommandRevealType): void;
345+
public moveTo(viewPosition: Position, arg2: number | NavigationCommandRevealType, arg3?: NavigationCommandRevealType): void {
346+
if (arg3 !== undefined) {
347+
CoreNavigationCommands.MoveTo.runCoreEditorCommand(this.viewModel, this._usualArgs(viewPosition, arg2 as number, arg3));
348+
} else {
349+
CoreNavigationCommands.MoveTo.runCoreEditorCommand(this.viewModel, this._usualArgs(viewPosition, arg2 as NavigationCommandRevealType));
350+
}
325351
}
326352

327-
private _moveToSelect(viewPosition: Position, revealType: NavigationCommandRevealType): void {
328-
CoreNavigationCommands.MoveToSelect.runCoreEditorCommand(this.viewModel, this._usualArgs(viewPosition, revealType));
353+
private _moveToSelect(viewPosition: Position, revealType: NavigationCommandRevealType): void;
354+
private _moveToSelect(viewPosition: Position, mouseColumn: number, revealType: NavigationCommandRevealType): void;
355+
private _moveToSelect(viewPosition: Position, arg2: number | NavigationCommandRevealType, arg3?: NavigationCommandRevealType): void {
356+
if (arg3 !== undefined) {
357+
CoreNavigationCommands.MoveToSelect.runCoreEditorCommand(this.viewModel, this._usualArgs(viewPosition, arg2 as number, arg3));
358+
} else {
359+
CoreNavigationCommands.MoveToSelect.runCoreEditorCommand(this.viewModel, this._usualArgs(viewPosition, arg2 as NavigationCommandRevealType));
360+
}
329361
}
330362

331363
private _columnSelect(viewPosition: Position, mouseColumn: number, doColumnSelect: boolean): void {
@@ -349,8 +381,14 @@ export class ViewController {
349381
});
350382
}
351383

352-
private _lastCursorMoveToSelect(viewPosition: Position, revealType: NavigationCommandRevealType): void {
353-
CoreNavigationCommands.LastCursorMoveToSelect.runCoreEditorCommand(this.viewModel, this._usualArgs(viewPosition, revealType));
384+
private _lastCursorMoveToSelect(viewPosition: Position, revealType: NavigationCommandRevealType): void;
385+
private _lastCursorMoveToSelect(viewPosition: Position, mouseColumn: number, revealType: NavigationCommandRevealType): void;
386+
private _lastCursorMoveToSelect(viewPosition: Position, arg2: number | NavigationCommandRevealType, arg3?: NavigationCommandRevealType): void {
387+
if (arg3 !== undefined) {
388+
CoreNavigationCommands.LastCursorMoveToSelect.runCoreEditorCommand(this.viewModel, this._usualArgs(viewPosition, arg2 as number, arg3));
389+
} else {
390+
CoreNavigationCommands.LastCursorMoveToSelect.runCoreEditorCommand(this.viewModel, this._usualArgs(viewPosition, arg2 as NavigationCommandRevealType));
391+
}
354392
}
355393

356394
private _wordSelect(viewPosition: Position, revealType: NavigationCommandRevealType): void {

src/vs/editor/browser/viewParts/viewCursors/viewCursor.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ export class ViewCursor {
5353
private _isVisible: boolean;
5454

5555
private _position: Position;
56+
private _leftoverVisibleColumns: number;
5657
private _pluralityClass: string;
5758

5859
private _lastRenderedContent: string;
@@ -80,6 +81,7 @@ export class ViewCursor {
8081
this._domNode.setDisplay('none');
8182

8283
this._position = new Position(1, 1);
84+
this._leftoverVisibleColumns = 0;
8385
this._pluralityClass = '';
8486
this.setPlurality(plurality);
8587

@@ -139,13 +141,14 @@ export class ViewCursor {
139141
return true;
140142
}
141143

142-
public onCursorPositionChanged(position: Position, pauseAnimation: boolean): boolean {
144+
public onCursorPositionChanged(position: Position, leftoverVisibleColumns: number, pauseAnimation: boolean): boolean {
143145
if (pauseAnimation) {
144146
this._domNode.domNode.style.transitionProperty = 'none';
145147
} else {
146148
this._domNode.domNode.style.transitionProperty = '';
147149
}
148150
this._position = position;
151+
this._leftoverVisibleColumns = leftoverVisibleColumns;
149152
return true;
150153
}
151154

@@ -192,6 +195,9 @@ export class ViewCursor {
192195
}
193196

194197
let left = visibleRange.left;
198+
if (this._leftoverVisibleColumns > 0 && this._leftoverVisibleColumns < 1000000 && this._context.configuration.options.get(EditorOption.virtualSpace)) {
199+
left += this._leftoverVisibleColumns * this._typicalHalfwidthCharacterWidth;
200+
}
195201
let paddingLeft = 0;
196202
if (width >= 2 && left >= 1) {
197203
// shift the cursor a bit between the characters
@@ -238,7 +244,12 @@ export class ViewCursor {
238244
height = 2;
239245
}
240246

241-
return new ViewCursorRenderData(top, range.left, 0, width, height, textContent, textContentClassName);
247+
let left = range.left;
248+
if (this._leftoverVisibleColumns > 0 && this._leftoverVisibleColumns < 1000000 && this._context.configuration.options.get(EditorOption.virtualSpace)) {
249+
left += this._leftoverVisibleColumns * this._typicalHalfwidthCharacterWidth;
250+
}
251+
252+
return new ViewCursorRenderData(top, left, 0, width, height, textContent, textContentClassName);
242253
}
243254

244255
private _getTokenClassName(position: Position): string {

src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -127,13 +127,13 @@ export class ViewCursors extends ViewPart {
127127
}
128128
return true;
129129
}
130-
private _onCursorPositionChanged(position: Position, secondaryPositions: Position[], reason: CursorChangeReason): void {
130+
private _onCursorPositionChanged(position: Position, leftoverVisibleColumns: number, secondaryPositions: Position[], secondaryLeftoverVisibleColumns: number[], reason: CursorChangeReason): void {
131131
const pauseAnimation = (
132132
this._secondaryCursors.length !== secondaryPositions.length
133133
|| (this._cursorSmoothCaretAnimation === 'explicit' && reason !== CursorChangeReason.Explicit)
134134
);
135135
this._primaryCursor.setPlurality(secondaryPositions.length ? CursorPlurality.MultiPrimary : CursorPlurality.Single);
136-
this._primaryCursor.onCursorPositionChanged(position, pauseAnimation);
136+
this._primaryCursor.onCursorPositionChanged(position, leftoverVisibleColumns, pauseAnimation);
137137
this._updateBlinking();
138138

139139
if (this._secondaryCursors.length < secondaryPositions.length) {
@@ -154,7 +154,7 @@ export class ViewCursors extends ViewPart {
154154
}
155155

156156
for (let i = 0; i < secondaryPositions.length; i++) {
157-
this._secondaryCursors[i].onCursorPositionChanged(secondaryPositions[i], pauseAnimation);
157+
this._secondaryCursors[i].onCursorPositionChanged(secondaryPositions[i], secondaryLeftoverVisibleColumns[i], pauseAnimation);
158158
}
159159

160160
}
@@ -163,7 +163,7 @@ export class ViewCursors extends ViewPart {
163163
for (let i = 0, len = e.selections.length; i < len; i++) {
164164
positions[i] = e.selections[i].getPosition();
165165
}
166-
this._onCursorPositionChanged(positions[0], positions.slice(1), e.reason);
166+
this._onCursorPositionChanged(positions[0], e.leftoverVisibleColumns[0], positions.slice(1), e.leftoverVisibleColumns.slice(1), e.reason);
167167

168168
const selectionIsEmpty = e.selections[0].isEmpty();
169169
if (this._selectionIsEmpty !== selectionIsEmpty) {

src/vs/editor/common/config/editorOptions.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -800,6 +800,10 @@ export interface IEditorOptions {
800800
* Controls whether suggestions allow matches in the middle of the word instead of only at the beginning
801801
*/
802802
matchOnWordStartOnly?: boolean;
803+
/**
804+
* Controls whether the cursor should be allowed to move into virtual space.
805+
*/
806+
virtualSpace?: boolean;
803807
/**
804808
* Control the behavior and rendering of the inline hints.
805809
*/
@@ -5931,6 +5935,10 @@ export const enum EditorOption {
59315935
inertialScroll,
59325936
inlayHints,
59335937
wrapOnEscapedLineFeeds,
5938+
/**
5939+
* Controls whether the cursor should be allowed to move into virtual space.
5940+
*/
5941+
virtualSpace,
59345942
// Leave these at the end (because they have dependencies!)
59355943
effectiveCursorStyle,
59365944
editorClassName,
@@ -6742,6 +6750,10 @@ export const EditorOptions = {
67426750
useShadowDOM: register(new EditorBooleanOption(
67436751
EditorOption.useShadowDOM, 'useShadowDOM', true
67446752
)),
6753+
virtualSpace: register(new EditorBooleanOption(
6754+
EditorOption.virtualSpace, 'virtualSpace', false,
6755+
{ description: nls.localize('virtualSpace', "Controls whether the cursor should be allowed to move into virtual space.") }
6756+
)),
67456757
useTabStops: register(new EditorBooleanOption(
67466758
EditorOption.useTabStops, 'useTabStops', true,
67476759
{ description: nls.localize('useTabStops', "Spaces and tabs are inserted and deleted in alignment with tab stops.") }

src/vs/editor/common/cursor/cursor.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -406,11 +406,13 @@ export class CursorsController extends Disposable {
406406
return false;
407407
}
408408

409+
const cursorStates = this._cursors.getAll();
409410
const selections = this._cursors.getSelections();
410411
const viewSelections = this._cursors.getViewSelections();
412+
const leftoverVisibleColumns = cursorStates.map(s => s.viewState.leftoverVisibleColumns);
411413

412414
// Let the view get the event first.
413-
eventsCollector.emitViewEvent(new ViewCursorStateChangedEvent(viewSelections, selections, reason));
415+
eventsCollector.emitViewEvent(new ViewCursorStateChangedEvent(viewSelections, selections, leftoverVisibleColumns, reason));
414416

415417
// Only after the view has been notified, let the rest of the world know...
416418
if (!oldState
@@ -566,14 +568,19 @@ export class CursorsController extends Disposable {
566568
const charLength = strings.nextCharLength(text, offset);
567569
const chr = text.substr(offset, charLength);
568570

571+
const cursorStates = this._cursors.getAll();
572+
const leftoverVisibleColumns = cursorStates.map(s => s.viewState.leftoverVisibleColumns);
573+
569574
// Here we must interpret each typed character individually
570-
this._executeEditOperation(TypeOperations.typeWithInterceptors(!!this._compositionState, this._prevEditOperationType, this.context.cursorConfig, this._model, this.getSelections(), this.getAutoClosedCharacters(), chr), reason);
575+
this._executeEditOperation(TypeOperations.typeWithInterceptors(!!this._compositionState, this._prevEditOperationType, this.context.cursorConfig, this._model, this.getSelections(), leftoverVisibleColumns, this.getAutoClosedCharacters(), chr), reason);
571576

572577
offset += charLength;
573578
}
574579

575580
} else {
576-
this._executeEditOperation(TypeOperations.typeWithoutInterceptors(this._prevEditOperationType, this.context.cursorConfig, this._model, this.getSelections(), text), reason);
581+
const cursorStates = this._cursors.getAll();
582+
const leftoverVisibleColumns = cursorStates.map(s => s.viewState.leftoverVisibleColumns);
583+
this._executeEditOperation(TypeOperations.typeWithoutInterceptors(this._prevEditOperationType, this.context.cursorConfig, this._model, this.getSelections(), leftoverVisibleColumns, text), reason);
577584
}
578585
}, eventsCollector, source);
579586
}
@@ -601,8 +608,10 @@ export class CursorsController extends Disposable {
601608
public paste(eventsCollector: ViewModelEventsCollector, text: string, pasteOnNewLine: boolean, multicursorText?: string[] | null | undefined, source?: string | null | undefined): void {
602609
const reason = EditSources.cursor({ kind: 'paste', detailedSource: source });
603610

611+
const cursorStates = this._cursors.getAll();
612+
const leftoverVisibleColumns = cursorStates.map(s => s.viewState.leftoverVisibleColumns);
604613
this._executeEdit(() => {
605-
this._executeEditOperation(TypeOperations.paste(this.context.cursorConfig, this._model, this.getSelections(), text, pasteOnNewLine, multicursorText || []), reason);
614+
this._executeEditOperation(TypeOperations.paste(this.context.cursorConfig, this._model, this.getSelections(), leftoverVisibleColumns, text, pasteOnNewLine, multicursorText || []), reason);
606615
}, eventsCollector, source, CursorChangeReason.Paste);
607616
}
608617

src/vs/editor/common/cursor/cursorColumnSelection.ts

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ export class ColumnSelection {
1717

1818
const result: SingleCursorState[] = [];
1919

20-
// console.log(`fromVisibleColumn: ${fromVisibleColumn}, toVisibleColumn: ${toVisibleColumn}`);
21-
2220
for (let i = 0; i < lineCount; i++) {
2321
const lineNumber = fromLineNumber + (reversed ? -i : i);
2422

@@ -27,29 +25,30 @@ export class ColumnSelection {
2725
const visibleStartColumn = config.visibleColumnFromColumn(model, new Position(lineNumber, startColumn));
2826
const visibleEndColumn = config.visibleColumnFromColumn(model, new Position(lineNumber, endColumn));
2927

30-
// console.log(`lineNumber: ${lineNumber}: visibleStartColumn: ${visibleStartColumn}, visibleEndColumn: ${visibleEndColumn}`);
28+
const leftoverStartColumn = Math.max(0, fromVisibleColumn - visibleStartColumn);
29+
const leftoverEndColumn = Math.max(0, toVisibleColumn - visibleEndColumn);
3130

3231
if (isLTR) {
33-
if (visibleStartColumn > toVisibleColumn) {
32+
if (visibleStartColumn + leftoverStartColumn > toVisibleColumn) {
3433
continue;
3534
}
36-
if (visibleEndColumn < fromVisibleColumn) {
35+
if (visibleEndColumn + leftoverEndColumn < fromVisibleColumn) {
3736
continue;
3837
}
3938
}
4039

4140
if (isRTL) {
42-
if (visibleEndColumn > fromVisibleColumn) {
41+
if (visibleEndColumn + leftoverEndColumn > fromVisibleColumn) {
4342
continue;
4443
}
45-
if (visibleStartColumn < toVisibleColumn) {
44+
if (visibleStartColumn + leftoverStartColumn < toVisibleColumn) {
4645
continue;
4746
}
4847
}
4948

5049
result.push(new SingleCursorState(
51-
new Range(lineNumber, startColumn, lineNumber, startColumn), SelectionStartKind.Simple, 0,
52-
new Position(lineNumber, endColumn), 0
50+
new Range(lineNumber, startColumn, lineNumber, startColumn), SelectionStartKind.Simple, leftoverStartColumn,
51+
new Position(lineNumber, endColumn), leftoverEndColumn
5352
));
5453
}
5554

@@ -58,10 +57,14 @@ export class ColumnSelection {
5857
for (let i = 0; i < lineCount; i++) {
5958
const lineNumber = fromLineNumber + (reversed ? -i : i);
6059
const maxColumn = model.getLineMaxColumn(lineNumber);
60+
const maxVisibleColumn = config.visibleColumnFromColumn(model, new Position(lineNumber, maxColumn));
61+
62+
const leftoverStartColumn = Math.max(0, fromVisibleColumn - maxVisibleColumn);
63+
const leftoverEndColumn = Math.max(0, toVisibleColumn - maxVisibleColumn);
6164

6265
result.push(new SingleCursorState(
63-
new Range(lineNumber, maxColumn, lineNumber, maxColumn), SelectionStartKind.Simple, 0,
64-
new Position(lineNumber, maxColumn), 0
66+
new Range(lineNumber, maxColumn, lineNumber, maxColumn), SelectionStartKind.Simple, leftoverStartColumn,
67+
new Position(lineNumber, maxColumn), leftoverEndColumn
6568
));
6669
}
6770
}

src/vs/editor/common/cursor/cursorMoveCommands.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ export class CursorMoveCommands {
252252
));
253253
}
254254

255-
public static moveTo(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean, _position: IPosition, _viewPosition: IPosition | undefined): PartialCursorState {
255+
public static moveTo(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean, _position: IPosition, _viewPosition: IPosition | undefined, leftoverVisibleColumns: number = 0): PartialCursorState {
256256
if (inSelectionMode) {
257257
if (cursor.modelState.selectionStartKind === SelectionStartKind.Word) {
258258
return this.word(viewModel, cursor, inSelectionMode, _position);
@@ -267,7 +267,7 @@ export class CursorMoveCommands {
267267
? viewModel.coordinatesConverter.validateViewPosition(new Position(_viewPosition.lineNumber, _viewPosition.column), position)
268268
: viewModel.coordinatesConverter.convertModelPositionToViewPosition(position)
269269
);
270-
return CursorState.fromViewState(cursor.viewState.move(inSelectionMode, viewPosition.lineNumber, viewPosition.column, 0));
270+
return CursorState.fromViewState(cursor.viewState.move(inSelectionMode, viewPosition.lineNumber, viewPosition.column, leftoverVisibleColumns));
271271
}
272272

273273
public static simpleMove(viewModel: IViewModel, cursors: CursorState[], direction: CursorMove.SimpleMoveDirection, inSelectionMode: boolean, value: number, unit: CursorMove.Unit): PartialCursorState[] | null {

0 commit comments

Comments
 (0)