Skip to content

Commit

Permalink
fix(buffer): prefer real path as buffer name (#1699)
Browse files Browse the repository at this point in the history
* fix(buffer): prefer real path as buffer name

* feat: wsl path support
  • Loading branch information
xiyaowong committed Dec 13, 2023
1 parent ec2b287 commit eff13f2
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 112 deletions.
12 changes: 12 additions & 0 deletions runtime/lua/vscode-neovim/internal.lua
Original file line number Diff line number Diff line change
Expand Up @@ -268,4 +268,16 @@ function M.start_visual(buf, anchor, active)
api.nvim_feedkeys((v == "V" or v == "\x16") and "gvv" or "gv", "n", false)
end

---Translate from a Windows path to a WSL path
---@param path string
---@return string
function M.wslpath(path)
local ok, ret = pcall(vim.fn.system, { "wslpath", path })
if not ok then
vim.notify(ret, vim.log.levels.ERROR)
return path
end
return vim.trim(ret)
end

return M
148 changes: 66 additions & 82 deletions src/buffer_manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,16 @@ import { ManualPromise, callAtomic, convertByteNumToCharNum, disposeAll, wait }

const logger = createLogger("BufferManager");

const BUFFER_NAME_PREFIX = "__vscode_neovim__-";

const BUFFER_SCHEME = "vscode-neovim";

function makeEditorOptionsVariable(options: TextEditorOptions) {
function makeEditorOptionsVariable(options?: TextEditorOptions) {
if (!options) {
const editorConfig = workspace.getConfiguration("editor");
const tabSize = editorConfig.get<number>("tabSize")!;
const insertSpaces = editorConfig.get<boolean>("insertSpaces")!;
const lineNumbers = editorConfig.get<"on" | "off" | "relative">("lineNumbers")!;
return { tabSize, insertSpaces, lineNumbers };
}
const { tabSize, insertSpaces, lineNumbers } = options;
return {
tabSize,
Expand Down Expand Up @@ -276,51 +281,53 @@ export class BufferManager implements Disposable {
}

private async handleExternalBuffer(data: EventBusData<"external-buffer">) {
const [name, idStr, expandTab, tabStop] = data;
if (name.startsWith(`${BUFFER_NAME_PREFIX}output:`)) {
const [bufferInfo, expandTab, tabStop] = data;
const {
name,
bufnr,
variables: { vscode_uri },
} = bufferInfo;

if (!vscode_uri) {
logger.debug(`Attaching new external buffer: '${name}', id: ${bufnr}`);
if (bufnr === 1) {
logger.debug(`${bufnr} is the first neovim buffer, skipping`);
return;
}
await this.attachNeovimExternalBuffer(name, bufnr, !!expandTab, tabStop);
return;
}
const id = parseInt(idStr, 10);
if (!(name && this.isVscodeUriName(name))) {
logger.debug(`Attaching new external buffer: '${name}', id: ${id}`);
if (id === 1) {
logger.debug(`${id} is the first neovim buffer, skipping`);
return;

const uri = Uri.parse(vscode_uri, true);
logger.debug(`Buffer request for ${uri.fsPath}, bufId: ${bufnr}`);
try {
let doc = this.findDocFromUri(uri.toString());
if (!doc) {
logger.debug(`Opening a doc: ${uri.fsPath}`);
doc = await workspace.openTextDocument(uri);
}
await this.attachNeovimExternalBuffer(name, id, !!expandTab, tabStop);
} else if (name) {
const normalizedName = name.startsWith(BUFFER_NAME_PREFIX) ? name.substring(18) : name;
logger.debug(`Buffer request for ${normalizedName}, bufId: ${idStr}`);
try {
let doc = this.findDocFromUri(normalizedName);
if (!doc) {
logger.debug(`Opening a doc: ${normalizedName}`);
doc = await workspace.openTextDocument(Uri.parse(normalizedName, true));
}
if (!this.textDocumentToBufferId.has(doc)) {
logger.debug(`No doc -> buffer mapping exists, assigning mapping and init buffer options`);
const buffers = await this.client.buffers;
const buf = buffers.find((b) => b.id === id);
if (buf) {
await this.initBufferForDocument(doc, buf);
}
this.textDocumentToBufferId.set(doc, id);
}
if (window.activeTextEditor?.document !== doc) {
// this.skipJumpsForUris.set(normalizedNamee, true);
const editor = await window.showTextDocument(doc, {
// viewColumn: vscode.ViewColumn.Active,
// !need to force editor to appear in the same column even if vscode 'revealIfOpen' setting is true
viewColumn: window.activeTextEditor ? window.activeTextEditor.viewColumn : ViewColumn.Active,
preserveFocus: false,
preview: false,
});
// force resync
this.onDidChangeEditorOptions(editor);
if (!this.textDocumentToBufferId.has(doc)) {
logger.debug(`No doc -> buffer mapping exists, assigning mapping and init buffer options`);
const buffers = await this.client.buffers;
const buf = buffers.find((b) => b.id === bufnr);
if (buf) {
await this.initBufferForDocument(doc, buf);
}
} catch {
// todo: show error
this.textDocumentToBufferId.set(doc, bufnr);
}
if (window.activeTextEditor?.document !== doc) {
const editor = await window.showTextDocument(doc, {
// viewColumn: vscode.ViewColumn.Active,
// !need to force editor to appear in the same column even if vscode 'revealIfOpen' setting is true
viewColumn: window.activeTextEditor ? window.activeTextEditor.viewColumn : ViewColumn.Active,
preserveFocus: false,
preview: false,
});
// force resync
this.onDidChangeEditorOptions(editor);
}
} catch {
// todo: show error
}
}

Expand Down Expand Up @@ -557,42 +564,32 @@ export class BufferManager implements Disposable {

/**
* Set buffer options from vscode document
* @param document
*/
private async initBufferForDocument(document: TextDocument, buffer: Buffer, editor?: TextEditor): Promise<void> {
const bufId = buffer.id;
logger.debug(`Init buffer for ${bufId}, doc: ${document.uri}`);

// !In vscode same document can have different insertSpaces/tabSize settings per editor
// !however in neovim it's per buffer. We make assumption here that these settings are same for all editors
let editorOptions: TextEditorOptions;
if (editor) {
editorOptions = editor.options;
} else {
const editorConfig = workspace.getConfiguration("editor");
const tabSize = editorConfig.get<number>("tabSize")!;
const insertSpaces = editorConfig.get<boolean>("insertSpaces")!;
const lineNumbers = editorConfig.get<"on" | "off" | "relative">("lineNumbers")!;
editorOptions = {
tabSize,
insertSpaces,
lineNumbers:
lineNumbers === "off"
? TextEditorLineNumbersStyle.Off
: lineNumbers === "relative"
? TextEditorLineNumbersStyle.Relative
: TextEditorLineNumbersStyle.On,
};
}
const { uri: docUri } = document;
logger.debug(`Init buffer for ${bufId}, doc: ${docUri}`);

const eol = document.eol === EndOfLine.LF ? "\n" : "\r\n";
const lines = document.getText().split(eol);
// We don't care about the name of the buffer if it's not a file
const bufname =
docUri.scheme === "file"
? config.useWsl
? await actions.lua("wslpath", docUri.fsPath)
: docUri.fsPath
: docUri.toString();

const requests: [string, unknown[]][] = [
["nvim_buf_set_lines", [bufId, 0, -1, false, lines]],
// set vscode controlled flag so we can check it neovim
["nvim_buf_set_var", [bufId, "vscode_controlled", true]],
["nvim_buf_set_var", [bufId, "vscode_editor_options", makeEditorOptionsVariable(editorOptions)]],
["nvim_buf_set_name", [bufId, BUFFER_NAME_PREFIX + document.uri.toString()]],
// In vscode same document can have different insertSpaces/tabSize settings per editor
// however in neovim it's per buffer. We make assumption here that these settings are same for all editors
["nvim_buf_set_var", [bufId, "vscode_editor_options", makeEditorOptionsVariable(editor?.options)]],
["nvim_buf_set_var", [bufId, "vscode_uri", docUri.toString()]],
["nvim_buf_set_var", [bufId, "vscode_uri_data", docUri.toJSON()]],
["nvim_buf_set_name", [bufId, bufname]],
["nvim_buf_set_option", [bufId, "modifiable", !this.isExternalTextDocument(document)]],
// force nofile, just in case if the buffer was created externally
["nvim_buf_set_option", [bufId, "buftype", "nofile"]],
Expand Down Expand Up @@ -625,19 +622,6 @@ export class BufferManager implements Disposable {
return win.id;
}

private isVscodeUriName(name: string): boolean {
if (/:\/\//.test(name)) {
return true;
}
if (name.startsWith("output:") || name.startsWith(`${BUFFER_NAME_PREFIX}output:`)) {
return true;
}
if (name.startsWith("/search-editor:") || name.startsWith(`${BUFFER_NAME_PREFIX}/search-editor:`)) {
return true;
}
return false;
}

private findPathFromFileName(name: string): string {
const folders = workspace.workspaceFolders;
return folders && folders.length > 0 ? path.resolve(folders[0].uri.fsPath, name) : name;
Expand Down
8 changes: 7 additions & 1 deletion src/eventBus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,12 +102,18 @@ type RedrawEventArgs = (
)[];
// #endregion

interface BufferInfo {
bufnr: number;
name: string;
variables: { vscode_uri?: string };
}

type EventsMapping = {
// nvim
redraw: RedrawEventArgs;
// custom
["open-file"]: [string, 1 | 0 | "all"];
["external-buffer"]: [string, string, number, number, number];
["external-buffer"]: [BufferInfo, 1 | 0, number];
["window-changed"]: [number];
["mode-changed"]: [string];
["notify-recording"]: undefined;
Expand Down
10 changes: 2 additions & 8 deletions src/main_controller.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ChildProcess, execSync, spawn } from "child_process";
import { ChildProcess, spawn } from "child_process";
import { existsSync, mkdirSync, writeFileSync } from "fs";
import path from "path";

Expand All @@ -21,7 +21,7 @@ import { ModeManager } from "./mode_manager";
import { MultilineMessagesManager } from "./multiline_messages_manager";
import { StatusLineManager } from "./status_line_manager";
import { TypingManager } from "./typing_manager";
import { disposeAll, findLastEvent, VSCodeContext } from "./utils";
import { disposeAll, findLastEvent, VSCodeContext, wslpath } from "./utils";
import { ViewportManager } from "./viewport_manager";

interface RequestResponse {
Expand Down Expand Up @@ -62,12 +62,6 @@ export class MainController implements vscode.Disposable {
public viewportManager!: ViewportManager;

public constructor(private extContext: ExtensionContext) {
const wslpath = (path: string) => {
// execSync returns a newline character at the end
const distro = config.wslDistribution.length ? `-d ${config.wslDistribution}` : "";
return execSync(`C:\\Windows\\system32\\wsl.exe ${distro} wslpath '${path}'`).toString().trim();
};

let extensionPath = extContext.extensionPath.replace(/\\/g, "\\\\");
if (config.useWsl) {
extensionPath = wslpath(extensionPath);
Expand Down
14 changes: 14 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { execSync } from "child_process";

import { calcPatch } from "fast-myers-diff";
import { NeovimClient } from "neovim";
import wcwidth from "ts-wcwidth";
Expand All @@ -13,6 +15,7 @@ import {
commands,
} from "vscode";

import { config } from "./config";
import { ILogger } from "./logger";

/**
Expand Down Expand Up @@ -409,3 +412,14 @@ export function rangesToSelections(
: new Selection(range.start, range.end);
});
}

/**
* Translate from a Windows path to a WSL path
* @param path Windows path
* @returns WSL path
*/
export const wslpath = (path: string) => {
// execSync returns a newline character at the end
const distro = config.wslDistribution.length ? `-d ${config.wslDistribution}` : "";
return execSync(`C:\\Windows\\system32\\wsl.exe ${distro} wslpath '${path}'`).toString().trim();
};
24 changes: 3 additions & 21 deletions vim/vscode-neovim.vim
Original file line number Diff line number Diff line change
Expand Up @@ -75,25 +75,6 @@ function! VSCodeClearUndo(bufId)
unlet oldlevels
endfunction


" This is called by extension when created new buffer
function! s:onBufEnter(name, id)
if exists('b:vscode_temp') && b:vscode_temp
return
endif
set conceallevel=0
let tabstop = &tabstop
let isJumping = 0
if exists('g:isJumping')
let isJumping = g:isJumping
endif
call VSCodeExtensionNotify('external-buffer', a:name, a:id, 1, tabstop, isJumping)
endfunction

function! s:runFileTypeDetection()
doautocmd BufRead
endfunction

function! s:onInsertEnter()
let reg = reg_recording()
if !empty(reg)
Expand All @@ -115,11 +96,12 @@ execute 'source ' . s:currDir . '/vscode-motion.vim'
augroup VscodeGeneral
autocmd!
" autocmd BufWinEnter,WinNew,WinEnter * :only
autocmd BufWinEnter * call <SID>onBufEnter(expand('<afile>'), expand('<abuf>'))
autocmd BufWinEnter * call VSCodeExtensionNotify('external-buffer', getbufinfo(bufnr())[0], &et, &ts)
" Help and other buffer types may explicitly disable line numbers - reenable them, !important - set nowrap since it may be overriden and this option is crucial for now
" autocmd FileType * :setlocal conceallevel=0 | :setlocal number | :setlocal numberwidth=8 | :setlocal nowrap | :setlocal nofoldenable
autocmd InsertEnter * call <SID>onInsertEnter()
autocmd BufAdd * call <SID>runFileTypeDetection()
" Trigger filetype detection
autocmd BufAdd * do BufRead
" Looks like external windows are coming with "set wrap" set automatically, disable them
" autocmd WinNew,WinEnter * :set nowrap
autocmd WinScrolled * call VSCodeExtensionNotify('window-scroll', win_getid(), winsaveview())
Expand Down

0 comments on commit eff13f2

Please sign in to comment.