Skip to content
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

feat: allow vscode sync viewport with neovim #919

Merged
merged 12 commits into from Jul 31, 2022
27 changes: 27 additions & 0 deletions runtime/lua/vscode.lua
@@ -0,0 +1,27 @@
local M = {}

---call from vscode to sync viewport with neovim
---@param vscode_topline number the top line of vscode visible range
---@param vscode_endline number the end line of vscode visible range
M.scroll_viewport = function(vscode_topline, vscode_endline)
local scrolloff = vim.wo.scrolloff ~= -1 and vim.wo.scrolloff or vim.o.scrolloff
local current_height = vim.api.nvim_win_get_height(0)
local new_height = vscode_endline - vscode_topline + scrolloff * 2 + 2

-- resize height
if current_height ~= new_height then
vim.api.nvim_win_set_height(0, new_height)
end

local top_line = vim.fn.line('w0')
local diff = top_line - vscode_topline + scrolloff

if diff ~= 0 and (vscode_topline - scrolloff > 0) then
vim.fn.winrestview({
topline = vscode_topline - scrolloff,
})
end
end

_G.vscode = M
return M
88 changes: 20 additions & 68 deletions src/cursor_manager.ts
Expand Up @@ -3,12 +3,14 @@ import { NeovimClient, Window } from "neovim";
import {
commands,
Disposable,
Range,
Selection,
TextEditor,
TextEditorCursorStyle,
TextEditorRevealType,
TextEditorSelectionChangeEvent,
TextEditorSelectionChangeKind,
TextEditorVisibleRangesChangeEvent,
window,
} from "vscode";

Expand Down Expand Up @@ -73,6 +75,7 @@ export class CursorManager
) {
this.disposables.push(window.onDidChangeTextEditorSelection(this.onSelectionChanged));
this.disposables.push(window.onDidChangeVisibleTextEditors(this.onDidChangeVisibleTextEditors));
this.disposables.push(window.onDidChangeTextEditorVisibleRanges(this.onDidChangeVisibleRange));
this.modeManager.onModeChange(this.onModeChange);
}
public dispose(): void {
Expand Down Expand Up @@ -461,6 +464,7 @@ export class CursorManager
value: Math.abs(deltaLine),
select: false,
});
this.scrollNeovim(editor.visibleRanges);
}
if (Math.abs(deltaChar) > 0) {
if (Math.abs(deltaLine) > 0) {
Expand Down Expand Up @@ -576,73 +580,21 @@ export class CursorManager
}
};

// Following lines are enabling vim-style cursor follow on scroll
// although it's working, unfortunately it breaks vscode jumplist when scrolling to definition from outline/etc
// I think it's better ot have more-less usable jumplist than such minor feature at this feature request will be implemented (https://github.com/microsoft/vscode/issues/84351)
// private onChangeVisibleRange = async (e: vscode.TextEditorVisibleRangesChangeEvent): Promise<void> => {
// if (e.textEditor !== vscode.window.activeTextEditor) {
// return;
// }
// const ranges = e.visibleRanges[0];
// if (!ranges) {
// return;
// }
// if (this.shouldIgnoreMouseSelection) {
// return;
// }
// const editorRevealLine = this.textEditorsRevealing.get(e.textEditor);
// if (editorRevealLine) {
// if (editorRevealLine < ranges.start.line || editorRevealLine > ranges.end.line) {
// return;
// }
// this.textEditorsRevealing.delete(e.textEditor);
// }
// if (!this.isInsertMode) {
// this.commitScrolling(e.textEditor);
// }
// };
private scrollNeovim(ranges: readonly Range[]): void {
if (!ranges || ranges.length == 0) {
return;
}
const startLine = ranges[0].start.line + 1;
// when it have fold we need get the last range. it need add 1 line on multiple fold
const endLine = ranges[ranges.length - 1].end.line + ranges.length;

this.client.executeLua("vscode.scroll_viewport(...)", [startLine, endLine]);
}

// private commitScrolling = throttle(
// (e: vscode.TextEditor) => {
// if (vscode.window.activeTextEditor !== e) {
// return;
// }
// const cursor = e.selection.active;
// const visibleRange = e.visibleRanges[0];
// if (!visibleRange) {
// return;
// }
// let updateCursor = false;
// if (cursor.line > visibleRange.end.line) {
// updateCursor = true;
// e.selections = [
// new vscode.Selection(
// visibleRange.end.line,
// cursor.character,
// visibleRange.end.line,
// cursor.character,
// ),
// ];
// } else if (cursor.line < visibleRange.start.line) {
// updateCursor = true;
// e.selections = [
// new vscode.Selection(
// visibleRange.start.line,
// cursor.character,
// visibleRange.start.line,
// cursor.character,
// ),
// ];
// }
// if (updateCursor && e.viewColumn) {
// const winId = this.editorColumnIdToWinId.get(e.viewColumn);
// if (winId) {
// this.updateCursorPositionInNeovim(winId, e.selection.active.line, e.selection.active.character);
// }
// }
// },
// 500,
// { leading: false },
// );
// private commitScrollingFast = throttle(this.updateScreenRowFromScrolling, 200, { leading: false });
private onDidChangeVisibleRange = async (e: TextEditorVisibleRangesChangeEvent): Promise<void> => {
if (e.textEditor !== window.activeTextEditor || !this.modeManager.isNormalMode) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be expanded to also include visual mode? Why is this check needed?

return;
}
this.scrollNeovim(e.visibleRanges);
};
}
4 changes: 2 additions & 2 deletions src/extension.ts
@@ -1,7 +1,7 @@
import * as vscode from "vscode";

import { MainController } from "./main_controller";
import { getNeovimPath, getNeovimInitPath, EXT_ID, EXT_NAME } from "./utils";
import { getNeovimPath, getNeovimInitPath, EXT_ID, EXT_NAME, getCurrentViewPortHeight } from "./utils";

// this method is called when your extension is activated
// your extension is activated the very first time the command is executed
Expand Down Expand Up @@ -45,7 +45,7 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
} as any,
mouseSelection: mouseVisualSelection,
neovimPath: neovimPath,
neovimViewportHeight: 201,
neovimViewportHeight: getCurrentViewPortHeight(vscode.window.activeTextEditor),
useWsl: ext.extensionKind === vscode.ExtensionKind.Workspace ? false : useWsl,
neovimViewportWidth: neovimWidth,
textDecorationsAtTop: textDecorationsAtTop,
Expand Down
7 changes: 2 additions & 5 deletions src/main_controller.ts
Expand Up @@ -24,7 +24,7 @@ import { CommandLineManager } from "./command_line_manager";
import { StatusLineManager } from "./status_line_manager";
import { HighlightManager } from "./highlight_manager";
import { CustomCommandsManager } from "./custom_commands_manager";
import { findLastEvent } from "./utils";
import { findLastEvent, getCurrentViewPortHeight } from "./utils";
import { MutlilineMessagesManager } from "./multiline_messages_manager";

interface RequestResponse {
Expand Down Expand Up @@ -52,9 +52,6 @@ export interface ControllerSettings {
const LOG_PREFIX = "MainController";

export class MainController implements vscode.Disposable {
// to not deal with screenrow positioning, we set height to high value and scrolloff to value / 2. so screenrow will be always constant
// big scrolloff is needed to make sure that editor visible space will be always within virtual vim boundaries, regardless of current
// cursor positioning
private NEOVIM_WIN_HEIGHT = 201;
private NEOVIM_WIN_WIDTH = 1000;

Expand Down Expand Up @@ -183,7 +180,7 @@ export class MainController implements vscode.Disposable {
this.disposables.push(this.modeManager);

this.bufferManager = new BufferManager(this.logger, this.client, {
neovimViewportHeight: 201,
neovimViewportHeight: getCurrentViewPortHeight(vscode.window.activeTextEditor),
neovimViewportWidth: 1000,
});
this.disposables.push(this.bufferManager);
Expand Down
8 changes: 8 additions & 0 deletions src/utils.ts
Expand Up @@ -608,3 +608,11 @@ export function applyEditorDiffOperations(
}
});
}

export function getCurrentViewPortHeight(editor: TextEditor | undefined, defaultValue = 201): number {
if (!editor || editor.visibleRanges.length === 0) {
return defaultValue;
}
//default value scrolloff is 3. It double the value
return editor.visibleRanges[editor.visibleRanges.length - 1].end.line - editor.visibleRanges[0].start.line + 6;
}
28 changes: 8 additions & 20 deletions vim/vscode-neovim.vim
Expand Up @@ -5,6 +5,9 @@ let s:currDir = fnamemodify(resolve(expand('<sfile>:p')), ':h')
" Adjust rtp path
let &runtimepath = &runtimepath . ',' . s:currDir . '/vim-altercmd'

let s:luaPath = fnamemodify(s:currDir, ':h') . '/runtime'
let &runtimepath = &runtimepath . ',' . s:luaPath

" Used to execute vscode command
let s:vscodeCommandEventName = 'vscode-command'
" Used to execute vscode command with some range (the specified range will be selected and the command will be executed on this range)
Expand Down Expand Up @@ -85,26 +88,6 @@ function! VSCodeClearUndo(bufId)
unlet oldlevels
endfunction

" Called from extension to align screen row in neovim after scrolling
" function! VSCodeAlignScreenRow(row)
" let currentRow = winline()
" let diff = abs(currentRow - a:row)
" if diff > 0
" if (a:row - currentRow) < 0
" if diff > 1
" silent! exe "normal! " . diff . "\<C-e>"
" else
" silent! exe "normal! \<C-e>"
" endif
" else
" if diff > 1
" silent! exe "normal! " . diff . "\<C-y>"
" else
" silent! exe "normal! \<C-y>"
" endif
" endif
" endif
" endfunction

" Set text decorations for given ranges. Used in easymotion
function! VSCodeSetTextDecorations(hlName, rowsCols)
Expand Down Expand Up @@ -174,3 +157,8 @@ augroup VscodeGeneral
" Looks like external windows are coming with "set wrap" set automatically, disable them
" autocmd WinNew,WinEnter * :set nowrap
augroup END


lua << EOF
require("vscode")
EOF
2 changes: 0 additions & 2 deletions vim/vscode-options.vim
Expand Up @@ -13,7 +13,6 @@ set nobackup
set nowb
set noswapfile
set noautoread
set scrolloff=100
set conceallevel=0
set nocursorline

Expand Down Expand Up @@ -64,7 +63,6 @@ set nolazyredraw
function s:forceLocalOptions()
setlocal nowrap
setlocal conceallevel=0
setlocal scrolloff=100
setlocal hidden
setlocal bufhidden=hide
setlocal noautowrite
Expand Down