require('vis') local m = vis.modes local mode_strings = { [m.NORMAL] = 'NORMAL', [m.OPERATOR_PENDING] = 'OPERATOR-PENDING', [m.VISUAL] = 'VISUAL', [m.VISUAL_LINE] = 'VISUAL-LINE', [m.INSERT] = 'INSERT', [m.REPLACE] = 'REPLACE', } local e = vis.events -- init e.subscribe(e.INIT, function() -- load theme require('themes/basic') -- mappings if os.getenv('WAYLAND_DISPLAY') then vis:map(m.NORMAL, 'y', '+') vis:map(m.VISUAL, 'y', '+') vis:map(m.NORMAL, 'y', '+') vis:map(m.VISUAL, 'd', '+') vis:map(m.VISUAL_LINE, 'd', '+') vis:map(m.VISUAL_LINE, 'd', '+') vis:map(m.NORMAL, 'p', '+') vis:map(m.VISUAL, 'p', '+') vis:map(m.VISUAL_LINE, 'p', '+') vis:map(m.NORMAL, 'P', '+') vis:map(m.VISUAL, 'P', '+') vis:map(m.VISUAL_LINE, 'P', '+') end vis:map(m.NORMAL, 'f', '') vis:map(m.OPERATOR_PENDING, 'f', '') vis:map(m.VISUAL, 'f', '') vis:map(m.NORMAL, 'F', '') vis:map(m.OPERATOR_PENDING, 'F', '') vis:map(m.VISUAL, 'F', '') vis:map(m.NORMAL, 't', '') vis:map(m.OPERATOR_PENDING, 't', '') vis:map(m.VISUAL, 't', '') vis:map(m.NORMAL, 'T', '') vis:map(m.OPERATOR_PENDING, 'T', '') vis:map(m.VISUAL, 'T', '') vis:map(m.NORMAL, '?', '') vis:map(m.NORMAL, '/', '') vis:map(m.NORMAL, 'zz', '') -- fixes a bug with `zz` vis:map(m.NORMAL, ' w', function() vis:command('w') end) vis:map(m.NORMAL, ' W', function() vis:command('w!') end) vis:map(m.NORMAL, ' q', function() vis:command('q') end) vis:map(m.NORMAL, ' Q', function() vis:command('q!') end) vis:map(m.NORMAL, ' e', function() local s, f = vis:pipe('find -name .git -prune -o -type f | vis-menu -l 5 -p "Edit file"') if s ~= 0 or f == nil then return end cmd = ('e "%s"'):format(f:sub(1, -2)) vis:info(cmd) vis:command(cmd) end, 'Edit file') vis:map(m.NORMAL, ' o', function() local s, f = vis:pipe('find -name .git -prune -o -type f | vis-menu -l 5 -p "Open file"') if s ~= 0 or f == nil then return end cmd = ('o "%s"'):format(f:sub(1, -2)) vis:info(cmd) vis:command(cmd) end, 'Open file') vis:map(m.NORMAL, ' cd', function() local s, f = vis:pipe('find -name .git -prune -o -type d | vis-menu -l 5 -p "Change directory"') if s ~= 0 or f == nil then return end cmd = ('cd "%s"'):format(f:sub(1, -2)) vis:info(cmd) vis:command(cmd) end, 'Change directory') -- editor options vis.options.autoindent = true end) -- window open e.subscribe(e.WIN_OPEN, function(win) win.options.colorcolumn = 81 win.options.numbers = true if win.syntax == 'markdown' then win.options.breakat = ' ,]_' win.options.expandtab = true win.options.tabwidth = 2 win.options.wrapcolumn = 80 end if win.syntax == 'css' then win.options.expandtab = true win.options.tabwidth = 2 end end) -- statusline e.subscribe(e.WIN_STATUS, function(win) local left_parts = {} local right_parts = {} local file = win.file local selection = win.selection -- file info table.insert(left_parts, (file.name or '[No Name]')..(file.modified and '[+]' or '')) -- selection table.insert(right_parts, selection.number..'/'..#win.selections) if vis.win == win then -- mode table.insert(left_parts, 1, mode_strings[vis.mode]) -- syntax table.insert(left_parts, win.syntax) -- input info table.insert(left_parts, '<' ..(vis.count or '') ..(vis.input_queue or '') ..(vis.recording and '@' or '') ..'>') -- character under cursor table.insert(right_parts, '<' ..(file:content(selection and selection.pos or 0, 1):byte() or 'nil') ..'>') -- line and column count table.insert(right_parts, #file.lines..'/'..selection.line) table.insert(right_parts, selection.col) end -- fillchars local left = table.concat(left_parts, ' ') local right = table.concat(right_parts, ' ') win:status(table.concat({ left, ('^'):rep(win.width - #left - #right - 2)}, ' '), right) end) -- set title local modified = false local function set_title(name) os.execute(('printf "\\e];%s%s\\e"'):format(name, modified and '[+]' or '')) end if os.getenv('TERM') ~= 'linux' then e.subscribe(e.WIN_OPEN, function(win) set_title(win.file.name or '[No Name]') end) e.subscribe(e.FILE_SAVE_POST, function(file) modified = false set_title(file.name) end) e.subscribe(e.WIN_STATUS, function(win) if not modified and win.file.modified then modified = true set_title(win.file.name or '[No Name]') end end) end -- cursor position local cursors_path = os.getenv('XDG_CACHE_HOME')..'/vis-cursors' local cursors = {} local files = {} local function get_cursors() local f = io.open(cursors_path) if f == nil then return end files = {} for l in f:lines() do local path, pos = l:match('(.+):(%d+)') cursors[path] = pos table.insert(files, path) end f:close() end e.subscribe(e.INIT, get_cursors) e.subscribe(e.WIN_OPEN, function(win) if win.file == nil or win.file.path == nil then return end if win.file.path:match('%.git/COMMIT_EDITMSG$') then return end if win.file.path:match('^/tmp/') then return end local pos = cursors[win.file.path] if pos == nil then cursors[win.file.path] = win.selection.pos return end win.selection.pos = tonumber(pos) vis:feedkeys('zz') end) e.subscribe(e.WIN_CLOSE, function(win) if win.file == nil or win.file.path == nil then return end get_cursors() for i, path in ipairs(files) do if path == win.file.path then table.remove(files, i) end end if win.selection.pos == 0 then return end table.insert(files, 1, win.file.path) cursors[win.file.path] = win.selection.pos end) e.subscribe(e.QUIT, function() local f = io.open(cursors_path, 'w+') if f == nil then return end local buf = {} for i, path in ipairs(files) do table.insert(buf, ('%s:%d'):format(path, cursors[path])) if i > 100 then break end -- remember only 100 end local out = table.concat(buf, '\n') f:write(out) f:close() end)