From 7d2249c579b0a9a19982170823675e25f3fba070 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 2 Aug 2026 14:00:48 +0200 Subject: [PATCH] docs: misc, :bcd, slug() --- runtime/autoload/README.txt | 25 ----------- runtime/doc/api.txt | 21 +++++---- runtime/doc/autocmd.txt | 2 +- runtime/doc/editing.txt | 26 ++++++++++- runtime/doc/lsp.txt | 16 +++++++ runtime/doc/lua.txt | 42 ++++++++---------- runtime/doc/news-0.10.txt | 1 + runtime/doc/news.txt | 4 +- runtime/doc/plugins.txt | 68 +++++++++++++++-------------- runtime/doc/terminal.txt | 11 ++--- runtime/doc/vi_diff.txt | 2 +- runtime/doc/vimfn.txt | 38 ++++++++-------- runtime/lua/vim/_meta/api.gen.lua | 11 +++-- runtime/lua/vim/_meta/vimfn.gen.lua | 37 ++++++++-------- runtime/lua/vim/fs.lua | 40 ++++++++--------- scripts/vim_na_files.txt | 1 + src/nvim/api/win_config.c | 11 +++-- src/nvim/eval.lua | 37 ++++++++-------- test/functional/lua/fs_spec.lua | 33 +++++++------- 19 files changed, 218 insertions(+), 208 deletions(-) delete mode 100644 runtime/autoload/README.txt diff --git a/runtime/autoload/README.txt b/runtime/autoload/README.txt deleted file mode 100644 index 5f531f8ea1..0000000000 --- a/runtime/autoload/README.txt +++ /dev/null @@ -1,25 +0,0 @@ -The autoload directory is for standard Vim autoload scripts. - -These are functions used by plugins and for general use. They will be loaded -automatically when the function is invoked. See ":help autoload". - -gzip.vim for editing compressed files -netrw*.vim browsing (remote) directories and editing remote files -tar.vim browsing tar files -zip.vim browsing zip files -paste.vim common code for mswin.vim and menu.vim -spellfile.vim downloading of a missing spell file - -Omni completion files: -adacomplete.vim Ada -beancount.vim Beancount -ccomplete.vim C -csscomplete.vim HTML / CSS -htmlcomplete.vim HTML -javascriptcomplete.vim Javascript -phpcomplete.vim PHP -pythoncomplete.vim Python -python3complete.vim Python -rubycomplete.vim Ruby -syntaxcomplete.vim from syntax highlighting -xmlcomplete.vim XML (uses files in the xml directory) diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index 133328071e..1ad9f62227 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -85,16 +85,16 @@ Nvim instance: sock.write([0, 0, 'nvim_command', ['echo "hello world!"']].to_msgpack) p MessagePack::Unpacker.new(sock).read.last < -Another way is to use the Python REPL with the "pynvim" package, -where API functions can be called interactively, or create a script: -(requires `pip install pynvim` or `uv add pynvim` or another python package manager) -> +Another way is to use the Python REPL with the "pynvim" package, where API +functions can be called interactively (requires `pip install pynvim` or +`uv add pynvim`): > + >>> from pynvim import attach >>> nvim = attach('socket', path='[address]') >>> nvim.command('echo "hello world!"') < You can also embed Nvim via |jobstart()|, and communicate using |rpcrequest()| -and |rpcnotify()| in Vimscript :e hello.vim, copy below content and then :so %: +and |rpcnotify()|: >vim let nvim = jobstart(['nvim', '--embed'], {'rpc': v:true}) echo rpcrequest(nvim, 'nvim_eval', '"Hello " . "world!"') @@ -4048,10 +4048,15 @@ nvim_open_win({buf}, {enter}, {config}) *nvim_open_win()* (`integer`) |window-ID|, or 0 on error nvim_win_get_config({win}) *nvim_win_get_config()* - Gets window configuration in the form of a dict which can be passed as the - `config` parameter of |nvim_open_win()|. + Gets window config as a dict which can be passed to |nvim_open_win()| as + the `config` parameter. - For non-floating windows, `relative` is empty. + For non-floating windows, `relative` is empty, thus you can check that + field to detect if a window is a floatwin: >lua + vim.print(vim.api.nvim_win_get_config(0).relative == '' and 'non-float' or 'float') + -- Or use win_gettype(). + vim.print(vim.fn.win_gettype()) +< Attributes: ~ Since: 0.4.0 diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index 98d903134d..0dbff5a5fc 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -1989,7 +1989,7 @@ and "++ff=" argument that are effective. These should be used for the command that reads/writes the file. The |v:cmdbang| variable is one when "!" was used, zero otherwise. -See the $VIMRUNTIME/pack/dist/opt/netrw/plugin/netrwPlugin.vim for examples. +See $VIMRUNTIME/plugin/net.lua for examples. ============================================================================== 11. Disabling autocommands *autocmd-disable* diff --git a/runtime/doc/editing.txt b/runtime/doc/editing.txt index 952801d486..ef4ea17450 100644 --- a/runtime/doc/editing.txt +++ b/runtime/doc/editing.txt @@ -275,8 +275,8 @@ CTRL-^ Edit the alternate file. Mostly the alternate file is 'includeexpr' to the filename. - If a [count] is given, the count'th file that is found in the 'path' is edited. - - If the name is a URL ("type://machine/path"), you - need the |netrw| plugin. + - If the name is a URL ("type://machine/path"), it is + handled by $VIMRUNTIME/plugin/net.lua - Environment variables are expanded. |expand-env|. - On unix: "~" is expanded. @@ -1488,6 +1488,28 @@ a:test and not write a:vim/test. But if you do ":w test" the file a:vim/test will be written, because you gave a new file name and did not refer to a filename before the ":cd". + +PROJECT DIRECTORY *project-dir* *workspace-dir* + +You can use |:bcd| to assign a "workspace" (or "project directory") to each +buffer, so commands like |:make|, |:grep| and |:terminal| always run relative +to the buffer's "workspace". + +Example: change to the project root (found by |vim.fs.root()|) of each buffer: >lua + + vim.api.nvim_create_autocmd('BufReadPost', { + callback = function(ev) + local root = vim.fs.root(ev.buf, { '.git', 'Makefile' }) + if root then + vim.cmd.bcd(root) + end + end, + }) +< +See also: +- |lsp-buf-working-dir| +- |terminal-osc7| + ============================================================================== 8. Editing binary files *edit-binary* diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index 681aced893..5deb9edba2 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -331,6 +331,22 @@ Example: Enable auto-completion and auto-formatting ("linting"): >lua end end, }) +< + *lsp-buf-working-dir* +The LSP server decides the "workspace root" (|lsp-root_dir()|) of a buffer, +but the Nvim |current-directory| does not follow it, so commands like |:make|, +|:grep| and |:terminal| do not run from the workspace root. You can use |:bcd| +to sync each buffer's directory to its LSP workspace root: >lua + + vim.api.nvim_create_autocmd('LspAttach', { + group = vim.api.nvim_create_augroup('my.lsp.bufdir', {}), + callback = function(ev) + local client = assert(vim.lsp.get_client_by_id(ev.data.client_id)) + if client.root_dir then + vim.cmd.bcd(client.root_dir) + end + end, + }) < To see the capabilities for a given server, try this in a LSP-enabled buffer: >vim diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index 2ba896d047..7fc6fbe3bf 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -2952,46 +2952,38 @@ vim.fs.root({source}, {marker}) *vim.fs.root()* if no directory was found. vim.fs.slug({path}, {opts}) *vim.fs.slug()* - Generates a bounded, filesystem-safe filename from an arbitrary identity - string. - • The input is normalized via |vim.fs.normalize()| so that equivalent - paths produce the same result (e.g., `~/foo` and `/home/username/foo`). - • `$HOME` is replaced with `~`. On Windows, UNC paths are replaced with - `=unc-`. - • An 8-character hex hash (|sha256()|) of the normalized input is appended - to prevent collisions. - • Unsafe characters (`/ \ : * ? " < > |`, whitespace, control characters) - are replaced with `-`, and trailing `-` and `.` are stripped. + Gets a filesystem-safe, mnemonic slug (readable prefix + short hash) of an + arbitrary filepath or other "identity string". + • The input is normalized so equivalent paths produce the same result. + • A hash of the normalized input is appended to prevent collisions. + • Unsafe chars are replaced with "-". + • `$HOME` is replaced with "~". + • UNC paths (Windows) are prefixed with "=unc-". • If `opts.maxlen` is exceeded, the result will be truncated to `{head}~~~{tail}-{hash8}`. • If the sanitized name is empty, the reserved label `=special` will be used. Examples: >lua - vim.fs.slug('/tmp/test/foo.md') - --> "tmp-test-foo.md-{hash}" - - vim.fs.slug('C:/src/project/main.c') - --> "C--src-project-main.c-{hash}" - - vim.fs.slug(('/a/very/long/path'):rep(10) .. '/file.txt', { maxlen = 60 }) + vim.print(vim.fs.slug('/tmp/test/foo.md')) --> "tmp-test-foo.md-{hash}" + vim.print(vim.fs.slug('C:/src/project/main.c')) --> "C--src-project-main.c-{hash}" + vim.print(vim.fs.slug(vim.fn.expand('~/file.txt'))) --> "~-file.txt-{hash}" + vim.print(vim.fs.slug('---')) --> "=special-{hash}" + vim.print(vim.fs.slug(('/a/very/long/path'):rep(10) .. '/file.txt', { maxlen = 60 })) --> "a-very-long-~~~-path-a-very-long-path-file.txt-{hash}" - - vim.fs.slug('home/username/file.txt') - --> "~-file.txt-{hash}" < Attributes: ~ Since: 0.13.0 Parameters: ~ - • {path} (`string`) a string that is not filesystem-safe. - • {opts} (`table?`) Optional parameters: - • maxlen: (integer) Max byte length of the result. Default is - 180. Value must be at least 8. + • {path} (`string`) Filepath (or other identity string). + • {opts} (`table?`) + • maxlen: (integer, default: 180) Max length (bytes) of the + result. Return: ~ - (`string`) Filesystem-safe file name + (`string`) Filesystem-safe, mnemonic slug. ============================================================================== diff --git a/runtime/doc/news-0.10.txt b/runtime/doc/news-0.10.txt index e666b1b5cb..540bfe3471 100644 --- a/runtime/doc/news-0.10.txt +++ b/runtime/doc/news-0.10.txt @@ -405,6 +405,7 @@ These existing features changed their behavior. • Editor: • |gx| now uses |vim.ui.open()| and not netrw. To customize, you can redefine `vim.ui.open` or remap `gx`. To continue using netrw (deprecated): >vim + :packadd netrw :call netrw#BrowseX(expand(exists("g:netrw_gx") ? g:netrw_gx : ''), netrw#CheckIfRemote()) • LSP: diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index 57f96463b1..ab53f55fbe 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -195,6 +195,8 @@ API -- After: vim.print(vim.api.nvim_get_mark('A')) • |nvim_buf_call()| and |nvim_win_call()| now preserve multiple return values. +• |nvim_del_keymap()|, |nvim_buf_del_keymap()| and |vim.keymap.del()| can + match only {lhs}, not {rhs}, with `opts.lhs=true`. • |nvim_set_hl()| supports "font" key. • |nvim_open_win()| `zindex` controls whether the UI will use a dimmed cursor shape when an unfocused float is on top of the cursor. @@ -486,8 +488,6 @@ These existing features changed their behavior. • |vim.lsp.util.open_floating_preview()| windows (e.g. |vim.lsp.buf.hover()|) converted to normal windows (e.g. with |CTRL-W_H|) are no longer closed automatically. -• |nvim_del_keymap()|, |nvim_buf_del_keymap()| and |vim.keymap.del()| can - match only {lhs}, not {rhs}, with `opts.lhs=true`. • |vim.fs.normalize()| `opts.expand_env=false` key was renamed to `opts.plain=true` and now does not expand leading tildes ("~") in addition to environment variables ("expand_env" is still accepted, for backwards diff --git a/runtime/doc/plugins.txt b/runtime/doc/plugins.txt index 0d4f05befe..054b71e8d3 100644 --- a/runtime/doc/plugins.txt +++ b/runtime/doc/plugins.txt @@ -25,7 +25,7 @@ Help-link Loaded Short description ~ |man.lua| Yes View manpages in Nvim |matchit| Yes Extended |%| matching |matchparen| Yes Highlight matching pairs -|netrw| Yes Reading and writing files over a network +|netrw| No Reading and writing files over a network |zip| Yes Read-only zip archive browser |package-cfilter| No Filtering quickfix/location list |package-justify| No Justify text @@ -46,21 +46,28 @@ Help-link Loaded Short description ~ ============================================================================== Builtin plugin: dir *dir* -Nvim opens a directory listing when |:edit| is used with a directory path. The -listing is a buffer with 'filetype' set to "directory". +Nvim opens a directory listing when you |:edit| a directory path, handled by +the builtin "dir" plugin. The listing is read-only, dir.lua does not provide +actions which modify the filesystem. + +Each "dir" buffer is initialized as follows: +- Sets 'filetype' to "directory". +- Sets |current-directory| via |:bcd|, so |gf|, |:!| and friends work as + expected. +- Keeps |alternate-file|, so |CTRL-^| returns to the buffer you came from. *g:loaded_nvim_dir_plugin* To disable the built-in directory browser, set this before startup: >lua - vim.g.loaded_nvim_dir_plugin = 1 + vim.g.loaded_nvim_dir_plugin = 1 < -Mappings: *dir-mappings* +GLOBAL MAPPINGS *dir-mappings* • - opens the parent directory of the current file or directory. • {count}- is like -, but a count of 1 opens the current working directory, and a higher count goes up that many levels. -Directory buffer mappings: *dir-buffer-mappings* +BUFFER-LOCAL MAPPINGS *dir-buffer-mappings* • opens the file or directory under the cursor. • - opens the parent directory. @@ -71,18 +78,10 @@ These keys map to (nvim-dir-open), (nvim-dir-up), and skipped when the target mapping is already mapped. The default global and directory-buffer "-" mappings are also skipped when "-" is already mapped. -The listing is read-only and does not modify the filesystem. - -Opening an entry leaves the |alternate-file| on the buffer you came from, so -that |CTRL-^| returns to it rather than to the listing. - -The listed directory is the buffer's directory (|:bcd|), so entry names resolve -without a prefix for |gf|, |:!|, and friends. Being buffer-local it is not -"sticky": it does not follow you out of the listing. - + *dir-config* Directory buffers follow the global 'hidden' option by default. To delete them after use: >vim - autocmd FileType directory setlocal bufhidden=delete + autocmd FileType directory setlocal bufhidden=delete < A discarded listing is rebuilt on the next visit, so the cursor starts at the first entry instead of the one it was left on. "wipe" discards the buffer @@ -92,23 +91,28 @@ itself, leaving no |alternate-file|; see 'bufhidden'. Replacing the directory browser *dir-disable* To use another directory browser for the current session, delete the -`nvim.dir` autocommand group on or after |VimEnter| and handle |FileType| "directory": ->lua - vim.api.nvim_del_augroup_by_name('nvim.dir') - vim.api.nvim_create_autocmd('FileType', { - pattern = 'directory', - callback = function(args) - require('my_browser').open(args.buf, vim.api.nvim_buf_get_name(args.buf)) - end, - }) -< -This stops the built-in directory-opening autocommands. The plugin and its -mappings remain loaded. +`nvim.dir` autocommand group on or after |VimEnter| and handle |FileType| "directory". +This clears the built-in directory-opening autocommands. >lua -Define replacement keymaps explicitly, for example: >lua - vim.keymap.set('n', '-', function() - require('my_browser').open_parent() - end) + vim.api.nvim_del_augroup_by_name('nvim.dir') + vim.api.nvim_create_autocmd('FileType', { + pattern = 'directory', + callback = function(args) + require('my_browser').open(args.buf, vim.api.nvim_buf_get_name(args.buf)) + end, + }) +< +To enable the legacy "netrw" plugin: >vim + + :packadd netrw + +The plugin and its mappings remain loaded (it is a core component used by +other features such as |zip|). Define replacement keymaps explicitly, for +example: >lua + + vim.keymap.set('n', '-', function() + require('my_browser').open_parent() + end) < ============================================================================== diff --git a/runtime/doc/terminal.txt b/runtime/doc/terminal.txt index 3f9c3f650f..c5076b5a47 100644 --- a/runtime/doc/terminal.txt +++ b/runtime/doc/terminal.txt @@ -170,10 +170,10 @@ To configure bash to emit OSC 7: >bash PROMPT_COMMAND='print_osc7' Having ensured that your shell emits OSC 7, you can now handle it in Nvim. The -following code will run :lcd whenever your shell CWD changes in a :terminal -buffer: >lua +following code runs |:bcd| whenever your shell CWD changes, so the :terminal +buffer-local directory always follows the shell: >lua - vim.api.nvim_create_autocmd({ 'TermRequest' }, { + vim.api.nvim_create_autocmd('TermRequest', { desc = 'Handles OSC 7 dir change requests', callback = function(ev) local val, n = string.gsub(ev.data.sequence, '\027]7;file://[^/]*', '') @@ -184,10 +184,7 @@ buffer: >lua vim.notify('invalid dir: '..dir) return end - vim.b[ev.buf].osc7_dir = dir - if vim.api.nvim_get_current_buf() == ev.buf then - vim.cmd.lcd(dir) - end + vim.cmd.bcd(dir) end end }) diff --git a/runtime/doc/vi_diff.txt b/runtime/doc/vi_diff.txt index 40c0cd66c9..712abca0a4 100644 --- a/runtime/doc/vi_diff.txt +++ b/runtime/doc/vi_diff.txt @@ -258,7 +258,7 @@ Extended search patterns. |pattern| "x\{2,4}" matches "x" 2 to 4 times. "\s" matches a white space character. -Directory, remote and archive browsing. |netrw| +Directory, remote and archive browsing. |dir| Vim can browse the file system. Simply edit a directory. Move around in the list with the usual commands and press to go to the directory or file under the cursor. diff --git a/runtime/doc/vimfn.txt b/runtime/doc/vimfn.txt index 806ee13d2e..004740e445 100644 --- a/runtime/doc/vimfn.txt +++ b/runtime/doc/vimfn.txt @@ -964,8 +964,7 @@ changenr() *changenr()* (`integer`) chansend({id}, {data}) *chansend()* - Lua: Prefer |nvim_chan_send()| for string data; list input and - the return value differ. + Lua: Prefer |nvim_chan_send()| for string (binary) data. Send data to channel {id}. For a job, it writes it to the stdin of the process. For the stdio channel |channel-stdio|, @@ -974,9 +973,12 @@ chansend({id}, {data}) *chansend()* See |channel-bytes| for more information. {data} may be a string, string convertible, |Blob|, or a list. + If {data} is a list, the items will be joined by newlines; any - newlines in an item will be sent as NUL. To send a final - newline, include a final empty string. Example: >vim + newlines in an item will be sent as NUL; to send a final + newline, include a final empty string. |NL-used-for-Nul| + + Example: >vim call chansend(id, ["abc", "123\n456", ""]) < will send "abc123456". @@ -989,7 +991,7 @@ chansend({id}, {data}) *chansend()* • {data} (`string|string[]`) Return: ~ - (`0|1`) + (`integer`) char2nr({string} [, {utf8}]) *char2nr()* Lua: Prefer |string.byte()|: only works with ASCII. @@ -12699,22 +12701,18 @@ win_getid([{win} [, {tab}]]) *win_getid()* (`integer`) win_gettype([{nr}]) *win_gettype()* - Return the type of the window: - "autocmd" autocommand window. Temporary window - used to execute autocommands. - "command" command-line window |cmdwin| - (empty) normal window - "loclist" |location-list-window| - "popup" floating window |api-floatwin| - "preview" preview window |preview-window| - "quickfix" |quickfix-window| - "unknown" window {nr} not found + Gets the type of the given window, or current window if {nr} + is omitted: + - (empty) Normal window + - "autocmd" Internal "context-switch" window. + - "command" Command-line window |cmdwin| + - "loclist" |location-list-window| + - "popup" Floating window |api-floatwin| + - "preview" Preview window |preview-window| + - "quickfix" |quickfix-window| + - "unknown" Window {nr} not found - When {nr} is omitted return the type of the current window. - When {nr} is given (|window-number| or |window-ID|) return the - type of that window. - - Also see the 'buftype' option. + See also the 'buftype' option. Parameters: ~ • {nr} (`integer?`) diff --git a/runtime/lua/vim/_meta/api.gen.lua b/runtime/lua/vim/_meta/api.gen.lua index 2999a8cdff..be346e9373 100644 --- a/runtime/lua/vim/_meta/api.gen.lua +++ b/runtime/lua/vim/_meta/api.gen.lua @@ -2415,10 +2415,15 @@ function vim.api.nvim_win_del_var(win, name) end --- @return integer # Buffer id function vim.api.nvim_win_get_buf(win) end ---- Gets window configuration in the form of a dict which can be passed as the `config` parameter of ---- `nvim_open_win()`. +--- Gets window config as a dict which can be passed to `nvim_open_win()` as the `config` parameter. --- ---- For non-floating windows, `relative` is empty. +--- For non-floating windows, `relative` is empty, thus you can check that field to detect if +--- a window is a floatwin: +--- ```lua +--- vim.print(vim.api.nvim_win_get_config(0).relative == '' and 'non-float' or 'float') +--- -- Or use win_gettype(). +--- vim.print(vim.fn.win_gettype()) +--- ``` --- --- @param win integer `window-ID`, or 0 for current window --- @return vim.api.keyset.win_config_ret # Map defining the window configuration, see |nvim_open_win()| diff --git a/runtime/lua/vim/_meta/vimfn.gen.lua b/runtime/lua/vim/_meta/vimfn.gen.lua index 8444b6d6f4..2ef0211f90 100644 --- a/runtime/lua/vim/_meta/vimfn.gen.lua +++ b/runtime/lua/vim/_meta/vimfn.gen.lua @@ -827,7 +827,7 @@ function vim.fn.chanclose(id, stream) end --- @return integer function vim.fn.changenr() end ---- Lua: Prefer |nvim_chan_send()| for string data; list input and the return value differ. +--- Lua: Prefer |nvim_chan_send()| for string (binary) data. --- --- Send data to channel {id}. For a job, it writes it to the --- stdin of the process. For the stdio channel |channel-stdio|, @@ -836,9 +836,12 @@ function vim.fn.changenr() end --- See |channel-bytes| for more information. --- --- {data} may be a string, string convertible, |Blob|, or a list. +--- --- If {data} is a list, the items will be joined by newlines; any ---- newlines in an item will be sent as NUL. To send a final ---- newline, include a final empty string. Example: >vim +--- newlines in an item will be sent as NUL; to send a final +--- newline, include a final empty string. |NL-used-for-Nul| +--- +--- Example: >vim --- call chansend(id, ["abc", "123\n456", ""]) --- 123456". --- @@ -848,7 +851,7 @@ function vim.fn.changenr() end --- --- @param id number --- @param data string|string[] ---- @return 0|1 +--- @return integer function vim.fn.chansend(id, data) end --- Lua: Prefer |string.byte()|: only works with ASCII. @@ -11355,22 +11358,18 @@ function vim.fn.win_findbuf(bufnr) end --- @return integer function vim.fn.win_getid(win, tab) end ---- Return the type of the window: ---- "autocmd" autocommand window. Temporary window ---- used to execute autocommands. ---- "command" command-line window |cmdwin| ---- (empty) normal window ---- "loclist" |location-list-window| ---- "popup" floating window |api-floatwin| ---- "preview" preview window |preview-window| ---- "quickfix" |quickfix-window| ---- "unknown" window {nr} not found +--- Gets the type of the given window, or current window if {nr} +--- is omitted: +--- - (empty) Normal window +--- - "autocmd" Internal "context-switch" window. +--- - "command" Command-line window |cmdwin| +--- - "loclist" |location-list-window| +--- - "popup" Floating window |api-floatwin| +--- - "preview" Preview window |preview-window| +--- - "quickfix" |quickfix-window| +--- - "unknown" Window {nr} not found --- ---- When {nr} is omitted return the type of the current window. ---- When {nr} is given (|window-number| or |window-ID|) return the ---- type of that window. ---- ---- Also see the 'buftype' option. +--- See also the 'buftype' option. --- --- @param nr? integer --- @return 'autocmd'|'command'|''|'loclist'|'popup'|'preview'|'quickfix'|'unknown' diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua index 9888144669..ec21ae3010 100644 --- a/runtime/lua/vim/fs.lua +++ b/runtime/lua/vim/fs.lua @@ -147,39 +147,33 @@ function M.joinpath(...) return (path:gsub(iswin and '[/\\][/\\]*' or '//+', '/')) end ---- Generates a bounded, filesystem-safe filename from an arbitrary identity string. +--- Gets a filesystem-safe, mnemonic slug (readable prefix + short hash) of an arbitrary filepath or +--- other "identity string". --- ---- - The input is normalized via |vim.fs.normalize()| so that equivalent paths produce the same ---- result (e.g., `~/foo` and `/home/username/foo`). ---- - `$HOME` is replaced with `~`. On Windows, UNC paths are replaced with `=unc-`. ---- - An 8-character hex hash (|sha256()|) of the normalized input is appended to prevent ---- collisions. ---- - Unsafe characters (`/ \ : * ? " < > |`, whitespace, control characters) are replaced with ---- `-`, and trailing `-` and `.` are stripped. +--- - The input is normalized so equivalent paths produce the same result. +--- - A hash of the normalized input is appended to prevent collisions. +--- - Unsafe chars are replaced with "-". +--- - `$HOME` is replaced with "~". +--- - UNC paths (Windows) are prefixed with "=unc-". --- - If `opts.maxlen` is exceeded, the result will be truncated to `{head}~~~{tail}-{hash8}`. --- - If the sanitized name is empty, the reserved label `=special` will be used. --- --- Examples: --- --- ```lua ---- vim.fs.slug('/tmp/test/foo.md') ---- --> "tmp-test-foo.md-{hash}" ---- ---- vim.fs.slug('C:/src/project/main.c') ---- --> "C--src-project-main.c-{hash}" ---- ---- vim.fs.slug(('/a/very/long/path'):rep(10) .. '/file.txt', { maxlen = 60 }) +--- vim.print(vim.fs.slug('/tmp/test/foo.md')) --> "tmp-test-foo.md-{hash}" +--- vim.print(vim.fs.slug('C:/src/project/main.c')) --> "C--src-project-main.c-{hash}" +--- vim.print(vim.fs.slug(vim.fn.expand('~/file.txt'))) --> "~-file.txt-{hash}" +--- vim.print(vim.fs.slug('---')) --> "=special-{hash}" +--- vim.print(vim.fs.slug(('/a/very/long/path'):rep(10) .. '/file.txt', { maxlen = 60 })) --- --> "a-very-long-~~~-path-a-very-long-path-file.txt-{hash}" ---- ---- vim.fs.slug('home/username/file.txt') ---- --> "~-file.txt-{hash}" --- ``` --- ---@since 15 ----@param path string a string that is not filesystem-safe. ----@param opts? table Optional parameters: ---- - maxlen: (integer) Max byte length of the result. Default is 180. Value must be at least 8. ----@return string # Filesystem-safe file name +---@param path string Filepath (or other identity string). +---@param opts? table +--- - maxlen: (integer, default: 180) Max length (bytes) of the result. +---@return string # Filesystem-safe, mnemonic slug. function M.slug(path, opts) vim.validate('path', path, 'string') opts = opts or {} @@ -188,7 +182,7 @@ function M.slug(path, opts) return true end return type(v) == 'number' and v >= 8 - end, '`opt.maxlen` must be at least 8') + end, '`opt.maxlen` must be >= 8') opts.maxlen = opts.maxlen or 180 -- Normalize before computing the hash so equivalent paths produce the same result diff --git a/scripts/vim_na_files.txt b/scripts/vim_na_files.txt index db8c811fc5..bacb78ab51 100644 --- a/scripts/vim_na_files.txt +++ b/scripts/vim_na_files.txt @@ -5,6 +5,7 @@ LICENSE Makefile SECURITY.md configure +runtime/autoload/README.txt runtime/bugreport.vim runtime/defaults.vim runtime/doc/channel.txt diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c index 8e82079a72..c7f3047a64 100644 --- a/src/nvim/api/win_config.c +++ b/src/nvim/api/win_config.c @@ -829,10 +829,15 @@ static void config_put_bordertext(Dict(win_config) *config, WinConfig *fconfig, } } -/// Gets window configuration in the form of a dict which can be passed as the `config` parameter of -/// |nvim_open_win()|. +/// Gets window config as a dict which can be passed to |nvim_open_win()| as the `config` parameter. /// -/// For non-floating windows, `relative` is empty. +/// For non-floating windows, `relative` is empty, thus you can check that field to detect if +/// a window is a floatwin: +/// ```lua +/// vim.print(vim.api.nvim_win_get_config(0).relative == '' and 'non-float' or 'float') +/// -- Or use win_gettype(). +/// vim.print(vim.fn.win_gettype()) +/// ``` /// /// @param win |window-ID|, or 0 for current window /// @param[out] err Error details, if any diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 3181857b53..011e19d082 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -1113,9 +1113,12 @@ M.funcs = { See |channel-bytes| for more information. {data} may be a string, string convertible, |Blob|, or a list. + If {data} is a list, the items will be joined by newlines; any - newlines in an item will be sent as NUL. To send a final - newline, include a final empty string. Example: >vim + newlines in an item will be sent as NUL; to send a final + newline, include a final empty string. |NL-used-for-Nul| + + Example: >vim call chansend(id, ["abc", "123\n456", ""]) 123456". @@ -1125,9 +1128,9 @@ M.funcs = { ]=], name = 'chansend', params = { { 'id', 'number' }, { 'data', 'string|string[]' } }, - returns = '0|1', + returns = 'integer', signature = 'chansend({id}, {data})', - see_lua = { '|nvim_chan_send()| for string data; list input and the return value differ' }, + see_lua = { '|nvim_chan_send()| for string (binary) data' }, }, char2nr = { args = { 1, 2 }, @@ -13629,22 +13632,18 @@ M.funcs = { args = { 0, 1 }, base = 1, desc = [=[ - Return the type of the window: - "autocmd" autocommand window. Temporary window - used to execute autocommands. - "command" command-line window |cmdwin| - (empty) normal window - "loclist" |location-list-window| - "popup" floating window |api-floatwin| - "preview" preview window |preview-window| - "quickfix" |quickfix-window| - "unknown" window {nr} not found + Gets the type of the given window, or current window if {nr} + is omitted: + - (empty) Normal window + - "autocmd" Internal "context-switch" window. + - "command" Command-line window |cmdwin| + - "loclist" |location-list-window| + - "popup" Floating window |api-floatwin| + - "preview" Preview window |preview-window| + - "quickfix" |quickfix-window| + - "unknown" Window {nr} not found - When {nr} is omitted return the type of the current window. - When {nr} is given (|window-number| or |window-ID|) return the - type of that window. - - Also see the 'buftype' option. + See also the 'buftype' option. ]=], name = 'win_gettype', diff --git a/test/functional/lua/fs_spec.lua b/test/functional/lua/fs_spec.lua index 81395a70b0..9664afef04 100644 --- a/test/functional/lua/fs_spec.lua +++ b/test/functional/lua/fs_spec.lua @@ -678,13 +678,12 @@ describe('vim.fs', function() eq('a-ca978112', vim.fs.slug('a/.')) end) - it('works without args', function() + it('works', function() -- `=special` eq('=special-8a5edab2', vim.fs.slug('/')) eq('=special-ab5df625', vim.fs.slug('...')) eq('=special-11d925ec', vim.fs.slug('-------')) eq('src-foo-init.lua-cf05d2fe', vim.fs.slug('/src/foo/init.lua')) - -- Windows paths normalize differently on Windows vs Unix eq('C--src-project-main.c-3b0eb5f5', vim.fs.slug('C:/src/project/main.c')) eq('con.txt-d3bde286', vim.fs.slug('con.txt')) -- Windows reserved names. @@ -699,11 +698,23 @@ describe('vim.fs', function() local p = vim.uv.os_homedir() .. '/my-project' local hash8_2 = vim.fn.sha256(vim.fs.normalize(p)):sub(1, 8) eq('~-my-project-' .. hash8_2, vim.fs.slug(p)) + + -- Windows-only cases. + if is_os('win') then + eq('=unc-foo-dir-file-549fb6e7', vim.fs.slug([[\\foo\dir\file]])) + -- `\\?\` and `\\.\` + eq( + '---Volume{a1b2c3d4-aa00-4000-a111-1a2b3c4d5e6f}-dir-file-cfda6a98', + vim.fs.slug([[\\?\Volume{a1b2c3d4-aa00-4000-a111-1a2b3c4d5e6f}\dir\file]]) + ) + eq('---C--dir-file-e8f30888', vim.fs.slug([[\\?\C:\dir\file]])) + eq('-.-COM1-e0e5710d', vim.fs.slug([[\\.\COM1]])) + end end) - it('works with `opt.maxlen`', function() + it('`opts.maxlen`', function() -- maxlen < 8 is an error - t.matches('`opt.maxlen` must be at least 8', t.pcall_err(vim.fs.slug, 'foo', { maxlen = 7 })) + t.matches('`opts.maxlen` must be >= 8', t.pcall_err(vim.fs.slug, 'foo', { maxlen = 7 })) eq('2c26b46b', vim.fs.slug('foo', { maxlen = 8 })) eq('2c26b46b', vim.fs.slug('foo', { maxlen = 11 })) @@ -723,20 +734,6 @@ describe('vim.fs', function() eq('~~~d-473a1da7', vim.fs.slug('foo/bar/longlonglong.md', { maxlen = 13 })) eq('f~~~md-473a1da7', vim.fs.slug('foo/bar/longlonglong.md', { maxlen = 15 })) end) - - it('works on Windows', function() - if t.skip(not is_os('win'), 'N/A Windows only') then - return - end - eq('=unc-foo-dir-file-549fb6e7', vim.fs.slug([[\\foo\dir\file]])) - -- `\\?\` and `\\.\` - eq( - '---Volume{a1b2c3d4-aa00-4000-a111-1a2b3c4d5e6f}-dir-file-cfda6a98', - vim.fs.slug([[\\?\Volume{a1b2c3d4-aa00-4000-a111-1a2b3c4d5e6f}\dir\file]]) - ) - eq('---C--dir-file-e8f30888', vim.fs.slug([[\\?\C:\dir\file]])) - eq('-.-COM1-e0e5710d', vim.fs.slug([[\\.\COM1]])) - end) end) describe('normalize()', function()