Skip to content

Commit

Permalink
Add remember last cursor position indicators & jump functionality (#52)
Browse files Browse the repository at this point in the history
* feat: Add last cursor position

* fix

* feat: Add last cursor position config option

* feat: Add command to jump to last position

* fix: Update last position on mode change

* feat: Add support for enter and leave for last position

* feat: Export get_last_positions for other plugins

* feat: Add support for multiple buffers

* refactor: Outsource last_positions into its own module

* refactor: Improve formatting

* refactor: Improve formatting

* docs: Add last position info to README.md

* fix: Get correct buffer

* chore: Apply stylua
  • Loading branch information
Myzel394 authored Jan 23, 2024
1 parent a755cfb commit f483b19
Show file tree
Hide file tree
Showing 8 changed files with 181 additions and 3 deletions.
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,11 @@ require('smoothcursor').setup({
disable_float_win = false, -- Disable in floating windows
enabled_filetypes = nil, -- Enable only for specific file types, e.g., { "lua", "vim" }
disabled_filetypes = nil, -- Disable for these file types, ignored if enabled_filetypes is set. e.g., { "TelescopePrompt", "NvimTree" }
-- Show the position of the latest input mode positions.
-- A value of "enter" means the position will be updated when entering the mode.
-- A value of "leave" means the position will be updated when leaving the mode.
-- `nil` = disabled
show_last_positions = nil,
})
```

Expand All @@ -119,6 +124,7 @@ https://github.com/gen740/SmoothCursor.nvim/assets/54583542/ba3f90b1-e9ff-4729-b
| :SmoothCursorFancyOn | Turn on fancy mode |
| :SmoothCursorFancyOff | Turn off fancy mode |
| :SmoothCursorDeleteSigns | Delete all signs if exist |
| :SmoothCursorJump <mode> | Jump to the given <mode>'s last position |

## FAQs

Expand Down Expand Up @@ -155,3 +161,19 @@ autocmd({ 'ModeChanged' }, {

https://user-images.githubusercontent.com/54583542/220056425-a7698013-7173-4247-9d40-d468b24df47a.mov


### How do I add icons (signs) for mode's last positions?

You can decide which modes should be added.
Define the `smoothcursor_<mode>` sign group and set your icon.
Each `<mode>` is a lowercase letter, e.g., `n` for normal mode, `i` for insert mode, etc.

**example**
```lua
vim.fn.sign_define('smoothcursor_v', { text = '' })
vim.fn.sign_define('smoothcursor_V', { text = '' })
vim.fn.sign_define('smoothcursor_i', { text = '' })
vim.fn.sign_define('smoothcursor_�', { text = '' })
vim.fn.sign_define('smoothcursor_R', { text = '󰊄' })
```

16 changes: 15 additions & 1 deletion lua/smoothcursor/callbacks/default.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ local callback = require('smoothcursor.callbacks')
local buffer = callback.buffer

local config = require('smoothcursor.config')
local last_positions = require('smoothcursor.last_positions')
local debug_callback = require('smoothcursor.debug').debug_callback

-- Default corsor callback. buffer["prev"] is always integer
-- Default cursor callback. buffer["prev"] is always integer
local function sc_default()
if not callback.is_enabled() then
return
end

buffer['.'] = vim.fn.line('.')
if buffer['prev'] == nil then
buffer['prev'] = buffer['.']
Expand All @@ -17,6 +19,7 @@ local function sc_default()
buffer['diff'] = math.min(buffer['diff'], vim.fn.winheight(0) * 2)
buffer['w0'] = vim.fn.line('w0')
buffer['w$'] = vim.fn.line('w$')

if math.abs(buffer['diff']) > config.value.threshold then
local counter = 1
callback.sc_timer:post(function()
Expand Down Expand Up @@ -54,10 +57,21 @@ local function sc_default()
else
buffer['prev'] = buffer['.']
buffer:all(buffer['.'])

callback.unplace_signs()

if callback.fancy_head_exists() then
callback.place_sign(buffer['prev'], 'smoothcursor')

local current_buf = vim.api.nvim_get_current_buf()

for name, pos in pairs(last_positions.get_positions(current_buf)) do
local line = pos[1]

callback.place_sign(line, 'smoothcursor_' .. name)
end
end

debug_callback(buffer, { 'Jump: False' })
end
end
Expand Down
7 changes: 5 additions & 2 deletions lua/smoothcursor/callbacks/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ local function buffer_set_all(value)
buffer['prev'] = value
buffer:all(value)

-- Debug
sc_debug.debug_callback(buffer, { 'Buffer Reset' }, function()
sc_debug.reset_counter = sc_debug.reset_counter + 1
end)
Expand All @@ -45,7 +44,11 @@ local function place_sign(position, name, priority)
if position < buffer['w0'] or position > buffer['w$'] then
return
end
if name ~= nil then

-- if len == 0, then sign is undefined
local is_sign_defined = #vim.fn.sign_getdefined(name) > 0

if is_sign_defined and name ~= nil then
vim.fn.sign_place(
0,
'SmoothCursor',
Expand Down
1 change: 1 addition & 0 deletions lua/smoothcursor/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -60,5 +60,6 @@ return {
disable_float_win = false,
disabled_filetypes = nil,
enabled_filetypes = nil,
show_last_positions = 'leave',
},
}
9 changes: 9 additions & 0 deletions lua/smoothcursor/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,15 @@ local config = require('smoothcursor.config')
---@field disable_float_win boolean
---@field disabled_filetypes string[]|nil
---@field enabled_filetypes string[]|nil
---@field show_last_positions string|nil -- "enter" | "leave"

-- Used for debugging, doesn't contain all the actual fields.
-- More meant to be used as an example.
---@class LastPositionsInfo
---@field i number[]
---@field n number[]
---@field V number[]
---@field v number[]

-- local function define_signs(name, cursor, texthl, )

Expand Down
48 changes: 48 additions & 0 deletions lua/smoothcursor/last_positions.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
local last_positions = {}

---Format a buffer for saving it
---@param buffer number
---@return string
local function format_buf(buffer)
return tostring(buffer)
end

---Set the last position of the cursor for a buffer
---@param buffer number
---@param mode string
---@param pos number[]
local function set_position(buffer, mode, pos)
if last_positions[format_buf(buffer)] == nil then
return
end

last_positions[format_buf(buffer)][mode] = pos
end

---Get the last positions for a buffer.
---Returns an empty table if the buffer is not found.
---@param buffer number
---@return LastPositionsInfo
local function get_positions(buffer)
return last_positions[format_buf(buffer)] or {}
end

---Register a buffer to be tracked
---We explicitly register buffers to avoid tracking buffers that shouldn't save
---last positions (such as floating windows for example).
---@param buffer any
local function register_buffer(buffer)
last_positions[format_buf(buffer)] = {}
end

local function unregister_buffer(buffer)
last_positions[format_buf(buffer)] = nil
end

return {
last_positions = last_positions,
set_position = set_position,
get_positions = get_positions,
register_buffer = register_buffer,
unregister_buffer = unregister_buffer,
}
59 changes: 59 additions & 0 deletions lua/smoothcursor/utils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ local smoothcursor_started = false
local callback = require('smoothcursor.callbacks')
local config = require('smoothcursor.config')
local init = require('smoothcursor.init')
local last_positions = require('smoothcursor.last_positions')
local buffer_leaved = false

--@param init_fire boolean
Expand Down Expand Up @@ -54,6 +55,64 @@ sc.smoothcursor_start = function(init_fire)
end,
})

local last_pos_config = config.value.show_last_positions

if last_pos_config ~= nil then
-- Please note that casting the buffer numbers to strings is necessary, otherwise
-- lua will interpret the buffer numbers as array indices
-- Add / Remove buffers to `last_positions` table
vim.api.nvim_create_autocmd({ 'BufAdd' }, {
group = 'SmoothCursor',
callback = function()
local buffer = vim.fn.bufnr('$')

last_positions.register_buffer(buffer)
end,
})
vim.api.nvim_create_autocmd({ 'BufDelete' }, {
group = 'SmoothCursor',
callback = function()
local buffer = vim.fn.bufnr('$')

last_positions.unregister_buffer(buffer)
end,
})
-- If starting into a buffer, the BufEnter event is not fired, but `VimEnter` is.
vim.api.nvim_create_autocmd({ 'VimEnter' }, {
group = 'SmoothCursor',
callback = function()
local buffer = vim.api.nvim_get_current_buf()

last_positions.register_buffer(buffer)
end,
})

-- Neovim always starts with normal mode, doesn't it?
local current_mode = 'n'

vim.api.nvim_create_autocmd({ 'ModeChanged' }, {
group = 'SmoothCursor',
callback = function()
local buffer = vim.api.nvim_get_current_buf()
local mode = vim.api.nvim_get_mode().mode

local last_pos = last_positions.get_positions(buffer)

-- if is nil, the current buffer is not a text file (could be floating window for example)
-- we don't want to set the last position in this case
if last_pos ~= nil then
if last_pos_config == 'enter' then
last_positions.set_position(buffer, mode, vim.api.nvim_win_get_cursor(0))
else
last_positions.set_position(buffer, current_mode, vim.api.nvim_win_get_cursor(0))

current_mode = vim.api.nvim_get_mode().mode
end
end
end,
})
end

smoothcursor_started = true
if init_fire then
callback.sc_callback()
Expand Down
22 changes: 22 additions & 0 deletions plugin/smoothcursor.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
-- Define Commands
local utils = require('smoothcursor.utils')
local last_positions = require('smoothcursor.last_positions')

vim.api.nvim_create_user_command('SmoothCursorStart', utils.smoothcursor_start, {})

Expand Down Expand Up @@ -37,3 +38,24 @@ end, {})
vim.api.nvim_create_user_command('SmoothCursorDeleteSigns', utils.smoothcursor_delete_signs, {})

vim.api.nvim_create_user_command('SmoothCursorDebug', require('smoothcursor.debug').debug, {})

-- Jump to last positions
-- TODO: Add autocomplete
vim.api.nvim_create_user_command('SmoothCursorJump', function(opt)
-- Get identifier from args
local identifier = opt.fargs[1]

local buffer = vim.api.nvim_get_current_buf()
local last_position = last_positions.get_positions(buffer)[identifier]

if last_position == nil then
vim.notify(string.format('No last position for identifier %s', identifier))
return
end

-- Jump to last position
vim.api.nvim_win_set_cursor(0, last_position)
end, {
nargs = 1,
desc = 'Jump to last position of identifier <identifier>',
})

0 comments on commit f483b19

Please sign in to comment.