commit fb76c9b46219e45ec4197a4454ff223da275aed4
parent a3000d8857f2f617f1cce7b89d55cfaeaf7bbff2
Author: Anders Damsgaard <anders@adamsgaard.dk>
Date: Wed, 19 Jun 2019 09:13:04 +0200
Add ÆØÅ snippets and ctags plugin
Diffstat:
3 files changed, 282 insertions(+), 1 deletion(-)
diff --git a/.config/vis/plugins/ctags.lua b/.config/vis/plugins/ctags.lua
@@ -0,0 +1,271 @@
+require('vis')
+
+local positions = {}
+
+local function get_path(prefix, path)
+ if string.find(path, '^./') ~= nil then
+ path = path:sub(3)
+ end
+
+ return prefix .. path, path
+end
+
+local function find_tags(path)
+ for i = #path, 1, -1 do
+ if path:sub(i, i) == '/' then
+ local prefix = path:sub(1, i)
+ local filename = prefix .. 'tags'
+ local file = io.open(filename, 'r')
+
+ if file ~= nil then
+ return file, prefix
+ end
+ end
+ end
+end
+
+local function bsearch(file, word)
+ local buffer_size = 8096
+ local format = '\n(.-)\t(.-)\t(.-);\"\t'
+
+ local from = 0
+ local to = file:seek('end')
+ local startpos = nil
+
+ while from <= to do
+ local mid = from + math.floor((to - from) / 2)
+ file:seek('set', mid)
+
+ local content = file:read(buffer_size, '*line')
+ if content ~= nil then
+ local key, filename, excmd = string.match(content, format)
+ if key == nil then
+ break
+ end
+
+ if key == word then
+ startpos = mid
+ end
+
+ if key >= word then
+ to = mid - 1
+ else
+ from = mid + 1
+ end
+ else
+ to = mid - 1
+ end
+ end
+
+ if startpos ~= nil then
+ file:seek('set', startpos)
+
+ local result = {}
+ while true do
+ local content = file:read(buffer_size, '*line')
+ if content == nil then
+ break
+ end
+
+ for key, filename, excmd in string.gmatch(content, format) do
+ if key == word then
+ result[#result + 1] = {name = filename, excmd = excmd}
+ else
+ return result
+ end
+ end
+ end
+
+ return result
+ end
+end
+
+local function get_query()
+ local line = vis.win.selection.line
+ local pos = vis.win.selection.col
+ local str = vis.win.file.lines[line]
+
+ local from, to = 0, 0
+ while pos > to do
+ from, to = str:find('[%a_]+[%a%d_]*', to + 1)
+ if from == nil or from > pos then
+ return nil
+ end
+ end
+
+ return string.sub(str, from, to)
+end
+
+local function get_matches(word, path)
+ local file, prefix = find_tags(path)
+
+ if file ~= nil then
+ local results = bsearch(file, word)
+ file:close()
+
+ if results ~= nil then
+ local matches = {}
+ for i = 1, #results do
+ local result = results[i]
+ local path, name = get_path(prefix, result.name)
+ local desc = string.format('%s%s', name, tonumber(result.excmd) and ":"..result.excmd or "")
+
+ matches[#matches + 1] = {desc = desc, path = path, excmd = result.excmd}
+ end
+
+ return matches
+ end
+ end
+end
+
+local function get_match(word, path)
+ local matches = get_matches(word, path)
+ if matches ~= nil then
+ for i = 1, #matches do
+ if matches[i].path == path then
+ return matches[i]
+ end
+ end
+
+ return matches[1]
+ end
+end
+
+local function escape(text)
+ return text:gsub("[][)(}{|+?*.]", "\\%0")
+ :gsub("%^", "\\^"):gsub("^/\\%^", "/^")
+ :gsub("%$", "\\$"):gsub("\\%$/$", "$/")
+ :gsub("\\\\%$%$/$", "\\$$")
+end
+
+--[[
+- Can't test vis:command() as it will still return true if the edit command fails.
+- Can't test File.modified as the edit command can succeed if the current file is
+ modified but open in another window and this behavior is useful.
+- Instead just check the path again after trying the edit command.
+]]
+local function goto_pos(pos)
+ if pos.path ~= vis.win.file.path then
+ vis:command(string.format('e %s', pos.path))
+ if pos.path ~= vis.win.file.path then
+ return false
+ end
+ end
+ if tonumber(pos.excmd) then
+ vis.win.selection:to(pos.excmd, pos.col)
+ else
+ vis.win.selection:to(1, 1)
+ vis:command(escape(pos.excmd))
+ vis.win.selection.pos = vis.win.selection.range.start
+ vis.mode = vis.modes.NORMAL
+ end
+ return true
+end
+
+local function goto_tag(path, excmd)
+ local old = {
+ path = vis.win.file.path,
+ excmd = vis.win.selection.line,
+ col = vis.win.selection.col,
+ }
+
+ local last_search = vis.registers['/']
+ if goto_pos({ path = path, excmd = excmd, col = 1 }) then
+ positions[#positions + 1] = old
+ vis.registers['/'] = last_search
+ end
+end
+
+local function pop_pos()
+ if #positions < 1 then
+ return
+ end
+ if goto_pos(positions[#positions]) then
+ table.remove(positions, #positions)
+ end
+end
+
+local function get_path()
+ if vis.win.file.path == nil then
+ return os.getenv('PWD') .. '/'
+ end
+ return vis.win.file.path
+end
+
+local function tag_cmd(tag)
+ local match = get_match(tag, get_path())
+ if match == nil then
+ vis:info(string.format('Tag not found: %s', tag))
+ else
+ goto_tag(match.path, match.excmd)
+ end
+end
+
+local function tselect_cmd(tag)
+ local matches = get_matches(tag, get_path())
+ if matches == nil then
+ vis:info(string.format('Tag not found: %s', tag))
+ else
+ local keys = {}
+ for i = 1, #matches do
+ table.insert(keys, matches[i].desc)
+ end
+
+ local command = string.format(
+ [[echo -e "%s" | vis-menu -p "Choose tag:"]], table.concat(keys, [[\n]]))
+
+ local status, output =
+ vis:pipe(vis.win.file, {start = 0, finish = 0}, command)
+
+ if status ~= 0 then
+ vis:info('Command failed')
+ return
+ end
+
+ local choice = string.match(output, '(.*)\n')
+ for i = 1, #matches do
+ local match = matches[i]
+ if match.desc == choice then
+ goto_tag(match.path, match.excmd)
+ break
+ end
+ end
+ end
+end
+
+vis:command_register("tag", function(argv, force, win, selection, range)
+ if #argv == 1 then
+ tag_cmd(argv[1])
+ end
+end)
+
+vis:command_register("tselect", function(argv, force, win, selection, range)
+ if #argv == 1 then
+ tselect_cmd(argv[1])
+ end
+end)
+
+vis:command_register("pop", function(argv, force, win, selection, range)
+ pop_pos()
+end)
+
+vis:map(vis.modes.NORMAL, '<C-]>', function(keys)
+ local query = get_query()
+ if query ~= nil then
+ tag_cmd(query)
+ end
+ return 0
+end)
+
+vis:map(vis.modes.NORMAL, 'g<C-]>', function(keys)
+ local query = get_query()
+ if query ~= nil then
+ tselect_cmd(query)
+ end
+ return 0
+end)
+
+vis:map(vis.modes.NORMAL, '<C-t>', function(keys)
+ pop_pos()
+ return 0
+end)
diff --git a/.config/vis/plugins/snippets.lua b/.config/vis/plugins/snippets.lua
@@ -1,4 +1,3 @@
--- snippets
vis:map(vis.modes.INSERT, '`sig',
'-- \n' ..
'Anders Damsgaard\n' ..
@@ -7,3 +6,10 @@ vis:map(vis.modes.INSERT, '`sig',
'Photography: https://andersdamsgaard.com\n' ..
'PGP public key: https://adamsgaard.dk/ad-public-key.txt\n' ..
'PGP fingerprint: 5C95 9DF2 43CE 4DD1 7A5B 2610 B790 F4AD 1BF8 58FE')
+
+vis:map(vis.modes.INSERT, '`ae', 'æ')
+vis:map(vis.modes.INSERT, '`AE', 'Æ')
+vis:map(vis.modes.INSERT, '`oe', 'ø')
+vis:map(vis.modes.INSERT, '`OE', 'Ø')
+vis:map(vis.modes.INSERT, '`aa', 'å')
+vis:map(vis.modes.INSERT, '`AA', 'Å')
diff --git a/.config/vis/visrc.lua b/.config/vis/visrc.lua
@@ -3,6 +3,7 @@ require('plugins/myfiletype')
require('plugins/vis-commentary')
require('plugins/fzf-mru')
require('plugins/snippets')
+require('plugins/ctags')
leader = '<Space>'
@@ -62,11 +63,13 @@ vis.events.subscribe(vis.events.INIT, function()
vis:map(vis.modes.NORMAL, leader..'B', ':e "$BIB"<Enter>')
vis:map(vis.modes.NORMAL, leader..'C', ':e ~/.config/vis/visrc.lua<Enter>')
+ vis:map(vis.modes.NORMAL, leader..'d', ':< date')
vis:map(vis.modes.NORMAL, leader..'e', ':fzf true<Enter>')
vis:map(vis.modes.NORMAL, leader..'g', ':rg true<Enter>')
vis:map(vis.modes.NORMAL, leader..'G', ':rg<Enter>')
vis:map(vis.modes.NORMAL, leader..'l', ':set show-newline!<Enter>')
vis:map(vis.modes.NORMAL, leader..'m', ':!make<Enter>')
+ -- vis:map(vis.modes.NORMAL, leader..'m', ':!tmux split-window -p 25 "make"',
vis:map(vis.modes.NORMAL, leader..'n', ':set numbers!<Enter>')
vis:map(vis.modes.NORMAL, leader..'N', ':set relativenumbers!<Enter>')
vis:map(vis.modes.NORMAL, leader..'o', ':fzf<Enter>')
@@ -76,6 +79,7 @@ vis.events.subscribe(vis.events.INIT, function()
vis:map(vis.modes.NORMAL, leader..'T', ':e ~/doc/todo.md<Enter>')
vis:map(vis.modes.NORMAL, leader..'w', ':w<Enter>')
vis:map(vis.modes.NORMAL, leader..'x', ':wq<Enter>')
+ vis:map(vis.modes.VISUAL, leader..'X', '')
vis:map(vis.modes.VISUAL, leader..'y', ':> xclip<Enter>')
vis:map(vis.modes.NORMAL, '<F7>', ':!sent $vis_filepath &<Enter>')