Compare commits

..

7 Commits

Author SHA1 Message Date
sammyette 1f5ab90586
docs: update 2023-07-09 23:41:25 -04:00
sammyette 4c6758c732
chore: merge from master (update branch) 2023-07-09 23:40:30 -04:00
sammyette 11da2c0c45
feat(greenhouse): add goto command
made some other minor changes (in terms of how much it
matters to the user)

the toc page is now a "special page"
in a next commit itll also be used for a help page

cursor gets hidden unless typing a command
2023-07-09 23:39:11 -04:00
sammyette 8b672f5b95
feat(greenhouse): add command handling 2023-07-09 23:07:32 -04:00
sammyette 0bb97a6f3b
feat: add table of contents to pager
another change in this commit is having a separate render
function for the greenhouse caller to handle ui

this means there isnt a hardcoded space at the bottom to
display command and status line
2023-07-09 22:38:34 -04:00
sammyette 713f24aa3e
feat(greenhouse): add functions for custom keybinds 2023-07-09 20:22:59 -04:00
sammyette cbc5e81c9d
feat: show indexes on cdr list and change home to tilde (#253)
* feat: show indexes on cdr list and change home to tilde

* fix: remove underline in cdr help page
2023-07-09 18:43:21 -04:00
8 changed files with 237 additions and 38 deletions

View File

@ -6,6 +6,7 @@
- `read()` method for retrieving input (so now the `in` sink of commanders is useful) - `read()` method for retrieving input (so now the `in` sink of commanders is useful)
- `flush()` and `autoFlush()` related to flushing outputs - `flush()` and `autoFlush()` related to flushing outputs
- `pipe` property to check if a sink with input is a pipe (like stdin) - `pipe` property to check if a sink with input is a pipe (like stdin)
- Show indexes on cdr list
### Fixed ### Fixed
- Replaced `sed` in-place editing with `grep` and `mv` for compatibility with BSD utils - Replaced `sed` in-place editing with `grep` and `mv` for compatibility with BSD utils

View File

@ -23,7 +23,7 @@ Inserts text into the line.
### getChar() -> string ### getChar() -> string
Reads a keystroke from the user. This is in a format Reads a keystroke from the user. This is in a format
of something like Ctrl-L. of something like Ctrl-L..
### setVimRegister(register, text) ### setVimRegister(register, text)
Sets the vim register at `register` to hold the passed text. Sets the vim register at `register` to hold the passed text.

View File

@ -41,7 +41,7 @@ function hilbish.editor.getVimRegister(register) end
function hilbish.editor.insert(text) end function hilbish.editor.insert(text) end
--- Reads a keystroke from the user. This is in a format --- Reads a keystroke from the user. This is in a format
--- of something like Ctrl-L. --- of something like Ctrl-L..
function hilbish.editor.getChar() end function hilbish.editor.getChar() end
--- Sets the vim register at `register` to hold the passed text. --- Sets the vim register at `register` to hold the passed text.

View File

@ -10,7 +10,7 @@ cdr: change directory to one which has been recently visied
usage: cdr <index> usage: cdr <index>
to get a list of recent directories, use {green}{underline}cdr list{reset}]]) to get a list of recent directories, use {green}cdr list{reset}]])
return return
end end
@ -20,7 +20,10 @@ to get a list of recent directories, use {green}{underline}cdr list{reset}]])
sinks.out:writeln 'No directories have been visited.' sinks.out:writeln 'No directories have been visited.'
return 1 return 1
end end
sinks.out:writeln(table.concat(recentDirs, '\n')) for idx, d in ipairs(dirs.recentDirs) do
if d:find(hilbish.home, 1, true) then d = fs.join('~', d:sub(hilbish.home:len() + 1)) end
sinks.out:writeln(lunacolors.format(string.format('{cyan}%d{reset} %s', idx, d)))
end
return return
end end

View File

@ -9,8 +9,102 @@ local Page = require 'nature.greenhouse.page'
commander.register('greenhouse', function(args, sinks) commander.register('greenhouse', function(args, sinks)
local gh = Greenhouse(sinks.out) local gh = Greenhouse(sinks.out)
local buffer = ''
local display = ''
local command = false
local commands = {
q = function()
gh.keybinds['Ctrl-D'](gh)
end,
['goto'] = function(args)
if not args[1] then
return 'nuh uh'
end
gh:jump(tonumber(args[1]))
end
}
function gh:resize()
local size = terminal.size()
self.region = {
width = size.width,
height = size.height - 2
}
end
local oldDraw = gh.draw
function gh:draw()
oldDraw(self)
local workingPage = self.pages[self.curPage]
local offset = self.offset
if self.isSpecial then
offset = self.specialOffset
workingPage = self.specialPage
end
self.sink:write(ansikit.getCSI((self.region.height + 2) - self.start.. ';1', 'H'))
if not self.isSpecial then
self.sink:write(string.format('\27[0mPage %d', self.curPage))
if workingPage.title ~= '' then
self.sink:writeln('' .. workingPage.title)
else
self.sink:writeln('')
end
end
self.sink:write(buffer == '' and display or buffer)
end
function gh:input(c)
-- command handling
if c == ':' and not command then
command = true
end
if c == 'Escape' then
if command then
command = false
buffer = ''
else
if self.isSpecial then gh:special() end
end
elseif c == 'Backspace' then
buffer = buffer:sub(0, -2)
if buffer == '' then
command = false
else
goto update
end
end
if command then
ansikit.showCursor()
if buffer:match '^:' then buffer = buffer .. c else buffer = c end
else
ansikit.hideCursor()
end
::update::
gh:update()
end
gh:resize()
gh:keybind('Enter', function(self)
if self.isSpecial then
self:jump(self.specialPageIdx)
self:special(true)
else
if buffer:len() < 2 then return end
local splitBuf = string.split(buffer, " ")
local command = commands[splitBuf[1]:sub(2)]
if command then
table.remove(splitBuf, 1)
buffer = command(splitBuf) or ''
end
self:update()
end
end)
if sinks['in'].pipe then if sinks['in'].pipe then
local page = Page(sinks['in']:readAll()) local page = Page('', sinks['in']:readAll())
gh:addPage(page) gh:addPage(page)
end end
@ -20,7 +114,7 @@ commander.register('greenhouse', function(args, sinks)
sinks.err:writeln(string.format('could not open file %s', name)) sinks.err:writeln(string.format('could not open file %s', name))
end end
local page = Page(f:read '*a') local page = Page(name, f:read '*a')
gh:addPage(page) gh:addPage(page)
end end

View File

@ -5,7 +5,9 @@
-- and flowerbook. -- and flowerbook.
local ansikit = require 'ansikit' local ansikit = require 'ansikit'
local lunacolors = require 'lunacolors'
local terminal = require 'terminal' local terminal = require 'terminal'
local Page = require 'nature.greenhouse.page'
local Object = require 'nature.object' local Object = require 'nature.object'
local Greenhouse = Object:extend() local Greenhouse = Object:extend()
@ -14,10 +16,21 @@ function Greenhouse:new(sink)
local size = terminal.size() local size = terminal.size()
self.region = size self.region = size
self.start = 1 self.start = 1
self.offset = 1 self.offset = 1 -- vertical text offset
self.sink = sink self.sink = sink
self.pages = {} self.pages = {}
self.curPage = 1 self.curPage = 1
self.keybinds = {
['Up'] = function(self) self:scroll 'up' end,
['Down'] = function(self) self:scroll 'down' end,
['Ctrl-Left'] = self.previous,
['Ctrl-Right'] = self.next,
['Ctrl-N'] = function(self) self:toc(true) end,
}
self.isSpecial = false
self.specialPage = nil
self.specialPageIdx = 1
self.specialOffset = 1
return self return self
end end
@ -32,22 +45,39 @@ function Greenhouse:updateCurrentPage(text)
end end
function Greenhouse:draw() function Greenhouse:draw()
local lines = self.pages[self.curPage].lines local workingPage = self.pages[self.curPage]
local offset = self.offset
if self.isSpecial then
offset = self.specialOffset
workingPage = self.specialPage
end
local lines = workingPage.lines
self.sink:write(ansikit.getCSI(self.start .. ';1', 'H')) self.sink:write(ansikit.getCSI(self.start .. ';1', 'H'))
self.sink:write(ansikit.getCSI(2, 'J')) self.sink:write(ansikit.getCSI(2, 'J'))
-- the -2 negate is for the command and status line -- the -2 negate is for the command and status line
for i = self.offset, self.offset + (self.region.height - self.start) - 2 do for i = offset, offset + (self.region.height - self.start) do
if i > #lines then break end if i > #lines then break end
self.sink:writeln('\r' .. lines[i]:gsub('\t', ' '):sub(0, self.region.width - 2)) self.sink:writeln('\r' .. lines[i]:gsub('\t', ' '):sub(0, self.region.width - 2))
end end
self.sink:write '\r' self.sink:write '\r'
self:render()
end
self.sink:write(ansikit.getCSI(self.region.height - self.start.. ';1', 'H')) function Greenhouse:render()
self.sink:writeln(string.format('\27[0mPage %d', self.curPage))
end end
function Greenhouse:scroll(direction) function Greenhouse:scroll(direction)
if self.isSpecial then
if direction == 'down' then
self:next(true)
elseif direction == 'up' then
self:previous(true)
end
return
end
local lines = self.pages[self.curPage].lines local lines = self.pages[self.curPage].lines
local oldOffset = self.offset local oldOffset = self.offset
@ -61,28 +91,96 @@ function Greenhouse:scroll(direction)
end end
function Greenhouse:update() function Greenhouse:update()
self:resize()
if self.isSpecial then
self:special()
end
self:draw()
end
function Greenhouse:special()
self.isSpecial = not self.isSpecial
end
function Greenhouse:toc(toggle)
if not self.isSpecial then
self.specialPageIdx = self.curPage
end
if toggle then self.isSpecial = not self.isSpecial end
-- Generate a special page for our table of contents
local tocText = string.format([[
%s
]], lunacolors.cyan(lunacolors.bold '―― Table of Contents ――'))
local genericPageCount = 1
for i, page in ipairs(self.pages) do
local title = page.title
if title == 'Page' then
title = 'Page #' .. genericPageCount
genericPageCount = genericPageCount + 1
end
if i == self.specialPageIdx then
title = lunacolors.invert(title)
end
tocText = tocText .. title .. '\n'
end
self.specialPage = Page('TOC', tocText)
self:draw()
end
function Greenhouse:resize()
local size = terminal.size() local size = terminal.size()
self.region = size self.region = size
self:draw()
end end
function Greenhouse:next() function Greenhouse:next(special)
local oldCurrent = self.curPage local oldCurrent = special and self.specialPageIdx or self.curPage
self.curPage = math.min(self.curPage + 1, #self.pages) local pageIdx = math.min(oldCurrent + 1, #self.pages)
if self.curPage ~= oldCurrent then
if special then
self.specialPageIdx = pageIdx
else
self.curPage = pageIdx
end
if pageIdx ~= oldCurrent then
self.offset = 1 self.offset = 1
self:draw() self:update()
end end
end end
function Greenhouse:previous() function Greenhouse:previous(special)
local oldCurrent = self.curPage local oldCurrent = special and self.specialPageIdx or self.curPage
self.curPage = math.max(self.curPage - 1, 1) local pageIdx = math.max(self.curPage - 1, 1)
if self.curPage ~= oldCurrent then
self.offset = 1 if special then
self:draw() self.specialPageIdx = pageIdx
else
self.curPage = pageIdx
end end
if pageIdx ~= oldCurrent then
self.offset = 1
self:update()
end
end
function Greenhouse:jump(idx)
if idx ~= self.curPage then
self.offset = 1
end
self.curPage = idx
self:update()
end
function Greenhouse:keybind(key, callback)
self.keybinds[key] = callback
end
function Greenhouse:input(char)
end end
function Greenhouse:initUi() function Greenhouse:initUi()
@ -104,25 +202,22 @@ function Greenhouse:initUi()
end) end)
ansikit.screenAlt() ansikit.screenAlt()
ansikit.hideCursor()
ansikit.clear(true) ansikit.clear(true)
self:draw() self:draw()
hilbish.goro(function() hilbish.goro(function()
while not done do while not done do
local c = read() local c = read()
if c == 'Ctrl-D' then self:keybind('Ctrl-D', function()
done = true done = true
end end)
if c == 'Up' then if self.keybinds[c] then
self:scroll 'up' self.keybinds[c](self)
else
self:input(c)
end end
if c == 'Down' then
self:scroll 'down'
end
if c == 'Ctrl-Right' then self:next() end
if c == 'Ctrl-Left' then self:previous() end
--[[ --[[
if c == 27 then if c == 27 then
@ -161,6 +256,7 @@ function Greenhouse:initUi()
while not done do while not done do
-- --
end end
ansikit.showCursor()
ansikit.screenMain() ansikit.screenMain()
end end

View File

@ -2,12 +2,17 @@ local Object = require 'nature.object'
local Page = Object:extend() local Page = Object:extend()
function Page:new(text) function Page:new(title, text)
self:setText(text) self:setText(text)
self.title = title or 'Page'
end end
function Page:setText(text) function Page:setText(text)
self.lines = string.split(text, '\n') self.lines = string.split(text, '\n')
end end
function Page:setTitle(title)
self.title = title
end
return Page return Page

View File

@ -155,10 +155,8 @@ func (rl *Instance) ReadChar() string {
case charCtrlG: return "Ctrl-G" case charCtrlG: return "Ctrl-G"
case charBackspace, charBackspace2: return "Backspace" case charBackspace, charBackspace2: return "Backspace"
case charTab: return "Tab" case charTab: return "Tab"
case charCtrlJ: return "Ctrl-J"
case charCtrlK: return "Ctrl-K" case charCtrlK: return "Ctrl-K"
case charCtrlL: return "Ctrl-L" case charCtrlL: return "Ctrl-L"
case charCtrlM: return "Ctrl-M"
case charCtrlN: return "Ctrl-N" case charCtrlN: return "Ctrl-N"
case charCtrlO: return "Ctrl-O" case charCtrlO: return "Ctrl-O"
case charCtrlP: return "Ctrl-P" case charCtrlP: return "Ctrl-P"
@ -172,6 +170,8 @@ func (rl *Instance) ReadChar() string {
case charCtrlX: return "Ctrl-X" case charCtrlX: return "Ctrl-X"
case charCtrlY: return "Ctrl-Y" case charCtrlY: return "Ctrl-Y"
case charCtrlZ: return "Ctrl-Z" case charCtrlZ: return "Ctrl-Z"
case '\r': fallthrough
case '\n': return "Enter"
case charEscape: case charEscape:
switch s { switch s {
case string(charEscape): return "Escape" case string(charEscape): return "Escape"
@ -188,5 +188,5 @@ func (rl *Instance) ReadChar() string {
} }
} }
return "???" return s
} }