├── .gitignore ├── .luarc.json ├── LICENSE ├── README.md └── init.lua /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode -------------------------------------------------------------------------------- /.luarc.json: -------------------------------------------------------------------------------- 1 | { 2 | "Lua": { 3 | "diagnostics": { 4 | "globals": ["vis"] 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2020 Erlend Lind Madsen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vis-cursors ✍️ 2 | 3 | A [vis](https://github.com/martanne/vis) [plugin](https://github.com/martanne/vis/wiki/Plugins) for saving cursor position per file. 4 | 5 | Default save path is `{XDG_CACHE_HOME|HOME/.cache}/vis-cursors.csv`. 6 | 7 | Set a custom path with `M.path`. 8 | 9 | Limit number of files/positions with `M.maxsize` (defaults to `1000`). 10 | 11 | Cursor positions per file are ordered by the last used at the top of `vis-cursors.csv`. 12 | -------------------------------------------------------------------------------- /init.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | local cursors = {} 3 | local files = {} 4 | 5 | -- default maxsize 6 | M.maxsize = 1000 7 | 8 | -- get the default system cache directory 9 | local get_default_cache_path = function() 10 | local HOME = os.getenv("HOME") 11 | local XDG_CACHE_HOME = os.getenv("XDG_CACHE_HOME") 12 | local cache_dir = XDG_CACHE_HOME or (HOME .. "/.cache") 13 | local cache_path = cache_dir .. "/" .. "vis-cursors.csv" 14 | return cache_path 15 | end 16 | 17 | -- default save path 18 | M.path = get_default_cache_path() 19 | 20 | local function read_files() 21 | -- read file 22 | local file = io.open(M.path) 23 | if file == nil then 24 | return 25 | end 26 | 27 | files = {} 28 | 29 | -- read positions per file path 30 | for line in file:lines() do 31 | local path, pos = string.match(line, '^(.+)[,%s](%d+)$') 32 | cursors[path] = pos 33 | table.insert(files, path) 34 | end 35 | 36 | file:close() 37 | end 38 | 39 | -- read cursors from file on init 40 | local on_init = function() 41 | read_files() 42 | end 43 | 44 | -- apply cursor pos on win open 45 | local on_win_open = function(win) 46 | if win.file == nil or win.file.path == nil then 47 | return 48 | end 49 | 50 | -- init cursor path if nil 51 | local pos = cursors[win.file.path] 52 | if pos == nil then 53 | cursors[win.file.path] = win.selection.pos 54 | return 55 | end 56 | 57 | -- set current cursor 58 | win.selection.pos = tonumber(pos) 59 | 60 | -- center view around cursor 61 | vis:feedkeys("zz") 62 | end 63 | 64 | -- set cursor pos on close 65 | local on_win_close = function(win) 66 | if win.file == nil or win.file.path == nil then 67 | return 68 | end 69 | 70 | -- re-read files in case they've changed 71 | read_files() 72 | 73 | -- remove old occurences of current path 74 | for i, path in ipairs(files) do 75 | if path == win.file.path then 76 | table.remove(files, i) 77 | end 78 | end 79 | 80 | -- ignore files with cursor at the beginning 81 | if win.selection.pos == 0 then 82 | return 83 | end 84 | 85 | 86 | -- insert current path to top of files 87 | table.insert(files, 1, win.file.path) 88 | 89 | -- set cursor pos for current file path 90 | cursors[win.file.path] = win.selection.pos 91 | end 92 | 93 | -- write cursors to file on quit 94 | local on_quit = function() 95 | local file = io.open(M.path, 'w+') 96 | if file == nil then 97 | return 98 | end 99 | 100 | -- buffer cursors string 101 | local buffer = {} 102 | for i, path in ipairs(files) do 103 | table.insert(buffer, string.format('%s,%d', path, cursors[path])) 104 | if M.maxsize and #buffer >= M.maxsize then 105 | break 106 | end 107 | end 108 | local output = table.concat(buffer, '\n') 109 | file:write(output) 110 | file:close() 111 | end 112 | 113 | vis.events.subscribe(vis.events.INIT, on_init) 114 | vis.events.subscribe(vis.events.WIN_OPEN, on_win_open) 115 | vis.events.subscribe(vis.events.WIN_CLOSE, on_win_close) 116 | vis.events.subscribe(vis.events.QUIT, on_quit) 117 | 118 | return M 119 | --------------------------------------------------------------------------------