feat(editor): :bcd changes buffer-local directory

Problem:
No way to set a buffer-local directory.
Use-cases:
- "Root dir" for LSP (and the "project" concept).
- `:terminal` OSC 7

Solution:
Add `:bcd` command.

- Extend `getcwd()` to take a third arg; `getcwd(-1, -1, bunfr)` returns
  the buffer-local working directory.
- Buffer-local directories have less priority than window-local
  ones, and more priority than tab-local ones.

Co-authored-by: Justin M. Keyes <justinkz@gmail.com>
This commit is contained in:
saher
2025-04-02 18:17:15 +03:00
committed by Justin M. Keyes
parent 838f130e6a
commit ea3868bcf9
23 changed files with 940 additions and 178 deletions

View File

@@ -952,6 +952,8 @@ function vim.fn.charidx(string, idx, countcc, utf16) end
--- changed to the scope of the current directory:
--- - If the window local directory (|:lcd|) is set, it
--- changes the current working directory for that scope.
--- - If the buffer local directory (|:bcd|) is set, it
--- changes the current working directory for that scope.
--- - Otherwise, if the tabpage local directory (|:tcd|) is
--- set, it changes the current directory for that scope.
--- - Otherwise, changes the global directory for that scope.
@@ -959,6 +961,7 @@ function vim.fn.charidx(string, idx, countcc, utf16) end
--- If {scope} is present, changes the current working directory
--- for the specified scope:
--- "window" Changes the window local directory. |:lcd|
--- "buffer" Changes the buffer local directory. |:bcd|
--- "tabpage" Changes the tabpage local directory. |:tcd|
--- "global" Changes the global directory. |:cd|
---
@@ -3318,27 +3321,42 @@ function vim.fn.getcursorcharpos(winid) end
--- Lua: Prefer |uv.cwd()| for the global working directory; tab-local and window-local scopes differ.
---
--- With no arguments, returns the name of the effective
--- |current-directory|. With {winnr} or {tabnr} the working
--- directory of that scope is returned, and 'autochdir' is
--- ignored. Tabs and windows are identified by their respective
--- numbers, 0 means current tab or window. Missing tab number
--- |current-directory|. With {winnr} or {tabnr} or {bufnr} the
--- working directory of that scope is returned, and 'autochdir'
--- is ignored.
---
--- Tabs, windows and buffers are identified by their respective
--- numbers, 0 means current tab or window or buffer. Missing tab
--- number
--- implies 0. Thus the following are equivalent: >vim
--- getcwd(0)
--- getcwd(0, 0)
--- <If {winnr} is -1 it is ignored, only the tab is resolved.
--- {winnr} is a |window-number| or |window-ID|.
--- If both {winnr} and {tabnr} are -1 the global working
--- directory is returned.
--- If both {winnr} and {tabnr} are -1 and {bufnr} is missing the
--- global working directory is returned.
--- Note: When {tabnr} is -1 Vim returns an empty string to
--- signal that it is invalid, whereas Nvim returns either the
--- global working directory if {winnr} is -1 or the working
--- directory of the window indicated by {winnr}.
--- Throw error if the arguments are invalid. |E5000| |E5001| |E5002|
---
--- If {bufnr} is provided, {winnr} and {tabnr} must be -1 and the
--- working directory of that buffer is returned. If {bufnr} is
--- -1, it is ignored, and the global working directory is
--- returned.
--- Examples of buffer usage: >vim
--- getcwd(-1, -1, 0) " Get current buffer's directory
--- getcwd(-1, -1, 3) " Get directory of buffer #3
--- getcwd(-1, -1, -1) " Get global directory
--- getcwd(-1, -1) " Get global directory
--- <Throw error if the arguments are invalid.
--- |E5000| |E5001| |E5002| |E5006| |E5007|
---
--- @param winnr? integer
--- @param tabnr? integer
--- @param bufnr? integer
--- @return string
function vim.fn.getcwd(winnr, tabnr) end
function vim.fn.getcwd(winnr, tabnr, bufnr) end
--- Lua: Prefer |vim.env|.
---
@@ -4445,14 +4463,15 @@ function vim.fn.has(feature) end
--- @return 0|1
function vim.fn.has_key(dict, key) end
--- Checks whether the window or tabpage has set a local working
--- directory. Returns 1 when the window has set a local path
--- via |:lcd| or when {winnr} is -1 and the tabpage has set a
--- local path via |:tcd|, otherwise 0.
--- Checks whether the window, tabpage or buffer has set a local
--- working directory. Returns 1 when the window has set a local
--- path via |:lcd|, or when {winnr} is -1 and the tabpage has set
--- a local path via |:tcd|, or when {winnr} and {tabnr} are -1
--- and {bufnr} has set a local path via |:bcd|, otherwise 0.
---
--- Tabs and windows are identified by their respective numbers,
--- 0 means current tab or window. Missing argument implies 0.
--- Thus the following are equivalent: >vim
--- Tabs, windows and buffers are identified by their respective
--- numbers, 0 means current tab or window. Missing argument
--- implies 0. Thus the following are equivalent: >vim
--- echo haslocaldir()
--- echo haslocaldir(0)
--- echo haslocaldir(0, 0)
@@ -4460,12 +4479,19 @@ function vim.fn.has_key(dict, key) end
--- With {winnr} and {tabnr} use the window in that tabpage.
--- {winnr} is a |window-number| or |window-ID|.
--- If {winnr} is -1 it is ignored, only the tab is resolved.
--- Throw error if the arguments are invalid. |E5000| |E5001| |E5002|
--- If {bufnr} is provided, {winnr} and {tabnr} must be -1 and
--- only the buffer is resolved.
--- Examples of buffer usage: >vim
--- haslocaldir(-1, -1, 0) " Current buf has a local directory?
--- haslocaldir(-1, -1, 3) " Buf #3 has a local directory?
--- <Throw error if the arguments are invalid.
--- |E5000| |E5001| |E5002| |E5006| |E5007|
---
--- @param winnr? integer
--- @param tabnr? integer
--- @param bufnr? integer
--- @return 0|1
function vim.fn.haslocaldir(winnr, tabnr) end
function vim.fn.haslocaldir(winnr, tabnr, bufnr) end
--- Checks whether a mapping exists whose rhs contains {what}.
--- Returns TRUE if there is such a mapping in one of the modes