Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
14 changes: 3 additions & 11 deletions src/configuration/decoration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,17 +180,9 @@ class DecorationImpl {

public load(configuration: IConfiguration) {
this.default = vscode.window.createTextEditorDecorationType({
backgroundColor: new vscode.ThemeColor('editorCursor.foreground'),
borderColor: new vscode.ThemeColor('editorCursor.foreground'),
dark: {
color: 'rgb(81,80,82)',
},
light: {
// used for light colored themes
color: 'rgb(255, 255, 255)',
},
borderStyle: 'solid',
borderWidth: '1px',
// Use outline instead of background to show the cursor without decorating the character itself.
// The character remains perfectly visible without any blinking effect on the text.
outlineColor: new vscode.ThemeColor('editorCursor.foreground'),
});

const searchHighlightBackgroundColor = configuration.searchHighlightColor
Expand Down
4 changes: 3 additions & 1 deletion src/mode/mode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@ export function getCursorStyle(cursorType: VSCodeVimCursorType) {
case VSCodeVimCursorType.UnderlineThin:
return vscode.TextEditorCursorStyle.UnderlineThin;
case VSCodeVimCursorType.TextDecoration:
return vscode.TextEditorCursorStyle.LineThin;
// Visual mode uses TextDecoration for fake block cursor rendering,
// but the actual VS Code cursor should be Block (solid) to indicate non-insert mode
return vscode.TextEditorCursorStyle.Block;
case VSCodeVimCursorType.Native:
default:
return vscode.TextEditorCursorStyle.Block;
Expand Down
54 changes: 34 additions & 20 deletions src/mode/modeHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -738,13 +738,18 @@ export class ModeHandler implements vscode.Disposable, IModeHandler {
// including the last character we will at the end of 'runAction' shift our stop position
// to the right. So here we shift it back by one so that our actions have our correct
// position instead of the position sent to VSCode.
if (this.vimState.currentMode === Mode.Visual) {
this.vimState.cursors = this.vimState.cursors.map((c) =>
c.start.isBefore(c.stop) ? c.withNewStop(c.stop.getLeftThroughLineBreaks(true)) : c,
);
}

// Make sure all cursors are within the document's bounds before running any action
// NOTE: Visual mode uses text decoration for cursor rendering, so we skip shifts to prevent
// double cursor artifacts. VisualLine and VisualBlock still need the shifts.
if (this.vimState.currentMode !== Mode.Visual) {
if (
this.vimState.currentMode === Mode.VisualLine ||
this.vimState.currentMode === Mode.VisualBlock
) {
this.vimState.cursors = this.vimState.cursors.map((c) =>
c.start.isBefore(c.stop) ? c.withNewStop(c.stop.getLeftThroughLineBreaks(true)) : c,
);
}
} // Make sure all cursors are within the document's bounds before running any action
// It's not 100% clear to me that this is the correct place to do this, but it should solve a lot of issues
this.vimState.cursors = this.vimState.cursors.map((c) => c.validate(this.vimState.document));

Expand Down Expand Up @@ -868,16 +873,23 @@ export class ModeHandler implements vscode.Disposable, IModeHandler {
// for VSCode to select including the last character we shift our stop position to the
// right now that all steps that need that position have already run. On the next action
// we will shift it back again on the start of 'runAction'.
if (this.vimState.currentMode === Mode.Visual) {
this.vimState.cursors = this.vimState.cursors.map((c) =>
c.start.isBeforeOrEqual(c.stop)
? c.withNewStop(
c.stop.isLineEnd(this.vimState.document)
? c.stop.getRightThroughLineBreaks()
: c.stop.getRight(),
)
: c,
);
// NOTE: Visual mode uses text decoration for cursor rendering, so we skip shifts to prevent
// double cursor artifacts. VisualLine and VisualBlock still need the shifts.
if (this.vimState.currentMode !== Mode.Visual && ranAction) {
if (
this.vimState.currentMode === Mode.VisualLine ||
this.vimState.currentMode === Mode.VisualBlock
) {
this.vimState.cursors = this.vimState.cursors.map((c) =>
c.start.isBeforeOrEqual(c.stop)
? c.withNewStop(
c.stop.isLineEnd(this.vimState.document)
? c.stop.getRightThroughLineBreaks()
: c.stop.getRight(),
)
: c,
);
}
}

// We've run a complete action sequence - wipe the slate clean with a new RecordedState
Expand Down Expand Up @@ -1501,7 +1513,9 @@ export class ModeHandler implements vscode.Disposable, IModeHandler {
if (this.currentMode === Mode.Visual) {
for (const { start: cursorStart, stop: cursorStop } of this.vimState.cursors) {
if (cursorStart.isBefore(cursorStop)) {
cursorRange.push(new vscode.Range(cursorStop.getLeft(), cursorStop));
// In Visual mode, cursorStop is at the actual last character position (not shifted).
// We render from cursorStop to cursorStop.getRight() to highlight the current character.
cursorRange.push(new vscode.Range(cursorStop, cursorStop.getRight()));
} else {
cursorRange.push(new vscode.Range(cursorStop, cursorStop.getRight()));
}
Expand Down Expand Up @@ -1691,9 +1705,9 @@ function getCursorType(vimState: VimState, mode: Mode): VSCodeVimCursorType {
case Mode.Visual:
return VSCodeVimCursorType.TextDecoration;
case Mode.VisualBlock:
return VSCodeVimCursorType.TextDecoration;
return VSCodeVimCursorType.Native;
case Mode.VisualLine:
return VSCodeVimCursorType.TextDecoration;
return VSCodeVimCursorType.Native;
case Mode.SearchInProgressMode:
return VSCodeVimCursorType.UnderlineThin;
case Mode.CommandlineInProgress:
Expand Down