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

Add search repeat motions #108

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 8 additions & 1 deletion lib/command_state.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ local numberUtils = dofile(vimModeScriptPath .. "lib/utils/number_utils.lua")

local CommandState = {}

function CommandState:new()
function CommandState:new(lastInlineSearch)
local state = {
lastInlineSearch = lastInlineSearch,
charsEntered = "",
motion = nil,
motionTimes = nil,
Expand All @@ -18,6 +19,12 @@ function CommandState:new()
return state
end

function CommandState:saveLastInlineSearch(lastInlineSearch)
self.lastInlineSearch = lastInlineSearch

return self
end

function CommandState:getCharsEntered()
return self.charsEntered
end
Expand Down
4 changes: 4 additions & 0 deletions lib/modal.lua
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ local EntireLine = dofile(vimModeScriptPath .. "lib/motions/entire_line.lua")
local FirstLine = dofile(vimModeScriptPath .. "lib/motions/first_line.lua")
local FirstNonBlank = dofile(vimModeScriptPath .. "lib/motions/first_non_blank.lua")
local ForwardSearch = dofile(vimModeScriptPath .. "lib/motions/forward_search.lua")
local RepeatSearch = dofile(vimModeScriptPath .. "lib/motions/repeat_search.lua")
local RepeatSearchOpposite = dofile(vimModeScriptPath .. "lib/motions/repeat_search_opposite.lua")
local InWord = dofile(vimModeScriptPath .. "lib/motions/in_word.lua")
local LastLine = dofile(vimModeScriptPath .. "lib/motions/last_line.lua")
local LineBeginning = dofile(vimModeScriptPath .. "lib/motions/line_beginning.lua")
Expand Down Expand Up @@ -182,6 +184,8 @@ local function createVimModal(vim)
:bindWithRepeat({}, 'e', motion(EndOfWord))
:bind({}, 'f', motionNeedingChar(ForwardSearch))
:bind({'shift'}, 'f', motionNeedingChar(BackwardSearch))
:bind({}, ';', motion(RepeatSearch))
:bind({}, ',', motion(RepeatSearchOpposite))
:bindWithRepeat({}, 'h', motion(Left))
:bindWithRepeat({}, 'j', motion(Down))
:bindWithRepeat({}, 'k', motion(Up))
Expand Down
11 changes: 9 additions & 2 deletions lib/motions/backward_search.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ local stringUtils = dofile(vimModeScriptPath .. "lib/utils/string_utils.lua")

local BackwardSearch = Motion:new{ name = 'backward_search' }

function BackwardSearch:getRange(buffer)
function BackwardSearch:getRange(buffer, opts)
local finish = buffer:getCaretPosition()
local stringFinish = finish + 1
local stringFinish = finish + (opts and opts.startOffset or 0) + 1
local searchChar = self:getExtraChar()

local prevOccurringIndex = stringUtils.findPrevIndex(
Expand All @@ -14,6 +14,13 @@ function BackwardSearch:getRange(buffer)
stringFinish - 1 -- start from the prev char
)

if buffer.vim and opts and opts.explicitMotion then
buffer.vim.commandState:saveLastInlineSearch({
search = opts and opts.isReversed and "f" or "F",
char = searchChar,
})
end

if not prevOccurringIndex then return nil end

return {
Expand Down
13 changes: 11 additions & 2 deletions lib/motions/forward_search.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ local stringUtils = dofile(vimModeScriptPath .. "lib/utils/string_utils.lua")

local ForwardSearch = Motion:new{ name = 'forward_search' }

function ForwardSearch:getRange(buffer)
function ForwardSearch:getRange(buffer, opts)
local start = buffer:getCaretPosition()
local stringStart = start + 1
local stringStart = start + (opts and opts.startOffset or 0) + 1
local searchChar = self:getExtraChar()

local nextOccurringIndex = stringUtils.findNextIndex(
Expand All @@ -14,6 +14,15 @@ function ForwardSearch:getRange(buffer)
stringStart + 1 -- start from the next char
)

-- buffer.vim is nil for tests
-- TODO: move commandState mutation somewhere else
if buffer.vim and opts and opts.explicitMotion then
buffer.vim.commandState:saveLastInlineSearch({
search = opts and opts.isReversed and "F" or "f",
char = searchChar,
})
end

if not nextOccurringIndex then return nil end

return {
Expand Down
38 changes: 38 additions & 0 deletions lib/motions/repeat_search.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
local ForwardSearch = dofile(vimModeScriptPath .. "lib/motions/forward_search.lua")
local BackwardSearch = dofile(vimModeScriptPath .. "lib/motions/backward_search.lua")
local TillAfterSearch = dofile(vimModeScriptPath .. "lib/motions/till_after_search.lua")
local TillBeforeSearch = dofile(vimModeScriptPath .. "lib/motions/till_before_search.lua")

local Motion = dofile(vimModeScriptPath .. "lib/motion.lua")

local RepeatSearch = Motion:new{ name = 'left' }

local charToMotion = {
["f"] = ForwardSearch,
["F"] = BackwardSearch,
["t"] = TillBeforeSearch,
["T"] = TillAfterSearch,
}

function RepeatSearch:getRange(buffer)
local lastInlineSearch = buffer.vim.commandState.lastInlineSearch

if lastInlineSearch == nil then
return nil
end

local MatchedMotion = charToMotion[lastInlineSearch.search]

if MatchedMotion then
local motionWithExtraChar =
MatchedMotion:new():setExtraChar(lastInlineSearch.char)

return motionWithExtraChar:getRange(buffer, { isRepeated = true })
end
end

function RepeatSearch.getMovements()
return nil
end

return RepeatSearch
38 changes: 38 additions & 0 deletions lib/motions/repeat_search_opposite.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
local ForwardSearch = dofile(vimModeScriptPath .. "lib/motions/forward_search.lua")
local BackwardSearch = dofile(vimModeScriptPath .. "lib/motions/backward_search.lua")
local TillAfterSearch = dofile(vimModeScriptPath .. "lib/motions/till_after_search.lua")
local TillBeforeSearch = dofile(vimModeScriptPath .. "lib/motions/till_before_search.lua")

local Motion = dofile(vimModeScriptPath .. "lib/motion.lua")

local RepeatSearchOpposite = Motion:new{ name = 'left' }

local charToMotion = {
["f"] = BackwardSearch,
["F"] = ForwardSearch,
["t"] = TillAfterSearch,
["T"] = TillBeforeSearch,
}

function RepeatSearchOpposite:getRange(buffer)
local lastInlineSearch = buffer.vim.commandState.lastInlineSearch

if lastInlineSearch == nil then
return nil
end

local MatchedMotion = charToMotion[lastInlineSearch.search]

if MatchedMotion then
local motionWithExtraChar =
MatchedMotion:new():setExtraChar(lastInlineSearch.char)

return motionWithExtraChar:getRange(buffer, { isRepeated = true, isReversed = true })
end
end

function RepeatSearchOpposite.getMovements()
return nil
end

return RepeatSearchOpposite
13 changes: 10 additions & 3 deletions lib/motions/till_after_search.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,16 @@ local BackwardSearch = dofile(vimModeScriptPath .. "lib/motions/backward_search.

local TillAfterSearch = Motion:new{ name = 'till_after_search' }

function TillAfterSearch:getRange(buffer, ...)
local motion = BackwardSearch:new():setExtraChar(self:getExtraChar())
local range = motion:getRange(buffer, ...)
function TillAfterSearch:getRange(buffer, opts)
local searchChar = self:getExtraChar()
local motion = BackwardSearch:new():setExtraChar(searchChar)
local startOffset = opts and opts.isRepeated and -1 or 0
local range = motion:getRange(buffer, { startOffset = startOffset })

buffer.vim.commandState:saveLastInlineSearch({
search = opts and opts.isReversed and "t" or "T",
char = searchChar,
})

if not range then return nil end

Expand Down
11 changes: 9 additions & 2 deletions lib/motions/till_before_search.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,16 @@ local ForwardSearch = dofile(vimModeScriptPath .. "lib/motions/forward_search.lu

local TillBeforeSearch = Motion:new{ name = 'till_before_search' }

function TillBeforeSearch:getRange(buffer, ...)
function TillBeforeSearch:getRange(buffer, opts)
local searchChar = self:getExtraChar()
local motion = ForwardSearch:new():setExtraChar(self:getExtraChar())
local range = motion:getRange(buffer, ...)
local startOffset = opts and opts.isRepeated and 1 or 0
local range = motion:getRange(buffer, { startOffset = startOffset })

buffer.vim.commandState:saveLastInlineSearch({
search = opts and opts.isReversed and "T" or "t",
char = searchChar,
})

if not range then return nil end

Expand Down
3 changes: 2 additions & 1 deletion lib/motions/word.lua
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ local isPrintableChar = stringUtils.isPrintableChar
--

-- TODO handle more edge cases for :help word
function Word.getRange(_, buffer, operator)
function Word.getRange(_, buffer, opts)
local operator = opts and opts.operator or nil
local start = buffer:getCaretPosition()

local range = {
Expand Down
2 changes: 1 addition & 1 deletion lib/strategies/accessibility_strategy.lua
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ function AccessibilityStrategy:fire()
buffer:setCaretPosition(self.vim.visualCaretPosition)
end

local range = motion:getRange(buffer, operator)
local range = motion:getRange(buffer, { operator = operator, explicitMotion = true })

-- just cancel if the motion decides there isn't anything
-- to operate on (end of buffer, etc)
Expand Down
4 changes: 3 additions & 1 deletion lib/vim.lua
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,9 @@ function VimMode:setPendingInput(value)
end

function VimMode:resetCommandState()
self.commandState = CommandState:new()
local lastInlineSearch =
self.commandState and self.commandState.lastInlineSearch or nil
self.commandState = CommandState:new(lastInlineSearch)
end

function VimMode:enterOperator(operator)
Expand Down
79 changes: 79 additions & 0 deletions spec/motions/backward_search_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
local Buffer = require("lib/buffer")
local BackwardSearch = require("lib/motions/backward_search")

describe("BackwardSearch", function()
it("has a name", function()
assert.are.equals("backward_search", BackwardSearch:new().name)
end)

describe("#getRange", function()
local cases = {
{
name = "prev char from the end of the line",
char = "o",
str = "cat dog mouse cat dog mouse",
from = " x",
to = " x ",
},
{
name = "prev char from the middle of the line",
char = "o",
str = "cat dog mouse cat dog mouse",
from = " x ",
to = " x ",
},
{
name = "prev char if placed right before cursor",
char = "o",
str = "cat dog mouse cat dog mouse",
from = " x ",
to = " x ",
},
{
name = "skips char if placed on it",
char = "o",
str = "cat dog mouse cat dog mouse",
from = " x ",
to = " x ",
},
}

for _, case in ipairs(cases) do
it(case.name, function()
local buffer = Buffer:new()
buffer:setValue(case.str)

local indexFromRaw = case.from:find("x")
local indexFrom = indexFromRaw
buffer:setSelectionRange(0, indexFrom)

local backwardSearch = BackwardSearch:new()
backwardSearch:setExtraChar(case.char)

local indexToRaw = case.to:find("x")
local indexTo = indexToRaw - 1
assert.are.same(
{
start = indexTo,
finish = indexFrom,
mode = "exclusive",
direction = "characterwise"
},
backwardSearch:getRange(buffer)
)
end)
end

it("does nothing of no occurence found", function()
local buffer = Buffer:new()
buffer:setValue("just a word")

buffer:setSelectionRange(7, 0)

local backwardSearch = BackwardSearch:new()
backwardSearch:setExtraChar("d")

assert.is_nil(backwardSearch:getRange(buffer))
end)
end)
end)