Compare commits
10
Commits
008cac1096
...
7b615d9eda
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7b615d9eda | ||
|
|
111b528272 | ||
|
|
990e567f55 | ||
|
|
955555921e | ||
|
|
17cff16264 | ||
|
|
ea18b8e453 | ||
|
|
6e63acc87e | ||
|
|
5cc37c9f0a | ||
|
|
aacc554f30 | ||
|
|
b87849804c |
No files matched your search
@@ -0,0 +1,10 @@
|
||||
# Contributions to vscode-neovim/vscode-neovim
|
||||
|
||||
Vim mode for VSCode, powered by Neovim
|
||||
|
||||
This repository holds only my commits to the upstream project, with their original author, date and diff.
|
||||
Each of my commits is preceded by a snapshot of the upstream files it touched, so every diff shows exactly what I changed.
|
||||
|
||||
## Merged pull requests
|
||||
|
||||
- [Keep visual selections whole over characters outside the BMP](https://github.com/vscode-neovim/vscode-neovim/pull/2683) (2026-09-23)
|
||||
@@ -177,4 +177,12 @@ function M.win_set_option(win, name, value)
|
||||
return M.set_option_value(name, value, { win = win })
|
||||
end
|
||||
|
||||
---Get a buffer variable safely. Returns nil if the variable does not exist.
|
||||
function M.buf_get_var(buf, name)
|
||||
local ok, value = pcall(api.nvim_buf_get_var, buf, name)
|
||||
if ok then
|
||||
return value
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
||||
@@ -0,0 +1,169 @@
|
||||
|
||||
function! s:split(...) abort
|
||||
let direction = a:1
|
||||
let file = exists('a:2') ? a:2 : ''
|
||||
call VSCodeCall(direction ==# 'h' ? 'workbench.action.splitEditorDown' : 'workbench.action.splitEditorRight')
|
||||
if !empty(file)
|
||||
call VSCodeExtensionNotify('open-file', expand(file), 'all')
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:splitNew(...)
|
||||
let file = a:2
|
||||
call s:split(a:1, empty(file) ? '__vscode_new__' : file)
|
||||
endfunction
|
||||
|
||||
function! s:closeOtherEditors()
|
||||
call VSCodeNotify('workbench.action.closeEditorsInOtherGroups')
|
||||
call VSCodeNotify('workbench.action.closeOtherEditors')
|
||||
endfunction
|
||||
|
||||
function! s:manageEditorHeight(...)
|
||||
let count = a:1
|
||||
let to = a:2
|
||||
for i in range(1, count ? count : 1)
|
||||
call VSCodeNotify(to ==# 'increase' ? 'workbench.action.increaseViewHeight' : 'workbench.action.decreaseViewHeight')
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
function! s:manageEditorWidth(...)
|
||||
let count = a:1
|
||||
let to = a:2
|
||||
for i in range(1, count ? count : 1)
|
||||
call VSCodeNotify(to ==# 'increase' ? 'workbench.action.increaseViewWidth' : 'workbench.action.decreaseViewWidth')
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
" A lone <C-w> runs the builtin window command once 'timeoutlen' passes, and that reads the next key
|
||||
" without mappings, skipping the ones below. Read the key here instead and feed the whole command
|
||||
" again, remapped when one of them handles it.
|
||||
function! s:windowCommand(mode) abort
|
||||
let keys = "\<C-w>" . getcharstr()
|
||||
call feedkeys((v:count ? v:count : '') . keys, (empty(maparg(keytrans(keys), a:mode)) ? 'n' : 'm') . 'i')
|
||||
endfunction
|
||||
|
||||
command! -complete=file -nargs=? Split call <SID>split('h', <q-args>)
|
||||
command! -complete=file -nargs=? Vsplit call <SID>split('v', <q-args>)
|
||||
command! -complete=file -nargs=? New call <SID>split('h', '__vscode_new__')
|
||||
command! -complete=file -nargs=? Vnew call <SID>split('v', '__vscode_new__')
|
||||
command! -bang Only if <q-bang> ==# '!' | call <SID>closeOtherEditors() | else | call VSCodeNotify('workbench.action.joinAllGroups') | endif
|
||||
|
||||
AlterCommand sp[lit] Split
|
||||
AlterCommand vs[plit] Vsplit
|
||||
AlterCommand new New
|
||||
AlterCommand vne[w] Vnew
|
||||
AlterCommand on[ly] Only
|
||||
|
||||
nnoremap <C-w> <Cmd>call <SID>windowCommand('n')<CR>
|
||||
xnoremap <C-w> <Cmd>call <SID>windowCommand('x')<CR>
|
||||
|
||||
" buffer management
|
||||
nnoremap <C-w>n <Cmd>call <SID>splitNew('h', '__vscode_new__')<CR>
|
||||
xnoremap <C-w>n <Cmd>call <SID>splitNew('h', '__vscode_new__')<CR>
|
||||
|
||||
nnoremap <C-w>q <Cmd>call VSCodeNotify('workbench.action.closeActiveEditor')<CR>
|
||||
xnoremap <C-w>q <Cmd>call VSCodeNotify('workbench.action.closeActiveEditor')<CR>
|
||||
nnoremap <C-w>c <Cmd>call VSCodeNotify('workbench.action.closeActiveEditor')<CR>
|
||||
xnoremap <C-w>c <Cmd>call VSCodeNotify('workbench.action.closeActiveEditor')<CR>
|
||||
nnoremap <C-w><C-c> <Cmd>call VSCodeNotify('workbench.action.closeActiveEditor')<CR>
|
||||
xnoremap <C-w><C-c> <Cmd>call VSCodeNotify('workbench.action.closeActiveEditor')<CR>
|
||||
|
||||
" window/splits management
|
||||
nnoremap <C-w>s <Cmd>call <SID>split('h')<CR>
|
||||
xnoremap <C-w>s <Cmd>call <SID>split('h')<CR>
|
||||
nnoremap <C-w><C-s> <Cmd>call <SID>split('h')<CR>
|
||||
xnoremap <C-w><C-s> <Cmd>call <SID>split('h')<CR>
|
||||
|
||||
nnoremap <C-w>v <Cmd>call <SID>split('v')<CR>
|
||||
xnoremap <C-w>v <Cmd>call <SID>split('v')<CR>
|
||||
nnoremap <C-w><C-v> <Cmd>call <SID>split('v')<CR>
|
||||
xnoremap <C-w><C-v> <Cmd>call <SID>split('v')<CR>
|
||||
|
||||
nnoremap <C-w>= <Cmd>call VSCodeNotify('workbench.action.evenEditorWidths')<CR>
|
||||
xnoremap <C-w>= <Cmd>call VSCodeNotify('workbench.action.evenEditorWidths')<CR>
|
||||
nnoremap <C-w>_ <Cmd>call VSCodeNotify('workbench.action.toggleEditorWidths')<CR>
|
||||
xnoremap <C-w>_ <Cmd>call VSCodeNotify('workbench.action.toggleEditorWidths')<CR>
|
||||
|
||||
nnoremap <C-w>+ <Cmd>call <SID>manageEditorHeight(v:count, 'increase')<CR>
|
||||
xnoremap <C-w>+ <Cmd>call <SID>manageEditorHeight(v:count, 'increase')<CR>
|
||||
nnoremap <C-w>- <Cmd>call <SID>manageEditorHeight(v:count, 'decrease')<CR>
|
||||
xnoremap <C-w>- <Cmd>call <SID>manageEditorHeight(v:count, 'decrease')<CR>
|
||||
nnoremap <C-w>> <Cmd>call <SID>manageEditorWidth(v:count, 'increase')<CR>
|
||||
xnoremap <C-w>> <Cmd>call <SID>manageEditorWidth(v:count, 'increase')<CR>
|
||||
nnoremap <C-w>< <Cmd>call <SID>manageEditorWidth(v:count, 'decrease')<CR>
|
||||
xnoremap <C-w>< <Cmd>call <SID>manageEditorWidth(v:count, 'decrease')<CR>
|
||||
|
||||
nnoremap <C-w>o <Cmd>call VSCodeNotify('workbench.action.joinAllGroups')<CR>
|
||||
xnoremap <C-w>o <Cmd>call VSCodeNotify('workbench.action.joinAllGroups')<CR>
|
||||
nnoremap <C-w><C-o> <Cmd>call VSCodeNotify('workbench.action.joinAllGroups')<CR>
|
||||
xnoremap <C-w><C-o> <Cmd>call VSCodeNotify('workbench.action.joinAllGroups')<CR>
|
||||
|
||||
" window navigation
|
||||
nnoremap <C-w>j <Cmd>call VSCodeNotify('workbench.action.navigateDown')<CR>
|
||||
xnoremap <C-w>j <Cmd>call VSCodeNotify('workbench.action.navigateDown')<CR>
|
||||
nnoremap <C-w>k <Cmd>call VSCodeNotify('workbench.action.navigateUp')<CR>
|
||||
xnoremap <C-w>k <Cmd>call VSCodeNotify('workbench.action.navigateUp')<CR>
|
||||
nnoremap <C-w>h <Cmd>call VSCodeNotify('workbench.action.navigateLeft')<CR>
|
||||
xnoremap <C-w>h <Cmd>call VSCodeNotify('workbench.action.navigateLeft')<CR>
|
||||
nnoremap <C-w>l <Cmd>call VSCodeNotify('workbench.action.navigateRight')<CR>
|
||||
xnoremap <C-w>l <Cmd>call VSCodeNotify('workbench.action.navigateRight')<CR>
|
||||
|
||||
nnoremap <C-w><Down> <Cmd>call VSCodeNotify('workbench.action.navigateDown')<CR>
|
||||
xnoremap <C-w><Down> <Cmd>call VSCodeNotify('workbench.action.navigateDown')<CR>
|
||||
nnoremap <C-w><Up> <Cmd>call VSCodeNotify('workbench.action.navigateUp')<CR>
|
||||
xnoremap <C-w><Up> <Cmd>call VSCodeNotify('workbench.action.navigateUp')<CR>
|
||||
nnoremap <C-w><Left> <Cmd>call VSCodeNotify('workbench.action.navigateLeft')<CR>
|
||||
xnoremap <C-w><Left> <Cmd>call VSCodeNotify('workbench.action.navigateLeft')<CR>
|
||||
nnoremap <C-w><Right> <Cmd>call VSCodeNotify('workbench.action.navigateRight')<CR>
|
||||
xnoremap <C-w><Right> <Cmd>call VSCodeNotify('workbench.action.navigateRight')<CR>
|
||||
|
||||
nnoremap <C-w><C-j> <Cmd>call VSCodeNotify('workbench.action.moveEditorToBelowGroup')<CR>
|
||||
xnoremap <C-w><C-j> <Cmd>call VSCodeNotify('workbench.action.moveEditorToBelowGroup')<CR>
|
||||
nnoremap <C-w><C-k> <Cmd>call VSCodeNotify('workbench.action.moveEditorToAboveGroup')<CR>
|
||||
xnoremap <C-w><C-k> <Cmd>call VSCodeNotify('workbench.action.moveEditorToAboveGroup')<CR>
|
||||
nnoremap <C-w><C-h> <Cmd>call VSCodeNotify('workbench.action.moveEditorToLeftGroup')<CR>
|
||||
xnoremap <C-w><C-h> <Cmd>call VSCodeNotify('workbench.action.moveEditorToLeftGroup')<CR>
|
||||
nnoremap <C-w><C-l> <Cmd>call VSCodeNotify('workbench.action.moveEditorToRightGroup')<CR>
|
||||
xnoremap <C-w><C-l> <Cmd>call VSCodeNotify('workbench.action.moveEditorToRightGroup')<CR>
|
||||
|
||||
nnoremap <C-w><C-Down> <Cmd>call VSCodeNotify('workbench.action.moveEditorToBelowGroup')<CR>
|
||||
xnoremap <C-w><C-Down> <Cmd>call VSCodeNotify('workbench.action.moveEditorToBelowGroup')<CR>
|
||||
nnoremap <C-w><C-Up> <Cmd>call VSCodeNotify('workbench.action.moveEditorToAboveGroup')<CR>
|
||||
xnoremap <C-w><C-Up> <Cmd>call VSCodeNotify('workbench.action.moveEditorToAboveGroup')<CR>
|
||||
nnoremap <C-w><C-Left> <Cmd>call VSCodeNotify('workbench.action.moveEditorToLeftGroup')<CR>
|
||||
xnoremap <C-w><C-Left> <Cmd>call VSCodeNotify('workbench.action.moveEditorToLeftGroup')<CR>
|
||||
nnoremap <C-w><C-Right> <Cmd>call VSCodeNotify('workbench.action.moveEditorToRightGroup')<CR>
|
||||
xnoremap <C-w><C-Right> <Cmd>call VSCodeNotify('workbench.action.moveEditorToRightGroup')<CR>
|
||||
|
||||
nnoremap <C-w><S-j> <Cmd>call VSCodeNotify('workbench.action.moveActiveEditorGroupDown')<CR>
|
||||
xnoremap <C-w><S-j> <Cmd>call VSCodeNotify('workbench.action.moveActiveEditorGroupDown')<CR>
|
||||
nnoremap <C-w><S-k> <Cmd>call VSCodeNotify('workbench.action.moveActiveEditorGroupUp')<CR>
|
||||
xnoremap <C-w><S-k> <Cmd>call VSCodeNotify('workbench.action.moveActiveEditorGroupUp')<CR>
|
||||
nnoremap <C-w><S-h> <Cmd>call VSCodeNotify('workbench.action.moveActiveEditorGroupLeft')<CR>
|
||||
xnoremap <C-w><S-h> <Cmd>call VSCodeNotify('workbench.action.moveActiveEditorGroupLeft')<CR>
|
||||
nnoremap <C-w><S-l> <Cmd>call VSCodeNotify('workbench.action.moveActiveEditorGroupRight')<CR>
|
||||
xnoremap <C-w><S-l> <Cmd>call VSCodeNotify('workbench.action.moveActiveEditorGroupRight')<CR>
|
||||
|
||||
nnoremap <C-w><S-Down> <Cmd>call VSCodeNotify('workbench.action.moveActiveEditorGroupDown')<CR>
|
||||
xnoremap <C-w><S-Down> <Cmd>call VSCodeNotify('workbench.action.moveActiveEditorGroupDown')<CR>
|
||||
nnoremap <C-w><S-Up> <Cmd>call VSCodeNotify('workbench.action.moveActiveEditorGroupUp')<CR>
|
||||
xnoremap <C-w><S-Up> <Cmd>call VSCodeNotify('workbench.action.moveActiveEditorGroupUp')<CR>
|
||||
nnoremap <C-w><S-Left> <Cmd>call VSCodeNotify('workbench.action.moveActiveEditorGroupLeft')<CR>
|
||||
xnoremap <C-w><S-Left> <Cmd>call VSCodeNotify('workbench.action.moveActiveEditorGroupLeft')<CR>
|
||||
nnoremap <C-w><S-Right> <Cmd>call VSCodeNotify('workbench.action.moveActiveEditorGroupRight')<CR>
|
||||
xnoremap <C-w><S-Right> <Cmd>call VSCodeNotify('workbench.action.moveActiveEditorGroupRight')<CR>
|
||||
|
||||
nnoremap <C-w>w <Cmd>call VSCodeNotify('workbench.action.focusNextGroup')<CR>
|
||||
xnoremap <C-w>w <Cmd>call VSCodeNotify('workbench.action.focusNextGroup')<CR>
|
||||
nnoremap <C-w><C-w> <Cmd>call VSCodeNotify('workbench.action.focusNextGroup')<CR>
|
||||
xnoremap <C-w><C-w> <Cmd>call VSCodeNotify('workbench.action.focusNextGroup')<CR>
|
||||
nnoremap <C-w>W <Cmd>call VSCodeNotify('workbench.action.focusPreviousGroup')<CR>
|
||||
xnoremap <C-w>W <Cmd>call VSCodeNotify('workbench.action.focusPreviousGroup')<CR>
|
||||
nnoremap <C-w>p <Cmd>call VSCodeNotify('workbench.action.focusPreviousGroup')<CR>
|
||||
xnoremap <C-w>p <Cmd>call VSCodeNotify('workbench.action.focusPreviousGroup')<CR>
|
||||
|
||||
nnoremap <C-w>t <Cmd>call VSCodeNotify('workbench.action.focusFirstEditorGroup')<CR>
|
||||
xnoremap <C-w>t <Cmd>call VSCodeNotify('workbench.action.focusFirstEditorGroup')<CR>
|
||||
nnoremap <C-w>b <Cmd>call VSCodeNotify('workbench.action.focusLastEditorGroup')<CR>
|
||||
xnoremap <C-w>b <Cmd>call VSCodeNotify('workbench.action.focusLastEditorGroup')<CR>
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,109 @@
|
||||
import { strict as assert } from "assert";
|
||||
import path from "path";
|
||||
|
||||
import { NeovimClient } from "neovim";
|
||||
import { Position, window } from "vscode";
|
||||
|
||||
import { wait, waitUntil } from "../../utils";
|
||||
|
||||
import {
|
||||
attachTestNvimClient,
|
||||
closeNvimClient,
|
||||
closeAllActiveEditors,
|
||||
sendVSCodeKeys,
|
||||
assertContent,
|
||||
sendNeovimKeys,
|
||||
openTextDocument,
|
||||
} from "./integrationUtils";
|
||||
|
||||
describe("Jumplist & jump actions & marks", () => {
|
||||
// abc
|
||||
let client: NeovimClient;
|
||||
before(async () => {
|
||||
client = await attachTestNvimClient();
|
||||
});
|
||||
after(async () => {
|
||||
await closeNvimClient(client);
|
||||
await closeAllActiveEditors();
|
||||
});
|
||||
|
||||
it("Jump to definition to another file", async function () {
|
||||
this.retries(3);
|
||||
|
||||
await openTextDocument(path.join(__dirname, "../../../test_fixtures/b.ts"));
|
||||
await wait(2000);
|
||||
|
||||
await sendVSCodeKeys("jjjjjl");
|
||||
await sendVSCodeKeys("gd", 2000);
|
||||
|
||||
await sendNeovimKeys(client, "<C-o>", 500);
|
||||
await assertContent(
|
||||
{
|
||||
cursor: [5, 1],
|
||||
},
|
||||
client,
|
||||
);
|
||||
});
|
||||
|
||||
// currently too flaky
|
||||
it.skip("Jump to definition in same file", async function () {
|
||||
this.retries(3);
|
||||
|
||||
await openTextDocument(path.join(__dirname, "../../../test_fixtures/go-to-def-same-file.ts"));
|
||||
await wait(2000);
|
||||
|
||||
await sendVSCodeKeys("gg049jgd", 2000);
|
||||
await sendVSCodeKeys("jgd", 1000);
|
||||
await assertContent(
|
||||
{
|
||||
cursor: [4, 9],
|
||||
},
|
||||
client,
|
||||
);
|
||||
|
||||
await sendNeovimKeys(client, "<C-o>");
|
||||
await assertContent(
|
||||
{
|
||||
cursor: [27, 9],
|
||||
},
|
||||
client,
|
||||
);
|
||||
await sendNeovimKeys(client, "<C-o>");
|
||||
await assertContent(
|
||||
{
|
||||
cursor: [49, 0],
|
||||
},
|
||||
client,
|
||||
);
|
||||
});
|
||||
|
||||
it("lower-case marks should still exist after changes #543", async function () {
|
||||
const editor = await openTextDocument({ content: ["abc", "def", "uvw", "xyz"].join("\n") });
|
||||
await wait(300);
|
||||
await sendNeovimKeys(client, "maG");
|
||||
editor.edit((builder) => {
|
||||
builder.replace(new Position(0, 0), "ABC");
|
||||
});
|
||||
await wait(300);
|
||||
await sendNeovimKeys(client, "'a");
|
||||
await wait(300);
|
||||
await assertContent({ cursor: [0, 0] }, client, editor);
|
||||
});
|
||||
|
||||
it("Global mark jump to another file keeps the mark position #2699", async () => {
|
||||
const markEditor = await openTextDocument(path.join(__dirname, "../../../test_fixtures/scrolltest.txt"));
|
||||
await sendVSCodeKeys("24G4lmA");
|
||||
await assertContent({ cursor: [23, 4] }, client);
|
||||
// Leave a different selection behind for VSCode to restore when the file is shown again
|
||||
await sendVSCodeKeys("29G02l");
|
||||
await assertContent({ cursor: [28, 2] }, client);
|
||||
|
||||
await openTextDocument(path.join(__dirname, "../../../test_fixtures/cursor-revealing.txt"));
|
||||
await sendNeovimKeys(client, "`A");
|
||||
await waitUntil(() => assert.equal(window.activeTextEditor?.document, markEditor.document));
|
||||
|
||||
// The restored selection would overwrite the mark position only after the editor is shown
|
||||
await wait(1000);
|
||||
await assertContent({ cursor: [23, 4] }, client);
|
||||
});
|
||||
});
|
||||
@@ -327,8 +327,10 @@ describe("Multi-width characters", () => {
|
||||
await sendVSCodeKeys("v");
|
||||
await assertContent({ vsCodeSelections: [new vscode.Selection(0, 0, 0, 1)] }, client);
|
||||
|
||||
// the cursor sits on the astral char, whose two UTF-16 units both belong to
|
||||
// the selection: a, the modifier letter and the pair take offsets 0 to 4
|
||||
await sendVSCodeKeys("ll");
|
||||
await assertContent({ vsCodeSelections: [new vscode.Selection(0, 0, 0, 3)] }, client);
|
||||
await assertContent({ vsCodeSelections: [new vscode.Selection(0, 0, 0, 4)] }, client);
|
||||
|
||||
await sendVSCodeKeys("l");
|
||||
await assertContent({ vsCodeSelections: [new vscode.Selection(0, 0, 0, 5)] }, client);
|
||||
|
||||
@@ -0,0 +1,655 @@
|
||||
import path from "path";
|
||||
import os from "os";
|
||||
import fs from "fs";
|
||||
import { strict as assert } from "assert";
|
||||
|
||||
import vscode, { Selection } from "vscode";
|
||||
import { NeovimClient } from "neovim";
|
||||
|
||||
import { wait, waitUntil } from "../../utils";
|
||||
|
||||
import {
|
||||
attachTestNvimClient,
|
||||
assertContent,
|
||||
setCursor,
|
||||
sendVSCodeKeys,
|
||||
closeAllActiveEditors,
|
||||
sendEscapeKey,
|
||||
closeNvimClient,
|
||||
getVScodeCursor,
|
||||
sendVSCodeKeysAtomic,
|
||||
openTextDocument,
|
||||
sendInsertKey,
|
||||
sendVSCodeCommand,
|
||||
sendNeovimKeys,
|
||||
itSkipMacos,
|
||||
} from "./integrationUtils";
|
||||
|
||||
describe("VSCode integration specific stuff", () => {
|
||||
let client: NeovimClient;
|
||||
before(async () => {
|
||||
client = await attachTestNvimClient();
|
||||
});
|
||||
after(async () => {
|
||||
await closeNvimClient(client);
|
||||
await closeAllActiveEditors();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await closeAllActiveEditors();
|
||||
});
|
||||
|
||||
it("Doesnt move cursor on peek definition", async () => {
|
||||
const doc = (
|
||||
await openTextDocument({
|
||||
content: 'declare function test(a: number): void;\n\ntest("")\n',
|
||||
language: "typescript",
|
||||
})
|
||||
).document;
|
||||
await setCursor(2, 1);
|
||||
|
||||
// peek definition opens another editor. make sure the cursor won't be leaked into primary editor
|
||||
await vscode.commands.executeCommand("editor.action.peekDefinition", doc.uri, new vscode.Position(2, 1));
|
||||
await wait(500);
|
||||
await vscode.commands.executeCommand("closeReferenceSearch", doc.uri, new vscode.Position(2, 1));
|
||||
await wait(500);
|
||||
await assertContent(
|
||||
{
|
||||
cursor: [2, 1],
|
||||
},
|
||||
client,
|
||||
);
|
||||
});
|
||||
|
||||
it("Moves on cursor on go definition", async () => {
|
||||
const doc = (
|
||||
await openTextDocument({
|
||||
content: 'declare function test(a: number): void;\n\ntest("")\n',
|
||||
language: "typescript",
|
||||
})
|
||||
).document;
|
||||
await setCursor(2, 1);
|
||||
|
||||
await vscode.commands.executeCommand("editor.action.goToTypeDefinition", doc.uri, new vscode.Position(2, 1));
|
||||
await wait(1000);
|
||||
await assertContent(
|
||||
{
|
||||
cursor: [0, 17],
|
||||
},
|
||||
client,
|
||||
);
|
||||
});
|
||||
|
||||
// TODO: always fails on CI, possible something with screen dimensions?
|
||||
it.skip("Editor cursor revealing", async () => {
|
||||
await openTextDocument(path.join(__dirname, "../../../test_fixtures/cursor-revealing.txt"));
|
||||
|
||||
await sendVSCodeKeys("90j");
|
||||
await assertContent({ cursor: [90, 0], vsCodeVisibleRange: { bottom: 90 } }, client);
|
||||
|
||||
await sendVSCodeKeys("zt");
|
||||
await assertContent({ cursor: [90, 0], vsCodeVisibleRange: { top: 90 } }, client);
|
||||
|
||||
// await sendVSCodeKeys("40k");
|
||||
// await assertContent({ cursor: [90, 0], vsCodeVisibleRange: { bottom: 50 } }, client);
|
||||
});
|
||||
|
||||
it("Scrolling actions", async () => {
|
||||
const editor = await openTextDocument(path.join(__dirname, "../../../test_fixtures/scrolltest.txt"));
|
||||
|
||||
await sendVSCodeCommand("vscode-neovim.ctrl-f");
|
||||
|
||||
let visibleRange = editor.visibleRanges[0];
|
||||
const topLine = visibleRange.start.line;
|
||||
const bottomLine = visibleRange.end.line;
|
||||
const cursorLine = editor.selection.active.line;
|
||||
// Viewport height differs across VS Code versions / CI; cursor should stay within the visible slice.
|
||||
assert.ok(
|
||||
cursorLine >= topLine && cursorLine <= bottomLine,
|
||||
`cursor ${cursorLine} not in visible range ${topLine}-${bottomLine}`,
|
||||
);
|
||||
await assertContent(
|
||||
{
|
||||
cursor: [cursorLine, 0],
|
||||
},
|
||||
client,
|
||||
);
|
||||
|
||||
await sendVSCodeKeys("L", 400);
|
||||
visibleRange = editor.visibleRanges[0];
|
||||
const cursor = getVScodeCursor(editor);
|
||||
assert.ok(cursor[0] <= visibleRange.end.line && cursor[0] >= visibleRange.end.line - 1);
|
||||
await assertContent(
|
||||
{
|
||||
cursor,
|
||||
},
|
||||
client,
|
||||
);
|
||||
|
||||
await sendVSCodeKeys("M", 400);
|
||||
visibleRange = editor.visibleRanges[0];
|
||||
await assertContent(
|
||||
{
|
||||
cursor: [editor.selection.active.line, 0],
|
||||
},
|
||||
client,
|
||||
);
|
||||
const middleline = visibleRange.start.line + (visibleRange.end.line - visibleRange.start.line) / 2;
|
||||
assert.ok(editor.selection.active.line >= middleline - 1);
|
||||
assert.ok(editor.selection.active.line <= middleline + 1);
|
||||
|
||||
await sendVSCodeKeys("H", 400);
|
||||
visibleRange = editor.visibleRanges[0];
|
||||
await assertContent(
|
||||
{
|
||||
cursor: [visibleRange.start.line, 0],
|
||||
},
|
||||
client,
|
||||
);
|
||||
});
|
||||
|
||||
// todo: sometimes it's failing, but most times works
|
||||
it("Go to definition in other file - cursor is ok", async function () {
|
||||
this.retries(3);
|
||||
|
||||
const doc2 = (await openTextDocument(path.join(__dirname, "../../../test_fixtures/b.ts"))).document;
|
||||
await setCursor(3, 1);
|
||||
|
||||
await vscode.commands.executeCommand("editor.action.goToTypeDefinition", doc2.uri, new vscode.Position(2, 1));
|
||||
await wait(500);
|
||||
|
||||
await assertContent(
|
||||
{
|
||||
cursor: [4, 16],
|
||||
content: [
|
||||
'export const a = "blah";',
|
||||
"",
|
||||
'export const b = "blah";',
|
||||
"",
|
||||
"export function someFunc(): void;",
|
||||
"",
|
||||
],
|
||||
},
|
||||
client,
|
||||
);
|
||||
});
|
||||
|
||||
itSkipMacos("Current mode is canceled when switching between editor panes", async () => {
|
||||
await openTextDocument({
|
||||
content: "blah1",
|
||||
});
|
||||
await wait(500);
|
||||
const doc2 = await vscode.workspace.openTextDocument({
|
||||
content: "blah2",
|
||||
});
|
||||
await vscode.window.showTextDocument(doc2, vscode.ViewColumn.Two);
|
||||
await wait(500);
|
||||
|
||||
await client.command("au BufEnter * stopinsert");
|
||||
await wait(500);
|
||||
await sendVSCodeCommand("workbench.action.focusSecondEditorGroup", "", 1000);
|
||||
await sendInsertKey("I");
|
||||
await assertContent(
|
||||
{
|
||||
content: ["blah2"],
|
||||
cursorStyle: "line",
|
||||
mode: "i",
|
||||
},
|
||||
client,
|
||||
);
|
||||
// make sure the changes will be synced with neovim
|
||||
await sendVSCodeKeys("test", 1000);
|
||||
await sendVSCodeCommand("workbench.action.focusFirstEditorGroup", "", 2000);
|
||||
await assertContent(
|
||||
{
|
||||
content: ["blah1"],
|
||||
cursorStyle: "block",
|
||||
mode: "n",
|
||||
},
|
||||
client,
|
||||
);
|
||||
|
||||
await sendVSCodeKeys("V");
|
||||
await assertContent(
|
||||
{
|
||||
content: ["blah1"],
|
||||
mode: "V",
|
||||
},
|
||||
client,
|
||||
);
|
||||
|
||||
await sendVSCodeCommand("workbench.action.focusSecondEditorGroup", "", 2000);
|
||||
await assertContent(
|
||||
{
|
||||
content: ["testblah2"],
|
||||
mode: "n",
|
||||
},
|
||||
client,
|
||||
);
|
||||
});
|
||||
|
||||
it("Current mode is canceled when switching between editor tabs", async () => {
|
||||
const doc1 = await vscode.workspace.openTextDocument({
|
||||
content: "blah1",
|
||||
});
|
||||
await vscode.window.showTextDocument(doc1, vscode.ViewColumn.One);
|
||||
const doc2 = await vscode.workspace.openTextDocument({
|
||||
content: "blah2",
|
||||
});
|
||||
await vscode.window.showTextDocument(doc2, vscode.ViewColumn.One);
|
||||
await wait(500);
|
||||
await client.command("au BufEnter * stopinsert");
|
||||
await wait(500);
|
||||
|
||||
await sendInsertKey("I");
|
||||
await assertContent(
|
||||
{
|
||||
content: ["blah2"],
|
||||
cursorStyle: "line",
|
||||
mode: "i",
|
||||
},
|
||||
client,
|
||||
);
|
||||
// make sure the changes will be synced with neovim
|
||||
await sendVSCodeKeys("test", 500);
|
||||
await sendVSCodeCommand("workbench.action.previousEditorInGroup", "", 500);
|
||||
await assertContent(
|
||||
{
|
||||
content: ["blah1"],
|
||||
cursorStyle: "block",
|
||||
mode: "n",
|
||||
},
|
||||
client,
|
||||
);
|
||||
|
||||
await sendVSCodeKeys("V");
|
||||
await assertContent(
|
||||
{
|
||||
content: ["blah1"],
|
||||
mode: "V",
|
||||
},
|
||||
client,
|
||||
);
|
||||
|
||||
await sendVSCodeCommand("workbench.action.nextEditorInGroup", "", 500);
|
||||
await assertContent(
|
||||
{
|
||||
content: ["testblah2"],
|
||||
cursorStyle: "block",
|
||||
mode: "n",
|
||||
},
|
||||
client,
|
||||
);
|
||||
});
|
||||
|
||||
it("Cursor is ok when go to def into editor in the other pane", async () => {
|
||||
const doc1 = await vscode.workspace.openTextDocument(path.join(__dirname, "../../../test_fixtures/bb.ts"));
|
||||
await vscode.window.showTextDocument(doc1, vscode.ViewColumn.One);
|
||||
await wait(1500);
|
||||
|
||||
const doc2 = await vscode.workspace.openTextDocument(
|
||||
path.join(__dirname, "../../../test_fixtures/def-with-scroll.ts"),
|
||||
);
|
||||
await vscode.window.showTextDocument(doc2, vscode.ViewColumn.Two, true);
|
||||
await wait(1500);
|
||||
|
||||
// make sure we're in first editor group
|
||||
await vscode.commands.executeCommand("workbench.action.focusFirstEditorGroup");
|
||||
await wait(400);
|
||||
|
||||
await sendVSCodeKeys("gg5j", 0);
|
||||
await wait(1000);
|
||||
|
||||
await vscode.commands.executeCommand(
|
||||
"editor.action.revealDefinitionAside",
|
||||
doc1.uri,
|
||||
new vscode.Position(5, 1),
|
||||
);
|
||||
await wait(1500);
|
||||
|
||||
await assertContent(
|
||||
{
|
||||
cursor: [115, 16],
|
||||
},
|
||||
client,
|
||||
);
|
||||
});
|
||||
|
||||
// !Passes only when the runner is in foreground
|
||||
itSkipMacos("Cursor is preserved if same doc is opened in two editor columns", async () => {
|
||||
const doc = (
|
||||
await openTextDocument(path.join(__dirname, "../../../test_fixtures/cursor-preserved-between-columns.txt"))
|
||||
).document;
|
||||
await wait(1000);
|
||||
await sendVSCodeKeys("gg050j", 1000);
|
||||
await vscode.window.showTextDocument(doc, vscode.ViewColumn.Two, false);
|
||||
await wait(1000);
|
||||
await sendVSCodeKeys("gg0100j", 1000);
|
||||
|
||||
await sendVSCodeCommand("workbench.action.focusFirstEditorGroup", "", 2000);
|
||||
await sendVSCodeKeys("l");
|
||||
await assertContent(
|
||||
{
|
||||
cursor: [50, 1],
|
||||
},
|
||||
client,
|
||||
);
|
||||
|
||||
await sendVSCodeCommand("workbench.action.focusSecondEditorGroup", "", 1000);
|
||||
await sendVSCodeKeys("l");
|
||||
await assertContent(
|
||||
{
|
||||
cursor: [100, 1],
|
||||
},
|
||||
client,
|
||||
);
|
||||
});
|
||||
|
||||
it("Window commands typed after timeoutlen still go to vscode #2466", async () => {
|
||||
await openTextDocument({ content: "blah" });
|
||||
const timeoutlen = (await client.getOption("timeoutlen")) as number;
|
||||
await client.setOption("timeoutlen", 100);
|
||||
try {
|
||||
await sendNeovimKeys(client, "<C-w>", 500);
|
||||
await sendNeovimKeys(client, "v");
|
||||
await waitUntil(() =>
|
||||
assert.equal(vscode.window.tabGroups.all.length, 2, "<C-w>v should split the editor"),
|
||||
);
|
||||
} finally {
|
||||
await client.setOption("timeoutlen", timeoutlen);
|
||||
await vscode.commands.executeCommand("workbench.action.joinAllGroups");
|
||||
}
|
||||
});
|
||||
|
||||
it("Opens a file through e command", async () => {
|
||||
const filePath = path.join(os.tmpdir(), Math.random().toString());
|
||||
fs.writeFileSync(filePath, ["line 1"].join("\n"), {
|
||||
encoding: "utf8",
|
||||
});
|
||||
|
||||
await openTextDocument({ content: "blah" });
|
||||
|
||||
await sendVSCodeKeysAtomic(":e " + filePath, 500);
|
||||
await sendVSCodeCommand("vscode-neovim.commit-cmdline", "", 500);
|
||||
await assertContent(
|
||||
{
|
||||
content: ["line 1"],
|
||||
},
|
||||
client,
|
||||
);
|
||||
});
|
||||
|
||||
it("Spawning command line from visual line mode produces vscode selection", async () => {
|
||||
await openTextDocument({ content: ["a1", "b1", "c1"].join("\n") });
|
||||
await sendVSCodeKeys("Vj");
|
||||
await sendVSCodeCommand("workbench.action.quickOpen");
|
||||
await assertContent(
|
||||
{
|
||||
vsCodeSelections: [new vscode.Selection(0, 0, 1, 2)],
|
||||
},
|
||||
client,
|
||||
);
|
||||
await vscode.commands.executeCommand("workbench.action.closeQuickOpen");
|
||||
await sendEscapeKey();
|
||||
|
||||
await sendVSCodeKeys("GVk");
|
||||
await sendVSCodeCommand("workbench.action.quickOpen");
|
||||
await assertContent(
|
||||
{
|
||||
vsCodeSelections: [new vscode.Selection(2, 2, 1, 0)],
|
||||
},
|
||||
client,
|
||||
);
|
||||
await vscode.commands.executeCommand("workbench.action.closeQuickOpen");
|
||||
});
|
||||
|
||||
it("Spawning command line from visual mode produces vscode selection", async () => {
|
||||
const documentContent = "Hello World!";
|
||||
await openTextDocument({ content: documentContent });
|
||||
await sendVSCodeKeys("v$");
|
||||
await sendVSCodeCommand("workbench.action.quickOpen");
|
||||
await assertContent(
|
||||
{
|
||||
vsCodeSelections: [new vscode.Selection(0, 0, 0, documentContent.length)],
|
||||
},
|
||||
client,
|
||||
);
|
||||
await vscode.commands.executeCommand("workbench.action.closeQuickOpen");
|
||||
await sendEscapeKey();
|
||||
|
||||
await sendVSCodeKeys("gvo");
|
||||
await sendVSCodeCommand("vscode-neovim.send", "<C-P>");
|
||||
await assertContent(
|
||||
{
|
||||
vsCodeSelections: [new vscode.Selection(0, documentContent.length, 0, 0)],
|
||||
},
|
||||
client,
|
||||
);
|
||||
await vscode.commands.executeCommand("workbench.action.closeQuickOpen");
|
||||
});
|
||||
|
||||
it("Filetype detection", async function () {
|
||||
this.retries(3);
|
||||
|
||||
const doc1 = await vscode.workspace.openTextDocument(path.join(__dirname, "../../../test_fixtures/bb.ts"));
|
||||
await vscode.window.showTextDocument(doc1, vscode.ViewColumn.One);
|
||||
await wait(1500);
|
||||
|
||||
const buf = await client.buffer;
|
||||
const type = await client.request("nvim_buf_get_option", [buf.id, "filetype"]);
|
||||
assert.strictEqual("typescript", type);
|
||||
});
|
||||
|
||||
it("Filetype detection (jupyter notebook)", async function () {
|
||||
this.retries(2);
|
||||
|
||||
const note = await vscode.workspace.openNotebookDocument(
|
||||
vscode.Uri.file(path.join(__dirname, "../../../test_fixtures/window-changed.ipynb")),
|
||||
);
|
||||
await vscode.window.showNotebookDocument(note, { viewColumn: vscode.ViewColumn.One });
|
||||
await wait(1000);
|
||||
|
||||
const buf = await client.buffer;
|
||||
const type = await client.request("nvim_buf_get_option", [buf.id, "filetype"]);
|
||||
assert.strictEqual("python", type);
|
||||
});
|
||||
|
||||
it("Next search result works", async () => {
|
||||
await openTextDocument(path.join(__dirname, "../../../test_fixtures/incsearch-scroll.ts"));
|
||||
|
||||
await sendVSCodeCommand("workbench.action.findInFiles", { query: "blah" }, 500);
|
||||
await sendVSCodeCommand("search.action.refreshSearchResults");
|
||||
await sendVSCodeCommand("workbench.action.focusFirstEditorGroup");
|
||||
await sendVSCodeCommand("search.action.focusNextSearchResult");
|
||||
await assertContent(
|
||||
{
|
||||
vsCodeSelections: [new Selection(115, 16, 115, 20)],
|
||||
neovimCursor: [115, 19],
|
||||
},
|
||||
client,
|
||||
);
|
||||
|
||||
await sendVSCodeCommand("search.action.focusNextSearchResult");
|
||||
await assertContent(
|
||||
{
|
||||
vsCodeSelections: [new Selection(170, 16, 170, 20)],
|
||||
neovimCursor: [170, 19],
|
||||
},
|
||||
client,
|
||||
);
|
||||
});
|
||||
|
||||
it("Edit on long lines works", async () => {
|
||||
await openTextDocument(path.join(__dirname, "../../../test_fixtures/long-line.txt"));
|
||||
|
||||
await sendVSCodeKeys("gg^");
|
||||
await sendVSCodeKeys("2jb");
|
||||
await assertContent(
|
||||
{
|
||||
cursor: [1, 1580],
|
||||
},
|
||||
client,
|
||||
);
|
||||
|
||||
await sendVSCodeKeys("iabc");
|
||||
await sendEscapeKey();
|
||||
await assertContent(
|
||||
{
|
||||
cursor: [1, 1582],
|
||||
},
|
||||
client,
|
||||
);
|
||||
|
||||
await sendVSCodeKeys("jhk");
|
||||
await assertContent(
|
||||
{
|
||||
cursor: [1, 10],
|
||||
},
|
||||
client,
|
||||
);
|
||||
|
||||
await sendVSCodeKeys("Aabc");
|
||||
await sendEscapeKey();
|
||||
await assertContent(
|
||||
{
|
||||
cursor: [1, 1589],
|
||||
},
|
||||
client,
|
||||
);
|
||||
|
||||
await sendVSCodeKeys("Iabc");
|
||||
await sendEscapeKey();
|
||||
await assertContent(
|
||||
{
|
||||
cursor: [1, 2],
|
||||
},
|
||||
client,
|
||||
);
|
||||
|
||||
await sendVSCodeKeys("$");
|
||||
await assertContent(
|
||||
{
|
||||
cursor: [1, 1592],
|
||||
},
|
||||
client,
|
||||
);
|
||||
});
|
||||
|
||||
it("cursorMove with wrappedLine should works #1498", async () => {
|
||||
await openTextDocument({ content: " hello\n\nworld" });
|
||||
await wait(200);
|
||||
await sendNeovimKeys(client, "ll", 200);
|
||||
vscode.commands.executeCommand("cursorMove", { to: "down", by: "wrappedLine" });
|
||||
await wait(200);
|
||||
vscode.commands.executeCommand("cursorMove", { to: "down", by: "wrappedLine" });
|
||||
await wait(200);
|
||||
await assertContent({ cursor: [2, 2] }, client);
|
||||
});
|
||||
|
||||
it("vim.ui.select works", async () => {
|
||||
await client.lua(`
|
||||
vim.ui.select({ 'red', 'green', 'blue' }, {}, function(item, idx)
|
||||
vim.g._ret_item = item
|
||||
vim.g._ret_idx = idx
|
||||
end)
|
||||
`);
|
||||
// wait enough time for it to open
|
||||
await wait(100);
|
||||
// select the last item
|
||||
await vscode.commands.executeCommand("workbench.action.quickOpenNavigateNext");
|
||||
await vscode.commands.executeCommand("workbench.action.quickOpenNavigateNext");
|
||||
await vscode.commands.executeCommand("workbench.action.acceptSelectedQuickOpenItem");
|
||||
// check the results
|
||||
const actual_item = await client.getVar("_ret_item");
|
||||
const actual_idx = await client.getVar("_ret_idx");
|
||||
assert.strictEqual(actual_item, "blue");
|
||||
assert.strictEqual(actual_idx, 3);
|
||||
});
|
||||
|
||||
it("vim.ui.select with format_item works", async () => {
|
||||
await client.lua(`
|
||||
vim.ui.select({{ label = 'apple', detail = 'red' }, { label = 'avocado', detail = 'green' }}, {
|
||||
format_item = function(item)
|
||||
return item.label
|
||||
end
|
||||
}, function(item, idx)
|
||||
vim.g._ret_item = item
|
||||
vim.g._ret_idx = idx
|
||||
end)
|
||||
`);
|
||||
// wait enough time for it to open
|
||||
await wait(100);
|
||||
// select the first item
|
||||
await vscode.commands.executeCommand("workbench.action.acceptSelectedQuickOpenItem");
|
||||
// check the results
|
||||
const actual_item = (await client.getVar("_ret_item")) as { label: string; detail: string };
|
||||
const actual_idx = await client.getVar("_ret_idx");
|
||||
assert.ok(typeof actual_item === "object");
|
||||
assert.strictEqual(actual_item.label, "apple");
|
||||
assert.strictEqual(actual_item.detail, "red");
|
||||
assert.strictEqual(actual_idx, 1);
|
||||
});
|
||||
|
||||
it("vim.ui.select cancel returns nil", async () => {
|
||||
await client.lua(`
|
||||
vim.ui.select({ 'red', 'green', 'blue' }, {}, function(item, idx)
|
||||
vim.g._ret_item = item
|
||||
vim.g._ret_idx = idx
|
||||
end)
|
||||
`);
|
||||
// wait enough time for it to open
|
||||
await wait(100);
|
||||
// cancel the dialog
|
||||
await vscode.commands.executeCommand("workbench.action.closeQuickOpen");
|
||||
// check the results
|
||||
const actual_item = await client.getVar("_ret_item");
|
||||
const actual_idx = await client.getVar("_ret_idx");
|
||||
assert.strictEqual(actual_item, null);
|
||||
assert.strictEqual(actual_idx, null);
|
||||
});
|
||||
|
||||
it("vim.ui.input works", async () => {
|
||||
await client.lua(`
|
||||
vim.ui.input({ prompt = 'Enter a value: ', default = 'test' }, function(input)
|
||||
vim.g._res = input
|
||||
end)
|
||||
`);
|
||||
// wait enough time for it to open
|
||||
await wait(100);
|
||||
// confirm the value
|
||||
await vscode.commands.executeCommand("workbench.action.acceptSelectedQuickOpenItem");
|
||||
// check the results
|
||||
const actual_input = await client.getVar("_res");
|
||||
assert.strictEqual(actual_input, "test");
|
||||
});
|
||||
|
||||
it("vim.ui.input empty not nil", async () => {
|
||||
await client.lua(`
|
||||
vim.ui.input({ prompt = 'Enter a value: ' }, function(input)
|
||||
vim.g._res = input
|
||||
end)
|
||||
`);
|
||||
// wait enough time for it to open
|
||||
await wait(100);
|
||||
// confirm the value
|
||||
await vscode.commands.executeCommand("workbench.action.acceptSelectedQuickOpenItem");
|
||||
// check the results
|
||||
const actual_input = await client.getVar("_res");
|
||||
assert.strictEqual(actual_input, "");
|
||||
});
|
||||
|
||||
it("vim.ui.input cancel returns nil", async () => {
|
||||
await client.lua(`
|
||||
vim.ui.input({ prompt = 'Enter a value: ' }, function(input)
|
||||
vim.g._res = input
|
||||
end)
|
||||
`);
|
||||
// wait enough time for it to open
|
||||
await wait(100);
|
||||
// cancel the dialog
|
||||
await vscode.commands.executeCommand("workbench.action.closeQuickOpen");
|
||||
// check the results
|
||||
const actual_input = await client.getVar("_res");
|
||||
assert.strictEqual(actual_input, null);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,6 @@
|
||||
export * from "./async";
|
||||
export * from "./fs";
|
||||
export * from "./neovim";
|
||||
export * from "./pending_updates";
|
||||
export * from "./text";
|
||||
export * from "./vscode";
|
||||
@@ -0,0 +1,18 @@
|
||||
import { NeovimClient } from "neovim";
|
||||
import { Position } from "vscode";
|
||||
|
||||
/**
|
||||
* Get the (0, 0)-indexed byte cursor of the current neovim window, if it shows the given buffer
|
||||
*/
|
||||
export async function getCurrentWinCursorInBuf(client: NeovimClient, bufId: number): Promise<Position | undefined> {
|
||||
const cursor = (await client.lua(
|
||||
`
|
||||
local buf = ...
|
||||
if vim.api.nvim_get_current_buf() == buf then
|
||||
return vim.api.nvim_win_get_cursor(0)
|
||||
end
|
||||
`,
|
||||
[bufId],
|
||||
)) as [number, number] | null;
|
||||
return cursor ? new Position(cursor[0] - 1, cursor[1]) : undefined;
|
||||
}
|
||||
Reference in new issue
Block a user