From f7636967c54153d10e6e5524a29cf4405fd2e6e7 Mon Sep 17 00:00:00 2001 From: "Kartik K. Agaram" Date: Sat, 23 Apr 2022 00:31:47 -0700 Subject: smol.tlv: start of a browser for the small web --- smol.tlv | 1581 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1581 insertions(+) create mode 100644 smol.tlv diff --git a/smol.tlv b/smol.tlv new file mode 100644 index 0000000..ae0107b --- /dev/null +++ b/smol.tlv @@ -0,0 +1,1581 @@ +# .tlv file generated by https://github.com/akkartik/teliva +# You may edit it if you are careful; however, you may see cryptic errors if you +# violate Teliva's assumptions. +# +# .tlv files are representations of Teliva programs. Teliva programs consist of +# sequences of definitions. Each definition is a table of key/value pairs. Keys +# and values are both strings. +# +# Lines in .tlv files always follow exactly one of the following forms: +# - comment lines at the top of the file starting with '#' at column 0 +# - beginnings of definitions starting with '- ' at column 0, followed by a +# key/value pair +# - key/value pairs consisting of ' ' at column 0, containing either a +# spaceless value on the same line, or a multi-line value +# - multiline values indented by more than 2 spaces, starting with a '>' +# +# If these constraints are violated, Teliva may unceremoniously crash. Please +# report bugs at http://akkartik.name/contact +- __teliva_timestamp: original + str_helpers: + >-- some string helpers from http://lua-users.org/wiki/StringIndexing + > + >-- index characters using [] + >getmetatable('').__index = function(str,i) + > if type(i) == 'number' then + > return str:sub(i,i) + > else + > return string[i] + > end + >end + > + >-- ranges using (), selected bytes using {} + >getmetatable('').__call = function(str,i,j) + > if type(i)~='table' then + > return str:sub(i,j) + > else + > local t={} + > for k,v in ipairs(i) do + > t[k]=str:sub(v,v) + > end + > return table.concat(t) + > end + >end + > + >-- iterate over an ordered sequence + >function q(x) + > if type(x) == 'string' then + > return x:gmatch('.') + > else + > return ipairs(x) + > end + >end + > + >-- insert within string + >function string.insert(str1, str2, pos) + > return str1:sub(1,pos)..str2..str1:sub(pos+1) + >end + > + >function string.remove(s, pos) + > return s:sub(1,pos-1)..s:sub(pos+1) + >end + > + >function string.pos(s, sub) + > return string.find(s, sub, 1, true) -- plain=true to disable regular expressions + >end + > + >-- TODO: backport utf-8 support from Lua 5.3 +- __teliva_timestamp: original + debugy: + >debugy = 5 +- __teliva_timestamp: original + dbg: + >-- helper for debug by print; overlay debug information towards the right + >-- reset debugy every time you refresh screen + >function dbg(window, s) + > local oldy = 0 + > local oldx = 0 + > oldy, oldx = window:getyx() + > window:mvaddstr(debugy, 60, s) + > debugy = debugy+1 + > window:mvaddstr(oldy, oldx, '') + >end +- __teliva_timestamp: original + check: + >function check(x, msg) + > if x then + > Window:addch('.') + > else + > print('F - '..msg) + > print(' '..str(x)..' is false/nil') + > teliva_num_test_failures = teliva_num_test_failures + 1 + > -- overlay first test failure on editors + > if teliva_first_failure == nil then + > teliva_first_failure = msg + > end + > end + >end +- __teliva_timestamp: original + check_eq: + >function check_eq(x, expected, msg) + > if eq(x, expected) then + > Window:addch('.') + > else + > print('F - '..msg) + > print(' expected '..str(expected)..' but got '..str(x)) + > teliva_num_test_failures = teliva_num_test_failures + 1 + > -- overlay first test failure on editors + > if teliva_first_failure == nil then + > teliva_first_failure = msg + > end + > end + >end +- __teliva_timestamp: original + eq: + >function eq(a, b) + > if type(a) ~= type(b) then return false end + > if type(a) == 'table' then + > if #a ~= #b then return false end + > for k, v in pairs(a) do + > if b[k] ~= v then + > return false + > end + > end + > for k, v in pairs(b) do + > if a[k] ~= v then + > return false + > end + > end + > return true + > end + > return a == b + >end +- __teliva_timestamp: original + str: + >-- smarter tostring + >-- slow; used only for debugging + >function str(x) + > if type(x) == 'table' then + > local result = '' + > result = result..#x..'{' + > for k, v in pairs(x) do + > result = result..str(k)..'='..str(v)..', ' + > end + > result = result..'}' + > return result + > elseif type(x) == 'string' then + > return '"'..x..'"' + > end + > return tostring(x) + >end +- __teliva_timestamp: original + find_index: + >function find_index(arr, x) + > for n, y in ipairs(arr) do + > if x == y then + > return n + > end + > end + >end +- __teliva_timestamp: original + trim: + >function trim(s) + > return s:gsub('^%s*', ''):gsub('%s*$', '') + >end +- __teliva_timestamp: original + split: + >function split(s, d) + > result = {} + > for match in (s..d):gmatch("(.-)"..d) do + > table.insert(result, match); + > end + > return result + >end +- __teliva_timestamp: original + map: + >-- only for arrays + >function map(l, f) + > result = {} + > for _, x in ipairs(l) do + > table.insert(result, f(x)) + > end + > return result + >end +- __teliva_timestamp: original + reduce: + >-- only for arrays + >function reduce(l, f, init) + > result = init + > for _, x in ipairs(l) do + > result = f(result, x) + > end + > return result + >end +- __teliva_timestamp: original + filter: + >function filter(h, f) + > result = {} + > for k, v in pairs(h) do + > if f(k, v) then + > result[k] = v + > end + > end + > return result + >end +- __teliva_timestamp: original + ifilter: + >-- only for arrays + >function ifilter(l, f) + > result = {} + > for _, x in ipairs(l) do + > if f(x) then + > table.insert(result, x) + > end + > end + > return result + >end +- __teliva_timestamp: original + sort_letters: + >function sort_letters(s) + > tmp = {} + > for i=1,#s do + > table.insert(tmp, s[i]) + > end + > table.sort(tmp) + > local result = '' + > for _, c in pairs(tmp) do + > result = result..c + > end + > return result + >end + > + >function test_sort_letters(s) + > check_eq(sort_letters(''), '', 'test_sort_letters: empty') + > check_eq(sort_letters('ba'), 'ab', 'test_sort_letters: non-empty') + > check_eq(sort_letters('abba'), 'aabb', 'test_sort_letters: duplicates') + >end +- __teliva_timestamp: original + count_letters: + >-- TODO: handle unicode + >function count_letters(s) + > local result = {} + > for i=1,s:len() do + > local c = s[i] + > if result[c] == nil then + > result[c] = 1 + > else + > result[c] = result[c] + 1 + > end + > end + > return result + >end +- __teliva_timestamp: original + count: + >-- turn an array of elements into a map from elements to their frequency + >-- analogous to count_letters for non-strings + >function count(a) + > local result = {} + > for i, v in ipairs(a) do + > if result[v] == nil then + > result[v] = 1 + > else + > result[v] = result[v] + 1 + > end + > end + > return result + >end +- __teliva_timestamp: original + union: + >function union(a, b) + > for k, v in pairs(b) do + > a[k] = v + > end + > return a + >end +- __teliva_timestamp: original + subtract: + >-- set subtraction + >function subtract(a, b) + > for k, v in pairs(b) do + > a[k] = nil + > end + > return a + >end +- __teliva_timestamp: original + all: + >-- universal quantifier on sets + >function all(s, f) + > for k, v in pairs(s) do + > if not f(k, v) then + > return false + > end + > end + > return true + >end +- __teliva_timestamp: original + to_array: + >-- turn a set into an array + >-- drops values + >function to_array(h) + > local result = {} + > for k, _ in pairs(h) do + > table.insert(result, k) + > end + > return result + >end +- __teliva_timestamp: original + append: + >-- concatenate list 'elems' into 'l', modifying 'l' in the process + >function append(l, elems) + > for i=1,#elems do + > table.insert(l, elems[i]) + > end + >end +- __teliva_timestamp: original + prepend: + >-- concatenate list 'elems' into the start of 'l', modifying 'l' in the process + >function prepend(l, elems) + > for i=1,#elems do + > table.insert(l, i, elems[i]) + > end + >end +- __teliva_timestamp: original + all_but: + >function all_but(x, idx) + > if type(x) == 'table' then + > local result = {} + > for i, elem in ipairs(x) do + > if i ~= idx then + > table.insert(result,elem) + > end + > end + > return result + > elseif type(x) == 'string' then + > if idx < 1 then return x:sub(1) end + > return x:sub(1, idx-1) .. x:sub(idx+1) + > else + > error('all_but: unsupported type '..type(x)) + > end + >end + > + >function test_all_but() + > check_eq(all_but('', 0), '', 'all_but: empty') + > check_eq(all_but('abc', 0), 'abc', 'all_but: invalid low index') + > check_eq(all_but('abc', 4), 'abc', 'all_but: invalid high index') + > check_eq(all_but('abc', 1), 'bc', 'all_but: first index') + > check_eq(all_but('abc', 3), 'ab', 'all_but: final index') + > check_eq(all_but('abc', 2), 'ac', 'all_but: middle index') + >end +- __teliva_timestamp: original + set: + >function set(l) + > local result = {} + > for i, elem in ipairs(l) do + > result[elem] = true + > end + > return result + >end +- __teliva_timestamp: original + set_eq: + >function set_eq(l1, l2) + > return eq(set(l1), set(l2)) + >end + > + >function test_set_eq() + > check(set_eq({1}, {1}), 'set_eq: identical') + > check(not set_eq({1, 2}, {1, 3}), 'set_eq: different') + > check(set_eq({1, 2}, {2, 1}), 'set_eq: order') + > check(set_eq({1, 2, 2}, {2, 1}), 'set_eq: duplicates') + >end +- __teliva_timestamp: original + clear: + >function clear(lines) + > while #lines > 0 do + > table.remove(lines) + > end + >end +- __teliva_timestamp: original + zap: + >function zap(target, src) + > clear(target) + > append(target, src) + >end +- __teliva_timestamp: original + mfactorial: + >-- memoized version of factorial + >-- doesn't memoize recursive calls, but may be good enough + >mfactorial = memo1(factorial) +- __teliva_timestamp: original + factorial: + >function factorial(n) + > local result = 1 + > for i=1,n do + > result = result*i + > end + > return result + >end +- __teliva_timestamp: original + memo1: + >-- a higher-order function that takes a function of a single arg + >-- (that never returns nil) + >-- and returns a memoized version of it + >function memo1(f) + > local memo = {} + > return function(x) + > if memo[x] == nil then + > memo[x] = f(x) + > end + > return memo[x] + > end + >end + > + >-- mfactorial doesn't seem noticeably faster + >function test_memo1() + > for i=0,30 do + > check_eq(mfactorial(i), factorial(i), 'memo1 over factorial: '..str(i)) + > end + >end +- __teliva_timestamp: original + num_permutations: + >-- number of permutations of n distinct objects, taken r at a time + >function num_permutations(n, r) + > return factorial(n)/factorial(n-r) + >end + > + >-- mfactorial doesn't seem noticeably faster + >function test_memo1() + > for i=0,30 do + > for j=0,i do + > check_eq(num_permutations(i, j), mfactorial(i)/mfactorial(i-j), 'num_permutations memoizes: '..str(i)..'P'..str(j)) + > end + > end + >end +- __teliva_timestamp: original + menu: + >-- To show app-specific hotkeys in the menu bar, add hotkey/command + >-- arrays of strings to the menu array. + >menu = {} +- __teliva_timestamp: original + Window: + >Window = curses.stdscr() +- __teliva_timestamp: original + window: + >-- constructor for fake screen and window + >-- call it like this: + >-- local w = window{ + >-- kbd=kbd('abc'), + >-- scr=scr{h=5, w=4}, + >-- } + >-- eventually it'll do everything a real ncurses window can + >function window(h) + > h.__index = h + > setmetatable(h, h) + > h.__index = function(table, key) + > return rawget(h, key) + > end + > h.attrset = function(self, x) + > self.scr.attrs = x + > end + > h.attron = function(self, x) + > -- currently same as attrset since Lua 5.1 doesn't have bitwise operators + > -- doesn't support multiple attrs at once + >-- local old = self.scr.attrs + >-- self.scr.attrs = old|x + > self.scr.attrs = x + > end + > h.attroff = function(self, x) + > -- currently borked since Lua 5.1 doesn't have bitwise operators + > -- doesn't support multiple attrs at once + >-- local old = self.scr.attrs + >-- self.scr.attrs = old & (~x) + > self.scr.attrs = curses.A_NORMAL + > end + > h.getch = function(self) + > local c = table.remove(h.kbd, 1) + > if c == nil then return c end + > return string.byte(c) -- for verisimilitude with ncurses + > end + > h.addch = function(self, c) + > local scr = self.scr + > if c == '\n' then + > scr.cursy = scr.cursy+1 + > scr.cursx = 0 + > return + > end + > if scr.cursy <= scr.h then + > scr[scr.cursy][scr.cursx] = {data=c, attrs=scr.attrs} + > scr.cursx = scr.cursx+1 + > if scr.cursx > scr.w then + > scr.cursy = scr.cursy+1 + > scr.cursx = 1 + > end + > end + > end + > h.addstr = function(self, s) + > for i=1,s:len() do + > self:addch(s[i]) + > end + > end + > h.mvaddch = function(self, y, x, c) + > self.scr.cursy = y + > self.scr.cursx = x + > self:addch(c) + > end + > h.mvaddstr = function(self, y, x, s) + > self.scr.cursy = y + > self.scr.cursx = x + > self:addstr(s) + > end + > h.clear = function(self) + > clear_scr(self.scr) + > end + > h.refresh = function(self) + > -- nothing + > end + > return h + >end +- __teliva_timestamp: original + kbd: + >function kbd(keys) + > local result = {} + > for i=1,keys:len() do + > table.insert(result, keys[i]) + > end + > return result + >end +- __teliva_timestamp: original + scr: + >function scr(props) + > props.cursx = 1 + > props.cursy = 1 + > clear_scr(props) + > return props + >end +- __teliva_timestamp: original + clear_scr: + >function clear_scr(props) + > props.cursy = 1 + > props.cursx = 1 + > for y=1,props.h do + > props[y] = {} + > for x=1,props.w do + > props[y][x] = {data=' ', attrs=curses.A_NORMAL} + > end + > end + > return props + >end +- __teliva_timestamp: original + check_screen: + >function check_screen(window, contents, message) + > local x, y = 1, 1 + > for i=1,contents:len() do + > check_eq(window.scr[y][x].data, contents[i], message..'/'..y..','..x) + > x = x+1 + > if x > window.scr.w then + > y = y+1 + > x = 1 + > end + > end + >end + > + >-- putting it all together, an example test of both keyboard and screen + >function test_check_screen() + > local lines = { + > c='123', + > d='234', + > a='345', + > b='456', + > } + > local w = window{ + > kbd=kbd('abc'), + > scr=scr{h=3, w=5}, + > } + > local y = 1 + > while true do + > local b = w:getch() + > if b == nil then break end + > w:mvaddstr(y, 1, lines[string.char(b)]) + > y = y+1 + > end + > check_screen(w, '345 '.. + > '456 '.. + > '123 ', + > 'test_check_screen') + >end +- __teliva_timestamp: original + check_reverse: + >function check_reverse(window, contents, message) + > local x, y = 1, 1 + > for i=1,contents:len() do + > if contents[i] ~= ' ' then + > -- hacky version while we're without bitwise operators on Lua 5.1 + >-- check(window.scr[y][x].attrs & curses.A_REVERSE, message..'/'..y..','..x) + > check_eq(window.scr[y][x].attrs, curses.A_REVERSE, message..'/'..y..','..x) + > else + > -- hacky version while we're without bitwise operators on Lua 5.1 + >-- check(window.scr[y][x].attrs & (~curses.A_REVERSE), message..'/'..y..','..x) + > check(window.scr[y][x].attrs ~= curses.A_REVERSE, message..'/'..y..','..x) + > end + > x = x+1 + > if x > window.scr.w then + > y = y+1 + > x = 1 + > end + > end + >end +- __teliva_timestamp: original + check_bold: + >function check_bold(window, contents, message) + > local x, y = 1, 1 + > for i=1,contents:len() do + > if contents[i] ~= ' ' then + > -- hacky version while we're without bitwise operators on Lua 5.1 + >-- check(window.scr[y][x].attrs & curses.A_BOLD, message..'/'..y..','..x) + > check_eq(window.scr[y][x].attrs, curses.A_BOLD, message..'/'..y..','..x) + > else + > -- hacky version while we're without bitwise operators on Lua 5.1 + >-- check(window.scr[y][x].attrs & (~curses.A_BOLD), message..'/'..y..','..x) + > check(window.scr[y][x].attrs ~= curses.A_BOLD, message..'/'..y..','..x) + > end + > x = x+1 + > if x > window.scr.w then + > y = y+1 + > x = 1 + > end + > end + >end +- __teliva_timestamp: original + check_color: + >-- check which parts of a screen have the given color_pair + >function check_color(window, cp, contents, message) + > local x, y = 1, 1 + > for i=1,contents:len() do + > if contents[i] ~= ' ' then + > -- hacky version while we're without bitwise operators on Lua 5.1 + >-- check(window.scr[y][x].attrs & curses.color_pair(cp), message..'/'..y..','..x) + > check_eq(window.scr[y][x].attrs, curses.color_pair(cp), message..'/'..y..','..x) + > else + > -- hacky version while we're without bitwise operators on Lua 5.1 + >-- check(window.scr[y][x].attrs & (~curses.A_BOLD), message..'/'..y..','..x) + > check(window.scr[y][x].attrs ~= curses.color_pair(cp), message..'/'..y..','..x) + > end + > x = x+1 + > if x > window.scr.w then + > y = y+1 + > x = 1 + > end + > end + >end +- __teliva_timestamp: original + sep: + >-- horizontal separator + >function sep(window) + > local y, _ = window:getyx() + > window:mvaddstr(y+1, 0, '') + > local _, cols = window:getmaxyx() + > for col=1,cols do + > window:addstr('_') + > end + >end +- __teliva_timestamp: original + render: + >function render(window) + > window:clear() + > -- draw stuff to screen here + > window:attron(curses.A_BOLD) + > window:mvaddstr(1, 5, "example app") + > window:attrset(curses.A_NORMAL) + > for i=0,15 do + > window:attrset(curses.color_pair(i)) + > window:mvaddstr(3+i, 5, "========================") + > end + > window:refresh() + >end +- __teliva_timestamp: original + update: + >function update(window) + > local key = window:getch() + > -- process key here + >end +- __teliva_timestamp: original + init_colors: + >function init_colors() + > for i=0,7 do + > curses.init_pair(i, i, -1) + > end + > curses.init_pair(8, 7, 0) + > curses.init_pair(9, 7, 1) + > curses.init_pair(10, 7, 2) + > curses.init_pair(11, 7, 3) + > curses.init_pair(12, 7, 4) + > curses.init_pair(13, 7, 5) + > curses.init_pair(14, 7, 6) + > curses.init_pair(15, -1, 15) + >end +- __teliva_timestamp: original + main: + >function main() + > init_colors() + > + > while true do + > render(Window) + > update(Window) + > end + >end +- __teliva_timestamp: original + doc:blurb: + >To show a brief description of the app on the 'big picture' screen, put the text in a special buffer called 'doc:blurb'. + > + >You can also override the default big picture screen entirely by creating a buffer called 'doc:main'. +- __teliva_timestamp: + >Mon Apr 11 21:47:50 2022 + main: + >function main() + > init_colors() + > read_feeds() + > + > while true do + > render(Window) + > update(Window) + > end + >end +- __teliva_timestamp: + >Mon Apr 11 21:49:18 2022 + read_feeds: + >function read_feeds() + > local f = start_reading('feeds') + > while true + > local line = f.read() + > if line == nil then break end + > end + >end +- __teliva_timestamp: + >Mon Apr 11 21:52:26 2022 + read_feeds: + >function read_feeds() + > local f = start_reading('feeds') + > Feeds = {} + > while true + > local feed = f.read() + > if feed == nil then break end + > table.insert(Feeds, feed) + > end + >end +- __teliva_timestamp: + >Mon Apr 11 21:52:33 2022 + Feeds: + >Feeds = {} +- __teliva_timestamp: + >Mon Apr 11 21:52:59 2022 + render: + >function render(window) + > window:clear() + > -- draw stuff to screen here + > for i, feed in ipairs(Feeds) do + > print(feed) + > end + > window:refresh() + >end +- __teliva_timestamp: + >Mon Apr 11 21:53:11 2022 + read_feeds: + >function read_feeds() + > local f = start_reading('feeds') + > Feeds = {} + > while true do + > local feed = f.read() + > if feed == nil then break end + > table.insert(Feeds, feed) + > end + >end +- __teliva_timestamp: + >Mon Apr 11 21:53:22 2022 + read_feeds: + >function read_feeds() + > local f = start_reading(nil, 'feeds') + > Feeds = {} + > while true do + > local feed = f.read() + > if feed == nil then break end + > table.insert(Feeds, feed) + > end + >end +- __teliva_timestamp: + >Mon Apr 11 21:53:39 2022 + read_feeds: + >function read_feeds() + > local f = start_reading(nil, 'feeds') + > if f == nil then return end + > Feeds = {} + > while true do + > local feed = f.read() + > if feed == nil then break end + > table.insert(Feeds, feed) + > end + >end +- __teliva_timestamp: + >Fri Apr 22 22:39:15 2022 + menu: + >-- To show app-specific hotkeys in the menu bar, add hotkey/command + >-- arrays of strings to the menu array. + >menu = { + > {'^r', 'refresh all feeds'}, + >} +- __teliva_timestamp: + >Fri Apr 22 22:39:45 2022 + update: + >function update(window) + > local key = window:getch() + > if key == 18 then -- ctrl-r + > print('aaa') + > window:getch() + > end + >end +- __teliva_timestamp: + >Fri Apr 22 22:40:02 2022 + update: + >function update(window) + > local key = window:getch() + > if key == 18 then -- ctrl-r + > download_feeds() + > end + >end +- __teliva_timestamp: + >Fri Apr 22 22:40:27 2022 + update: + >function update(window) + > local key = window:getch() + > if key == 18 then -- ctrl-r + > reload_feeds() + > end + >end +- __teliva_timestamp: + >Fri Apr 22 22:40:37 2022 + menu: + >-- To show app-specific hotkeys in the menu bar, add hotkey/command + >-- arrays of strings to the menu array. + >menu = { + > {'^r', 'reload feeds'}, + >} +- __teliva_timestamp: + >Fri Apr 22 22:41:00 2022 + reload_feeds: + >function reload_feeds() + > print('aaa') + > Window:getch() + >end +- __teliva_timestamp: + >Fri Apr 22 22:41:37 2022 + reload_feeds: + >function reload_feeds() + > for _, feed in ipairs(Feeds) do + > print(feed) + > end + > Window:getch() + >end +- __teliva_timestamp: + >Fri Apr 22 22:43:36 2022 + reload_feeds: + >function reload_feeds() + > for _, feed in ipairs(Feeds) do + > local response, status, headers = http.request(feed) + > Window:clear() + > print(response) + > Window:getch() + > end + >end +- __teliva_timestamp: + >Fri Apr 22 22:55:25 2022 + Items: + >Items = {} +- __teliva_timestamp: + >Fri Apr 22 22:57:54 2022 + reload_feeds: + >function reload_feeds() + > for _, feed in ipairs(Feeds) do + > local response, status, headers = http.request(feed) + > print(type(status)) + >--? parse_items(response) + > Window:clear() + > print(response) + >--? print(feed, #Items) + > Window:getch() + > end + >end +- __teliva_timestamp: + >Fri Apr 22 22:58:15 2022 + reload_feeds: + >function reload_feeds() + > for _, feed in ipairs(Feeds) do + > local response, status, headers = http.request(feed) + > Window:clear() + > print(type(status)) + >--? parse_items(response) + > print(response) + >--? print(feed, #Items) + > Window:getch() + > end + >end +- __teliva_timestamp: + >Fri Apr 22 22:59:10 2022 + reload_feeds: + >function reload_feeds() + > for _, feed in ipairs(Feeds) do + > local response, status, headers = http.request(feed) + > if response == 200 then + > parse_items(response) + > Window:clear() + > print(response) + >--? print(feed, #Items) + > Window:getch() + > end + > end + >end +- __teliva_timestamp: + >Fri Apr 22 22:59:38 2022 + reload_feeds: + >function reload_feeds() + > for _, feed in ipairs(Feeds) do + > local response, status, headers = http.request(feed) + > print(status) + > if status == 200 then + > parse_items(response) + > Window:clear() + > print(response) + >--? print(feed, #Items) + > Window:getch() + > end + > end + >end +- __teliva_timestamp: + >Fri Apr 22 23:01:46 2022 + parse_items: + >function parse_items(lines) + > for line in lines:gmatch('[^\n]*') do + > print('^', line, '$') + > end + >end +- __teliva_timestamp: + >Fri Apr 22 23:02:26 2022 + reload_feeds: + >function reload_feeds() + > for _, feed in ipairs(Feeds) do + > local response, status, headers = http.request(feed) + > print(status) + > if status == 200 then + > Window:clear() + > parse_items(response) + > print(response) + >--? print(feed, #Items) + > Window:getch() + > end + > end + >end +- __teliva_timestamp: + >Fri Apr 22 23:02:53 2022 + parse_items: + >function parse_items(lines) + > for line in lines:gmatch('[^\n]+') do + > print('^', line, '$') + > end + >end +- __teliva_timestamp: + >Fri Apr 22 23:06:14 2022 + parse_items: + >function parse_items(lines) + > for line in lines:gmatch('[^\n]+') do + > local t, text = line:gmatch('([^%s]+)\t(.*)') + > print('^', t, '--', text, '$') + > end + >end +- __teliva_timestamp: + >Fri Apr 22 23:06:29 2022 + parse_items: + >function parse_items(lines) + > for line in lines:gmatch('[^\n]+') do + > local t, text = line:match('([^%s]+)\t(.*)') + > print('^', t, '--', text, '$') + > end + >end +- __teliva_timestamp: + >Fri Apr 22 23:07:06 2022 + reload_feeds: + >function reload_feeds() + > for _, feed in ipairs(Feeds) do + > local response, status, headers = http.request(feed) + > print(status) + > if status == 200 then + > Window:clear() + > parse_items(feed, response) + > print(response) + >--? print(feed, #Items) + > Window:getch() + > end + > end + >end +- __teliva_timestamp: + >Fri Apr 22 23:10:44 2022 + parse_items: + >function parse_items(feed, lines) + > if Items[feed] == nil then Items[feed] = {} end + > for line in lines:gmatch('[^\n]+') do + > local t, text = line:match('([^%s]+)\t(.*)') + > table.insert(Items[feed], {time=parse_time(t), text=text}) + > end + > table.sort(Items[feed], function(a, b) return a.time < b.time end) + >end +- __teliva_timestamp: + >Fri Apr 22 23:11:24 2022 + reload_feeds: + >function reload_feeds() + > for _, feed in ipairs(Feeds) do + > local response, status, headers = http.request(feed) + > print(status) + > if status == 200 then + > Window:clear() + > parse_items(feed, response) + > for _, x in ipairs(Items) do + > print(x.time, x.text) + >--? print(feed, #Items) + > Window:getch() + > end + > end + >end +- __teliva_timestamp: + >Fri Apr 22 23:11:36 2022 + reload_feeds: + >function reload_feeds() + > for _, feed in ipairs(Feeds) do + > local response, status, headers = http.request(feed) + > print(status) + > if status == 200 then + > Window:clear() + > parse_items(feed, response) + > for _, x in ipairs(Items) do + > print(x.time, x.text) + > end + >--? print(feed, #Items) + > Window:getch() + > end + > end + >end +- __teliva_timestamp: + >Fri Apr 22 23:12:05 2022 + parse_time: + >function parse_time(t) + > return t + >end +- __teliva_timestamp: + >Fri Apr 22 23:13:02 2022 + reload_feeds: + >function reload_feeds() + > for _, feed in ipairs(Feeds) do + > local response, status, headers = http.request(feed) + > print(status) + > if status == 200 then + > Window:clear() + > parse_items(feed, response) + > for _, x in ipairs(Items[feed]) do + > print(x.time, x.text) + > end + >--? print(feed, #Items) + > Window:getch() + > end + > end + >end +- __teliva_timestamp: + >Fri Apr 22 23:13:50 2022 + parse_items: + >function parse_items(feed, lines) + > if Items[feed] == nil then Items[feed] = {} end + > for line in lines:gmatch('[^\n]+') do + > local t, text = line:match('([^%s]+)\t(.*)') + > print(t, text) + > table.insert(Items[feed], {time=parse_time(t), text=text}) + > end + > table.sort(Items[feed], function(a, b) return a.time < b.time end) + >end +- __teliva_timestamp: + >Fri Apr 22 23:14:36 2022 + parse_items: + >function parse_items(feed, lines) + > if Items[feed] == nil then Items[feed] = {} end + > for line in lines:gmatch('[^\n]+') do + > print(line) + > local t, text = line:match('([^%s]+)\t(.*)') + > print(t, text) + > table.insert(Items[feed], {time=parse_time(t), text=text}) + > end + > table.sort(Items[feed], function(a, b) return a.time < b.time end) + >end +- __teliva_timestamp: + >Fri Apr 22 23:19:20 2022 + parse_items: + >function parse_items(feed, lines) + > if Items[feed] == nil then Items[feed] = {} end + > for line in lines:gmatch('[^\n]+') do + > print(line) + > local t, status = line:match('([^%s]+)\t(.*)') + > if t and status then + > print(t, text) + > table.insert(Items[feed], {time=parse_time(t), text=text}) + > end + > end + > table.sort(Items[feed], function(a, b) return a.time < b.time end) + >end +- __teliva_timestamp: + >Fri Apr 22 23:19:44 2022 + parse_items: + >function parse_items(feed, lines) + > if Items[feed] == nil then Items[feed] = {} end + > for line in lines:gmatch('[^\n]+') do + > print(line) + > local t, status = line:match('([^%s]+)\t(.*)') + > if t and status then + > print(t, status) + > table.insert(Items[feed], {time=parse_time(t), status=status}) + > end + > end + > table.sort(Items[feed], function(a, b) return a.time < b.time end) + >end +- __teliva_timestamp: + >Fri Apr 22 23:19:56 2022 + reload_feeds: + >function reload_feeds() + > for _, feed in ipairs(Feeds) do + > local response, status, headers = http.request(feed) + > print(status) + > if status == 200 then + > Window:clear() + > parse_items(feed, response) + > for _, x in ipairs(Items[feed]) do + > print(x.time, x.status) + > end + >--? print(feed, #Items) + > Window:getch() + > end + > end + >end +- __teliva_timestamp: + >Fri Apr 22 23:21:40 2022 + parse_items: + >function parse_items(feed, lines) + > if Items[feed] == nil then Items[feed] = {} end + > for line in lines:gmatch('[^\n]+') do + > local t, status = line:match('([^%s]+)\t(.*)') + > if t and status then + > table.insert(Items[feed], {time=parse_time(t), status=status}) + > end + > end + > table.sort(Items[feed], function(a, b) return a.time < b.time end) + >end +- __teliva_timestamp: + >Fri Apr 22 23:23:29 2022 + reload_feeds: + >function reload_feeds() + > for _, feed in ipairs(Feeds) do + > local response, status, headers = http.request(feed) + > print(status) + > if status == 200 then + > Window:clear() + > parse_items(feed, response) + > local all_items = {} + > for feed, items in pairs(Items) do + > for _, item in ipairs(items) do + > table.insert(all_items, item) + > end + > end + > table.sort(all_items, function(a, b) return a.time < b.time end) + > for _, item in ipairs(all_items) do + > print(x.time, x.status) + > end + >--? print(feed, #Items) + > Window:getch() + > end + > end + >end +- __teliva_timestamp: + >Fri Apr 22 23:23:48 2022 + reload_feeds: + >function reload_feeds() + > for _, feed in ipairs(Feeds) do + > local response, status, headers = http.request(feed) + > print(status) + > if status == 200 then + > Window:clear() + > parse_items(feed, response) + > local all_items = {} + > for feed, items in pairs(Items) do + > for _, item in ipairs(items) do + > table.insert(all_items, item) + > end + > end + > table.sort(all_items, function(a, b) return a.time < b.time end) + > for _, x in ipairs(all_items) do + > print(x.time, x.status) + > end + >--? print(feed, #Items) + > Window:getch() + > end + > end + >end +- __teliva_timestamp: + >Fri Apr 22 23:24:28 2022 + parse_items: + >function parse_items(feed, lines) + > if Items[feed] == nil then Items[feed] = {} end + > for line in lines:gmatch('[^\n]+') do + > local t, status = line:match('([^%s]+)\t(.*)') + > if t and status then + > table.insert(Items[feed], {feed=feed, time=parse_time(t), status=status}) + > end + > end + > table.sort(Items[feed], function(a, b) return a.time < b.time end) + >end +- __teliva_timestamp: + >Fri Apr 22 23:25:34 2022 + reload_feeds: + >function reload_feeds() + > for _, feed in ipairs(Feeds) do + > local response, status, headers = http.request(feed) + > print(status) + > if status == 200 then + > Window:clear() + > parse_items(feed, response) + > local all_items = {} + > for feed, items in pairs(Items) do + > for _, item in ipairs(items) do + > table.insert(all_items, item) + > end + > end + > table.sort(all_items, function(a, b) return a.time < b.time end) + > for _, x in ipairs(all_items) do + > print(x.feed, x.time, x.status) + > end + >--? print(feed, #Items) + > Window:getch() + > end + > end + >end +- __teliva_timestamp: + >Fri Apr 22 23:27:08 2022 + reload_feeds: + >function reload_feeds() + > for _, feed in ipairs(Feeds) do + > local response, status, headers = http.request(feed) + > print(status) + > if status == 200 then + > Window:clear() + > parse_items(feed, response) + > local all_items = {} + > for feed, items in pairs(Items) do + > for _, item in ipairs(items) do + > table.insert(all_items, item) + > end + > end + > table.sort(all_items, function(a, b) return a.time < b.time end) + > for _, x in ipairs(all_items) do + > print(x.feed, x.time, x.status) + > end + > Window:getch() + > end + > end + >end +- __teliva_timestamp: + >Fri Apr 22 23:29:49 2022 + parse_items: + >function parse_items(feed, lines) + > if Items[feed] == nil then Items[feed] = {} end + > for line in lines:gmatch('[^\n]+') do + > local t, status = line:match('([^%s]+)\t(.*)') + > if t and status then + > table.insert(Items[feed], {feed=feed, time=t, parsed_time=parse_time(t), status=status}) + > end + > end + > table.sort(Items[feed], compare_parsed_time) + >end +- __teliva_timestamp: + >Fri Apr 22 23:31:16 2022 + compare_parsed_time: + >function compare_parsed_time(a, b) + > local a, b = a.parsed_time, b.parsed_time + > if a. +- __teliva_timestamp: + >Fri Apr 22 23:34:46 2022 + parse_time: + >function parse_time(t) + > local year, month, day, hour, min, sec, patt_end = str:match("^(%d%d%d%d)%-(%d%d)%-(%d%d)[Tt](%d%d%.?%d*):(%d%d):(%d%d)()") + > return { year=year, month=month, day=day, hour=hour, min=min, sec=sec } + >end +- __teliva_timestamp: + >Fri Apr 22 23:36:13 2022 + compare_parsed_time: + >function compare_parsed_time(a, b) + > local a, b = a.parsed_time, b.parsed_time + > if a.year ~= b.year then return a.year < b.year end + > if a.month ~= b.month then return a.month < b.month end + > if a.day ~= b.day then return a.day < b.day end + > if a.hour ~= b.hour then return a.hour < b.hour end + > if a.min ~= b.min then return a.min < b.min end + > return a.sec < b.sec + >end +- __teliva_timestamp: + >Fri Apr 22 23:36:35 2022 + parse_time: + >function parse_time(t) + > local year, month, day, hour, min, sec, patt_end = t:match("^(%d%d%d%d)%-(%d%d)%-(%d%d)[Tt](%d%d%.?%d*):(%d%d):(%d%d)()") + > return { year=year, month=month, day=day, hour=hour, min=min, sec=sec } + >end +- __teliva_timestamp: + >Fri Apr 22 23:39:20 2022 + parse_time: + >function parse_time(t) + > local year, month, day, hour, min, sec, tz_hour, tz_min = t:match("^(%d%d%d%d)%-(%d%d)%-(%d%d)[Tt](%d%d%.?%d*):(%d%d):(%d%d)[+-](%d%d):(%d%d)") + > return { year=year, month=month, day=day, hour=hour, min=min, sec=sec, tz_hour=tz_hour, tz_min=tz_min } + >end +- __teliva_timestamp: + >Fri Apr 22 23:47:55 2022 + parse_time: + >function parse_time(t) + > local year, month, day, hour, min, sec, tz_op, tz_hour, tz_min = t:match("^(%d%d%d%d)%-(%d%d)%-(%d%d)[Tt](%d%d%.?%d*):(%d%d):(%d%d)([+-])(%d%d):(%d%d)") + > if tz_op == '+' then + > hour = int(hour) + int(tz_hour) + > min = int(min) + int(tz_min) + > else + > hour = int(hour) - int(tz_hour) + > min = int(min) - int(tz_min) + > end + > return os.time{ year=int(year), month=int(month), day=int(day), hour=int(hour), min=int(min), sec=int(sec) } + >end +- __teliva_timestamp: + >Fri Apr 22 23:48:46 2022 + compare_parsed_time: + >function compare_parsed_time(a, b) + > return a.parsed_time < b.parsed_time + >end + > + >-- unused + >function compare_parsed_time2(a, b) + > local a, b = a.parsed_time, b.parsed_time + > if a.year ~= b.year then return a.year < b.year end + > if a.month ~= b.month then return a.month < b.month end + > if a.day ~= b.day then return a.day < b.day end + > if a.hour ~= b.hour then return a.hour < b.hour end + > if a.min ~= b.min then return a.min < b.min end + > return a.sec < b.sec + >end +- __teliva_timestamp: + >Fri Apr 22 23:49:13 2022 + parse_time: + >function parse_time(t) + > local year, month, day, hour, min, sec, tz_op, tz_hour, tz_min = t:match("^(%d%d%d%d)%-(%d%d)%-(%d%d)[Tt](%d%d%.?%d*):(%d%d):(%d%d)([+-])(%d%d):(%d%d)") + > local int = tonumber + > if tz_op == '+' then + > hour = int(hour) + int(tz_hour) + > min = int(min) + int(tz_min) + > else + > hour = int(hour) - int(tz_hour) + > min = int(min) - int(tz_min) + > end + > return os.time{ year=int(year), month=int(month), day=int(day), hour=int(hour), min=int(min), sec=int(sec) } + >end +- __teliva_timestamp: + >Fri Apr 22 23:51:08 2022 + parse_time: + >function parse_time(t) + > local year, month, day, hour, min, sec, tz_op, tz_hour, tz_min = t:match("^(%d%d%d%d)%-(%d%d)%-(%d%d)[Tt](%d%d%.?%d*):(%d%d):(%d%d)([+-])(%d%d):(%d%d)") + > local int = tonumber + > -- convert to UTC + > if tz_op == '+' then + > hour = int(hour) - int(tz_hour) + > min = int(min) - int(tz_min) + > else + > hour = int(hour) + int(tz_hour) + > min = int(min) + int(tz_min) + > end + > return os.time{ year=int(year), month=int(month), day=int(day), hour=hour, min=min, sec=int(sec) } + >end +- __teliva_timestamp: + >Fri Apr 22 23:52:27 2022 + __teliva_note: + >parsing twtxt feeds and sorting items after accounting for timezones + reload_feeds: + >function reload_feeds() + > for _, feed in ipairs(Feeds) do + > local response, status, headers = http.request(feed) + > print(status) + > if status == 200 then + > Window:clear() + > parse_items(feed, response) + > local all_items = {} + > for feed, items in pairs(Items) do + > for _, item in ipairs(items) do + > table.insert(all_items, item) + > end + > end + > table.sort(all_items, function(a, b) return a.parsed_time < b.parsed_time end) + > for _, x in ipairs(all_items) do + > print(x.feed, x.time, x.status) + > end + > Window:getch() + > end + > end + >end +- __teliva_timestamp: + >Fri Apr 22 23:57:10 2022 + update: + >function update(window) + > local key = window:getch() + > if key == 18 then -- ctrl-r + > reload_feeds(window) + > end + >end +- __teliva_timestamp: + >Sat Apr 23 00:13:26 2022 + render_feed_status: + >function render_feed_status(window, fs) + > window:clear() + > for _, fs in ipairs(feed_status) do + > if fs.status then + > if fs.status/100 == 2 then + > window:attrset(curses.color_pair(10)) + > window:addstr(fs.feed..'\n') + > window:attrset(curses.A_NORMAL) + > else + > window:attrset(curses.color_pair(9)) + > window:addstr(fs.feed..'\n') + > window:attrset(curses.A_NORMAL) + > end + > else + > window:addstr(fs.feed..'\n') + > end + > end + > window:refresh() + >end +- __teliva_timestamp: + >Sat Apr 23 00:18:59 2022 + render: + >function render(window) + > window:clear() + > -- draw feeds + > for i, feed in ipairs(Feeds) do + > print(feed) + > end + > -- draw items + > local all_items = {} + > local n = 0 + > for feed, items in pairs(Items) do + > n = n+1 + > for _, item in ipairs(items) do + > table.insert(all_items, item) + > end + > end + > table.sort(all_items, function(a, b) return a.parsed_time < b.parsed_time end) + > for _, x in ipairs(all_items) do + > print(x.feed, x.time, x.status) + > end + > window:refresh() + >end +- __teliva_timestamp: + >Sat Apr 23 00:20:31 2022 + __teliva_note: + >show progress when downloading feeds + reload_feeds: + >function reload_feeds(window) + > window:nodelay(true) + > feed_status = {} + > for _, feed in ipairs(Feeds) do + > table.insert(feed_status, {feed=feed}) + > end + > for _, fs in ipairs(feed_status) do + > local response, status, headers = http.request(fs.feed) + > fs.status = status + > parse_items(fs.feed, response) + > render_feed_status(window, fs) + > if window:getch() then break end + > end + > window:nodelay(false) + >end +- __teliva_timestamp: + >Sat Apr 23 00:28:09 2022 + render: + >function render(window) + > window:clear() + > -- draw feeds + > print(str(#Feeds)..' feeds') + > -- draw items + > local all_items = {} + > local n = 0 + > for feed, items in pairs(Items) do + > n = n+1 + > for _, item in ipairs(items) do + > table.insert(all_items, item) + > end + > end + > table.sort(all_items, function(a, b) return a.parsed_time < b.parsed_time end) + > for _, x in ipairs(all_items) do + > print(x.feed, x.time, x.status) + > end + > window:refresh() + >end +- __teliva_timestamp: + >Sat Apr 23 00:29:10 2022 + reload_feeds: + >function reload_feeds(window) + > window:nodelay(true) + > feed_status = {} + > for _, feed in ipairs(Feeds) do + > table.insert(feed_status, {feed=feed}) + > end + > render_feed_status( + > for _, fs in ipairs(feed_status) do + > local response, status, headers = http.request(fs.feed) + > fs.status = status + > parse_items(fs.feed, response) + > render_feed_status(window, fs) + > if window:getch() then break end + > end + > window:nodelay(false) + >end +- __teliva_timestamp: + >Sat Apr 23 00:29:23 2022 + render_feed_status: + >function render_feed_status(window, feed_status) + > window:clear() + > for _, fs in ipairs(feed_status) do + > if fs.status then + > if fs.status/100 == 2 then + > window:attrset(curses.color_pair(10)) + > window:addstr(fs.feed..'\n') + > window:attrset(curses.A_NORMAL) + > else + > window:attrset(curses.color_pair(9)) + > window:addstr(fs.feed..'\n') + > window:attrset(curses.A_NORMAL) + > end + > else + > window:addstr(fs.feed..'\n') + > end + > end + > window:refresh() + >end +- __teliva_timestamp: + >Sat Apr 23 00:29:48 2022 + reload_feeds: + >function reload_feeds(window) + > window:nodelay(true) + > local feed_status = {} + > for _, feed in ipairs(Feeds) do + > table.insert(feed_status, {feed=feed}) + > end + > render_feed_status(feed_status) + > for _, fs in ipairs(feed_status) do + > local response, status, headers = http.request(fs.feed) + > fs.status = status + > parse_items(fs.feed, response) + > render_feed_status(window, feed_status) + > if window:getch() then break end + > end + > window:nodelay(false) + >end +- __teliva_timestamp: + >Sat Apr 23 00:30:18 2022 + __teliva_note: + >fix an accidental global variable + reload_feeds: + >function reload_feeds(window) + > window:nodelay(true) + > local feed_status = {} + > for _, feed in ipairs(Feeds) do + > table.insert(feed_status, {feed=feed}) + > end + > render_feed_status(window, feed_status) + > for _, fs in ipairs(feed_status) do + > local response, status, headers = http.request(fs.feed) + > fs.status = status + > parse_items(fs.feed, response) + > render_feed_status(window, feed_status) + > if window:getch() then break end + > end + > window:nodelay(false) + >end -- cgit 1.4.1-2-gfad0