diff --git a/lua/grug-far/actions/replace.lua b/lua/grug-far/actions/replace.lua index 6e9bc341..a7c6914d 100644 --- a/lua/grug-far/actions/replace.lua +++ b/lua/grug-far/actions/replace.lua @@ -82,14 +82,14 @@ end local function getActionMessage(err, count, total, time) local msg = 'replace ' if err then - return msg .. ' failed!' + return msg .. 'failed!' end if count == total and total ~= 0 then - return msg .. ' completed in ' .. time .. 'ms!' + return msg .. 'completed in ' .. time .. 'ms!' end - return msg .. ' ' .. count .. ' / ' .. total .. ' (buffer temporarily not modifiable)' + return msg .. count .. ' / ' .. total .. ' (buffer temporarily not modifiable)' end local function replace(params) @@ -129,13 +129,12 @@ local function replace(params) vim.api.nvim_buf_set_option(buf, 'modifiable', true) state.status = 'error' - state.progressCount = nil state.actionMessage = getActionMessage(errorMessage) resultsList.setError(buf, context, errorMessage) renderResultsHeader(buf, context) end - local on_finish_all = vim.schedule_wrap(function(status, errorMessage) + local on_finish_all = vim.schedule_wrap(function(status, errorMessage, customActionMessage) vim.api.nvim_buf_set_option(buf, 'modifiable', true) if status == 'error' then @@ -144,20 +143,27 @@ local function replace(params) end state.status = status - state.progressCount = nil local time = uv.now() - startTime -- not passing in total as 3rd arg cause of paranoia if counts don't end up matching - state.actionMessage = status == nil and 'replace cannot work with current arguments!' or + state.actionMessage = status == nil and customActionMessage or getActionMessage(nil, filesCount, filesCount, time) renderResultsHeader(buf, context) end) + if #state.inputs.search == 0 or #state.inputs.replacement == 0 then + on_finish_all(nil, nil, 'replace cannot work due to missing search/replacement inputs!') + return + end + fetchFilesWithMatches({ inputs = context.state.inputs, options = context.options, on_fetch_chunk = reportMatchingFilesUpdate, on_finish = vim.schedule_wrap(function(status, errorMessage, files) - if not status or status == 'error' then + if not status then + on_finish_all(nil, nil, 'replace cannot work due to improper flags! (blacklisted because of undesirable results)') + return + elseif status == 'error' then on_finish_all(status, errorMessage) return end diff --git a/lua/grug-far/actions/search.lua b/lua/grug-far/actions/search.lua index 78ffb083..7fc449f1 100644 --- a/lua/grug-far/actions/search.lua +++ b/lua/grug-far/actions/search.lua @@ -40,7 +40,6 @@ local function search(params) state.abortSearch = nil state.status = status - state.progressCount = nil if status == 'error' then state.stats = nil resultsList.setError(buf, context, errorMessage) diff --git a/lua/grug-far/opts.lua b/lua/grug-far/opts.lua index 4cbc534c..b61dffc0 100644 --- a/lua/grug-far/opts.lua +++ b/lua/grug-far/opts.lua @@ -3,7 +3,7 @@ local M = {} local defaultOptions = { -- debounce milliseconds for issuing search while user is typing -- prevents excesive searching - debounceMs = 500, + debounceMs = 700, -- minimum number of chars which will cause a search to happen -- prevents performance issues in larger dirs diff --git a/lua/grug-far/render.lua b/lua/grug-far/render.lua index 3692fb27..7d6dd456 100644 --- a/lua/grug-far/render.lua +++ b/lua/grug-far/render.lua @@ -73,7 +73,8 @@ local function render(params, context) extmarkName = "flags", icon = 'flagsInput', label = "Flags:", - placeholder = "ex: --hidden (-.) --ignore-case (-i) --multiline (-U)", + -- TODO (sbadragan): make those configurable + placeholder = "ex: --hidden (-.) --ignore-case (-i) --multiline (-U) --fixed-strings (-F)", }, context)) lineNr = lineNr + BEFORE_RESULTS_LINES diff --git a/lua/grug-far/render/resultsList.lua b/lua/grug-far/render/resultsList.lua index 20804655..7fc4cfdd 100644 --- a/lua/grug-far/render/resultsList.lua +++ b/lua/grug-far/render/resultsList.lua @@ -1,9 +1,16 @@ local M = {} +local function setBufLines(buf, start, ending, strict_indexing, replacement) + local isModifiable = vim.api.nvim_buf_get_option(buf, 'modifiable') + vim.api.nvim_buf_set_option(buf, 'modifiable', true) + vim.api.nvim_buf_set_lines(buf, start, ending, strict_indexing, replacement) + vim.api.nvim_buf_set_option(buf, 'modifiable', isModifiable) +end + function M.appendResultsChunk(buf, context, data) -- add text local lastline = vim.api.nvim_buf_line_count(buf) - vim.api.nvim_buf_set_lines(buf, lastline, lastline, false, data.lines) + setBufLines(buf, lastline, lastline, false, data.lines) -- add highlights for i = 1, #data.highlights do @@ -64,7 +71,7 @@ function M.setError(buf, context, error) local startLine = context.state.headerRow + 1 local err_lines = vim.split((error and #error > 0) and error or 'Unexpected error!', '\n') - vim.api.nvim_buf_set_lines(buf, startLine, startLine, false, err_lines) + setBufLines(buf, startLine, startLine, false, err_lines) for i = startLine, startLine + #err_lines do vim.api.nvim_buf_add_highlight(buf, context.namespace, 'DiagnosticError', i, 0, -1) @@ -74,7 +81,7 @@ end function M.clear(buf, context) -- remove all lines after heading and add one blank line local headerRow = context.state.headerRow - vim.api.nvim_buf_set_lines(buf, headerRow, -1, false, { "" }) + setBufLines(buf, headerRow, -1, false, { "" }) vim.api.nvim_buf_clear_namespace(buf, context.locationsNamespace, 0, -1) context.state.resultLocationByExtmarkId = {} diff --git a/lua/grug-far/rg/blacklistedReplaceFlags.lua b/lua/grug-far/rg/blacklistedReplaceFlags.lua new file mode 100644 index 00000000..912f3adc --- /dev/null +++ b/lua/grug-far/rg/blacklistedReplaceFlags.lua @@ -0,0 +1,15 @@ +-- TODO (sbadragan): might need to disable some flags, like: +-- --no-include-zero --no-byte-offset +-- --hyperlink-format=none +-- --max-columns=0 +-- --no-max-columns-preview --no-trim +-- blacklist: --help --quiet +-- Hmmm, there are just too many things that could completely screw it up ... I think we need a whitelist of useful +-- flags that we allow the user to pass, otherwise replacing would not work +return { + '--pre', + '--pre-glob', + '--search-zip', '-z', + + '--help' +} diff --git a/lua/grug-far/rg/fetchFilesWithMatches.lua b/lua/grug-far/rg/fetchFilesWithMatches.lua index 02b4a7a4..e1b144f5 100644 --- a/lua/grug-far/rg/fetchFilesWithMatches.lua +++ b/lua/grug-far/rg/fetchFilesWithMatches.lua @@ -1,4 +1,5 @@ local getArgs = require('grug-far/rg/getArgs') +local blacklistedReplaceFlags = require('grug-far/rg/blacklistedReplaceFlags') local fetchWithRg = require('grug-far/rg/fetchWithRg') local function fetchFilesWithMatches(params) @@ -6,7 +7,7 @@ local function fetchFilesWithMatches(params) local args = getArgs(params.inputs, params.options, { '--files-with-matches' - }) + }, blacklistedReplaceFlags) return fetchWithRg({ args = args, diff --git a/lua/grug-far/rg/fetchReplacedFileContent.lua b/lua/grug-far/rg/fetchReplacedFileContent.lua index 5f4f9bc6..8c498c20 100644 --- a/lua/grug-far/rg/fetchReplacedFileContent.lua +++ b/lua/grug-far/rg/fetchReplacedFileContent.lua @@ -1,4 +1,5 @@ local getArgs = require('grug-far/rg/getArgs') +local blacklistedReplaceFlags = require('grug-far/rg/blacklistedReplaceFlags') local fetchWithRg = require('grug-far/rg/fetchWithRg') local function fetchReplacedFileContent(params) @@ -10,7 +11,7 @@ local function fetchReplacedFileContent(params) '--no-heading', '--no-filename', params.file, - }) + }, blacklistedReplaceFlags) local content = '' return fetchWithRg({ diff --git a/lua/grug-far/rg/getArgs.lua b/lua/grug-far/rg/getArgs.lua index 55488be7..a98f2cfe 100644 --- a/lua/grug-far/rg/getArgs.lua +++ b/lua/grug-far/rg/getArgs.lua @@ -1,16 +1,23 @@ --- TODO (sbadragan): might need to disable some flags, like: --- --no-include-zero --no-byte-offset --- --hyperlink-format=none --- --max-columns=0 --- --no-max-columns-preview --no-trim --- blacklist: --help --quiet --- Hmmm, there are just too many things that could completely screw it up ... I think we need a whitelist of useful --- flags that we allow the user to pass, otherwise replacing would not work -local function isValidArg(arg) +local function isProperFlag(arg) return vim.startswith(arg, '-') and arg ~= '--' end -local function getArgs(inputs, options, extraArgs) +local function isBlacklistedFlag(flag, blacklistedFlags) + if not blacklistedFlags then + return false + end + + for i = 1, #blacklistedFlags do + local badFlag = blacklistedFlags[i] + if flag == badFlag or vim.startswith(flag, badFlag .. ' ') or vim.startswith(flag, badFlag .. '=') then + return true + end + end + + return false +end + +local function getArgs(inputs, options, extraArgs, blacklistedFlags) local args = nil if #inputs.search < (options.minSearchChars or 1) then return nil @@ -26,7 +33,10 @@ local function getArgs(inputs, options, extraArgs) local extraUserArgs = options.extraRgArgs and vim.trim(options.extraRgArgs) or '' if #extraUserArgs > 0 then for arg in string.gmatch(extraUserArgs, "%S+") do - if isValidArg(arg) then + if isBlacklistedFlag(arg, blacklistedFlags) then + return nil + end + if isProperFlag(arg) then table.insert(args, arg) end end @@ -34,7 +44,10 @@ local function getArgs(inputs, options, extraArgs) if #inputs.flags > 0 then for flag in string.gmatch(inputs.flags, "%S+") do - if isValidArg(flag) then + if isBlacklistedFlag(flag, blacklistedFlags) then + return nil + end + if isProperFlag(flag) then table.insert(args, flag) end end