mirror of
				https://github.com/sammy-ette/Hilbish
				synced 2025-08-10 02:52:03 +00:00 
			
		
		
		
	Compare commits
	
		
			7 Commits
		
	
	
		
			60f267260b
			...
			1f5ab90586
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 1f5ab90586 | |||
| 4c6758c732 | |||
| 11da2c0c45 | |||
| 8b672f5b95 | |||
| 0bb97a6f3b | |||
| 713f24aa3e | |||
| cbc5e81c9d | 
@ -6,6 +6,7 @@
 | 
			
		||||
  - `read()` method for retrieving input (so now the `in` sink of commanders is useful)
 | 
			
		||||
  - `flush()` and `autoFlush()` related to flushing outputs
 | 
			
		||||
  - `pipe` property to check if a sink with input is a pipe (like stdin)
 | 
			
		||||
- Show indexes on cdr list
 | 
			
		||||
 | 
			
		||||
### Fixed
 | 
			
		||||
- Replaced `sed` in-place editing with `grep` and `mv` for compatibility with BSD utils
 | 
			
		||||
 | 
			
		||||
@ -23,7 +23,7 @@ Inserts text into the line.
 | 
			
		||||
 | 
			
		||||
### getChar() -> string
 | 
			
		||||
Reads a keystroke from the user. This is in a format
 | 
			
		||||
of something like Ctrl-L.
 | 
			
		||||
of something like Ctrl-L..
 | 
			
		||||
 | 
			
		||||
### setVimRegister(register, text)
 | 
			
		||||
Sets the vim register at `register` to hold the passed text.
 | 
			
		||||
 | 
			
		||||
@ -41,7 +41,7 @@ function hilbish.editor.getVimRegister(register) end
 | 
			
		||||
function hilbish.editor.insert(text) end
 | 
			
		||||
 | 
			
		||||
--- 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
 | 
			
		||||
 | 
			
		||||
--- Sets the vim register at `register` to hold the passed text.
 | 
			
		||||
 | 
			
		||||
@ -10,7 +10,7 @@ cdr: change directory to one which has been recently visied
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
	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.'
 | 
			
		||||
			return 1
 | 
			
		||||
		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
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -9,8 +9,102 @@ local Page = require 'nature.greenhouse.page'
 | 
			
		||||
commander.register('greenhouse', function(args, sinks)
 | 
			
		||||
	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
 | 
			
		||||
		local page = Page(sinks['in']:readAll())
 | 
			
		||||
		local page = Page('', sinks['in']:readAll())
 | 
			
		||||
		gh:addPage(page)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
@ -20,7 +114,7 @@ commander.register('greenhouse', function(args, sinks)
 | 
			
		||||
			sinks.err:writeln(string.format('could not open file %s', name))
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
		local page = Page(f:read '*a')
 | 
			
		||||
		local page = Page(name, f:read '*a')
 | 
			
		||||
		gh:addPage(page)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,9 @@
 | 
			
		||||
-- and flowerbook.
 | 
			
		||||
 | 
			
		||||
local ansikit = require 'ansikit'
 | 
			
		||||
local lunacolors = require 'lunacolors'
 | 
			
		||||
local terminal = require 'terminal'
 | 
			
		||||
local Page = require 'nature.greenhouse.page'
 | 
			
		||||
local Object = require 'nature.object'
 | 
			
		||||
 | 
			
		||||
local Greenhouse = Object:extend()
 | 
			
		||||
@ -14,10 +16,21 @@ function Greenhouse:new(sink)
 | 
			
		||||
	local size = terminal.size()
 | 
			
		||||
	self.region = size
 | 
			
		||||
	self.start = 1
 | 
			
		||||
	self.offset = 1
 | 
			
		||||
	self.offset = 1 -- vertical text offset
 | 
			
		||||
	self.sink = sink
 | 
			
		||||
	self.pages = {}
 | 
			
		||||
	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
 | 
			
		||||
end
 | 
			
		||||
@ -32,22 +45,39 @@ function Greenhouse:updateCurrentPage(text)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
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(2, 'J'))
 | 
			
		||||
 | 
			
		||||
	-- 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
 | 
			
		||||
		self.sink:writeln('\r' .. lines[i]:gsub('\t', '        '):sub(0, self.region.width - 2))
 | 
			
		||||
	end
 | 
			
		||||
	self.sink:write '\r'
 | 
			
		||||
	self:render()
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
	self.sink:write(ansikit.getCSI(self.region.height - self.start.. ';1', 'H'))
 | 
			
		||||
	self.sink:writeln(string.format('\27[0mPage %d', self.curPage))
 | 
			
		||||
function Greenhouse:render()
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
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 oldOffset = self.offset
 | 
			
		||||
@ -61,28 +91,96 @@ function Greenhouse:scroll(direction)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function Greenhouse:update()
 | 
			
		||||
	local size = terminal.size()
 | 
			
		||||
	self.region = size
 | 
			
		||||
	self:resize()
 | 
			
		||||
	if self.isSpecial then
 | 
			
		||||
		self:special()
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	self:draw()
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function Greenhouse:next()
 | 
			
		||||
	local oldCurrent = self.curPage
 | 
			
		||||
	self.curPage = math.min(self.curPage + 1, #self.pages)
 | 
			
		||||
	if self.curPage ~= oldCurrent then
 | 
			
		||||
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()
 | 
			
		||||
	self.region = size
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function Greenhouse:next(special)
 | 
			
		||||
	local oldCurrent = special and self.specialPageIdx or self.curPage
 | 
			
		||||
	local pageIdx = math.min(oldCurrent + 1, #self.pages)
 | 
			
		||||
 | 
			
		||||
	if special then
 | 
			
		||||
		self.specialPageIdx = pageIdx
 | 
			
		||||
	else
 | 
			
		||||
		self.curPage = pageIdx
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	if pageIdx ~= oldCurrent then
 | 
			
		||||
		self.offset = 1
 | 
			
		||||
		self:draw()
 | 
			
		||||
		self:update()
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function Greenhouse:previous()
 | 
			
		||||
	local oldCurrent = self.curPage
 | 
			
		||||
	self.curPage = math.max(self.curPage - 1, 1)
 | 
			
		||||
	if self.curPage ~= oldCurrent then
 | 
			
		||||
		self.offset = 1
 | 
			
		||||
		self:draw()
 | 
			
		||||
function Greenhouse:previous(special)
 | 
			
		||||
	local oldCurrent = special and self.specialPageIdx or self.curPage
 | 
			
		||||
	local pageIdx = math.max(self.curPage - 1, 1)
 | 
			
		||||
 | 
			
		||||
	if special then
 | 
			
		||||
		self.specialPageIdx = pageIdx
 | 
			
		||||
	else
 | 
			
		||||
		self.curPage = pageIdx
 | 
			
		||||
	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
 | 
			
		||||
 | 
			
		||||
function Greenhouse:initUi()
 | 
			
		||||
@ -104,25 +202,22 @@ function Greenhouse:initUi()
 | 
			
		||||
	end)
 | 
			
		||||
 | 
			
		||||
	ansikit.screenAlt()
 | 
			
		||||
	ansikit.hideCursor()
 | 
			
		||||
	ansikit.clear(true)
 | 
			
		||||
	self:draw()
 | 
			
		||||
 | 
			
		||||
	hilbish.goro(function()
 | 
			
		||||
		while not done do
 | 
			
		||||
			local c = read()
 | 
			
		||||
			if c == 'Ctrl-D' then
 | 
			
		||||
			self:keybind('Ctrl-D', function()
 | 
			
		||||
				done = true
 | 
			
		||||
			end
 | 
			
		||||
			end)
 | 
			
		||||
 | 
			
		||||
			if c == 'Up' then
 | 
			
		||||
				self:scroll 'up'
 | 
			
		||||
			if self.keybinds[c] then
 | 
			
		||||
				self.keybinds[c](self)
 | 
			
		||||
			else
 | 
			
		||||
				self:input(c)
 | 
			
		||||
			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
 | 
			
		||||
@ -161,6 +256,7 @@ function Greenhouse:initUi()
 | 
			
		||||
	while not done do
 | 
			
		||||
		--
 | 
			
		||||
	end
 | 
			
		||||
	ansikit.showCursor()
 | 
			
		||||
	ansikit.screenMain()
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -2,12 +2,17 @@ local Object = require 'nature.object'
 | 
			
		||||
 | 
			
		||||
local Page = Object:extend()
 | 
			
		||||
 | 
			
		||||
function Page:new(text)
 | 
			
		||||
function Page:new(title, text)
 | 
			
		||||
	self:setText(text)
 | 
			
		||||
	self.title = title or 'Page'
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function Page:setText(text)
 | 
			
		||||
	self.lines = string.split(text, '\n')
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function Page:setTitle(title)
 | 
			
		||||
	self.title = title
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
return Page
 | 
			
		||||
 | 
			
		||||
@ -155,10 +155,8 @@ func (rl *Instance) ReadChar() string {
 | 
			
		||||
		case charCtrlG: return "Ctrl-G"
 | 
			
		||||
		case charBackspace, charBackspace2: return "Backspace"
 | 
			
		||||
		case charTab: return "Tab"
 | 
			
		||||
		case charCtrlJ: return "Ctrl-J"
 | 
			
		||||
		case charCtrlK: return "Ctrl-K"
 | 
			
		||||
		case charCtrlL: return "Ctrl-L"
 | 
			
		||||
		case charCtrlM: return "Ctrl-M"
 | 
			
		||||
		case charCtrlN: return "Ctrl-N"
 | 
			
		||||
		case charCtrlO: return "Ctrl-O"
 | 
			
		||||
		case charCtrlP: return "Ctrl-P"
 | 
			
		||||
@ -172,6 +170,8 @@ func (rl *Instance) ReadChar() string {
 | 
			
		||||
		case charCtrlX: return "Ctrl-X"
 | 
			
		||||
		case charCtrlY: return "Ctrl-Y"
 | 
			
		||||
		case charCtrlZ: return "Ctrl-Z"
 | 
			
		||||
		case '\r': fallthrough
 | 
			
		||||
		case '\n': return "Enter"
 | 
			
		||||
		case charEscape:
 | 
			
		||||
			switch s {
 | 
			
		||||
				case string(charEscape): return "Escape"
 | 
			
		||||
@ -188,5 +188,5 @@ func (rl *Instance) ReadChar() string {
 | 
			
		||||
			}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return "???"
 | 
			
		||||
	return s
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user