Skip to content

Commit

Permalink
refactor: move config & core logic into separate modules
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisgrieser committed Jun 3, 2024
1 parent 1876e83 commit 98f3ae1
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 104 deletions.
29 changes: 29 additions & 0 deletions lua/spider/config.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
local M = {}
--------------------------------------------------------------------------------

---@class customPatterns
---@field patterns string[]? string array of lua patterns to match against.
---@field overrideDefault boolean? set to false to extend the default patterns with customPatterns. Defaults to true.

---@class (exact) optsObj
---@field skipInsignificantPunctuation boolean?
---@field subwordMovement boolean? determines movement through camelCase and snake_case. Defaults to true.
---@field customPatterns customPatterns|string[]? user defined patterns to match for motion movement

---@type optsObj
local defaultOpts = {
skipInsignificantPunctuation = true,
consistentOperatorPending = false,
subwordMovement = true,
customPatterns = {
patterns = {},
overrideDefault = true,
},
}
M.globalOpts = defaultOpts

---@param userOpts? optsObj
function M.setup(userOpts) M.globalOpts = vim.tbl_deep_extend("force", defaultOpts, userOpts or {}) end

--------------------------------------------------------------------------------
return M
111 changes: 7 additions & 104 deletions lua/spider/init.lua
Original file line number Diff line number Diff line change
@@ -1,36 +1,8 @@
local M = {}
local strFuncs = require("spider.utf8-support").stringFuncs

-- PERF avoid importing submodules here, since it results in them all being loaded
-- on initialization instead of lazy-loading them when needed.

local strFuncs = require("utf8-support").stringFuncs
--------------------------------------------------------------------------------

---@class customPatterns
---@field patterns string[]? string array of lua patterns to match against.
---@field overrideDefault boolean? set to false to extend the default patterns with customPatterns. Defaults to true.

-- CONFIG
---@class (exact) optsObj
---@field skipInsignificantPunctuation boolean?
---@field subwordMovement boolean? determines movement through camelCase and snake_case. Defaults to true.
---@field customPatterns customPatterns|string[]? user defined patterns to match for motion movement

---@type optsObj
local defaultOpts = {
skipInsignificantPunctuation = true,
consistentOperatorPending = false,
subwordMovement = true,
customPatterns = {
patterns = {},
overrideDefault = true,
},
}
local globalOpts = defaultOpts

---@param userOpts? optsObj
function M.setup(userOpts) globalOpts = vim.tbl_deep_extend("force", defaultOpts, userOpts or {}) end

--------------------------------------------------------------------------------

---Equivalent to fn.getline(), but using more efficient nvim api.
Expand All @@ -42,88 +14,19 @@ local function getline(lnum)
return lineContent[1]
end

-- INFO This method is necessary as opposed to a simple `:find`, to correctly
-- determine a word the cursor is already standing on.
---@param line string
---@param pattern string
---@param endOfWord boolean look for the end of the pattern instead of the start
---@param offset number -- look for the first match after this number
---@nodiscard
---@return number|nil returns nil if none is found
local function firstMatchAfter(line, pattern, endOfWord, offset)
-- special case: pattern with ^/$, since there can only be one match
-- and since gmatch won't work with them
if pattern:find("^%^") or pattern:find("%$$") then
if pattern:find("%$$") and offset > strFuncs.len(line) then return nil end -- checking for high col count for virtualedit
if pattern:find("^%^") and offset ~= 0 then return nil end

local start, endPos = strFuncs.find(line, pattern)
if start == nil or endPos == nil then return nil end

local pos = endOfWord and endPos or start
if pos > offset then
return pos
else
return nil
end
end

if endOfWord then
pattern = pattern .. "()" -- INFO "()" makes gmatch return the position of that group
else
pattern = "()" .. pattern
end
-- `:gmatch` will return all locations in the string where the pattern is
-- found, the loop looks for the first one that is higher than the offset
-- to look from
for pos in strFuncs.gmatch(line, pattern) do
if type(pos) == "string" then return nil end

if endOfWord then pos = pos - 1 end
if pos > offset then return pos end
end
return nil
end

---@param line string input string where to find the pattern
---@param offset number position to start looking from
---@param key "w"|"e"|"b"|"ge" the motion to perform
---@param opts optsObj configuration table as in setup()
---@nodiscard
---@return number|nil pattern position, returns nil if no pattern was found
local function getNextPosition(line, offset, key, opts)
local endOfWord = (key == "ge") or (key == "e")
local backwards = (key == "b") or (key == "ge")
local patterns = require("spider.pattern-variants").get(opts, backwards)

if backwards then
line = strFuncs.reverse(line)
endOfWord = not endOfWord

local isSameLine = offset ~= 0
if isSameLine then offset = strFuncs.len(line) - offset + 1 end
end

-- search for patterns, get closest one
local matches = {}
for _, pattern in pairs(patterns) do
local match = firstMatchAfter(line, pattern, endOfWord, offset)
if match then table.insert(matches, match) end
end
if vim.tbl_isempty(matches) then return nil end -- none found in this line
local nextPos = math.min(unpack(matches))

if backwards then nextPos = strFuncs.len(line) - nextPos + 1 end
return nextPos
end

local function normal(keys) vim.cmd.normal { keys, bang = true } end

--------------------------------------------------------------------------------

---@param userOpts? optsObj
function M.setup(userOpts) require("spider.config").setup(userOpts) end

---@param key "w"|"e"|"b"|"ge" the motion to perform
---@param motionOpts? optsObj configuration table as in setup()
function M.motion(key, motionOpts)
local globalOpts = require("spider.config").globalOpts
local getNextPosition = require("spider.motion-logic").getNextPosition

local opts = motionOpts and vim.tbl_deep_extend("force", globalOpts, motionOpts) or globalOpts

-- GUARD validate motion parameter
Expand Down
81 changes: 81 additions & 0 deletions lua/spider/motion-logic.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
local M = {}
local strFuncs = require("spider.utf8-support").stringFuncs
--------------------------------------------------------------------------------

-- INFO This method is necessary as opposed to a simple `:find`, to correctly
-- determine a word the cursor is already standing on.
---@param line string
---@param pattern string
---@param endOfWord boolean look for the end of the pattern instead of the start
---@param offset number -- look for the first match after this number
---@nodiscard
---@return number|nil returns nil if none is found
local function firstMatchAfter(line, pattern, endOfWord, offset)
-- special case: pattern with ^/$, since there can only be one match
-- and since gmatch won't work with them
if pattern:find("^%^") or pattern:find("%$$") then
if pattern:find("%$$") and offset > strFuncs.len(line) then return nil end -- checking for high col count for virtualedit
if pattern:find("^%^") and offset ~= 0 then return nil end

local start, endPos = strFuncs.find(line, pattern)
if start == nil or endPos == nil then return nil end

local pos = endOfWord and endPos or start
if pos > offset then
return pos
else
return nil
end
end

if endOfWord then
pattern = pattern .. "()" -- INFO "()" makes gmatch return the position of that group
else
pattern = "()" .. pattern
end
-- `:gmatch` will return all locations in the string where the pattern is
-- found, the loop looks for the first one that is higher than the offset
-- to look from
for pos in strFuncs.gmatch(line, pattern) do
if type(pos) == "string" then return nil end

if endOfWord then pos = pos - 1 end
if pos > offset then return pos end
end
return nil
end

---@param line string input string where to find the pattern
---@param offset number position to start looking from
---@param key "w"|"e"|"b"|"ge" the motion to perform
---@param opts optsObj configuration table as in setup()
---@nodiscard
---@return number|nil pattern position, returns nil if no pattern was found
function M.getNextPosition(line, offset, key, opts)
local endOfWord = (key == "ge") or (key == "e")
local backwards = (key == "b") or (key == "ge")
local patterns = require("spider.pattern-variants").get(opts, backwards)

if backwards then
line = strFuncs.reverse(line)
endOfWord = not endOfWord

local isSameLine = offset ~= 0
if isSameLine then offset = strFuncs.len(line) - offset + 1 end
end

-- search for patterns, get closest one
local matches = {}
for _, pattern in pairs(patterns) do
local match = firstMatchAfter(line, pattern, endOfWord, offset)
if match then table.insert(matches, match) end
end
if vim.tbl_isempty(matches) then return nil end -- none found in this line
local nextPos = math.min(unpack(matches))

if backwards then nextPos = strFuncs.len(line) - nextPos + 1 end
return nextPos
end

--------------------------------------------------------------------------------
return M

0 comments on commit 98f3ae1

Please sign in to comment.