From b033e406caa0824e0e9ae006d9d0a18e3cd1fa72 Mon Sep 17 00:00:00 2001 From: bhagwan Date: Wed, 9 Feb 2022 19:41:19 -0800 Subject: [PATCH] grep|live_grep: fish shell special chars fixes (closes #340) --- lua/fzf-lua/core.lua | 12 ++++++++---- lua/fzf-lua/fzf.lua | 1 + lua/fzf-lua/libuv.lua | 33 ++++++++++++++++++++++++++++++++- lua/fzf-lua/providers/grep.lua | 13 ++++++++----- 4 files changed, 49 insertions(+), 10 deletions(-) diff --git a/lua/fzf-lua/core.lua b/lua/fzf-lua/core.lua index 59c2fdc..dfb855a 100644 --- a/lua/fzf-lua/core.lua +++ b/lua/fzf-lua/core.lua @@ -349,19 +349,23 @@ M.mt_cmd_wrapper = function(opts) elseif opts.multiprocess or opts.force_multiprocess then local fn_preprocess = [[return require("make_entry").preprocess]] local fn_transform = [[return require("make_entry").file]] + -- replace all below 'fn.shellescape' with our version + -- replacing the surrounding single quotes with double + -- as this was causing resume to fail with fish shell + -- due to fzf replacing ' with \ (no idea why) if not opts.no_remote_config then fn_transform = ([[_G._fzf_lua_server=%s; %s]]):format( - vim.fn.shellescape(vim.g.fzf_lua_server), + libuv.shellescape(vim.g.fzf_lua_server), fn_transform) end if config._devicons_setup then fn_transform = ([[_G._devicons_setup=%s; %s]]) :format( - vim.fn.shellescape(config._devicons_setup), + libuv.shellescape(config._devicons_setup), fn_transform) end if config._devicons_path then fn_transform = ([[_G._devicons_path=%s; %s]]) :format( - vim.fn.shellescape(config._devicons_path), + libuv.shellescape(config._devicons_path), fn_transform) end local cmd = libuv.wrap_spawn_stdio(opts_to_str(opts), @@ -441,7 +445,7 @@ M.set_header = function(opts, type) header_str = header_str .. (cwd_str or '') end if not header_str or #header_str==0 then return opts end - opts.fzf_opts['--header'] = vim.fn.shellescape(header_str) + opts.fzf_opts['--header'] = libuv.shellescape(header_str) return opts end diff --git a/lua/fzf-lua/fzf.lua b/lua/fzf-lua/fzf.lua index efba5f6..0a3b150 100644 --- a/lua/fzf-lua/fzf.lua +++ b/lua/fzf-lua/fzf.lua @@ -134,6 +134,7 @@ function M.raw_fzf(contents, fzf_cli_args, opts) local co = coroutine.running() vim.fn.termopen({"sh", "-c", cmd}, { cwd = cwd, + env = { ['SHELL'] = 'sh' }, on_exit = function(_, rc, _) local f = io.open(outputtmpname) local output = get_lines_from_file(f) diff --git a/lua/fzf-lua/libuv.lua b/lua/fzf-lua/libuv.lua index e6cdedd..04c9a81 100644 --- a/lua/fzf-lua/libuv.lua +++ b/lua/fzf-lua/libuv.lua @@ -419,6 +419,37 @@ M.spawn_stdio = function(opts, fn_transform, fn_preprocess) end) end +-- our own version of vim.fn.shellescape compatibile with fish shells +-- * don't double-escape '\' (#340) +-- * if possible, replace surrounding single quote with double +-- from ':help shellescape': +-- If 'shell' contains "fish" in the tail, the "\" character will +-- be escaped because in fish it is used as an escape character +-- inside single quotes. +-- this function is a better fit for utils but we're +-- trying to avoid having any 'require' in this file +M.shellescape = function(s) + local shell = vim.o.shell + if not shell or not shell:match("fish$") then + return vim.fn.shellescape(s) + else + local ret = nil + vim.o.shell = "sh" + if not s:match([["]]) then + -- if the original string does not contain double quotes + -- replace surrounding single quote with double quotes + -- temporarily replace all single quotes with double + -- quotes and restore after the call to shellescape + ret = vim.fn.shellescape(s:gsub([[']], [["]])) + ret = [["]] .. ret:gsub([["]], [[']]):sub(2, #ret-1) .. [["]] + else + ret = vim.fn.shellescape(s) + end + vim.o.shell = shell + return ret + end +end + M.wrap_spawn_stdio = function(opts, fn_transform, fn_preprocess) assert(opts and type(opts) == 'string') assert(not fn_transform or type(fn_transform) == 'string') @@ -431,7 +462,7 @@ M.wrap_spawn_stdio = function(opts, fn_transform, fn_preprocess) end local cmd_str = ("%s -n --headless --clean --cmd %s"):format( vim.fn.shellescape(nvim_bin), - vim.fn.shellescape(("lua loadfile([[%s]])().spawn_stdio(%s)") + M.shellescape(("lua loadfile([[%s]])().spawn_stdio(%s)") :format(__FILE__, call_args))) return cmd_str end diff --git a/lua/fzf-lua/providers/grep.lua b/lua/fzf-lua/providers/grep.lua index 37fa214..3389e50 100644 --- a/lua/fzf-lua/providers/grep.lua +++ b/lua/fzf-lua/providers/grep.lua @@ -2,6 +2,7 @@ local path = require "fzf-lua.path" local core = require "fzf-lua.core" local utils = require "fzf-lua.utils" local config = require "fzf-lua.config" +local libuv = require "fzf-lua.libuv" local function get_last_search() local last_search = config.globals.grep._last_search or {} @@ -58,7 +59,9 @@ local get_grep_cmd = function(opts, search_query, no_esc) -- do not escape at all if not (no_esc == 2 or opts.no_esc == 2) then - search_query = vim.fn.shellescape(search_query) + -- we need to use our own version of 'shellescape' + -- that doesn't escape '\' on fish shell (#340) + search_query = libuv.shellescape(search_query) end return string.format('%s %s %s', command, search_query, search_path) @@ -257,17 +260,17 @@ M.live_grep_mt = function(opts) opts.prompt = nil -- since we surrounded the skim placeholder with quotes -- we need to escape them in the initial query - opts.fzf_opts['--cmd-query'] = vim.fn.shellescape(utils.sk_escape(query)) + opts.fzf_opts['--cmd-query'] = libuv.shellescape(utils.sk_escape(query)) opts._fzf_cli_args = string.format("-i -c %s", vim.fn.shellescape(reload_command)) else opts.fzf_fn = {} if opts.exec_empty_query or (opts.search and #opts.search > 0) then opts.fzf_fn = initial_command:gsub(placeholder, - vim.fn.shellescape(query)) + libuv.shellescape(query)) end opts.fzf_opts['--phony'] = '' - opts.fzf_opts['--query'] = vim.fn.shellescape(query) + opts.fzf_opts['--query'] = libuv.shellescape(query) opts._fzf_cli_args = string.format('--bind=%s', vim.fn.shellescape(("change:reload:%s"):format( ("%s || true"):format(reload_command)))) @@ -325,7 +328,7 @@ M.live_grep_glob = function(opts) -- do not escape at all if not (no_esc == 2 or o.no_esc == 2) then - search_query = vim.fn.shellescape(search_query) + search_query = libuv.shellescape(search_query) end local cmd = ("rg %s %s -- %s %s")