commit 295932c4d57612863de2c3943d1856247ccfa1d0
parent b19e7c94c42e1faa6a9fd3381e26accc41735927
Author: Tomas Nemec <nemi@skaut.cz>
Date: Mon, 9 Aug 2021 09:01:43 +0200
feat: make it public #2
Diffstat:
11 files changed, 340 insertions(+), 138 deletions(-)
diff --git a/README b/README
@@ -1,91 +1,121 @@
-# Track run
-TrackRun parse stdin of command and cache status to file.
-Status is taken from handler module via `handle` function.
+# Track run [v1.1.0 09.08.2021]
+ TrackRun listen on stdin and for each line it gets status which is written to
+ status_file.
+ Status is taken from handler module via `handle` function.
+
+ ! Lots of thing are explained inside scripts so check them out.
-! Lots of thing are explained inside scripts so check them out.
# Dependencies
-It is written in lua and used/tested on linux. I have no plan to make it work
-for windows. But besides that nothing else is really needed.
-- If you want to trun to better handle process signals you will want to install
- luaposix via loarocks (luarocks install luaposix). So trun can cleanup files on sig{term,kill,int,hup}
+ It is written in lua and used/tested on linux. I have no plan to make it work
+ for windows. But besides that nothing else is really needed.
+ - If you want to `run` to better handle process signals you will want to
+ install luaposix via loarocks (luarocks install luaposix). So `trun` can
+ cleanup files on sig{term,kill,int,hup}
+
-And for some specific task you would need other tools:
-- For sending command to neovim instances you will need neovim-remote
- (https://github.com/mhinz/neovim-remote).
+# Project structure
+ Inside project you will find number of different files.
+ - helper_functions.lua - useful functions for handler usage. (send command
+ to neovim instances and more)
+ - examples/ - handler examples
+ - handlers/ - real life handlers i use. (contact me to add more)
+ - TODO
+ - tools/ - other tools that uses `trun` to make life easier
+ - print_status.lua: i use it for show semaphor of cmd (green,orange,red)
+ - trun_to_neovim_quickfix.lua: plugin to fill qf-list from cmd
-# Folders
-Inside project you will find number of different files.
-- examples/ - handler examples
-- handlers/ - real life handlers i use. (contact me to add more)
- - TODO
-- tools/ - other tools that uses trun to make life easier
- - print_status.lua: i use it for show semaphor of cmd (green,orange,red)
- - trun_to_neovim_quickfix.lua: plugin to fill qf-list from cmd
# Install
-You can install trun with:
-make install
+ You can install `trun` with:
+ make install
+
+ Also uninstall via `make uninstall`.
+ But you can install it manually. Just take a look at Makefile and see what it
+ does... It is straightforward.
-Also uninstall via `make uninstall`. But take a look at Makefile and see what
-it does... It is straightforward.
+ Code inside other files are made for you to copy and modify as you need.
# Usage
-<cmd> 1> >(trun <handler> <name>)
+ <cmd> >&1 > >(trun <handler> <name>)
+ NOTE: this cmd is for zsh.
-_explanation_
-`1>` - copy output of cmd to stdout
-`>(...)` - copy output of cmd to stdin of subprocess
-So if you don't need output in terminal you can simply `|` (pipe) to trun
+ _explanation_
+ `>&1` - copy output of cmd to terminal
+ `> >(...)` - copy output of cmd to stdin of subprocess
+ So if you don't need output in terminal you can simply `|`(pipe) to `trun`
# Handler
-Handler is module that implements specific functions to catch some key
-moment of tracking. Every handler must implement 'handle' function that
-returns status for that line. It takes two arguments:
-1. `userdata` is table for storing data between hooks
-2. `line` is current line to get status from.
+ Handler is module that implements specific functions to catch some key
+ moment of tracking:
+ There are these hook functions you can implement:
+ - handle(userdata, line): returns status for current line
+ - (optional) on_start(userdata): before first line is passed to handle function
+ - (optional) on_update(userdata, line, status): when status change
+ - (optional) on_end(userdata): when script end
-Returned status can be anything you want to store. Everything is just written
-to status_file
-
-Handlers must have `lua` ext. and be inside specific folder. viz->#Config
+ Every handler must implement 'handle' function, others are optional.
+ Handlers must have `lua` ext. and be inside specific folder. viz->#Config
## Handle function
-handle function can return either table or string. When table is returned,
-every item will end up on new line in status_file so it can be easily used
-by other scripts.
-
-## Hooks
-There are these hook functions you can implement:
-- on_start(userdata): before first line is passed to handle function
-- on_update(userdata, line, status): when status change
-- on_end(userdata): when script end
+ Returned status can be anything you want to store. Everything is just written
+ to status_file. Handle function can return either table or string. When table
+ is returned, every item inside table will end up on new line in status_file so
+ it can be easily used by other scripts.
## Userdata
-userdata has pre filled `name` key with value of trun name
+ userdata has pre filled `name` key with value of `trun` name
+
# Config
-There are 2 ENV variables you may want to set:
-- TRUN_HANDLERS_DIR: directory where handlers are stored (default to $XDG_CONFIG_HOME/trun)
-- TRUN_STATUS_DIR: directory to store files with statuses (default to $XDG_CACHE_HOME/trun)
+ There are 2 ENV variables you may want to set:
+ - TRUN_HANDLERS_DIR: directory where handlers are stored
+ (defaults to $XDG_CONFIG_HOME/trun)
+ - TRUN_STATUS_DIR: directory to store files with statuses
+ (defaults to $XDG_CACHE_HOME/trun)
+
+
+# Usage steps
+1. create handler
+ inside $TRUN_HANDLERS_DIR add `your_handler.lua` file.
+ viz -> examples
+
+2. Run cmd and pipe output to trun like `<cmd> | trun your_handler my_app`.
+
+ For output also in terminal: `<cmd> >&1 > >(trun your_handler my_app)`
+ NOTE: this is for zsh!
+
+3. status file
+ Inside $TRUN_STATUS_DIR is created `my_app.your_handler` file with current
+ status in it.
+
+
+# Test
+ Inside test/ folder is playground. You can edit handler, modify gen.lua for
+ your specific output and run `run.zsh` to see behaviour.
+
+# FAQ
+
+ 1. What is different between storing status in `status_file` or in `userdata`
+
+ Data inside status_file you can use outside of handler
+ (viz->tools/print_status.lua). And `userdata` is passed to each hook
+ function.
+
+ 2. I want to contribute.
+ Sure thing! Send me an email with suggestion on feature or better with git
+ patch and we can discuss. viz->#Contact
-# Example
-1. angular handler
-inside $TRUN_HANDLERS_DIR/angular.lua you have module for angular serving
-output. Inside it you have simple handling just for status if build is
-running, succeeded or has error.
-viz -> examples/handler_simple.lua for way to run neovim command
+ 3. Why your English so bad?
-2. You can run angular serving cmd and pipe output to trun like `<cmd> | trun angular my_app`.
+ Sorry for that, i am trying my best.
-3. angular status file
-Inside $TRUN_STATUS_DIR is created `my_app.angular` with current status in it.
# Contact - Bugs, Suggestions, Questions...
-Feel free to contact me via mail on: <nemi@skaut.cz>
+ Feel free to contact me via mail on: <nemi@skaut.cz>
diff --git a/examples/handler_lastline.lua b/examples/handler_lastline.lua
@@ -0,0 +1,18 @@
+-- Trun handler to store last line of build.
+--
+-- interface
+-- - handle(userdata, line): return status
+-- - (optional) on_start(userdata): call once before start reading
+-- - (optional) on_update(userdata, line, status): calls every time when status changed
+-- - (optional) on_end(userdata): calls once after reading end
+--
+return {
+ -- handle(userdata, line): return status
+ handle = function(_, line) return line end,
+
+ -- on_update(userdata, line, status): calls every time when status changed
+ on_update = function(_, _, status)
+ -- status contain last line of command output
+ -- For example we can parse progress if cmd is providing it.
+ end,
+}
diff --git a/examples/handler_simple.lua b/examples/handler_simple.lua
@@ -1,4 +1,4 @@
---- Trun simple handler for just status of command
+-- Trun simple handler
--
-- interface
-- - handle(userdata, line): return status
@@ -6,40 +6,24 @@
-- - (optional) on_update(userdata, line, status): calls every time when status changed
-- - (optional) on_end(userdata): calls once after reading end
--
-local status_map = {['running'] = 0, ['success'] = 1, ['error'] = -1}
-
--- send nvim_cmd to all neovim instances
-local nvim_cmd = function(nvim_cmd)
- local servers = io.popen('nvr --serverlist')
- for socket in servers:lines() do
- local cmd = string.format('nvr --servername "%s" -cc "%s" --nostart -s &', socket, nvim_cmd)
- print(cmd)
- os.execute(cmd)
- end
- servers:close()
-end
-
return {
-- handle(userdata, line): return status
handle = function(_, line)
- if line:find('SUCCESS') then
- return status_map.success
- elseif line:find('RUNNING') then
- return status_map.running
+ if line:find('success') then
+ return 1
+ elseif line:find('running') then
+ return 0
else
- return status_map.error
+ return -1
end
end,
-- on_update(userdata, line, status): calls every time when status changed
- on_update = function(data, _, status)
-
- -- you can send linux notifications
- os.execute(string.format('notify-send "trun status changed for %s" "%s"', data.name, status))
+ on_update = function(_, _, status)
+ if status == 1 or status == -1 then
+ os.execute('notify-send "build finished"')
+ end
- -- or you can run neovim command to trigger neotification.
- -- You need neovim-remote (https://github.com/mhinz/neovim-remote) to make it work
- nvim_cmd(string.format('lua require(\'notify\')(\'%s\', \'%s\')', data.name, 'error'))
- -- make sure to use single quotes so vim can properly handle command.
+ -- viz->examples/handler_functions.lua for examples functions to use.
end,
}
diff --git a/examples/handler_temp_output_file.lua b/examples/handler_temp_output_file.lua
@@ -1,27 +1,22 @@
--- Trun advanced handler for *track status* and *cache* last output betwen status
--- changes in tmp_file. It saves path to that file in status_file on second line.
+-- Trun advanced handler _caches_ output betwen status changes to tmp_file.
--
--- So for exmple, on error you can load this output to quickfix list
--- inside vim.
--- viz->tools/trun_to_neovim_quickfix.lua and examples/handler_simple.lua for
--- examples of neovim-remote (to notify inside neovim or you can run Trunqf to
--- load quickfix list on fly)
+-- So for exmple, on error you fill quickfix list inside vim.
+--
+-- viz->tools/trun_to_neovim_quickfix.lua and helper_functions.lua
+-- for examples to send command to neovim instances.
--
-- interface
-- - handle(userdata, line): return status
--- - (optional) on_start(userdata): call once before start reading
--- - (optional) on_update(userdata, line, status): calls every time when status changed
--- - (optional) on_end(userdata): calls once after reading end
+-- - (optional) on_start(userdata): before start reading
+-- - (optional) on_update(userdata, line, status): when status changed
+-- - (optional) on_end(userdata): after reading end
--
local status_map = {['running'] = 0, ['success'] = 1, ['error'] = -1}
-local notify =
- function(status) os.execute('notify-send "trun status changed" "' .. status .. '"') end
-
local function get_status(line)
- if line:find('SUCCESS') then
+ if line:find('success') then
return status_map.success
- elseif line:find('RUNNING') then
+ elseif line:find('running') then
return status_map.running
else
return status_map.error
@@ -30,9 +25,9 @@ end
return {
- -- on_start(userdata): call once before start reading
+ -- on_start(userdata): before start reading
on_start = function(data)
- -- re-create tmp_file and save it to data table.
+ -- re-create tmp_file and save it to userdata table.
data.tmpfile = '/tmp/' .. data.name .. '.trun'
os.execute('rm ' .. data.tmpfile .. ' 2>/dev/null')
end,
@@ -44,22 +39,21 @@ return {
tmpfile:write(line, '\n')
tmpfile:close()
return {get_status(line), data.tmpfile}
+ -- if you send table, each item is added on new line to tmpfile
end,
- -- on_update(userdata, line, status): calls every time when status changed
+ -- on_update(userdata, line, status): time when status changed
on_update = function(_, status, data)
-- on success, clear tmp_file.
+ -- Here status is the same table as it was given inside `handle` function.
if status[1] == status_map.success then
io.open(data.tmpfile, 'w'):close()
end
- notify()
-
-- viz->examples/handler_simple.lua for other examples wit neovim-remote
end,
- -- on_end(userdata): calls once after reading end
+ -- on_end(userdata): after reading end
on_end = function(data)
- notify()
-- remove tmp_file
os.execute('rm ' .. data.tmpfile)
end,
diff --git a/handlers/angulardart.lua b/handlers/angulardart.lua
@@ -0,0 +1,86 @@
+--- Trun module handler for `pub run build_runner serve`
+-- NOTE: This does not work for `webdev server`
+--
+-- Status number i use for semaphor in WM status bar.
+-- viz->tools/print_status.lua
+--
+-- Last output is inside tmpfile so i can load errors to quickfix list
+-- inside vim.
+-- viz->examples/handler_temp_output_file.lua -> for caching last output
+-- viz->tools/trun_to_neovim_quickfix.lua -> for load qf-list
+--
+--
+local s_map = {['running'] = 0, ['success'] = 1, ['severe'] = -1}
+
+local notify = function()
+ -- Notify system, neovim, etc...
+end
+
+-- Output types:
+-- [SEVERE] Failed after 11.4s
+-- [INFO] Succeeded after 9.8s with 304 outputs (8 actions)
+--
+-- Sometimes build is not ending with these lines. There may be couple of empty
+-- lines after.
+
+local succeeded = function(line)
+ if line:match('%[INFO%] Succeeded') then
+ return true
+ elseif line:match('Serving') then
+ return true
+ end
+end
+
+local last_status
+local get_status = function(line)
+ if last_status and #line == 0 then
+ return last_status
+ end
+ if (succeeded(line)) then
+ return s_map.success
+ end
+
+ local line_type = line:match('^%[(.*)%] .*');
+ if not line_type then
+ return s_map.running
+ end
+
+ if (line_type == 'SEVERE') then
+ return s_map.severe
+ end
+
+ return s_map.running
+end
+
+return {
+
+ -- on_start(userdata): before start reading
+ on_start = function(data)
+ data.tmpfile = '/tmp/' .. data.name .. '.trun'
+ os.execute('rm -f ' .. data.tmpfile .. ' 2>/dev/null')
+ end,
+
+ -- handle(userdata, line): return status
+ handle = function(data, line)
+ local tmpfile = io.open(data.tmpfile, 'a')
+ tmpfile:write(line, '\n')
+ tmpfile:close()
+ local status = get_status(line)
+ last_status = status
+ return {status, data.tmpfile}
+ end,
+
+ -- on_update(userdata, line, status): when status changed
+ on_update = function(data, _, status)
+ if status[1] == s_map.success then
+ io.open(data.tmpfile, 'w'):close()
+ end
+ notify()
+ end,
+
+ -- on_end(userdata): after reading end
+ on_end = function(data)
+ notify()
+ os.execute('rm -f ' .. data.tmpfile)
+ end,
+}
diff --git a/helper_functions.lua b/helper_functions.lua
@@ -0,0 +1,33 @@
+#!/usr/bin/env lua
+
+-- This is list of functions you can use inside handler
+--
+-- Run any os command (add `&` for non blocking execution)
+os.execute('... &')
+
+-- Send command to all neovim instances
+local nvim_cmd = function(nvim_cmd)
+ local servers = io.popen('nvr --serverlist')
+ for socket in servers:lines() do
+ local cmd = string.format('nvr --servername "%s" -cc "%s" --nostart -s &',
+ socket, nvim_cmd)
+ os.execute(cmd)
+ end
+ servers:close()
+end
+-- TODO: send command to only specific neovim instances.
+-- NOTE: to send command to only specific neovim instance, you can create
+-- custom function inside neovim that will determine this. So for example if
+-- you are inside right directory. `vim.fn.getcwd()`
+
+-- Send notification (in this case: https://github.com/rcarriga/nvim-notify) to
+-- neovim instances. Use with `nvim_cmd`.
+local nvim_notify = function(msg, title, level)
+ local cmd = string.format(
+ 'lua require(\'notify\')(\'%s\', \'%s\', {title=\'%s\'})', msg,
+ level, title)
+ nvim_cmd(cmd)
+end
+
+-- TODO: Add more...
+
diff --git a/test/gen.lua b/test/gen.lua
@@ -0,0 +1,24 @@
+#!/usr/bin/env lua
+-- simple script that output lines every second. Run run.zsh script to test
+-- handler.lua and watch status file being updated
+--
+local sleep = function(s) os.execute('sleep ' .. s) end
+
+local success = 'success'
+local normal = 'normal'
+local error = 'error'
+
+local out
+local leaps = 10
+while leaps > 0 do
+ if (leaps == 5) then
+ out = error
+ elseif (leaps == 1) then
+ out = success
+ else
+ out = normal
+ end
+ print(out)
+ sleep(1)
+ leaps = leaps - 1
+end
diff --git a/test/handler.lua b/test/handler.lua
@@ -0,0 +1,44 @@
+--- Trun test handler
+--
+-- interface
+-- - handle(userdata, line): return status
+-- - (optional) on_start(userdata): call once before start reading
+-- - (optional) on_update(userdata, line, status): calls every time when status changed
+-- - (optional) on_end(userdata): calls once after reading end
+--
+local status_map = {['running'] = 0, ['success'] = 1, ['error'] = -1}
+
+-- send nvim_cmd to all neovim instances
+local nvim_cmd = function(nvim_cmd)
+ local servers = io.popen('nvr --serverlist')
+ for socket in servers:lines() do
+ local cmd = string.format('nvr --servername "%s" -cc "%s" --nostart -s &',
+ socket, nvim_cmd)
+ os.execute(cmd)
+ end
+ servers:close()
+end
+
+return {
+ -- handle(userdata, line): return status
+ handle = function(_, line)
+ if line:find('success') then
+ return status_map.success
+ elseif line:find('normal') then
+ return status_map.running
+ else
+ return status_map.error
+ end
+ end,
+
+ -- on_update(userdata, line, status): calls every time when status changed
+ on_update = function(data, _, status)
+ if status == status_map.success or status == status_map.error then
+ local type_map = {[1] = 'info', [-1] = 'error'}
+ local cmd = string.format(
+ 'lua require(\'notify\')(\'%s\', \'%s\', {title=\'%s\'})',
+ data.name, type_map[status], 'Build')
+ nvim_cmd(cmd)
+ end
+ end,
+}
diff --git a/test/run.zsh b/test/run.zsh
@@ -0,0 +1,7 @@
+#!/usr/bin/env zsh
+
+# Set handler dir and status dir to this test dir.
+env \
+ TRUN_HANDLERS_DIR=@/ \
+ TRUN_STATUS_DIR=@/ \
+ ./gen.lua >&1 > >(../trun.lua handler test)
diff --git a/tools/print_status.lua b/tools/print_status.lua
@@ -1,6 +1,6 @@
#!/usr/bin/env lua
--- Usage: trun_status.lua [-] [<name>]
+-- Usage: trun_status.lua [<name>]
--
-- Return formatted string for all truns to use inside status bar of your WM.
-- It depends on status codes to be:
@@ -12,31 +12,21 @@
--
-- example output: [%{F#ff0000}NAME%{F-} %{F#00ff00}OTHERNAME%{F-}]
--
--- To ignore formatting add `-`(single dash) argument. Mostly for debugging.
--- - It will just print output of status_file
---
-- To get only single trun, add `name` as argument.
--
--- It does not print any errors. (TODO)
---
-- Config
-local trun_status = os.getenv('TRUN_STATUS_DIR') or os.getenv('XDG_CONFIG_HOME') .. '/trun'
+local status_dir_def = os.getenv('XDG_CONFIG_HOME') .. '/trun'
+local status_dir = os.getenv('TRUN_STATUS_DIR') or status_dir_def
-- map status to foreground colors.
local status_map = {[0] = 'ffa500', [1] = '00ff00', [-1] = 'ff0000'}
---
-- silent fail if dir not exists
-local status_dir = trun_status
if not io.open(status_dir) then
return ''
end
local trun_name = arg[1]
-local raw -- single dash argument means - no formatting, just print
-if arg[1] == '-' then
- raw = true
- trun_name = arg[2]
-end
-- get all status files
local status_files = {}
@@ -51,22 +41,18 @@ local format = function(file, name)
if not file then
table.insert(output, '')
else
- if raw then
- local status = file:read('*a')
- output = status
- else
- local status = file:read('*n')
- -- Edit this to your liking output
- output = '%{F#' .. status_map[tonumber(status)] .. '} ' .. name:upper() .. ' %{F-}'
- file:close()
- end
+ local status = file:read('*n') -- 'n' means read a number
+ local color = status_map[tonumber(status)]
+ -- Edit this to your liking
+ output = string.format('%%{F#%s} %s %%{F-}', color, name:upper())
+ file:close()
end
return output
end
local result = {}
-- For every file fill out results
-for _, status_file_name in pairs(status_files) do
+for _, status_file_name in ipairs(status_files) do
local name, _ = status_file_name:match('(.*)%.(.*)')
if name then
local status_file_path = status_dir .. '/' .. status_file_name
@@ -83,9 +69,5 @@ end
-- print out results
if #result > 0 then
- if raw then
- print(table.concat(result, '\n'))
- else
- print('[' .. table.concat(result, ',') .. ']')
- end
+ print('[' .. table.concat(result, ',') .. ']')
end
diff --git a/trun.lua b/trun.lua
@@ -1,6 +1,6 @@
#!/usr/bin/env lua
--- Version: 1.0.0 ( 08.08.2021 )
+-- Version: 1.1.0 ( 09.08.2021 )
--
-- Usage
-- <cmd> 1> >(trun <handler> <name>)