Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Visual selection utility fixes #265

Merged
merged 7 commits into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions doc/grug-far.txt
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,13 @@ require('grug-far').get_current_visual_selection() *grug-far.get_current_visual
Return: ~
{visual_selection}(string)

require('grug-far').get_current_visual_selection_lines() *grug-far.get_current_visual_selection_lines()*
mehalter marked this conversation as resolved.
Show resolved Hide resolved
Gets the current visual selection as a string array for each line.
This is provided as a utility for users so they don't have to rewrite.

Return: ~
{visual_selection}(string[])

==============================================================================
4. Highlights *grug-far-highlights*

Expand Down
62 changes: 33 additions & 29 deletions lua/grug-far.lua
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,15 @@ function M.setup(options)
highlights.setup()
vim.api.nvim_create_user_command('GrugFar', function(params)
local engineParam = params.fargs[1]
local is_visual = params.range > 0
local visual_selection_lines
if params.range > 0 then
visual_selection_lines = M.get_current_visual_selection_lines()
end
local resolvedOpts = opts.with_defaults({ engine = engineParam }, globalOptions)
if params.mods and #params.mods > 0 then
resolvedOpts.windowCreationCommand = params.mods .. ' split'
end
M._open_internal(resolvedOpts, { is_visual = is_visual })
M._open_internal(resolvedOpts, { visual_selection_lines = visual_selection_lines })
end, {
nargs = '?',
range = true,
Expand Down Expand Up @@ -251,21 +254,17 @@ end
function M.open(options)
ensure_configured()
local resolvedOpts = opts.with_defaults(options or {}, globalOptions)
local is_visual = false
if not resolvedOpts.ignoreVisualSelection and vim.fn.mode():lower():find('v') ~= nil then
is_visual = true
end
if is_visual then
-- needed to make visual selection work
vim.cmd([[normal! vv]])
local visual_selection_lines
if not resolvedOpts.ignoreVisualSelection then
visual_selection_lines = M.get_current_visual_selection_lines(true)
end

return M._open_internal(resolvedOpts, { is_visual = is_visual })
return M._open_internal(resolvedOpts, { visual_selection_lines = visual_selection_lines })
end

--- launch grug-far with the given options and params
---@param options GrugFarOptions
---@param params { is_visual: boolean }
---@param params { visual_selection_lines: string[]? }
---@return string instanceName
function M._open_internal(options, params)
if options.instanceName and namedInstances[options.instanceName] then
Expand All @@ -276,8 +275,11 @@ function M._open_internal(options, params)
if not options.instanceName then
options.instanceName = '__grug_far_instance__' .. context.count
end
if params.is_visual then
options.prefills = context.engine.getInputPrefillsForVisualSelection(options.prefills)
if params.visual_selection_lines then
options.prefills = context.engine.getInputPrefillsForVisualSelection(
params.visual_selection_lines,
options.prefills
)
end

local win = createWindow(context)
Expand Down Expand Up @@ -422,28 +424,30 @@ end
function M.with_visual_selection(options)
ensure_configured()

local isVisualMode = vim.fn.mode():lower():find('v') ~= nil
if isVisualMode then
-- needed to make visual selection work
vim.cmd([[normal! vv]])
end

local resolvedOpts = opts.with_defaults(options or {}, globalOptions)
return M._open_internal(resolvedOpts, { is_visual = true })
local visual_selection_lines = M.get_current_visual_selection_lines()
return M._open_internal(resolvedOpts, { visual_selection_lines = visual_selection_lines })
end

--- gets the current visual selection as a string
--- gets the current visual selection as a string array of lines
--- This is provided as a utility for users so they don't have to rewrite
---@return string
function M.get_current_visual_selection()
local isVisualMode = vim.fn.mode():lower():find('v') ~= nil
if isVisualMode then
-- needed to make visual selection work
vim.cmd([[normal! vv]])
---@param strict? boolean Whether to require visual mode to be active, defaults to False
---@return string[]?
function M.get_current_visual_selection_lines(strict)
local was_visual = utils.leaveVisualMode()
if strict and not was_visual then
return
end
return utils.getVisualSelectionLines()
end

local selection_lines = utils.getVisualSelectionLines()
return vim.fn.join(selection_lines, '\n')
--- gets the current visual selection as a single string
--- This is provided as a utility for users so they don't have to rewrite
---@param strict? boolean Whether to require visual mode to be active to return, defaults to False
---@return string?
function M.get_current_visual_selection(strict)
local selection_lines = M.get_current_visual_selection_lines(strict)
return selection_lines and vim.fn.join(selection_lines, '\n')
end

---@deprecated use open(same options) instead
Expand Down
2 changes: 1 addition & 1 deletion lua/grug-far/engine.lua
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ M.DiffSeparatorChars = ' '
---@field replace fun(params: EngineReplaceParams): (abort: fun()?) performs replace
---@field isSyncSupported fun(): boolean whether sync operation is supported
---@field sync fun(params: EngineSyncParams): (abort: fun()?) syncs given changes to their originating files
---@field getInputPrefillsForVisualSelection fun(initialPrefills: GrugFarPrefills): GrugFarPrefills gets prefills updated with visual selection searchand any additional flags that are necessary (for example --fixed-strings for rg)
---@field getInputPrefillsForVisualSelection fun(visual_selection: string[], initialPrefills: GrugFarPrefills): GrugFarPrefills gets prefills updated with visual selection searchand any additional flags that are necessary (for example --fixed-strings for rg)

--- returns engine given type
---@param type GrugFarEngineType
Expand Down
6 changes: 2 additions & 4 deletions lua/grug-far/engine/astgrep.lua
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
local utils = require('grug-far/utils')
local search = require('grug-far/engine/astgrep/search')
local replace = require('grug-far/engine/astgrep/replace')

Expand Down Expand Up @@ -27,10 +26,9 @@ local AstgrepEngine = {
-- not supported
end,

getInputPrefillsForVisualSelection = function(initialPrefills)
getInputPrefillsForVisualSelection = function(visual_selection, initialPrefills)
local prefills = vim.deepcopy(initialPrefills)
local selection_lines = utils.getVisualSelectionLines()
prefills.search = vim.fn.join(selection_lines, '\n')
prefills.search = vim.fn.join(visual_selection, '\n')
return prefills
end,
}
Expand Down
8 changes: 3 additions & 5 deletions lua/grug-far/engine/ripgrep.lua
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
local search = require('grug-far/engine/ripgrep/search')
local replace = require('grug-far/engine/ripgrep/replace')
local sync = require('grug-far/engine/ripgrep/sync')
local utils = require('grug-far/utils')

---@type GrugFarEngine
local RipgrepEngine = {
Expand All @@ -26,16 +25,15 @@ local RipgrepEngine = {

sync = sync.sync,

getInputPrefillsForVisualSelection = function(initialPrefills)
getInputPrefillsForVisualSelection = function(visual_selection, initialPrefills)
local prefills = vim.deepcopy(initialPrefills)

local selection_lines = utils.getVisualSelectionLines()
prefills.search = vim.fn.join(selection_lines, '\n')
prefills.search = vim.fn.join(visual_selection, '\n')
local flags = prefills.flags or ''
if not flags:find('%-%-fixed%-strings') then
flags = (#flags > 0 and flags .. ' ' or flags) .. '--fixed-strings'
end
if #selection_lines > 1 and not flags:find('%-%-multiline') then
if #visual_selection > 1 and not flags:find('%-%-multiline') then
flags = (#flags > 0 and flags .. ' ' or flags) .. '--multiline'
end
prefills.flags = flags
Expand Down
13 changes: 12 additions & 1 deletion lua/grug-far/utils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -268,13 +268,24 @@ function M.ensureBufTopEmptyLines(buf, count)
end
end

--- leave visual mode if in visual mode
---@return boolean if left visual mode
function M.leaveVisualMode()
local isVisualMode = vim.fn.mode():lower():find('v') ~= nil
if isVisualMode then
-- needed to make visual selection work
vim.fn.feedkeys(':', 'nx')
mehalter marked this conversation as resolved.
Show resolved Hide resolved
end
return isVisualMode
end

--- get text lines in visual selection
---@return string[]
function M.getVisualSelectionLines()
local start_row, start_col = unpack(vim.api.nvim_buf_get_mark(0, '<'))
local end_row, end_col = unpack(vim.api.nvim_buf_get_mark(0, '>'))
local lines = vim.fn.getline(start_row, end_row) --[[ @as string[] ]]
if #lines > 0 and start_col and end_col then
if #lines > 0 and start_col and end_col and end_col < string.len(lines[#lines]) then
if start_row == end_row then
lines[1] = lines[1]:sub(start_col + 1, end_col + 1)
else
Expand Down
Loading