From f3ca3f8988c2af29c8d13b5336d1bfefb3e36c81 Mon Sep 17 00:00:00 2001 From: Raymaekers Luca Date: Thu, 20 Jun 2024 12:46:19 +0200 Subject: fix error on multiple branches --- bin/common/gt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bin/common/gt b/bin/common/gt index 53d2cab..c679b23 100755 --- a/bin/common/gt +++ b/bin/common/gt @@ -5,7 +5,6 @@ # dependencies: # - git # - $EDITOR: -e -# - herbe (optional): -s repos=$HOME/sync/share/git-track.txt # prevent file not found errors @@ -47,7 +46,8 @@ status() { >&2 printf '\r\033[0K' status="$(git status --porcelain 2> /dev/null | awk '{print $1}' | uniq | tr -d '\n')" - remote="$(git branch -v 2>/dev/null | sed 's/ahead/↑/;s/behind/↓/;s/[^↓↑]*//g')" + remote="$(git branch -v 2>/dev/null | + sed '/^*/!d;s/ahead/↑/;s/behind/↓/;s/[^↓↑]*//g')" printf '%s %s %s\n' "$repo_pretty" "$status" "$remote" done < "$repos" @@ -116,6 +116,6 @@ fi if [ "$f_status" ] then status - which herbe > /dev/null 2>&1 && - eval "herbe $(status | sed 's/"/\"/g;s/.*/"&"/' | tr '\n' ' ')" & fi + +# eval "herbe $(status | sed 's/"/\"/g;s/.*/"&"/' | tr '\n' ' ')" -- cgit v1.2.3 From 41d3ecdd5245c8c3f8adaa76bbdca24eacd9e007 Mon Sep 17 00:00:00 2001 From: Raymaekers Luca Date: Thu, 20 Jun 2024 21:18:38 +0200 Subject: checkpoint --- bin/extra/clock | 4 +++- config/essentials/vis/plugins/init.lua | 2 ++ config/essentials/vis/visrc.lua | 4 +--- config/extra/mutt/.gitignore | 3 ++- 4 files changed, 8 insertions(+), 5 deletions(-) create mode 100644 config/essentials/vis/plugins/init.lua diff --git a/bin/extra/clock b/bin/extra/clock index fdffae0..bd6efd2 100755 --- a/bin/extra/clock +++ b/bin/extra/clock @@ -7,7 +7,8 @@ then printf 'start,end,message\n' > "$clocks" fi -if [ "$1" = "-c" ] +# print clocks file prettily +if [ "$1" = "-p" ] then # empty [ "$(wc -l < "$clocks")" -eq 1 ] && exit @@ -23,6 +24,7 @@ then exit fi +# edit clocks file in $EDITOR if [ "$1" = "-e" ] then $EDITOR "$clocks" diff --git a/config/essentials/vis/plugins/init.lua b/config/essentials/vis/plugins/init.lua new file mode 100644 index 0000000..80c9280 --- /dev/null +++ b/config/essentials/vis/plugins/init.lua @@ -0,0 +1,2 @@ +require("plugins.vis-cursors") +require("plugins.vis-title") diff --git a/config/essentials/vis/visrc.lua b/config/essentials/vis/visrc.lua index 1813888..81e09eb 100644 --- a/config/essentials/vis/visrc.lua +++ b/config/essentials/vis/visrc.lua @@ -3,9 +3,7 @@ ------------------------------------ require('vis') -require('plugins/vis-cursors') -require('plugins/vis-title') -require('plugins/vis-snippets') +require('plugins') ------------------------------------ --- EVENTS diff --git a/config/extra/mutt/.gitignore b/config/extra/mutt/.gitignore index 5e46596..ad7bbfd 100644 --- a/config/extra/mutt/.gitignore +++ b/config/extra/mutt/.gitignore @@ -1 +1,2 @@ -cache \ No newline at end of file +cache +mailboxes -- cgit v1.2.3 From 3d099d5157ce479b6a15e30ff2efbe1ee0d377c3 Mon Sep 17 00:00:00 2001 From: Raymaekers Luca Date: Thu, 20 Jun 2024 22:30:24 +0200 Subject: checkpoint --- config/essentials/vis/Makefile | 16 +++ config/essentials/vis/cursors.lua | 122 +++++++++++++++++++++ config/essentials/vis/plugins/init.lua | 2 - config/essentials/vis/plugins/vis-cursors | 1 - .../essentials/vis/plugins/vis-snippets/init.lua | 0 config/essentials/vis/plugins/vis-title | 1 - config/essentials/vis/title.lua | 22 ++++ config/essentials/vis/visrc.lua | 69 +++++++----- 8 files changed, 202 insertions(+), 31 deletions(-) create mode 100644 config/essentials/vis/Makefile create mode 100644 config/essentials/vis/cursors.lua delete mode 100644 config/essentials/vis/plugins/init.lua delete mode 160000 config/essentials/vis/plugins/vis-cursors delete mode 100644 config/essentials/vis/plugins/vis-snippets/init.lua delete mode 160000 config/essentials/vis/plugins/vis-title create mode 100644 config/essentials/vis/title.lua diff --git a/config/essentials/vis/Makefile b/config/essentials/vis/Makefile new file mode 100644 index 0000000..5f41043 --- /dev/null +++ b/config/essentials/vis/Makefile @@ -0,0 +1,16 @@ +.PHONY: check format check-luacheck check-format + +LUA_FILES := $(shell find -name "*.lua") + +check: check-luacheck + +all: check check-format + +check-luacheck: + luacheck --globals=vis -- $(LUA_FILES) + +check-format: + set -e; for lf in $(LUA_FILES); do tools/check-format "$${lf}"; done + +format: + lua-format -i $(LUA_FILES) diff --git a/config/essentials/vis/cursors.lua b/config/essentials/vis/cursors.lua new file mode 100644 index 0000000..a4e7fa3 --- /dev/null +++ b/config/essentials/vis/cursors.lua @@ -0,0 +1,122 @@ +local M = {} +local cursors = {} +local files = {} + +-- default maxsize +M.maxsize = 1000 + +-- get the default system cache directory +local get_default_cache_path = function() + local HOME = os.getenv('HOME') + local XDG_CACHE_HOME = os.getenv('XDG_CACHE_HOME') + local BASE = XDG_CACHE_HOME or HOME + return BASE .. '/.vis-cursors' +end + +-- default save path +M.path = get_default_cache_path() + +local read_files = function() + + -- read file + local file = io.open(M.path) + if file == nil then + return + end + + files = {} + + -- read positions per file path + for line in file:lines() do + for path, pos in string.gmatch(line, '(.+)[,%s](%d+)') do + cursors[path] = pos + table.insert(files, path) + end + end + + file:close() +end + +-- read cursors from file on init +local on_init = function() + read_files() +end + +-- apply cursor pos on win open +local on_win_open = function(win) + + if win.file == nil or win.file.path == nil then + return + end + + -- init cursor path if nil + local pos = cursors[win.file.path] + if pos == nil then + cursors[win.file.path] = win.selection.pos + return + end + + -- set current cursor + win.selection.pos = tonumber(pos) + + -- center view around cursor + vis:feedkeys("zz") +end + +-- set cursor pos on close +local on_win_close = function(win) + + if win.file == nil or win.file.path == nil then + return + end + + -- re-read files in case they've changed + read_files() + + -- remove old occurences of current path + for i, path in ipairs(files) do + if path == win.file.path then + table.remove(files, i) + end + end + + -- ignore files with cursor at the beginning + if win.selection.pos == 0 then + return + end + + + -- insert current path to top of files + table.insert(files, 1, win.file.path) + + -- set cursor pos for current file path + cursors[win.file.path] = win.selection.pos +end + +-- write cursors to file on quit +local on_quit = function() + + local file = io.open(M.path, 'w+') + if file == nil then + return + end + + -- buffer cursors string + local buffer = {} + for _, path in ipairs(files) do + table.insert(buffer, string.format('%s,%d', path, cursors[path])) + if M.maxsize and #buffer >= M.maxsize then + break + end + end + local output = table.concat(buffer, '\n') + file:write(output) + file:close() +end + +vis.events.subscribe(vis.events.INIT, on_init) +vis.events.subscribe(vis.events.WIN_OPEN, on_win_open) +vis.events.subscribe(vis.events.WIN_CLOSE, on_win_close) +vis.events.subscribe(vis.events.QUIT, on_quit) + +return M diff --git a/config/essentials/vis/plugins/init.lua b/config/essentials/vis/plugins/init.lua deleted file mode 100644 index 80c9280..0000000 --- a/config/essentials/vis/plugins/init.lua +++ /dev/null @@ -1,2 +0,0 @@ -require("plugins.vis-cursors") -require("plugins.vis-title") diff --git a/config/essentials/vis/plugins/vis-cursors b/config/essentials/vis/plugins/vis-cursors deleted file mode 160000 index f86c584..0000000 --- a/config/essentials/vis/plugins/vis-cursors +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f86c584fc2d4a2bab47df0cd5d187dd81fb71856 diff --git a/config/essentials/vis/plugins/vis-snippets/init.lua b/config/essentials/vis/plugins/vis-snippets/init.lua deleted file mode 100644 index e69de29..0000000 diff --git a/config/essentials/vis/plugins/vis-title b/config/essentials/vis/plugins/vis-title deleted file mode 160000 index 9c808f7..0000000 --- a/config/essentials/vis/plugins/vis-title +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 9c808f7e71b43aca31dee8553dcfce2214d7fc40 diff --git a/config/essentials/vis/title.lua b/config/essentials/vis/title.lua new file mode 100644 index 0000000..c87088b --- /dev/null +++ b/config/essentials/vis/title.lua @@ -0,0 +1,22 @@ +require('vis') + +local function set_title(title) + -- print() cannot be used here as it will mess up vis + vis:command(string.format(":!printf '\\033]2;vis %s\\007'", title)) +end + +vis.events.subscribe(vis.events.INIT, function() + print('\27[22;2t') +end) + +vis.events.subscribe(vis.events.WIN_OPEN, function(win) + set_title(win.file.name or '[No Name]') +end) + +vis.events.subscribe(vis.events.FILE_SAVE_POST, function(file) + set_title(file.name) +end) + +vis.events.subscribe(vis.events.QUIT, function() + print('\27[23;2t') +end) diff --git a/config/essentials/vis/visrc.lua b/config/essentials/vis/visrc.lua index 81e09eb..05df7ee 100644 --- a/config/essentials/vis/visrc.lua +++ b/config/essentials/vis/visrc.lua @@ -3,50 +3,41 @@ ------------------------------------ require('vis') -require('plugins') ------------------------------------- ---- EVENTS ------------------------------------- +-- plugins +require("backup") +require("cursors") +require("title") -vis.events.subscribe(vis.events.INIT, function() - vis.options.ignorecase = true - vis.options.autoindent = true - vis.options.shell = "/bin/sh" - theme = "nord" - vis:command("set theme " .. theme) -end) - -vis.events.subscribe(vis.events.WIN_OPEN, function(win) -- luacheck: no unused args - win.options.relativenumbers = true -end) ------------------------------------ --- FUNCTIONS ------------------------------------ -function map_cmd(mode, map, command, help) - vis:map(mode, map, function() - vis:command(command) - end, help) +local map_cmd = function(mode, map, command, help) + vis:map(mode, map, function() vis:command(command) end, help) end -function map_cmd_restore(mode, map, command, help) +local map_cmd_restore = function(mode, map, command, help) vis:map(mode, map, function() if (mode == vis.modes.INSERT) then vis:feedkeys("") end - + vis:feedkeys("m") vis:command(command) vis:feedkeys("M") if (mode == vis.modes.INSERT) then vis:feedkeys("i") - end + end end, help) end +local map_keys = function(mode, map, keys, help) + vis:map(mode, map, function() vis:feedkeys(keys) end, help) +end + ------------------------------------ --- VARIABLES ------------------------------------ @@ -55,11 +46,9 @@ local m = vis.modes ------------------------------------ --- COMMANDS ------------------------------------- +----------------------------------- -vis:command_register("Q", function(argv, force, win, selection, range) - vis:command("qa!") -end, "Quit all") +vis:command_register("Q", function() vis:command("qa!") end, "Quit all") ------------------------------------- --- MAPPINGS @@ -77,7 +66,33 @@ vis:map(m.NORMAL, " eh", function() vis:command("!lowdown $vis_filepath > ${vis_filepath%.md}.html") vis:info("exported.") end, "Export markdown to html") -vis:map(m.NORMAL, " nl", function() vis:feedkeys(":", "Remove trailing whitespace") -- select markdown list element: ,x/^(\d+\.|[-*])\s+.+\n(^ .+\n)*/ + + + +------------------------------------ +--- EVENTS +------------------------------------ + +vis.events.subscribe(vis.events.INIT, function() + vis.options.ignorecase = true + vis.options.autoindent = true + vis.options.shell = "/bin/sh" + local theme = "nord" + vis:command("set theme " .. theme) +end) + +vis.events.subscribe(vis.events.WIN_OPEN, function(win) -- luacheck: no unused args + win.options.relativenumbers = true + + if win.syntax == "bash" then + map_keys(m.NORMAL, " v", + "V:x/^(\\s*)(.+)$/ c/\\1>\\&2 printf '\\2: %s\\\\n' \"$\\2\"/", "Print variable") + end + +end) + -- cgit v1.2.3 From 2f8816e47e0797083a667d5cf7e778940dd1f789 Mon Sep 17 00:00:00 2001 From: Raymaekers Luca Date: Thu, 20 Jun 2024 22:50:03 +0200 Subject: checkpoint --- config/essentials/vis/Makefile | 15 ++-- config/essentials/vis/backup.lua | 47 ++++++++++++ config/essentials/vis/build.lua | 66 +++++++++++++++++ config/essentials/vis/cursors.lua | 123 ++++++++++++++----------------- config/essentials/vis/themes/nord.lua | 132 +++++++++++++++++----------------- config/essentials/vis/title.lua | 22 +++--- config/essentials/vis/visrc.lua | 68 +++++++++--------- 7 files changed, 278 insertions(+), 195 deletions(-) create mode 100644 config/essentials/vis/backup.lua create mode 100644 config/essentials/vis/build.lua diff --git a/config/essentials/vis/Makefile b/config/essentials/vis/Makefile index 5f41043..be230e3 100644 --- a/config/essentials/vis/Makefile +++ b/config/essentials/vis/Makefile @@ -1,16 +1,11 @@ -.PHONY: check format check-luacheck check-format +.PHONY: check format all -LUA_FILES := $(shell find -name "*.lua") +LUA_FILES := $(shell find . -name "*.lua") -check: check-luacheck +all: check format -all: check check-format - -check-luacheck: - luacheck --globals=vis -- $(LUA_FILES) - -check-format: - set -e; for lf in $(LUA_FILES); do tools/check-format "$${lf}"; done +check: + luacheck --no-color --globals=vis -- $(LUA_FILES) format: lua-format -i $(LUA_FILES) diff --git a/config/essentials/vis/backup.lua b/config/essentials/vis/backup.lua new file mode 100644 index 0000000..f48895f --- /dev/null +++ b/config/essentials/vis/backup.lua @@ -0,0 +1,47 @@ +local backup = {} + +-- Return the backup path concatenated with the filename, replace / with % +backup.entire_path_with_double_percentage_signs = + function(backup_dir, path) + return backup_dir .. "/" .. string.gsub(path, "/", "%%") + end + +-- Return the backup path concatenated with the filename, replace / with % +-- and append the current time using time_format +backup.entire_path_with_double_percentage_signs_and_timestamp = function( + backup_dir, path) + return backup_dir .. "/" .. os.date(backup.time_format) .. + string.gsub(path, "/", "%%") +end + +-- Before saving the file, copy the current contents of the file to a backup file +vis.events.subscribe(vis.events.FILE_SAVE_PRE, function(file, path) + if file.size > backup.byte_limit then return end + + -- E.g. when editing stdin as an interactive filter + -- `vis -` + if path == nil then return end + + local backup_path = backup.get_fname(backup.directory, path) + + local backup_file = io.open(backup_path, "w") + local current_file = io.open(path) + if backup_file == nil or current_file == nil then return end + + for line in current_file:lines() do backup_file:write(line .. "\n") end + + backup_file:close() +end) + +-- Set defaults +backup.directory = os.getenv("XDG_DATA_HOME") .. "/Trash/vis-backups" + +backup.get_fname = backup.entire_path_with_double_percentage_signs + +backup.time_format = "%H-%M-" + +-- Do not make backups if the file is greater than this +-- 1MB by default +backup.byte_limit = 1000000 + +return backup diff --git a/config/essentials/vis/build.lua b/config/essentials/vis/build.lua new file mode 100644 index 0000000..8a062cd --- /dev/null +++ b/config/essentials/vis/build.lua @@ -0,0 +1,66 @@ +-- Copyright (c) 2024 Florian Fischer. All rights reserved. +-- +-- vis-build is free software: you can redistribute it and/or modify it under +-- the terms of the GNU General Public License as published by the Free Software +-- Foundation, either version 3 of the License, or (at your option) any later +-- version. +-- +-- vis-build is distributed in the hope that it will be useful, but WITHOUT +-- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +-- FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License along with +-- vis-build found in the LICENSE file. +-- If not, see . +local M = {} +M.get_default_build_cmd = function() return 'make' end + +local build_id = 0 +local builds = {} + +M.new_build = function(cmd) + build_id = build_id + 1 + local build_name = 'build' .. build_id + local build_fd = vis:communicate(build_name, cmd) + local build = {fd = build_fd, out = '', err = '', cmd = cmd} + builds[build_name] = build +end + +vis.events.subscribe(vis.events.PROCESS_RESPONSE, + function(name, event, code, msg) + local build = builds[name] + if not build then return end + + if event == 'EXIT' or event == 'SIGNAL' then + if code ~= 0 then + vis:message('build: ' .. name .. ' cmd: ' .. build.cmd) + if event == 'EXIT' then + vis:message('failed with: ' .. code) + else + vis:message('got signal: ' .. code) + end + vis:message('stdout:\n' .. build.out) + vis:message('stderr:\n' .. build.err) + else + vis:message(name .. ':\n' .. build.out) + end + builds[name] = nil + end + + if event == 'STDOUT' then + build.out = build.out .. msg + elseif event == 'STDERR' then + build.err = build.err .. msg + end +end) + +vis:command_register('build', function(argv) + M.new_build(argv[1] or M.get_default_build_cmd()) +end, 'Asynchronously build the current file or project') + +vis:map(vis.modes.NORMAL, '', function() + vis:command('build') + return 0 +end, 'Asynchronously build the current file or project') + +return M diff --git a/config/essentials/vis/cursors.lua b/config/essentials/vis/cursors.lua index a4e7fa3..37165b6 100644 --- a/config/essentials/vis/cursors.lua +++ b/config/essentials/vis/cursors.lua @@ -7,10 +7,10 @@ M.maxsize = 1000 -- get the default system cache directory local get_default_cache_path = function() - local HOME = os.getenv('HOME') - local XDG_CACHE_HOME = os.getenv('XDG_CACHE_HOME') - local BASE = XDG_CACHE_HOME or HOME - return BASE .. '/.vis-cursors' + local HOME = os.getenv('HOME') + local XDG_CACHE_HOME = os.getenv('XDG_CACHE_HOME') + local BASE = XDG_CACHE_HOME or HOME + return BASE .. '/.vis-cursors' end -- default save path @@ -18,100 +18,83 @@ M.path = get_default_cache_path() local read_files = function() - -- read file - local file = io.open(M.path) - if file == nil then - return - end + -- read file + local file = io.open(M.path) + if file == nil then return end - files = {} + files = {} - -- read positions per file path - for line in file:lines() do - for path, pos in string.gmatch(line, '(.+)[,%s](%d+)') do - cursors[path] = pos - table.insert(files, path) - end - end + -- read positions per file path + for line in file:lines() do + for path, pos in string.gmatch(line, '(.+)[,%s](%d+)') do + cursors[path] = pos + table.insert(files, path) + end + end - file:close() + file:close() end -- read cursors from file on init -local on_init = function() - read_files() -end +local on_init = function() read_files() end -- apply cursor pos on win open local on_win_open = function(win) - if win.file == nil or win.file.path == nil then - return - end + if win.file == nil or win.file.path == nil then return end - -- init cursor path if nil - local pos = cursors[win.file.path] - if pos == nil then - cursors[win.file.path] = win.selection.pos - return - end + -- init cursor path if nil + local pos = cursors[win.file.path] + if pos == nil then + cursors[win.file.path] = win.selection.pos + return + end - -- set current cursor - win.selection.pos = tonumber(pos) + -- set current cursor + win.selection.pos = tonumber(pos) - -- center view around cursor - vis:feedkeys("zz") + -- center view around cursor + vis:feedkeys("zz") end -- set cursor pos on close local on_win_close = function(win) - if win.file == nil or win.file.path == nil then - return - end - - -- re-read files in case they've changed - read_files() + if win.file == nil or win.file.path == nil then return end - -- remove old occurences of current path - for i, path in ipairs(files) do - if path == win.file.path then - table.remove(files, i) - end - end + -- re-read files in case they've changed + read_files() - -- ignore files with cursor at the beginning - if win.selection.pos == 0 then - return - end + -- remove old occurences of current path + for i, path in ipairs(files) do + if path == win.file.path then table.remove(files, i) end + end + -- ignore files with cursor at the beginning + if win.selection.pos == 0 then return end - -- insert current path to top of files - table.insert(files, 1, win.file.path) + -- insert current path to top of files + table.insert(files, 1, win.file.path) - -- set cursor pos for current file path - cursors[win.file.path] = win.selection.pos + -- set cursor pos for current file path + cursors[win.file.path] = win.selection.pos end -- write cursors to file on quit local on_quit = function() - local file = io.open(M.path, 'w+') - if file == nil then - return - end - - -- buffer cursors string - local buffer = {} - for _, path in ipairs(files) do - table.insert(buffer, string.format('%s,%d', path, cursors[path])) - if M.maxsize and #buffer >= M.maxsize then - break - end - end - local output = table.concat(buffer, '\n') - file:write(output) - file:close() + local file = io.open(M.path, 'w+') + if file == nil then return end + + -- buffer cursors string + local buffer = {} + for _, path in ipairs(files) do + table.insert(buffer, string.format('%s,%d', path, cursors[path])) + if M.maxsize and #buffer >= M.maxsize then break end + end + local output = table.concat(buffer, '\n') + file:write(output) + file:close() end vis.events.subscribe(vis.events.INIT, on_init) diff --git a/config/essentials/vis/themes/nord.lua b/config/essentials/vis/themes/nord.lua index a21f0fa..71635cf 100644 --- a/config/essentials/vis/themes/nord.lua +++ b/config/essentials/vis/themes/nord.lua @@ -1,65 +1,64 @@ -- base16-vis (https://github.com/pshevtsov/base16-vis) -- by Petr Shevtsov -- Nord scheme by arcticicestudio - local lexers = vis.lexers local colors = { - ['bg'] = '#2E3440', - ['black'] = '#3B4252', - ['light_black'] = '#434C5E', - ['dark_gray'] = '#4C566A', - ['gray'] = '#D8DEE9', - ['light_gray'] = '#616E88', - ['fg'] = '#E5E9F0', - ['white'] = '#ECEFF4', - ['turquoise'] = '#8FBCBB', - ['light_cyan'] = '#88C0D0', - ['cyan'] = '#81A1C1', - ['blue'] = '#5E81AC', - ['red'] = '#BF616A', - ['orange'] = '#D08770', - ['yellow'] = '#EBCB8B', - ['green'] = '#A3BE8C', - ['magenta'] = '#B48EAD', + ['bg'] = '#2E3440', + ['black'] = '#3B4252', + ['light_black'] = '#434C5E', + ['dark_gray'] = '#4C566A', + ['gray'] = '#D8DEE9', + ['light_gray'] = '#616E88', + ['fg'] = '#E5E9F0', + ['white'] = '#ECEFF4', + ['turquoise'] = '#8FBCBB', + ['light_cyan'] = '#88C0D0', + ['cyan'] = '#81A1C1', + ['blue'] = '#5E81AC', + ['red'] = '#BF616A', + ['orange'] = '#D08770', + ['yellow'] = '#EBCB8B', + ['green'] = '#A3BE8C', + ['magenta'] = '#B48EAD' } lexers.colors = colors -local fg = 'fore:'..colors.fg -local bg = 'back:'..colors.bg +local fg = 'fore:' .. colors.fg +local bg = 'back:' .. colors.bg -lexers.STYLE_DEFAULT = bg..','..fg +lexers.STYLE_DEFAULT = bg .. ',' .. fg lexers.STYLE_NOTHING = bg -lexers.STYLE_CLASS = 'fore:'..colors.blue -lexers.STYLE_COMMENT = 'fore:'..colors.light_gray..',italics' -lexers.STYLE_CONSTANT = 'fore:'..colors.cyan -lexers.STYLE_DEFINITION = 'fore:'..colors.green -lexers.STYLE_ERROR = 'fore:'..colors.light_cyan..',italics' -lexers.STYLE_FUNCTION = 'fore:'..colors.light_cyan..',bold' -lexers.STYLE_HEADING = 'fore:'..colors.bg..',back:'..colors.yellow -lexers.STYLE_KEYWORD = 'fore:'..colors.cyan..',bold' -lexers.STYLE_LABEL = 'fore:'..colors.blue -lexers.STYLE_NUMBER = 'fore:'..colors.magenta -lexers.STYLE_OPERATOR = 'fore:'..colors.light_cyan -lexers.STYLE_REGEX = 'fore:'..colors.orange -lexers.STYLE_STRING = 'fore:'..colors.green -lexers.STYLE_PREPROCESSOR = 'fore:'..colors.blue -lexers.STYLE_TAG = 'fore:'..colors.blue -lexers.STYLE_TYPE = 'fore:'..colors.cyan -lexers.STYLE_VARIABLE = 'fore:'..colors.cyan..',bold' -lexers.STYLE_WHITESPACE = 'fore:'..colors.light_black -lexers.STYLE_EMBEDDED = 'fore:'..colors.magenta -lexers.STYLE_IDENTIFIER = fg..',bold' - -lexers.STYLE_LINENUMBER = 'fore:'..colors.light_black..',back:'..colors.bg -lexers.STYLE_CURSOR = 'fore:'..colors.bg..',back:'..colors.fg -lexers.STYLE_CURSOR_PRIMARY = 'fore:'..colors.bg..',back:'..colors.fg -lexers.STYLE_CURSOR_LINE = 'back:'..colors.black -lexers.STYLE_COLOR_COLUMN = 'back:'..colors.black -lexers.STYLE_SELECTION = 'back:'..colors.light_black -lexers.STYLE_STATUS = 'fore:'..colors.gray..',back:'..colors.black -lexers.STYLE_STATUS_FOCUSED = 'fore:'..colors.cyan..',back:'..colors.black +lexers.STYLE_CLASS = 'fore:' .. colors.blue +lexers.STYLE_COMMENT = 'fore:' .. colors.light_gray .. ',italics' +lexers.STYLE_CONSTANT = 'fore:' .. colors.cyan +lexers.STYLE_DEFINITION = 'fore:' .. colors.green +lexers.STYLE_ERROR = 'fore:' .. colors.light_cyan .. ',italics' +lexers.STYLE_FUNCTION = 'fore:' .. colors.light_cyan .. ',bold' +lexers.STYLE_HEADING = 'fore:' .. colors.bg .. ',back:' .. colors.yellow +lexers.STYLE_KEYWORD = 'fore:' .. colors.cyan .. ',bold' +lexers.STYLE_LABEL = 'fore:' .. colors.blue +lexers.STYLE_NUMBER = 'fore:' .. colors.magenta +lexers.STYLE_OPERATOR = 'fore:' .. colors.light_cyan +lexers.STYLE_REGEX = 'fore:' .. colors.orange +lexers.STYLE_STRING = 'fore:' .. colors.green +lexers.STYLE_PREPROCESSOR = 'fore:' .. colors.blue +lexers.STYLE_TAG = 'fore:' .. colors.blue +lexers.STYLE_TYPE = 'fore:' .. colors.cyan +lexers.STYLE_VARIABLE = 'fore:' .. colors.cyan .. ',bold' +lexers.STYLE_WHITESPACE = 'fore:' .. colors.light_black +lexers.STYLE_EMBEDDED = 'fore:' .. colors.magenta +lexers.STYLE_IDENTIFIER = fg .. ',bold' + +lexers.STYLE_LINENUMBER = 'fore:' .. colors.light_black .. ',back:' .. colors.bg +lexers.STYLE_CURSOR = 'fore:' .. colors.bg .. ',back:' .. colors.fg +lexers.STYLE_CURSOR_PRIMARY = 'fore:' .. colors.bg .. ',back:' .. colors.fg +lexers.STYLE_CURSOR_LINE = 'back:' .. colors.black +lexers.STYLE_COLOR_COLUMN = 'back:' .. colors.black +lexers.STYLE_SELECTION = 'back:' .. colors.light_black +lexers.STYLE_STATUS = 'fore:' .. colors.gray .. ',back:' .. colors.black +lexers.STYLE_STATUS_FOCUSED = 'fore:' .. colors.cyan .. ',back:' .. colors.black lexers.STYLE_SEPARATOR = lexers.STYLE_DEFAULT lexers.STYLE_INFO = 'fore:default,back:default,bold' lexers.STYLE_EOF = '' @@ -67,9 +66,9 @@ lexers.STYLE_EOF = '' -- lexer specific styles -- Diff -lexers.STYLE_ADDITION = 'back:'..colors.green..',fore:'..colors.bg -lexers.STYLE_DELETION = 'back:'..colors.red..',fore:'..colors.bg -lexers.STYLE_CHANGE = 'back:'..colors.yellow..',fore:'..colors.bg +lexers.STYLE_ADDITION = 'back:' .. colors.green .. ',fore:' .. colors.bg +lexers.STYLE_DELETION = 'back:' .. colors.red .. ',fore:' .. colors.bg +lexers.STYLE_CHANGE = 'back:' .. colors.yellow .. ',fore:' .. colors.bg -- CSS lexers.STYLE_PROPERTY = lexers.STYLE_ATTRIBUTE @@ -91,19 +90,21 @@ lexers.STYLE_TARGET = '' -- Markdown lexers.STYLE_HR = '' -lexers.STYLE_HEADING_H1 = 'fore:'..colors.orange..',bold' -lexers.STYLE_HEADING_H2 = 'fore:'..colors.red..',bold' -for i = 3,6 do lexers['STYLE_HEADING_H'..i] = 'fore:'..colors.magenta..',bold' end +lexers.STYLE_HEADING_H1 = 'fore:' .. colors.orange .. ',bold' +lexers.STYLE_HEADING_H2 = 'fore:' .. colors.red .. ',bold' +for i = 3, 6 do + lexers['STYLE_HEADING_H' .. i] = 'fore:' .. colors.magenta .. ',bold' +end lexers.STYLE_BOLD = 'bold' lexers.STYLE_ITALIC = 'italics' lexers.STYLE_LIST = lexers.STYLE_KEYWORD -lexers.STYLE_LINK = 'fore:'..colors.yellow..',italics' -lexers.STYLE_REFERENCE = 'fore:'..colors.blue -lexers.STYLE_CODE = 'back:'..colors.black..',fore:'..colors.turquoise +lexers.STYLE_LINK = 'fore:' .. colors.yellow .. ',italics' +lexers.STYLE_REFERENCE = 'fore:' .. colors.blue +lexers.STYLE_CODE = 'back:' .. colors.black .. ',fore:' .. colors.turquoise -- Output lexers.STYE_FILENAME = 'bold' -lexers.STYLE_LINE = 'fore:'..colors.green +lexers.STYLE_LINE = 'fore:' .. colors.green lexers.STYLE_COLUMN = 'underline' lexers.STYLE_MESSAGE = '' @@ -111,13 +112,12 @@ lexers.STYLE_MESSAGE = '' lexers.STYLE_KEYWORD_SOFT = '' -- YAML -lexers.STYLE_ERROR_INDENT = 'back:'..colors.red - +lexers.STYLE_ERROR_INDENT = 'back:' .. colors.red -- GO -lexers.STYLE_CONSTANT_BUILTIN = 'fore:'..colors.yellow -lexers.STYLE_FUNCTION_METHOD = 'fore:'..colors.light_cyan -lexers.STYLE_FUNCTION_BUILTIN = 'fore:'..colors.light_cyan..',bold' +lexers.STYLE_CONSTANT_BUILTIN = 'fore:' .. colors.yellow +lexers.STYLE_FUNCTION_METHOD = 'fore:' .. colors.light_cyan +lexers.STYLE_FUNCTION_BUILTIN = 'fore:' .. colors.light_cyan .. ',bold' -- Lua -lexers.STYLE_ATTRIBUTE = 'fore:'..colors.yellow..',bold' +lexers.STYLE_ATTRIBUTE = 'fore:' .. colors.yellow .. ',bold' diff --git a/config/essentials/vis/title.lua b/config/essentials/vis/title.lua index c87088b..d743b63 100644 --- a/config/essentials/vis/title.lua +++ b/config/essentials/vis/title.lua @@ -1,22 +1,16 @@ require('vis') local function set_title(title) - -- print() cannot be used here as it will mess up vis - vis:command(string.format(":!printf '\\033]2;vis %s\\007'", title)) + -- print() cannot be used here as it will mess up vis + vis:command(string.format(":!printf '\\033]2;vis %s\\007'", title)) end -vis.events.subscribe(vis.events.INIT, function() - print('\27[22;2t') -end) +vis.events.subscribe(vis.events.INIT, function() print('\27[22;2t') end) -vis.events.subscribe(vis.events.WIN_OPEN, function(win) - set_title(win.file.name or '[No Name]') -end) +vis.events.subscribe(vis.events.WIN_OPEN, + function(win) set_title(win.file.name or '[No Name]') end) -vis.events.subscribe(vis.events.FILE_SAVE_POST, function(file) - set_title(file.name) -end) +vis.events.subscribe(vis.events.FILE_SAVE_POST, + function(file) set_title(file.name) end) -vis.events.subscribe(vis.events.QUIT, function() - print('\27[23;2t') -end) +vis.events.subscribe(vis.events.QUIT, function() print('\27[23;2t') end) diff --git a/config/essentials/vis/visrc.lua b/config/essentials/vis/visrc.lua index 05df7ee..3a9ff1d 100644 --- a/config/essentials/vis/visrc.lua +++ b/config/essentials/vis/visrc.lua @@ -1,41 +1,38 @@ ------------------------------------ --- LIBRARIES ------------------------------------ - require('vis') -- plugins +require("build") require("backup") require("cursors") require("title") +vis:command_register("make", function() vis:communicate() end, "make") ------------------------------------ --- FUNCTIONS ------------------------------------ local map_cmd = function(mode, map, command, help) - vis:map(mode, map, function() vis:command(command) end, help) + vis:map(mode, map, function() vis:command(command) end, help) end local map_cmd_restore = function(mode, map, command, help) - vis:map(mode, map, function() - if (mode == vis.modes.INSERT) then - vis:feedkeys("") - end - - vis:feedkeys("m") - vis:command(command) - vis:feedkeys("M") - - if (mode == vis.modes.INSERT) then - vis:feedkeys("i") - end - end, help) + vis:map(mode, map, function() + if (mode == vis.modes.INSERT) then vis:feedkeys("") end + + vis:feedkeys("m") + vis:command(command) + vis:feedkeys("M") + + if (mode == vis.modes.INSERT) then vis:feedkeys("i") end + end, help) end local map_keys = function(mode, map, keys, help) - vis:map(mode, map, function() vis:feedkeys(keys) end, help) + vis:map(mode, map, function() vis:feedkeys(keys) end, help) end ------------------------------------ @@ -49,6 +46,9 @@ local m = vis.modes ----------------------------------- vis:command_register("Q", function() vis:command("qa!") end, "Quit all") +vis:command_register("delws", + function() vis:command("x/[ \t]+$|^[ \t]+$/d") end, + "Remove trailing whitespace") ------------------------------------- --- MAPPINGS @@ -60,39 +60,37 @@ map_cmd(m.NORMAL, " c", "e ~/.config/vis/visrc.lua", "Edit config file") map_cmd(m.NORMAL, " q", "q!", "Quit (force)") map_cmd(m.NORMAL, " s", "!doas vis $vis_filepath", "Edit as superuser") map_cmd(m.NORMAL, " w", "w", "Write") -map_cmd(m.NORMAL, " x", "!chmod u+x $vis_filepath", "Make active file executable") +map_cmd(m.NORMAL, " x", "!chmod u+x $vis_filepath", + "Make active file executable") vis:map(m.NORMAL, " eh", function() - vis:command("!lowdown $vis_filepath > ${vis_filepath%.md}.html") - vis:info("exported.") + vis:command("!lowdown $vis_filepath > ${vis_filepath%.md}.html") + vis:info("exported.") end, "Export markdown to html") map_keys(m.NORMAL, " nl", ":", "Remove trailing whitespace") -- select markdown list element: ,x/^(\d+\.|[-*])\s+.+\n(^ .+\n)*/ - - ------------------------------------ --- EVENTS ------------------------------------ vis.events.subscribe(vis.events.INIT, function() - vis.options.ignorecase = true - vis.options.autoindent = true - vis.options.shell = "/bin/sh" - local theme = "nord" - vis:command("set theme " .. theme) + vis.options.ignorecase = true + vis.options.autoindent = true + vis.options.shell = "/bin/sh" + local theme = "nord" + vis:command("set theme " .. theme) end) -vis.events.subscribe(vis.events.WIN_OPEN, function(win) -- luacheck: no unused args - win.options.relativenumbers = true - - if win.syntax == "bash" then - map_keys(m.NORMAL, " v", - "V:x/^(\\s*)(.+)$/ c/\\1>\\&2 printf '\\2: %s\\\\n' \"$\\2\"/", "Print variable") - end +vis.events.subscribe(vis.events.WIN_OPEN, + function(win) -- luacheck: no unused args + win.options.relativenumbers = true + if win.syntax == "bash" then + map_keys(m.NORMAL, " v", + "V:x/^(\\s*)(.+)$/ c/\\1>\\&2 printf '\\2: %s\\\\n' \"$\\2\"/", + "Print variable") + end end) - -- cgit v1.2.3 From a0f45aece21a6280633023ec84f60eb0d5e0849a Mon Sep 17 00:00:00 2001 From: Raymaekers Luca Date: Thu, 20 Jun 2024 23:46:00 +0200 Subject: checkpoint --- config/essentials/vis/Makefile | 2 +- config/essentials/vis/commentary.lua | 251 ++++++++++++++++++++++++++++++++ config/essentials/vis/complete-line.lua | 141 ++++++++++++++++++ config/essentials/vis/cursors.lua | 12 +- config/essentials/vis/format.lua | 131 +++++++++++++++++ config/essentials/vis/visrc.lua | 108 ++++++++------ 6 files changed, 593 insertions(+), 52 deletions(-) create mode 100644 config/essentials/vis/commentary.lua create mode 100644 config/essentials/vis/complete-line.lua create mode 100644 config/essentials/vis/format.lua diff --git a/config/essentials/vis/Makefile b/config/essentials/vis/Makefile index be230e3..1599b58 100644 --- a/config/essentials/vis/Makefile +++ b/config/essentials/vis/Makefile @@ -2,7 +2,7 @@ LUA_FILES := $(shell find . -name "*.lua") -all: check format +all: format check check: luacheck --no-color --globals=vis -- $(LUA_FILES) diff --git a/config/essentials/vis/commentary.lua b/config/essentials/vis/commentary.lua new file mode 100644 index 0000000..26d06b5 --- /dev/null +++ b/config/essentials/vis/commentary.lua @@ -0,0 +1,251 @@ +-- +-- vis-commentary +-- +local comment_string = { + actionscript = '//', + ada = '--', + ansi_c = '/*|*/', + antlr = '//', + apdl = '!', + apl = '#', + applescript = '--', + asp = '\'', + autoit = ';', + awk = '#', + b_lang = '//', + bash = '#', + batch = ':', + bibtex = '%', + boo = '#', + chuck = '//', + cmake = '#', + coffeescript = '#', + context = '%', + cpp = '//', + crystal = '#', + csharp = '//', + css = '/*|*/', + cuda = '//', + dart = '//', + desktop = '#', + django = '{#|#}', + dmd = '//', + dockerfile = '#', + dot = '//', + eiffel = '--', + elixir = '#', + erlang = '%', + faust = '//', + fennel = ';;', + fish = '#', + forth = '|\\', + fortran = '!', + fsharp = '//', + gap = '#', + gettext = '#', + gherkin = '#', + glsl = '//', + gnuplot = '#', + go = '//', + groovy = '//', + gtkrc = '#', + haskell = '--', + html = '', + icon = '#', + idl = '//', + inform = '!', + ini = '#', + Io = '#', + java = '//', + javascript = '//', + json = '/*|*/', + jsp = '//', + latex = '%', + ledger = '#', + less = '//', + lilypond = '%', + lisp = ';', + logtalk = '%', + lua = '--', + makefile = '#', + markdown = '', + matlab = '#', + moonscript = '--', + myrddin = '//', + nemerle = '//', + nsis = '#', + objective_c = '//', + pascal = '//', + perl = '#', + php = '//', + pico8 = '//', + pike = '//', + pkgbuild = '#', + prolog = '%', + props = '#', + protobuf = '//', + ps = '%', + pure = '//', + python = '#', + rails = '#', + rc = '#', + rebol = ';', + rest = '.. ', + rexx = '--', + rhtml = '', + rstats = '#', + ruby = '#', + rust = '//', + sass = '//', + scala = '//', + scheme = ';', + smalltalk = '"|"', + sml = '(*)', + snobol4 = '#', + sql = '#', + tcl = '#', + tex = '%', + text = '', + toml = '#', + vala = '//', + vb = '\'', + vbscript = '\'', + verilog = '//', + vhdl = '--', + wsf = '', + xml = '', + yaml = '#', + zig = '//', + nim = '#', + julia = '#', + rpmspec = '#' +} + +-- escape all magic characters with a '%' +local function esc(str) + if not str then return "" end + return (str:gsub('%%', '%%%%'):gsub('^%^', '%%^'):gsub('%$$', '%%$'):gsub( + '%(', '%%('):gsub('%)', '%%)'):gsub('%.', '%%.') + :gsub('%[', '%%['):gsub('%]', '%%]'):gsub('%*', '%%*') + :gsub('%+', '%%+'):gsub('%-', '%%-'):gsub('%?', '%%?')) +end + +-- escape '%' +local function pesc(str) + if not str then return "" end + return str:gsub('%%', '%%%%') +end + +local function rtrim(s) + local n = #s + while n > 0 and s:find("^%s", n) do n = n - 1 end + return s:sub(1, n) +end + +local function comment_line(lines, lnum, prefix, suffix) + if suffix ~= "" then suffix = " " .. suffix end + lines[lnum] = string.gsub(lines[lnum], "(%s*)(.*)", + "%1" .. pesc(prefix) .. " %2" .. pesc(suffix)) +end + +local function uncomment_line(lines, lnum, prefix, suffix) + local match_str = "^(%s*)" .. esc(prefix) .. "%s?(.*)" .. esc(suffix) + local m = table.pack(lines[lnum]:match(match_str)) + lines[lnum] = m[1] .. rtrim(m[2]) +end + +local function is_comment(line, prefix) + return (line:match("^%s*(.+)"):sub(0, #prefix) == prefix) +end + +local function toggle_line_comment(lines, lnum, prefix, suffix) + if not lines or not lines[lnum] then return end + if not lines[lnum]:match("^%s*(.+)") then return end -- ignore empty lines + if is_comment(lines[lnum], prefix) then + uncomment_line(lines, lnum, prefix, suffix) + else + comment_line(lines, lnum, prefix, suffix) + end +end + +-- if one line inside the block is not a comment, comment the block. +-- only uncomment, if every single line is comment. +local function block_comment(lines, a, b, prefix, suffix) + local uncomment = true + for i = a, b do + if lines[i]:match("^%s*(.+)") and not is_comment(lines[i], prefix) then + uncomment = false + end + end + + if uncomment then + for i = a, b do + if lines[i]:match("^%s*(.+)") then + uncomment_line(lines, i, prefix, suffix) + end + end + else + for i = a, b do + if lines[i]:match("^%s*(.+)") then + comment_line(lines, i, prefix, suffix) + end + end + end +end + +vis:map(vis.modes.NORMAL, "gcc", function() + local win = vis.win + local lines = win.file.lines + local comment = comment_string[win.syntax] + if not comment then return end + local prefix, suffix = comment:match('^([^|]+)|?([^|]*)$') + if not prefix then return end + + for sel in win:selections_iterator() do + local lnum = sel.line + local col = sel.col + + toggle_line_comment(lines, lnum, prefix, suffix) + sel:to(lnum, col) -- restore cursor position + end + + win:draw() +end, "Toggle comment on a the current line") + +local function visual_f(i) + return function() + local win = vis.win + local lines = win.file.lines + + local comment = comment_string[win.syntax] + if not comment then return end + + local prefix, suffix = comment:match('^([^|]+)|?([^|]*)$') + if not prefix then return end + + for sel in win:selections_iterator() do + local r = sel.range + local lnum = sel.line -- line number of cursor + local col = sel.col -- column of cursor + + if sel.anchored and r then + sel.pos = r.start + local a = sel.line + sel.pos = r.finish + local b = sel.line - i + + block_comment(lines, a, b, prefix, suffix) + + sel:to(lnum, col) -- restore cursor position + end + end + + win:draw() + vis.mode = vis.modes.NORMAL -- go to normal mode + end +end + +vis:map(vis.modes.VISUAL_LINE, "gc", visual_f(1), + "Toggle comment on the selected lines") +vis:map(vis.modes.VISUAL, "gc", visual_f(0), + "Toggle comment on the selected lines") diff --git a/config/essentials/vis/complete-line.lua b/config/essentials/vis/complete-line.lua new file mode 100644 index 0000000..93728a3 --- /dev/null +++ b/config/essentials/vis/complete-line.lua @@ -0,0 +1,141 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later +-- © 2020 Georgi Kirilov +local progname = ... + +local lpeg = require("lpeg") +local R, C, Cmt = lpeg.R, lpeg.C, lpeg.Cmt + +-- copied from vis.h +local VIS_MOVE_NOP = 64 + +local cont = R("\128\191") + +local charpattern = R("\0\127") + R("\194\223") * cont + R("\224\239") * cont * + cont + R("\240\244") * cont * cont * cont + +local function concat_keys(tbl) + local keys = {} + for k in pairs(tbl) do table.insert(keys, k) end + return table.concat(keys, "\n"), #keys +end + +local function line_complete() + local file = vis.win.file + local sel = vis.win.selection + local cur_line = file.lines[sel.line] + local indent_patt = "^[ \t\v\f]+" + local prefix = cur_line:sub(1, sel.col - 1):gsub(indent_patt, "") + local candidates = {} + for l in file:lines_iterator() do + local unindented = l:gsub(indent_patt, "") + local start, finish = unindented:find(prefix, 1, true) + if start == 1 and finish < #unindented then + candidates[unindented] = true + end + end + local candidates_str, n = concat_keys(candidates) + if n < 2 then + if n == 1 then vis:insert(candidates_str:sub(#prefix + 1)) end + return + end + -- XXX: with too many candidates this command will become longer that the shell can handle: + local command = string.format("vis-menu -l %d <<'EOF'\n%s\nEOF\n", + math.min(n, math.ceil(vis.win.height / 2)), + candidates_str) + local status, output = vis:pipe(nil, nil, command) + if n > 0 and status == 0 then + vis:insert(output:sub(#prefix + 1):gsub("\n$", "")) + end +end + +local function selection_by_pos(pos) + for s in vis.win:selections_iterator() do + if s.pos == pos then return s end + end +end + +local function charwidth(cells_so_far, char) + if char == "\t" then + local tw = vis.tabwidth or 8 + local trail = cells_so_far % tw + return tw - trail + else + return 1 + end +end + +local function virtcol(line, col) + local ncells = 0 + local nchars = 0 + local function upto(_, _, char) + if nchars < col - 1 then + ncells = ncells + charwidth(ncells, char) + nchars = nchars + 1 + return true + end + end + (Cmt(C(charpattern), upto) ^ 0):match(line) + return ncells + 1 +end + +local function neighbor(lines, ln, col, direction) + local line = ln + direction > 0 and lines[ln + direction] + if not line then return end + local column = virtcol(lines[ln], col) + local ncells = 0 + local function upto(_, _, char) + ncells = ncells + charwidth(ncells, char) + return ncells < column + end + return + (Cmt(C(charpattern), upto) ^ (-column + 1) / 0 * C(charpattern)):match( + line) +end + +local function dup_neighbor(direction) + return function(file, _, pos) + local sel = selection_by_pos(pos) + local char = neighbor(file.lines, sel.line, sel.col, direction) + if not char then return pos end + file:insert(pos, char) + return pos + #char + end +end + +local function dup_neighbor_feedkeys(direction) + local sel = vis.win.selection + local file = vis.win.file + local char = neighbor(file.lines, sel.line, sel.col, direction) + if not char then return end + vis:feedkeys(char) +end + +local function operator(handler) + local id = vis:operator_register(handler) + return id >= 0 and function() + vis:operator(id) + vis:motion(VIS_MOVE_NOP) + end +end + +vis.events.subscribe(vis.events.INIT, function() + local function h(msg) return string.format("|@%s| %s", progname, msg) end + + local function column_complete(direction) + local binding = operator(dup_neighbor(direction)) + return function() + if #vis.win.selections == 1 then + dup_neighbor_feedkeys(direction) + else + return binding() + end + end + end + + vis:map(vis.modes.INSERT, "", column_complete(-1), + h "Insert the character which is above the cursor") + vis:map(vis.modes.INSERT, "", column_complete(1), + h "Insert the character which is below the cursor") + vis:map(vis.modes.INSERT, "", line_complete, + h "Complete the current line") +end) diff --git a/config/essentials/vis/cursors.lua b/config/essentials/vis/cursors.lua index 37165b6..5b3d43b 100644 --- a/config/essentials/vis/cursors.lua +++ b/config/essentials/vis/cursors.lua @@ -6,7 +6,7 @@ local files = {} M.maxsize = 1000 -- get the default system cache directory -local get_default_cache_path = function() +local function get_default_cache_path() local HOME = os.getenv('HOME') local XDG_CACHE_HOME = os.getenv('XDG_CACHE_HOME') local BASE = XDG_CACHE_HOME or HOME @@ -16,7 +16,7 @@ end -- default save path M.path = get_default_cache_path() -local read_files = function() +local function read_files() -- read file local file = io.open(M.path) @@ -36,10 +36,10 @@ local read_files = function() end -- read cursors from file on init -local on_init = function() read_files() end +local function on_init() read_files() end -- apply cursor pos on win open -local on_win_open = function(win) +local function on_win_open(win) if win.file == nil or win.file.path == nil then return end @@ -58,7 +58,7 @@ local on_win_open = function(win) end -- set cursor pos on close -local on_win_close = function(win) +local function on_win_close(win) if win.file == nil or win.file.path == nil then return end @@ -81,7 +81,7 @@ local on_win_close = function(win) end -- write cursors to file on quit -local on_quit = function() +local function on_quit() local file = io.open(M.path, 'w+') if file == nil then return end diff --git a/config/essentials/vis/format.lua b/config/essentials/vis/format.lua new file mode 100644 index 0000000..15488dd --- /dev/null +++ b/config/essentials/vis/format.lua @@ -0,0 +1,131 @@ +local global_options = {check_same = true} + +local function stdio_formatter(cmd, options) + local function apply(win, range, pos) + local size = win.file.size + local all = {start = 0, finish = size} + if range == nil then range = all end + local command = type(cmd) == 'function' and cmd(win, range, pos) or cmd + local check_same = (options and options.check_same ~= nil) and + options.check_same or global_options.check_same + local check = check_same == true or + (type(check_same) == 'number' and check_same >= size) + local status, out, err = vis:pipe(win.file, all, command) + if status ~= 0 then + vis:message(err) + elseif out == nil or out == '' then + vis:info('No output from formatter') + elseif not check or win.file:content(all) ~= out then + local start, finish = range.start, range.finish + win.file:delete(range) + win.file:insert(start, + out:sub(start + 1, finish + (out:len() - size))) + end + return pos + end + return { + apply = apply, + options = options or {ranged = type(cmd) == 'function'} + } +end + +local function with_filename(win, option) + if win.file.path then + return option .. "'" .. win.file.path:gsub("'", "\\'") .. "'" + else + return '' + end +end + +local formatters = {} +formatters = { + bash = stdio_formatter(function(win) + return 'shfmt ' .. with_filename(win, '--filename ') .. ' -' + end), + csharp = stdio_formatter('dotnet csharpier'), + go = stdio_formatter('gofmt'), + lua = { + pick = function(win) + local _, out = vis:pipe(win.file, + {start = 0, finish = win.file.size}, + 'test -e .lua-format && echo luaformatter || echo stylua') + return formatters[out:gsub('\n$', '')] + end + }, + luaformatter = stdio_formatter('lua-format'), + markdown = stdio_formatter(function(win) + if win.options and win.options.colorcolumn ~= 0 then + return 'prettier --parser markdown --prose-wrap always ' .. + ('--print-width ' .. (win.options.colorcolumn - 1) .. ' ') .. + with_filename(win, '--stdin-filepath ') + else + return 'prettier --parser markdown ' .. + with_filename(win, '--stdin-filepath ') + end + end, {ranged = false}), + powershell = stdio_formatter([[ + "$( (command -v powershell.exe || command -v pwsh) 2>/dev/null )" -c ' + Invoke-Formatter -ScriptDefinition ` + ([IO.StreamReader]::new([Console]::OpenStandardInput()).ReadToEnd()) + ' | sed -e :a -e '/^[\r\n]*$/{$d;N;};/\n$/ba' + ]]), + rust = stdio_formatter('rustfmt'), + stylua = stdio_formatter(function(win, range) + if range and (range.start ~= 0 or range.finish ~= win.file.size) then + return + 'stylua -s --range-start ' .. range.start .. ' --range-end ' .. + range.finish .. with_filename(win, ' --stdin-filepath ') .. + ' -' + else + return 'stylua -s ' .. with_filename(win, '--stdin-filepath ') .. + ' -' + end + end), + text = stdio_formatter(function(win) + if win.options and win.options.colorcolumn ~= 0 then + return 'fmt -w ' .. (win.options.colorcolumn - 1) + else + return 'fmt' + end + end, {ranged = false}) +} + +local function getwinforfile(file) + for win in vis:windows() do + if win and win.file and win.file.path == file.path then + return win + end + end +end + +local function apply(file_or_keys, range, pos) + local win = + type(file_or_keys) ~= 'string' and getwinforfile(file_or_keys) or + vis.win + local ret = type(file_or_keys) ~= 'string' and function() return pos end or + function() return 0 end + pos = pos or win.selection.pos + local formatter = formatters[win.syntax] + if formatter and formatter.pick then formatter = formatter.pick(win) end + if formatter == nil then + vis:info('No formatter for ' .. win.syntax) + return ret() + end + if range ~= nil and not formatter.options.ranged and range.start ~= 0 and + range.finish ~= win.file.size then + vis:info('Formatter for ' .. win.syntax .. ' does not support ranges') + return ret() + end + pos = formatter.apply(win, range, pos) or pos + vis:insert('') -- redraw and friends don't work + win.selection.pos = pos + return ret() +end + +return { + formatters = formatters, + options = global_options, + apply = apply, + stdio_formatter = stdio_formatter, + with_filename = with_filename +} diff --git a/config/essentials/vis/visrc.lua b/config/essentials/vis/visrc.lua index 3a9ff1d..60162bc 100644 --- a/config/essentials/vis/visrc.lua +++ b/config/essentials/vis/visrc.lua @@ -1,71 +1,83 @@ ------------------------------------ --- LIBRARIES ------------------------------------ -require('vis') +require("vis") -- plugins require("build") require("backup") require("cursors") require("title") +require("commentary") +require("complete-line") +local format = require("format") -vis:command_register("make", function() vis:communicate() end, "make") +-- todo: +-- c-scope +-- c-tags ------------------------------------ ---- FUNCTIONS +--- VARIABLES ------------------------------------ -local map_cmd = function(mode, map, command, help) - vis:map(mode, map, function() vis:command(command) end, help) -end - -local map_cmd_restore = function(mode, map, command, help) - vis:map(mode, map, function() - if (mode == vis.modes.INSERT) then vis:feedkeys("") end +local m = vis.modes - vis:feedkeys("m") - vis:command(command) - vis:feedkeys("M") +------------------------------------ +--- FUNCTIONS +------------------------------------ - if (mode == vis.modes.INSERT) then vis:feedkeys("i") end - end, help) +local function map_cmd(mode, map, command, help) + vis:map(mode, map, function() + vis:command(command) + end, help) end -local map_keys = function(mode, map, keys, help) - vis:map(mode, map, function() vis:feedkeys(keys) end, help) +-- TOOD: use window selection to restore position +local function wrap_restore(f, ...) + local pos = vis.win.selection.pos + f(...) + vis.win.selection.pos = pos end ------------------------------------- ---- VARIABLES ------------------------------------- - -local m = vis.modes +local function map_keys(mode, map, keys, help) + vis:map(mode, map, function() + vis:feedkeys(keys) + end, help) +end ------------------------------------ --- COMMANDS ----------------------------------- -vis:command_register("Q", function() vis:command("qa!") end, "Quit all") -vis:command_register("delws", - function() vis:command("x/[ \t]+$|^[ \t]+$/d") end, - "Remove trailing whitespace") +vis:command_register("make", function() + vis:command("!make && head -n 1") +end, "make") +vis:command_register("Q", function() + vis:command("qa!") +end, "Quit all") +vis:command_register("delws", function() + vis:command("x/[ \t]+$|^[ \t]+$/d") +end, "Remove trailing whitespace") ------------------------------------- --- MAPPINGS ------------------------------------- -map_cmd_restore(m.NORMAL, " r", "e $vis_filepath", "Reload active file") +vis:map(m.NORMAL, " r", function() + wrap_restore(vis.command, vis, "e $vis_filepath") +end, "Reload active file") + +vis:map(m.NORMAL, "=", format.apply, "Format active file") map_cmd(m.NORMAL, " c", "e ~/.config/vis/visrc.lua", "Edit config file") map_cmd(m.NORMAL, " q", "q!", "Quit (force)") map_cmd(m.NORMAL, " s", "!doas vis $vis_filepath", "Edit as superuser") map_cmd(m.NORMAL, " w", "w", "Write") -map_cmd(m.NORMAL, " x", "!chmod u+x $vis_filepath", - "Make active file executable") +map_cmd(m.NORMAL, " x", "!chmod u+x $vis_filepath", "Make active file executable") vis:map(m.NORMAL, " eh", function() - vis:command("!lowdown $vis_filepath > ${vis_filepath%.md}.html") - vis:info("exported.") + vis:command("!lowdown $vis_filepath > ${vis_filepath%.md}.html") + vis:info("exported.") end, "Export markdown to html") map_keys(m.NORMAL, " nl", ":\\&2 printf '\\2: %s\\\\n' \"$\\2\"/", - "Print variable") - end +vis.events.subscribe(vis.events.WIN_OPEN, function(win) -- luacheck: no unused args + win.options.relativenumbers = true + + if win.syntax == "bash" then + map_keys( + m.NORMAL, + " v", + "V:x/^(\\s*)(.+)$/ c/\\1>\\&2 printf '\\2: %s\\\\n' \"$\\2\"/", + "Print variable" + ) + end + + vis:command_register("pipe", function() + vis:pipe(win.file, nil, "sed 's/.*/- &/'") + end, "pipe test") end) -- cgit v1.2.3 From c9cc72113521b793d1baa0d2f558b97478a6acf4 Mon Sep 17 00:00:00 2001 From: Raymaekers Luca Date: Fri, 21 Jun 2024 01:12:20 +0200 Subject: checkpoint --- config/essentials/vis/Makefile | 9 +- config/essentials/vis/format.lua | 217 +++++++++---------- config/essentials/vis/fzf-mru.lua | 75 +++++++ config/essentials/vis/fzf-open.lua | 81 ++++++++ config/essentials/vis/vis-go.lua | 102 +++++++++ config/essentials/vis/vis-ultisnips/init.lua | 149 +++++++++++++ .../vis/vis-ultisnips/snipmate-parser.lua | 128 ++++++++++++ .../vis/vis-ultisnips/testlpeg-snipmate.lua | 160 ++++++++++++++ .../vis/vis-ultisnips/testlpeg-ultisnips.lua | 230 +++++++++++++++++++++ .../vis/vis-ultisnips/ultisnips-parser.lua | 211 +++++++++++++++++++ config/essentials/vis/visrc.lua | 33 ++- config/essentials/vis/yank-highlight.lua | 37 ++++ 12 files changed, 1311 insertions(+), 121 deletions(-) create mode 100644 config/essentials/vis/fzf-mru.lua create mode 100644 config/essentials/vis/fzf-open.lua create mode 100644 config/essentials/vis/vis-go.lua create mode 100644 config/essentials/vis/vis-ultisnips/init.lua create mode 100644 config/essentials/vis/vis-ultisnips/snipmate-parser.lua create mode 100644 config/essentials/vis/vis-ultisnips/testlpeg-snipmate.lua create mode 100644 config/essentials/vis/vis-ultisnips/testlpeg-ultisnips.lua create mode 100644 config/essentials/vis/vis-ultisnips/ultisnips-parser.lua create mode 100644 config/essentials/vis/yank-highlight.lua diff --git a/config/essentials/vis/Makefile b/config/essentials/vis/Makefile index 1599b58..f2d386b 100644 --- a/config/essentials/vis/Makefile +++ b/config/essentials/vis/Makefile @@ -1,11 +1,6 @@ -.PHONY: check format all +.PHONY: check -LUA_FILES := $(shell find . -name "*.lua") - -all: format check +LUA_FILES := $(shell find . -type f -name "*.lua") check: luacheck --no-color --globals=vis -- $(LUA_FILES) - -format: - lua-format -i $(LUA_FILES) diff --git a/config/essentials/vis/format.lua b/config/essentials/vis/format.lua index 15488dd..e39320e 100644 --- a/config/essentials/vis/format.lua +++ b/config/essentials/vis/format.lua @@ -1,131 +1,134 @@ -local global_options = {check_same = true} +local global_options = { check_same = true } local function stdio_formatter(cmd, options) - local function apply(win, range, pos) - local size = win.file.size - local all = {start = 0, finish = size} - if range == nil then range = all end - local command = type(cmd) == 'function' and cmd(win, range, pos) or cmd - local check_same = (options and options.check_same ~= nil) and - options.check_same or global_options.check_same - local check = check_same == true or - (type(check_same) == 'number' and check_same >= size) - local status, out, err = vis:pipe(win.file, all, command) - if status ~= 0 then - vis:message(err) - elseif out == nil or out == '' then - vis:info('No output from formatter') - elseif not check or win.file:content(all) ~= out then - local start, finish = range.start, range.finish - win.file:delete(range) - win.file:insert(start, - out:sub(start + 1, finish + (out:len() - size))) - end - return pos - end - return { - apply = apply, - options = options or {ranged = type(cmd) == 'function'} - } + local function apply(win, range, pos) + local size = win.file.size + local all = { start = 0, finish = size } + if range == nil then + range = all + end + local command = type(cmd) == "function" and cmd(win, range, pos) or cmd + local check_same = (options and options.check_same ~= nil) and options.check_same or global_options.check_same + local check = check_same == true or (type(check_same) == "number" and check_same >= size) + local status, out, err = vis:pipe(win.file, all, command) + if status ~= 0 then + vis:message(err) + elseif out == nil or out == "" then + vis:info("No output from formatter") + elseif not check or win.file:content(all) ~= out then + local start, finish = range.start, range.finish + win.file:delete(range) + win.file:insert(start, out:sub(start + 1, finish + (out:len() - size))) + end + return pos + end + return { + apply = apply, + options = options or { ranged = type(cmd) == "function" }, + } end local function with_filename(win, option) - if win.file.path then - return option .. "'" .. win.file.path:gsub("'", "\\'") .. "'" - else - return '' - end + if win.file.path then + return option .. "'" .. win.file.path:gsub("'", "\\'") .. "'" + else + return "" + end end local formatters = {} formatters = { - bash = stdio_formatter(function(win) - return 'shfmt ' .. with_filename(win, '--filename ') .. ' -' - end), - csharp = stdio_formatter('dotnet csharpier'), - go = stdio_formatter('gofmt'), - lua = { - pick = function(win) - local _, out = vis:pipe(win.file, - {start = 0, finish = win.file.size}, - 'test -e .lua-format && echo luaformatter || echo stylua') - return formatters[out:gsub('\n$', '')] - end - }, - luaformatter = stdio_formatter('lua-format'), - markdown = stdio_formatter(function(win) - if win.options and win.options.colorcolumn ~= 0 then - return 'prettier --parser markdown --prose-wrap always ' .. - ('--print-width ' .. (win.options.colorcolumn - 1) .. ' ') .. - with_filename(win, '--stdin-filepath ') - else - return 'prettier --parser markdown ' .. - with_filename(win, '--stdin-filepath ') - end - end, {ranged = false}), - powershell = stdio_formatter([[ + bash = stdio_formatter(function(win) + return "shfmt " .. with_filename(win, "--filename ") .. " -" + end), + csharp = stdio_formatter("dotnet csharpier"), + go = stdio_formatter("gofmt"), + lua = { + pick = function(win) + local _, out = vis:pipe( + win.file, + { start = 0, finish = win.file.size }, + "test -e .lua-format && echo luaformatter || echo stylua" + ) + return formatters[out:gsub("\n$", "")] + end, + }, + luaformatter = stdio_formatter("lua-format"), + markdown = stdio_formatter(function(win) + if win.options and win.options.colorcolumn ~= 0 then + return "prettier --parser markdown --prose-wrap always " + .. ("--print-width " .. (win.options.colorcolumn - 1) .. " ") + .. with_filename(win, "--stdin-filepath ") + else + return "prettier --parser markdown " .. with_filename(win, "--stdin-filepath ") + end + end, { ranged = false }), + powershell = stdio_formatter([[ "$( (command -v powershell.exe || command -v pwsh) 2>/dev/null )" -c ' Invoke-Formatter -ScriptDefinition ` ([IO.StreamReader]::new([Console]::OpenStandardInput()).ReadToEnd()) ' | sed -e :a -e '/^[\r\n]*$/{$d;N;};/\n$/ba' ]]), - rust = stdio_formatter('rustfmt'), - stylua = stdio_formatter(function(win, range) - if range and (range.start ~= 0 or range.finish ~= win.file.size) then - return - 'stylua -s --range-start ' .. range.start .. ' --range-end ' .. - range.finish .. with_filename(win, ' --stdin-filepath ') .. - ' -' - else - return 'stylua -s ' .. with_filename(win, '--stdin-filepath ') .. - ' -' - end - end), - text = stdio_formatter(function(win) - if win.options and win.options.colorcolumn ~= 0 then - return 'fmt -w ' .. (win.options.colorcolumn - 1) - else - return 'fmt' - end - end, {ranged = false}) + rust = stdio_formatter("rustfmt"), + stylua = stdio_formatter(function(win, range) + if range and (range.start ~= 0 or range.finish ~= win.file.size) then + return "stylua -s --range-start " + .. range.start + .. " --range-end " + .. range.finish + .. with_filename(win, " --stdin-filepath ") + .. " -" + else + return "stylua -s " .. with_filename(win, "--stdin-filepath ") .. " -" + end + end), + text = stdio_formatter(function(win) + if win.options and win.options.colorcolumn ~= 0 then + return "fmt -w " .. (win.options.colorcolumn - 1) + else + return "fmt" + end + end, { ranged = false }), } local function getwinforfile(file) - for win in vis:windows() do - if win and win.file and win.file.path == file.path then - return win - end - end + for win in vis:windows() do + if win and win.file and win.file.path == file.path then + return win + end + end end local function apply(file_or_keys, range, pos) - local win = - type(file_or_keys) ~= 'string' and getwinforfile(file_or_keys) or - vis.win - local ret = type(file_or_keys) ~= 'string' and function() return pos end or - function() return 0 end - pos = pos or win.selection.pos - local formatter = formatters[win.syntax] - if formatter and formatter.pick then formatter = formatter.pick(win) end - if formatter == nil then - vis:info('No formatter for ' .. win.syntax) - return ret() - end - if range ~= nil and not formatter.options.ranged and range.start ~= 0 and - range.finish ~= win.file.size then - vis:info('Formatter for ' .. win.syntax .. ' does not support ranges') - return ret() - end - pos = formatter.apply(win, range, pos) or pos - vis:insert('') -- redraw and friends don't work - win.selection.pos = pos - return ret() + local win = type(file_or_keys) ~= "string" and getwinforfile(file_or_keys) or vis.win + local ret = type(file_or_keys) ~= "string" and function() + return pos + end or function() + return 0 + end + pos = pos or win.selection.pos + local formatter = formatters[win.syntax] + if formatter and formatter.pick then + formatter = formatter.pick(win) + end + if formatter == nil then + vis:info("No formatter for " .. win.syntax) + return ret() + end + if range ~= nil and not formatter.options.ranged and range.start ~= 0 and range.finish ~= win.file.size then + vis:info("Formatter for " .. win.syntax .. " does not support ranges") + return ret() + end + pos = formatter.apply(win, range, pos) or pos + vis:redraw() + win.selection.pos = pos + return ret() end return { - formatters = formatters, - options = global_options, - apply = apply, - stdio_formatter = stdio_formatter, - with_filename = with_filename + formatters = formatters, + options = global_options, + apply = apply, + stdio_formatter = stdio_formatter, + with_filename = with_filename, } diff --git a/config/essentials/vis/fzf-mru.lua b/config/essentials/vis/fzf-mru.lua new file mode 100644 index 0000000..6c2510d --- /dev/null +++ b/config/essentials/vis/fzf-mru.lua @@ -0,0 +1,75 @@ +local module = {} +module.fzfmru_filepath = os.getenv("XDG_CACHE_HOME") .. "/vis-fzf-mru" +module.fzfmru_path = "fzf" +module.fzfmru_args = "--height=40%" +module.fzfmru_history = 20 + +local function read_mru() + local mru = {} + local f = io.open(module.fzfmru_filepath) + if f == nil then return end + for line in f:lines() do table.insert(mru, line) end + f:close() + + return mru +end + +local function write_mru(win) + local file_path = win.file.path + local mru = read_mru() + + -- check if mru data exists + if mru == nil then mru = {} end + -- check if we opened any file + if file_path == nil then return end + -- check duplicate + if file_path == mru[1] then return end + + local f = io.open(module.fzfmru_filepath, "w+") + if f == nil then return end + + table.insert(mru, 1, file_path) + + for i, k in ipairs(mru) do + if i > module.fzfmru_history then break end + if i == 1 or k ~= file_path then + f:write(string.format("%s\n", k)) + end + end + + f:close() +end + +vis.events.subscribe(vis.events.WIN_OPEN, write_mru) + +vis:command_register("fzfmru", function(argv) + local command = "cat " .. module.fzfmru_filepath .. " | " .. + module.fzfmru_path .. " " .. module.fzfmru_args .. " " .. + table.concat(argv, " ") + + local file = io.popen(command) + local output = file:read() + local _, _, status = file:close() + + if status == 0 then + vis:command(string.format("e '%s'", output)) + elseif status == 1 then + vis:info(string.format( + "fzf-open: No match. Command %s exited with return value %i.", + command, status)) + elseif status == 2 then + vis:info(string.format( + "fzf-open: Error. Command %s exited with return value %i.", + command, status)) + elseif status ~= 130 then + vis:info(string.format( + "fzf-open: Unknown exit status %i. command %s exited with return value %i", + status, command, status, status)) + end + + vis:redraw() + + return true +end) + +return module diff --git a/config/essentials/vis/fzf-open.lua b/config/essentials/vis/fzf-open.lua new file mode 100644 index 0000000..1c9d1e6 --- /dev/null +++ b/config/essentials/vis/fzf-open.lua @@ -0,0 +1,81 @@ +-- Copyright (C) 2017 Guillaume Chérel +-- Copyright (C) 2023 Matěj Cepl +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU Lesser General Public License as +-- published by the Free Software Foundation, either version 3 of the +-- License, or (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU Lesser General Public License for more details. +-- +-- You should have received a copy of the GNU Lesser General Public License +-- along with this program. If not, see . +local M = {} + +M.fzf_path = "fzf" +M.fzf_args = "--height=40%" + +vis:command_register("fzf", function(argv) + local fzf_path = M.fzf_path + if argv[1] == "--search-path" then + table.remove(argv, 1) + local dir = table.remove(argv, 1) + fzf_path = ([[FZF_DEFAULT_COMMAND="$FZF_DEFAULT_COMMAND --search-path ]] .. dir .. [[" fzf]]) + end + + local command = string.gsub( + [[ + $fzf_path \ + --header="Enter:edit,^s:split,^v:vsplit" \ + --expect="ctrl-s,ctrl-v" \ + $fzf_args $args + ]], + "%$([%w_]+)", + { + fzf_path = fzf_path, + fzf_args = M.fzf_args, + args = table.concat(argv, " "), + } + ) + + local file = io.popen(command) + local output = {} + for line in file:lines() do + table.insert(output, line) + end + local _, _, status = file:close() + + if status == 0 then + local action = "e" + + if output[1] == "ctrl-s" then + action = "split" + elseif output[1] == "ctrl-v" then + action = "vsplit" + end + + vis:feedkeys(string.format(":%s '%s'", action, output[2])) + elseif status == 1 then + vis:info(string.format("fzf-open: No match. Command %s exited with return value %i.", command, status)) + elseif status == 2 then + vis:info(string.format("fzf-open: Error. Command %s exited with return value %i.", command, status)) + elseif status ~= 130 then + vis:info( + string.format( + "fzf-open: Unknown exit status %i. command %s exited with return value %i", + status, + command, + status + ) + ) + end + + vis:redraw() + + return true +end, "Select file to open with fzf") + +return M diff --git a/config/essentials/vis/vis-go.lua b/config/essentials/vis/vis-go.lua new file mode 100644 index 0000000..b0b383f --- /dev/null +++ b/config/essentials/vis/vis-go.lua @@ -0,0 +1,102 @@ +local function jump_to(path, line, col) + if path then + vis:command(string.format("e %s", path)) + end + vis.win.selection:to(line, col) +end + +local Gostack = { s = {}, i = 1 } + +function Gostack:push(v) + self.s[self.i] = v + self.i = self.i + 1 +end + +function Gostack:pop() + if self.i == 1 then + return nil + end + self.i = self.i - 1 + return self.s[self.i] +end + +local function godef() + local win = vis.win + if win.syntax ~= "go" then + return 0 + end + + local file = win.file + local pos = win.selection.pos + local cmd = string.format("godef -i -o %d", pos) + local status, out, err = vis:pipe(file, { start = 0, finish = file.size }, cmd) + if status ~= 0 or not out then + if err then + vis:info(err) + end + return status + end + + Gostack:push({ path = file.path, line = win.selection.line, col = win.selection.col }) + + local path, line, col = string.match(out, "([^:]+):([^:]+):([^:]+)") + if not path then + -- same file + line, col = string.match(out, "([^:]+):([^:]+)") + end + jump_to(path, line, col) +end + +local function godef_back() + if vis.win.syntax ~= "go" then + return 0 + end + + local pos = Gostack:pop() + if pos then + jump_to(pos.path, pos.line, pos.col) + end +end + +vis:map(vis.modes.NORMAL, "gd", godef, "Jump to Go symbol/definition") +vis:map(vis.modes.NORMAL, "gD", godef_back, "Jump back to previous Go symbol/definition") + +local function gorename(argv, force, win, selection) + if win.syntax ~= "go" then + return true + end + + local name = argv[1] + if not name then + vis:info("empty new name provided") + return false + end + + local forceFlag = "" + if force then + forceFlag = "-force" + end + + local pos = selection.pos + local f = + io.popen(string.format("gorename -offset %s:#%d -to %s %s 2>&1", win.file.path, pos, name, forceFlag), "r") + local out = f:read("*all") + local success, _, _ = f:close() + if not success then + vis:message(out) + return false + end + + -- refresh current file + vis:command("e") + win.selection.pos = pos + + vis:info(out) + return true +end + +vis:command_register( + "gorename", + gorename, + "Perform precise type-safe renaming of identifiers in Go source code: :gorename newName" +) diff --git a/config/essentials/vis/vis-ultisnips/init.lua b/config/essentials/vis/vis-ultisnips/init.lua new file mode 100644 index 0000000..52faa55 --- /dev/null +++ b/config/essentials/vis/vis-ultisnips/init.lua @@ -0,0 +1,149 @@ +-------------------------------------------------------------------------------- +-- Modules + +local M = {} +local cwd = ... +local SnipMate = require(cwd .. '.snipmate-parser') +local UltiSnips = require(cwd .. '.ultisnips-parser') + + + +-------------------------------------------------------------------------------- +-- Config + +M.snipmate = '' +M.ultisnips = '' + + + +-------------------------------------------------------------------------------- +-- Helper functions + +-- Takes list of snippets and concatenates them into the string suitable +-- for passing to dmenu (or, very probably, vis-menu) +local function snippetslist(snippets) + local list = '' + + for k,v in pairs(snippets) do + if not v.description then + list = list .. k .. '\n' + else + list = list .. k .. ' - ' .. v.description .. '\n' + end + end + + return list +end + + + +local function load_ultisnips() + local snippetfile = M.ultisnips .. vis.win.syntax .. '.snippets' + local snippets, success = UltiSnips.load_snippets(snippetfile) + if not success then + vis:info('Failed to load a correct UltiSnip: ' .. snippetfile) + end + return snippets, success +end + + + +local function load_snipmate() + local snippetfile = M.snipmate .. vis.win.syntax .. '.snippets' + local snippets, success = SnipMate.load_snippets(snippetfile) + if not success then + vis:info('Failed to load a correct SnipMate: ' .. snippetfile) + end + return snippets, success +end + + + +-- Second will append to first using suffix for distinguishing +local function merge_and_override(snips1, snips2, suffix) + for k,v in pairs(snips2) do + snips1[k .. suffix] = v + end + return snips1 +end + + + +-------------------------------------------------------------------------------- +-- Plugging it all in + +vis:map(vis.modes.INSERT, "", function() + local snippets = merge_and_override(load_snipmate(), load_ultisnips(), '_us') + + local win = vis.win + local file = win.file + local pos = win.selection.pos + + if not pos then + return + end + -- TODO do something clever here + + -- Use prefix W if exists + local initial = ' ' + local range = file:text_object_longword(pos > 0 and pos - 1 or pos) + if range then + initial = initial .. file:content(range) + end + + -- Note, for one reason or another, using vis-menu corrupts my terminal + -- (urxvt) for exact amount of lines that vis-menu takes + -- dmenu has no such problems, but can't take initial input :-\ + --local stdout = io.popen("echo '" .. snippetslist(snippets) .. "' | dmenu -l 5", "r") + local stdout = io.popen("echo '" .. snippetslist(snippets) .. "' | vis-menu " .. initial, "r") + local chosen = stdout:lines()() + local _, msg, status = stdout:close() + if status ~= 0 or not chosen then + vis:message(msg) + return + end + + local trigger = chosen:gmatch('[^ ]+')() + local snipcontent = snippets[trigger].content + if range then + file:delete(range) + -- Update position after deleting the range + pos = pos - (range.finish - range.start) + vis:redraw() + end + + vis:insert(snipcontent.str) + + + if #snipcontent.tags > 0 then + vis:info("Use 'g>' and 'g<' to navigate between anchors.") + + -- Create selections iteratively using `:#n,#n2 p` command and `gs` to + -- save it in the jumplist + for _,v in ipairs(snipcontent.tags) do + -- Can't use 'x' command because it'd select stuff across + -- whole file + vis:command('#' .. pos + v.selstart ..',#' .. pos + v.selend .. ' p') + --vis:feedkeys('gs') -- Tested, works without this too, but just in case + --vis:message('Command: ' .. cmd) + end + + -- Backtrack through all selections we've made first + -- (so that we can use g> to move us forward)... + for _ in ipairs(snipcontent.tags) do + vis:feedkeys('g<') + end + + -- ... then set us on the first selection + vis:feedkeys('g>') + else + win.selection.pos = pos + #snipcontent.str + end +end, "Insert a snippet") + + + +-------------------------------------------------------------------------------- +-- End module + +return M \ No newline at end of file diff --git a/config/essentials/vis/vis-ultisnips/snipmate-parser.lua b/config/essentials/vis/vis-ultisnips/snipmate-parser.lua new file mode 100644 index 0000000..9d735f1 --- /dev/null +++ b/config/essentials/vis/vis-ultisnips/snipmate-parser.lua @@ -0,0 +1,128 @@ +-------------------------------------------------------------------------------- +-- Module table + +local M = {} + +local lpeg = require('lpeg') + + + +-------------------------------------------------------------------------------- +-- lpeg rules + +-- Base definitions +-- local tws = lpeg.S' ' ^ 1 +local tnewline = lpeg.S'\n' +-- local tlowcasedword = lpeg.R'az' ^ 1 +local tdigit = lpeg.locale()['digit'] +local talphanum = lpeg.locale()['alnum'] +local tanyprintable = lpeg.locale()['print'] +-- local tcontrol = lpeg.locale()['cntrl'] +local ttabtrigger = tanyprintable ^ 1 +local ttag = lpeg.Cg(lpeg.Cp(), 'selstart') + * lpeg.P'${' + * lpeg.Cg(tdigit^1, 'tag-order') + * ( + (lpeg.S':' * lpeg.Cg(talphanum^1, 'default-value') * lpeg.S'}') + + lpeg.S'}' + ) + * lpeg.Cg(lpeg.Cp(), 'selend') +local tsnippetdecl = lpeg.P'snippet' * lpeg.S' ' * lpeg.Cg(ttabtrigger, 'tabtrigger') * tnewline +local tsnippetcontent = lpeg.C( + lpeg.Cp() * + (lpeg.S'\t '^1 + * (lpeg.Ct(ttag) + tanyprintable)^1 + * tnewline + )^1 + ) + +-- Constructs +local tsnippet = tsnippetdecl * tsnippetcontent +local tcomment = lpeg.S'#' * tanyprintable^0 * tnewline + +-- The way grammar captures: +-- Every snippet gets its own table, and every table has: +-- 'tabtrigger' - the tabtrigger +-- [1] - full content +-- [2] - start of snippet content (need to subtract from selstart/selend +-- [3..n] - tags +local tsnippetsfile = lpeg.Ct((tcomment + lpeg.Ct(tsnippet) + tnewline) ^1) + +-------------------------------------------------------------------------------- +-- Functions + +local function trim_tabs(content) + local trim = function (s) + return (string.gsub(s, "^\t(.-)$", "%1")) + end + + local ret='' + for str in string.gmatch(content, '([^\n]+)') do + ret = ret .. trim(str) .. '\n' + end + return ret +end + +-- Tags are on the top level of th table, +-- defined starting with index '3' +-- Index '2' is start of the content +-- Structure: +-- { tag-order: int +-- , selstart: int +-- , selend: int +-- , default-value: str +-- } +local function extract_tags(tableau) + local tags = {} + for k,v in ipairs(tableau) do + if k >= 3 then -- Only process starting with ix 2 + tags[k - 2] = { selstart = v.selstart - tableau[2] - 1 + , selend = v.selend - tableau[2] - 1 + , default = v['default-value'] + , order = v['tag-order'] + } +-- vis:message('snippet ' .. tableau.tabtrigger .. ' tag ' .. +-- tostring(tags[k - 1].order) .. ' has start/end: ' .. +-- tostring(tags[k - 1].selstart) .. '/' .. +-- tostring(tags[k - 1].selend)) + end + end + return tags +end + +M.load_snippets = function(snippetfile) + local snippets = {} + + local f = io.open(snippetfile, 'r') + if f then + local content = f:read("*all") + + -- TODO hmmm, this'll make whole file unsuable, when it could + -- in fact have usable snippets + local m = tsnippetsfile:match(content) + if not m then + vis:info('Failed to parse SnipMate file: '.. snippetfile) + return nil + else + -- k is index of snippet definition, v is table of snippet def + for _,v in pairs(m) do + snippets[v.tabtrigger] = { description = nil + , options = {} + , content = { str = trim_tabs(v[1]) + , tags = extract_tags(v) + } + } + end + end + + f:close() + return snippets, true + else + return snippets, false + end +end + +-------------------------------------------------------------------------------- +-- End module + +return M diff --git a/config/essentials/vis/vis-ultisnips/testlpeg-snipmate.lua b/config/essentials/vis/vis-ultisnips/testlpeg-snipmate.lua new file mode 100644 index 0000000..997365f --- /dev/null +++ b/config/essentials/vis/vis-ultisnips/testlpeg-snipmate.lua @@ -0,0 +1,160 @@ +local lpeg = require("lpeg") + +-------------------------------------------------------------------------------- + +-- Base definitions +-- local tws = lpeg.S(" ") ^ 1 +local tnewline = lpeg.S("\n") +-- local tlowcasedword = lpeg.R("az") ^ 1 +local tdigit = lpeg.locale()["digit"] +local talphanum = lpeg.locale()["alnum"] +local tanyprintable = lpeg.locale()["print"] +-- local tcontrol = lpeg.locale()["cntrl"] +local ttabtrigger = tanyprintable ^ 1 +local ttag = lpeg.Cg(lpeg.Cp(), "selstart") + * lpeg.P("${") + * lpeg.Cg(tdigit ^ 1, "tag-order") + * ((lpeg.S(":") * lpeg.Cg(talphanum ^ 1, "default-value") * lpeg.S("}")) + lpeg.S("}")) + * lpeg.Cg(lpeg.Cp(), "selend") +local tsnippetdecl = lpeg.P("snippet") * lpeg.S(" ") * lpeg.Cg(ttabtrigger, "tabtrigger") * tnewline +local tsnippetcontent = lpeg.C(lpeg.Cp() * (lpeg.S("\t ") ^ 1 * (lpeg.Ct(ttag) + tanyprintable) ^ 1 * tnewline) ^ 1) + +-- Constructs +local tsnippet = tsnippetdecl * tsnippetcontent +local tcomment = lpeg.S("#") * tanyprintable ^ 0 * tnewline + +-- The way grammar captures: +-- Every snippet gets its own table, and every table has: +-- 'tabtrigger' - the tabtrigger +-- [1] - full content +-- [2..n] - tags +local tsnippetsfile = lpeg.Ct((tcomment + lpeg.Ct(tsnippet) + tnewline) ^ 1) +-------------------------------------------------------------------------------- + +-- local testsingle = [[ +-- snippet sim +-- ${1:public} static int Main(string[] args) +-- { +-- ${0} +-- return 0; +-- } +-- ]] + +-- local testmulti = [[ +-- snippet sim +-- ${1:public} static int Main(string[] args) +-- { +-- ${0} +-- return 0; +-- } +-- snippet simc +-- public class Application +-- { +-- ${1:public} static int Main(string[] args) +-- { +-- ${0} +-- return 0; +-- } +-- } +-- snippet svm +-- ${1:public} static void Main(string[] args) +-- { +-- ${0} +-- } +-- ]] + +local testfile = [[ +# I'll most propably add more stuff in here like +# * List/Array constructio +# * Mostly used generics +# * Linq +# * Funcs, Actions, Predicates +# * Lambda +# * Events +# +# Feedback is welcome! +# +# Main +snippet sim + ${1:public} static int Main(string[] args) + { + ${0} + return 0; + } +snippet simc + public class Application + { + ${1:public} static int Main(string[] args) + { + ${0} + return 0; + } + } +snippet svm + ${1:public} static void Main(string[] args) + { + ${0} + } +# if condition +snippet if + if (${1:true}) + { + ${0:${VISUAL}} + } +snippet el + else + { + ${0:${VISUAL}} + } +]] + +-------------------------------------------------------------------------------- +-- Test + +local function print_table(tableau, tabwidth) + if tabwidth == nil then + tabwidth = 0 + end + + -- Iterate + for k, v in pairs(tableau) do + local tabs = ("\t"):rep(tabwidth) + + print(tabs .. k .. ':"' .. tostring(v) .. '"') + if type(v) == "table" then + print_table(v, tabwidth + 1) + end + end +end + +--print("------------ header ------------------------------------") +--p = lpeg.Ct(tsnippetdecl) +--t = p:match([[ +--snippet classy +--]]) +--print_table(t) +--print("--------------------------------------------------------------") + +--print("------------ tag ------------------------------------") +--print_table( +-- lpeg.Ct(ttag):match('${0:VISUAL}') +--) +--print_table( +-- lpeg.Ct(ttag):match('${12:Badonkadong}') +--) +--print_table( +-- lpeg.Ct(ttag):match('${1}') +--) +--print("--------------------------------------------------------------") + +--print("------------ single snippet test ------------------------------------") +--print_table(lpeg.Ct(tsnippet):match(testsingle)) +--print("--------------------------------------------------------------") + +--print("------------ multi snippet test ------------------------------------") +--print_table(lpeg.Ct(tsnippetsfile):match(testmulti)) +--print("--------------------------------------------------------------") + +print("------------ file with comments -------------------------------------") +print_table(tsnippetsfile:match(testfile)) +print("--------------------------------------------------------------") diff --git a/config/essentials/vis/vis-ultisnips/testlpeg-ultisnips.lua b/config/essentials/vis/vis-ultisnips/testlpeg-ultisnips.lua new file mode 100644 index 0000000..79df900 --- /dev/null +++ b/config/essentials/vis/vis-ultisnips/testlpeg-ultisnips.lua @@ -0,0 +1,230 @@ +local lpeg = require("lpeg") + +-------------------------------------------------------------------------------- + +local tsep = lpeg.S(" ") +local tws = tsep ^ 1 +local tnewline = lpeg.S("\n") +local tlowcasedword = lpeg.R("az") ^ 1 +local tdigit = lpeg.locale()["digit"] +-- local talphanum = lpeg.locale()['alnum'] +local tanyprintable = lpeg.locale()["print"] +local tcontrol = lpeg.locale()["cntrl"] +local function quoted(p) + return lpeg.S('"') * p * lpeg.S('"') +end +local function anythingbut(ch) + return (tanyprintable + tcontrol) - lpeg.S(ch) +end + +local ttabtriggercomplex = quoted(tlowcasedword * lpeg.S("()[]?0123456789-") ^ 1) +-- TODO This is just retarded +local ttabtriggerweird = lpeg.S("!") * (lpeg.R("az") + lpeg.S("?()")) ^ 1 * lpeg.S("!") +local ttabtriggerweird2 = lpeg.P("#!") +local ttabtrigger = ttabtriggercomplex + ttabtriggerweird + ttabtriggerweird2 + tlowcasedword +local tdescription = quoted(lpeg.Cg((tanyprintable - lpeg.S('"')) ^ 1, "description")) +local toption = lpeg.R("az") + +local tstartsnippet = lpeg.P("snippet") + * tws + * lpeg.Cg(ttabtrigger, "tabtrigger") + * tws + * tdescription + * tws ^ 0 + * lpeg.Cg(toption ^ 0, "options") +local tendsnippet = lpeg.P("endsnippet") + +-- The content parsing needs cleanup, its really convoluted due to me learning +-- lpeg while using it +--tcontent = ((tanyprintable + tcontrol)^1 - tendsnippet) * tnewline +local tcontent = ((lpeg.S(" \t") + tanyprintable) ^ 1 - tendsnippet) * tnewline +local tsnippet = tstartsnippet * tnewline * ((tendsnippet * tnewline) + lpeg.Cg(tcontent ^ 1, "content")) + +local tcomment = lpeg.S("#") * tanyprintable ^ 0 * tnewline +local tpriority = lpeg.P("priority") * tws * lpeg.Cg(lpeg.S("-") ^ 0 * tdigit ^ 1, "priority") + +-- TODO doesn't work +local tsnippetsfile = (lpeg.Ct(tsnippet) + tpriority + tcomment + tnewline) ^ 1 + +-- TODO does parse values correctly, but parsing out nested tags will +-- require recursion at the callsite since I have no clue how to do it +local ttag = { + "T", + Expr = lpeg.C((lpeg.V("T") + anythingbut("}")) ^ 1), + Tnum = lpeg.Cg(tdigit ^ 1, "tagnum"), + Ps = lpeg.Cg(lpeg.Cp(), "selstart"), + Pe = lpeg.Cg(lpeg.Cp(), "selend"), + Tc = lpeg.V("Ps") + * lpeg.P("${") + * lpeg.V("Tnum") + * lpeg.S(":") + * lpeg.Cg(lpeg.V("Expr"), "expr") + * lpeg.V("Pe") + * lpeg.S("}"), + Ts = lpeg.V("Ps") * lpeg.S("$") * lpeg.V("Pe") * lpeg.V("Tnum"), + T = lpeg.V("Tc") + lpeg.V("Ts"), +} + +-------------------------------------------------------------------------------- + +-- local testheader = [[ +-- snippet #! "#!/usr/bin/env lua" b +-- ]] + +local testcontent = [[ +for ${1:idx},${2:val} in ipairs(${3:table_name}) do + $0 +end +]] + +local testsnippet = [[ +snippet fori "ipair for foop" b +for ${1:idx},${2:val} in ipairs(${3:table_name}) do + $0 +end +endsnippet +]] + +local luasnippetfile = [[ +priority -50 + +################################# +# Snippets for the Lua language # +################################# +snippet #! "#!/usr/bin/env lua" b +#!/usr/bin/env lua +$0 +endsnippet + +snippet !fun(ction)?! "New function" br +local function ${1:new_function}(${2:args}) + $0 +end +endsnippet + +snippet forp "pair for loop" b +for ${1:name},${2:val} in pairs(${3:table_name}) do + $0 +end +endsnippet + +snippet fori "ipair for foop" b +for ${1:idx},${2:val} in ipairs(${3:table_name}) do + $0 +end +endsnippet + +snippet for "numeric for loop" b +for ${1:i}=${2:first},${3:last}${4/^..*/(?0:,:)/}${4:step} do + $0 +end +endsnippet + +snippet do "do block" +do + $0 +end +endsnippet + +snippet repeat "repeat loop" b +repeat + $1 +until $0 +endsnippet + +snippet while "while loop" b +while $1 do + $0 +end +endsnippet + +snippet if "if statement" b +if $1 then + $0 +end +endsnippet + +snippet ife "if/else statement" b +if $1 then + $2 +else + $0 +end +endsnippet + +snippet eif "if/elseif statement" b +if $1 then + $2 +elseif $3 then + $0 +end +endsnippet + +snippet eife "if/elseif/else statement" b +if $1 then + $2 +elseif $3 then + $4 +else + $0 +end +endsnippet + +snippet pcall "pcall statement" b +local ok, err = pcall(${1:your_function}) +if not ok then + handler(${2:ok, err}) +${3:else + success(${4:ok, err}) +}end +endsnippet + +snippet local "local x = 1" +local ${1:x} = ${0:1} +endsnippet + +# vim:ft=snippets: +]] + +-------------------------------------------------------------------------------- +-- Test + +local function print_table(tableau, tabwidth) + if tabwidth == nil then + tabwidth = 0 + end + + -- Iterate + for k, v in pairs(tableau) do + local tabs = ("\t"):rep(tabwidth) + + print(tabs .. k .. ': "' .. tostring(v) .. '"') + if type(v) == "table" then + print_table(v, tabwidth + 1) + end + end +end + +do + print("------------ snippet test ------------------------------------") + local p = lpeg.Ct(tsnippet) + local t = p:match(testsnippet) + print_table(t) + print("--------------------------------------------------------------") +end + +do + print("------------ snippetfile test ------------------------------------") + local p = lpeg.Ct(tsnippetsfile) + local t = p:match(luasnippetfile) + print_table(t) + print("--------------------------------------------------------------") +end + +do + print("------------ tags test -------------------------------------") + local p = lpeg.Ct((lpeg.Ct(ttag) + tanyprintable + tcontrol) ^ 1) + local t = p:match(testcontent) + print_table(t) + print("--------------------------------------------------------------") +end diff --git a/config/essentials/vis/vis-ultisnips/ultisnips-parser.lua b/config/essentials/vis/vis-ultisnips/ultisnips-parser.lua new file mode 100644 index 0000000..a4240b8 --- /dev/null +++ b/config/essentials/vis/vis-ultisnips/ultisnips-parser.lua @@ -0,0 +1,211 @@ +-------------------------------------------------------------------------------- +-- Module table + +local M = {} + +local lpeg = require('lpeg') + + + +-------------------------------------------------------------------------------- +-- lpeg rules + +local tsep = lpeg.S' \t' +local tws = tsep ^ 1 +local tnewline = lpeg.S'\n' +local tlowcasedword = lpeg.R'az' ^ 1 +local tdigit = lpeg.locale()['digit'] +-- local talphanum = lpeg.locale()['alnum'] +local tanyprintable = lpeg.locale()['print'] +local tcontrol = lpeg.locale()['cntrl'] +local function surrounded(ch, p) return lpeg.S(ch) * p * lpeg.S(ch) end +local function anythingbut(ch) return (tanyprintable + tcontrol) - lpeg.S(ch) end + +local ttabtriggercomplex = surrounded ('"', + tlowcasedword * lpeg.S'()[]?0123456789-'^1 + ) +-- TODO This is just retarded +-- Check the actual grammar and see what special starting chars are +-- then relax the grammar a bit +local ttabtriggerweird = surrounded('!', + (lpeg.R'az' + lpeg.S'?()') ^ 1 + ) +local ttabtriggerweird2 = lpeg.P'#!' +local ttabtriggerweird3 = surrounded('/', + (anythingbut'/') ^1 + ) +local ttabtrigger = ttabtriggercomplex + + ttabtriggerweird + + ttabtriggerweird2 + + ttabtriggerweird3 + + (tlowcasedword + lpeg.S'.') +local tdescription = surrounded ('"', + lpeg.Cg( (tanyprintable - lpeg.S'"')^1, 'description') + ) +local toption = lpeg.R'az' + +local tstartsnippet = lpeg.P'snippet' + * tws + * lpeg.Cg(ttabtrigger, 'tabtrigger') + * tws + * tdescription + * tws ^ 0 + * lpeg.Cg(toption^0, 'options') +local tendsnippet = lpeg.P'endsnippet' + +-- The content parsing needs cleanup, its really convoluted due to me learning +-- lpeg while using it +--tcontent = ((tanyprintable + tcontrol)^1 - tendsnippet) * tnewline +local tcontent = ((lpeg.S' \t' + tanyprintable)^1 - tendsnippet) + * tnewline +local tsnippet = tstartsnippet + * tnewline + * ((tendsnippet * tnewline) + lpeg.Cg(tcontent ^ 1, 'content')) + +-- local tcomment = lpeg.S'#' +-- * tanyprintable^0 +-- * tnewline +-- local tpriority = lpeg.P'priority' +-- * tws +-- * lpeg.Cg(lpeg.S('-')^0 * tdigit^1, 'priority') + +-- TODO doesn't work +-- local tsnippetsfile = (lpeg.Ct(tsnippet) + tpriority + tcomment + tnewline) ^ 1 + + +-- TODO does parse values correctly, but parsing out nested tags will +-- require recursion at the callsite since I have no clue how to do it +local ttag = { 'T' + ; Expr = lpeg.C((lpeg.V'T' + ((tanyprintable + tcontrol) - lpeg.S'}'))^1) + , Tnum = lpeg.Cg(tdigit ^ 1, 'tagnum') + , Ps = lpeg.Cg(lpeg.Cp(), 'selstart') + , Pe = lpeg.Cg(lpeg.Cp(), 'selend') + , Tc = lpeg.V'Ps' + * lpeg.P'${' + * lpeg.V'Tnum' + * lpeg.S(':') + * lpeg.Cg(lpeg.V'Expr', 'expr') + * lpeg.V'Pe' + * lpeg.S'}' + , Ts = lpeg.V'Ps' * lpeg.S'$' * lpeg.V'Pe' * lpeg.V'Tnum' + , T = lpeg.V'Tc' + lpeg.V'Ts' + } + + + +-------------------------------------------------------------------------------- +-- Functions + +-- Parses the snippet's content to create a table we later use +-- to corrently insert the text, the selections, and the default values +local function create_content(str) + local content = {} + content.str = str + content.tags = {} + + local p = vis.lpeg.Ct((lpeg.Ct(ttag) + tanyprintable + tcontrol) ^ 1) + local m = p:match(str) + + local s = 1 -- We start from 1 to adjust position from $^0 to ^$0 + for k,v in ipairs(m) do + content.tags[k] = v + -- TODO recurse over tag.expr to extract nested tags + -- Of course this will actually have to be used later on, depending + -- on whether the tag is added or not + + -- We need to keep track of how much we remove, and readjust all + -- subsequent selection points + -- Note to self, I hate all this bookkeeping + local tagtext = string.sub(str, v.selstart, v.selend) + if v.expr ~= nil then + content.str = string.gsub(content.str, tagtext, v.expr) + content.tags[k].selstart = content.tags[k].selstart - s + content.tags[k].selend = content.tags[k].selstart + #v.expr + s = s + #'${' + #tostring(k) + #':' + 1 + else + content.str = string.gsub(content.str, tagtext, '') + content.tags[k].selstart = content.tags[k].selstart - s + content.tags[k].selend = content.tags[k].selstart + s = s + #'$' + 1 + end + end + + return content +end + + + +-- Takes a line starting with 'snippet' and a lines iterator, and creates +-- a 'snippet' table to be used +-- If it fails it returns nil, otherwise returns two values, a tabtrigger +-- and a snippet +local function create_snippet(start_line, linesit) + local snippetstr = start_line .. '\n' + -- Read content into list of lines until we hit `endsnippet` + for line in linesit do + local s, _ = string.find(line, 'endsnippet') + if s == 1 then + snippetstr = snippetstr .. 'endsnippet' .. '\n' + break + else + snippetstr = snippetstr .. line .. '\n' + end + end + + local p = vis.lpeg.Ct(tsnippet) + local m = p:match(snippetstr) + + if not m then + -- Enable this when debugging, otherwise it nukes whole app + vis:info('Failed to parse some snippets!') + -- vis:message('Failed to parse snippet: ' .. snippetstr) + return nil + else + local tabtrigger = m.tabtrigger + local snippet = {} + snippet.description = m.description + snippet.options = m.options + snippet.content = create_content(m.content) + return tabtrigger, snippet + end +end + + + +-- Loads all snippets from passed '.snippets' file. Should probably be +-- triggered when new file is loaded or when syntax is set/changed +M.load_snippets = function(snippetfile) + local snippets = {} + + local f = io.open(snippetfile, 'r') + if f then + io.input(f) + local linesit = io.lines() + + for line in linesit do + -- TODO read whole file, then apply lpeg grammar that parses all + -- snippets out rather than being pedestrian about it like this + local s, _ = string.find(line, 'snippet') + -- Find lines that start with 'snippet' and enter + -- snippet reading loop + if s == 1 then + local tabtrigger, snippet = create_snippet(line, linesit) + if tabtrigger then + snippets[tabtrigger] = snippet + end + end + end + + io.close(f) + return snippets, true + else + return snippets, false + end +end + + + +-------------------------------------------------------------------------------- +-- End module + +return M \ No newline at end of file diff --git a/config/essentials/vis/visrc.lua b/config/essentials/vis/visrc.lua index 60162bc..7847784 100644 --- a/config/essentials/vis/visrc.lua +++ b/config/essentials/vis/visrc.lua @@ -1,25 +1,45 @@ ------------------------------------ ---- LIBRARIES +--- REQUIRES ------------------------------------ require("vis") -- plugins require("build") +-- use Trash directory instead, remove set_dir function require("backup") require("cursors") require("title") require("commentary") require("complete-line") +-- removed formatting because already fulfilled by format.lua +require("vis-go") +-- set height to 40% +require("fzf-open") +require("vis-ultisnips") +-- TODO: doesn't work when using with 'e|b' +-- require("yank-highlight") + +-- save position before formatting, use vis:redraw local format = require("format") +-- set height to 40% +local fzfmru = require("fzf-mru") +fzfmru.fzfmru_path = 'grep "^' .. os.getenv("PWD") .. '" | fzf' + + -- todo: -- c-scope -- c-tags +-- ... +-- vis-goto, favor open-file-under-cursor +-- ... +-- ultisnips +-- ... +-- vis-yank-highlight ------------------------------------ --- VARIABLES ------------------------------------ - local m = vis.modes ------------------------------------ @@ -56,13 +76,16 @@ vis:command_register("Q", function() vis:command("qa!") end, "Quit all") vis:command_register("delws", function() - vis:command("x/[ \t]+$|^[ \t]+$/d") + vis:command(",x/[ \t]+$|^[ \t]+$/d") end, "Remove trailing whitespace") ------------------------------------- --- MAPPINGS ------------------------------------- +vis:map(m.NORMAL, "", function() vis:command("fzf") end, "Open file with fzf") + + vis:map(m.NORMAL, " r", function() wrap_restore(vis.command, vis, "e $vis_filepath") end, "Reload active file") @@ -107,8 +130,4 @@ vis.events.subscribe(vis.events.WIN_OPEN, function(win) -- luacheck: no unused a "Print variable" ) end - - vis:command_register("pipe", function() - vis:pipe(win.file, nil, "sed 's/.*/- &/'") - end, "pipe test") end) diff --git a/config/essentials/vis/yank-highlight.lua b/config/essentials/vis/yank-highlight.lua new file mode 100644 index 0000000..37a9578 --- /dev/null +++ b/config/essentials/vis/yank-highlight.lua @@ -0,0 +1,37 @@ +require("vis") + +local M = { + style = "reverse", -- Style used for highlighting + duration = 0.2, -- [s] Time to remain highlighted (10 ms precision) +} + +vis.events.subscribe(vis.events.INIT, function() + local yank = vis:action_register("highlighted-yank", function() + vis.win:style_define(vis.win.STYLE_SELECTION, M.style) + vis:redraw() + local tstamp = os.clock() + while os.clock() - tstamp < M.duration do end + vis.win:style_define(vis.win.STYLE_SELECTION, vis.lexers.STYLE_SELECTION) + vis:redraw() + vis:feedkeys("") + end, "Yank operator with highlighting") + vis:map(vis.modes.OPERATOR_PENDING, "y", yank) + vis:map(vis.modes.VISUAL, "y", yank) + vis:map(vis.modes.VISUAL_LINE, "y", yank) + + vis:map(vis.modes.NORMAL, "y", function(keys) + local sel_end_chrs = "$%^{}()wp" + if #keys < 1 or sel_end_chrs:find(keys:sub(-1), 1, true) == nil then + if keys:find("") then + return #keys + end + return -1 + end + vis:feedkeys("") + vis:feedkeys(keys) + vis:feedkeys("y") + return #keys + end) +end) + +return M -- cgit v1.2.3