diff --git a/README.md b/README.md index 4d4f7d2..33be1a1 100644 --- a/README.md +++ b/README.md @@ -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, }) ``` @@ -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 | Jump to the given 's last position | ## FAQs @@ -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_` sign group and set your icon. +Each `` 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 = '󰊄' }) +``` + diff --git a/lua/smoothcursor/callbacks/default.lua b/lua/smoothcursor/callbacks/default.lua index 2c80507..bb2c38b 100644 --- a/lua/smoothcursor/callbacks/default.lua +++ b/lua/smoothcursor/callbacks/default.lua @@ -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['.'] @@ -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() @@ -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 diff --git a/lua/smoothcursor/callbacks/init.lua b/lua/smoothcursor/callbacks/init.lua index f1aca21..c8959d7 100644 --- a/lua/smoothcursor/callbacks/init.lua +++ b/lua/smoothcursor/callbacks/init.lua @@ -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) @@ -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', diff --git a/lua/smoothcursor/config.lua b/lua/smoothcursor/config.lua index 40fecc1..f9453dd 100644 --- a/lua/smoothcursor/config.lua +++ b/lua/smoothcursor/config.lua @@ -60,5 +60,6 @@ return { disable_float_win = false, disabled_filetypes = nil, enabled_filetypes = nil, + show_last_positions = 'leave', }, } diff --git a/lua/smoothcursor/init.lua b/lua/smoothcursor/init.lua index a4b8039..59cdf43 100644 --- a/lua/smoothcursor/init.lua +++ b/lua/smoothcursor/init.lua @@ -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, ) diff --git a/lua/smoothcursor/last_positions.lua b/lua/smoothcursor/last_positions.lua new file mode 100644 index 0000000..b01960d --- /dev/null +++ b/lua/smoothcursor/last_positions.lua @@ -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, +} diff --git a/lua/smoothcursor/utils.lua b/lua/smoothcursor/utils.lua index 19eec56..1da0700 100644 --- a/lua/smoothcursor/utils.lua +++ b/lua/smoothcursor/utils.lua @@ -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 @@ -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() diff --git a/plugin/smoothcursor.lua b/plugin/smoothcursor.lua index 835aba1..50d344c 100644 --- a/plugin/smoothcursor.lua +++ b/plugin/smoothcursor.lua @@ -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, {}) @@ -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 ', +})