From d7d32a3850768c62f92dc476fc77394e75608c1b Mon Sep 17 00:00:00 2001 From: James Trew <66286082+jamestrew@users.noreply.github.com> Date: Fri, 22 Mar 2024 23:56:56 -0400 Subject: [PATCH] fix(utils.path_expand): improve windows support (#2999) In order to maintain plenary compatibility (for now, and slightly for other edgecase reasons), avoid converting backslashes to forward slashes. (cherry picked from commit 1e591885751d255b12c3649253cb6c2838e2c724) --- doc/telescope.txt | 17 ++++++++++-- lua/telescope/utils.lua | 44 +++++++++++++++++++----------- lua/tests/automated/utils_spec.lua | 25 +++++++++++++++++ 3 files changed, 67 insertions(+), 19 deletions(-) diff --git a/doc/telescope.txt b/doc/telescope.txt index 226f45e..671424b 100644 --- a/doc/telescope.txt +++ b/doc/telescope.txt @@ -2262,9 +2262,20 @@ UTILS *telescope.utils* Utilities for writing telescope pickers utils.path_expand({path}) *telescope.utils.path_expand()* - Selective `expand`. `vim.fn.expand` is overly aggressive, sometimes - expanding valid absolute paths into non-existent paths or straight up - erroring + Hybrid of `vim.fn.expand()` and custom `vim.fs.normalize()` + + Paths starting with '%', '#' or '<' are expanded with `vim.fn.expand()`. + Otherwise avoids using `vim.fn.expand()` due to its overly aggressive + expansion behavior which can sometimes lead to errors or the creation of + non-existent paths when dealing with valid absolute paths. + + Other paths will have '~' and environment variables expanded. Unlike + `vim.fs.normalize()`, backslashes are preserved. This has better + compatibility with `plenary.path` and also avoids mangling valid Unix paths + with literal backslashes. + + Trailing slashes are trimmed. With the exception of root paths. eg. `/` on + Unix or `C:\` on Windows diff --git a/lua/telescope/utils.lua b/lua/telescope/utils.lua index 7b4448e..4ab9f10 100644 --- a/lua/telescope/utils.lua +++ b/lua/telescope/utils.lua @@ -17,15 +17,32 @@ local utils = {} local iswin = vim.loop.os_uname().sysname == "Windows_NT" ---- `vim.fs.normalize` co-opted for 0.1.x (neovim 0.7) compat ---- TODO: get rid of this and use `vim.fs.normalize` directly for future releases +--- Hybrid of `vim.fn.expand()` and custom `vim.fs.normalize()` +--- +--- Paths starting with '%', '#' or '<' are expanded with `vim.fn.expand()`. +--- Otherwise avoids using `vim.fn.expand()` due to its overly aggressive +--- expansion behavior which can sometimes lead to errors or the creation of +--- non-existent paths when dealing with valid absolute paths. +--- +--- Other paths will have '~' and environment variables expanded. +--- Unlike `vim.fs.normalize()`, backslashes are preserved. This has better +--- compatibility with `plenary.path` and also avoids mangling valid Unix paths +--- with literal backslashes. +--- +--- Trailing slashes are trimmed. With the exception of root paths. +--- eg. `/` on Unix or `C:\` on Windows +--- ---@param path string ---@return string -local function path_normalize(path) +utils.path_expand = function(path) vim.validate { path = { path, { "string" } }, } + if path:match "^[%%#<]" then + path = vim.fn.expand(path) + end + if path:sub(1, 1) == "~" then local home = vim.loop.os_homedir() or "~" if home:sub(-1) == "\\" or home:sub(-1) == "/" then @@ -35,23 +52,18 @@ local function path_normalize(path) end path = path:gsub("%$([%w_]+)", vim.loop.os_getenv) - path = path:gsub("\\", "/"):gsub("/+", "/") - if iswin and path:match "^%w:/$" then - return path + path = path:gsub("/+", "/") + if iswin then + path = path:gsub("\\+", "\\") + if path:match "^%w:\\$" then + return path + else + return (path:gsub("(.)\\$", "%1")) + end end return (path:gsub("(.)/$", "%1")) end ---- Selective `expand`. ---- `vim.fn.expand` is overly aggressive, sometimes expanding valid absolute paths into ---- non-existent paths or straight up erroring ---- ----@param path string ----@return string -utils.path_expand = function(path) - return path:match "^[%%#<]" and vim.fn.expand(path) or path_normalize(path) -end - utils.get_separator = function() return Path.path.sep end diff --git a/lua/tests/automated/utils_spec.lua b/lua/tests/automated/utils_spec.lua index 5edf356..dd3abab 100644 --- a/lua/tests/automated/utils_spec.lua +++ b/lua/tests/automated/utils_spec.lua @@ -1,5 +1,30 @@ local utils = require "telescope.utils" +describe("path_expand()", function() + it("removes trailing /", function() + assert.is.equal("/home/user", utils.path_expand "/home/user/") + end) + + it("works with /", function() + assert.is.equal("/", utils.path_expand "/") + end) + + it("works with ~", function() + assert.is.equal(vim.loop.os_homedir() .. "/src/foo", utils.path_expand "~/src/foo") + end) + + it("handles duplicate /", function() + assert.is.equal("/home/user", utils.path_expand "/home///user") + end) + + it("preserves fake whitespace characters and whitespace", function() + local path_space = "/home/user/hello world" + assert.is.equal(path_space, utils.path_expand(path_space)) + local path_newline = [[/home/user/hello\nworld]] + assert.is.equal(path_newline, utils.path_expand(path_newline)) + end) +end) + describe("is_uri", function() describe("detects valid uris", function() local uris = {