implement an interactive "chooser"

This commit is contained in:
Daylin Morgan 2024-09-23 17:50:46 -05:00
parent 398b77cc2b
commit 38b1854c12
Signed by: daylin
GPG key ID: 950D13E9719334AD
5 changed files with 153 additions and 5 deletions

View file

@ -12,7 +12,8 @@ task docs, "Deploy doc html + search index to public/ directory":
deployDir = getCurrentDir() / "public" deployDir = getCurrentDir() / "public"
pkgName = "hwylterm" pkgName = "hwylterm"
gitUrl = fmt"https://github.com/daylinmorgan/{pkgName}" gitUrl = fmt"https://github.com/daylinmorgan/{pkgName}"
selfExec fmt"doc --docRoot:{getCurrentDir()}/src/ --index:on --outdir:{deployDir} src/hwylterm/cli" for module in ["cli", "chooser"]:
selfExec fmt"doc --docRoot:{getCurrentDir()}/src/ --index:on --outdir:{deployDir} src/hwylterm/{module}"
selfExec fmt"doc --project --index:on --git.url:{gitUrl} --git.commit:main --outdir:{deployDir} --project src/{pkgName}.nim" selfExec fmt"doc --project --index:on --git.url:{gitUrl} --git.commit:main --outdir:{deployDir} --project src/{pkgName}.nim"
withDir deployDir: withDir deployDir:
mvFile(pkgName & ".html", "index.html") mvFile(pkgName & ".html", "index.html")
@ -24,3 +25,4 @@ task docs, "Deploy doc html + search index to public/ directory":
when withDir(thisDir(), system.dirExists("nimbledeps")): when withDir(thisDir(), system.dirExists("nimbledeps")):
--path:"./nimbledeps/pkgs2/cligen-1.7.5-f3ffe7329c8db755677d3ca377d02ff176cec8b1" --path:"./nimbledeps/pkgs2/cligen-1.7.5-f3ffe7329c8db755677d3ca377d02ff176cec8b1"
--path:"./nimbledeps/pkgs2/illwill-0.4.1-9c58351502f89a16caf031cbd1992ad3fdfd3c67"

View file

@ -1,8 +1,10 @@
##[ ##[
# Hwylterm # Hwylterm
[see bbansi](./hwylterm/bbansi.html) see also these utility modules:
[see cli](./hwylterm/cli.html)
- [cli](./hwylterm/cli.html), requires [cligen](https://github.com/c-blake/cligen)
- [chooser](./hwylterm/chooser.html), requires [illwill](https://github.com/johnnovak/illwill)
]## ]##
import hwylterm/[spin, bbansi] import hwylterm/[spin, bbansi]

139
src/hwylterm/chooser.nim Normal file
View file

@ -0,0 +1,139 @@
##[
# Hwylterm Chooser
```nim
import hwylterm/chooser
let items = ["a","b","c"]
let item = choose(items)
```
]##
import std/[enumerate, os, strutils, sequtils, sets, terminal]
template canImport(x): bool = compiles: import x
when not canImport(illwill): {.fatal: "hwylterm/choose requires illwill >= 0.4.1".}
import illwill
proc exitProc() {.noconv.} =
illwillDeInit()
showCursor()
proc quitProc() {.noconv.} =
exitProc()
quit(0)
type
State = object
lastKey: Key
buffer: string
selections: HashSet[Natural]
height, max, pos, low, high: Natural
func newState[T](things: openArray[T], height: Natural): State =
result.max = len(things) - 1
result.height = height
result.high = height
func up(s: var State) =
if s.pos > 0: dec s.pos
if (s.pos < s.low):
dec s.low
dec s.high
func down(s: var State) =
if s.pos < s.max:
inc s.pos
if ((s.pos - s.low) > s.height) and
(s.pos > s.high) and
(s.high < s.max):
inc s.low
inc s.high
func pressed(s: var State, k: Key) = s.lastKey = k
func select(s: var State ) =
s.selections =
symmetricDifference(s.selections, toHashSet([s.pos]))
proc clip(s: string, length: int): string =
if s.len > length: s[0..length]
else: s
# proc addHelp(s: var screen) =
func addThingsWindow[T](state: var State, things: openArray[T]) =
var window: string
for i, t in enumerate(things[state.low..state.high]):
window.add (
if (i + state.low) == state.pos: ">"
else: " "
)
window.add (
if (i + state.low) in state.selections: ">"
else: " "
)
window.add $t
window.add "\n"
state.buffer.add window
proc draw(s: var State) =
let maxWidth = terminalWidth()
var lines= (
s.buffer.splitLines().mapIt((" " & it).clip(maxWidth).alignLeft(maxWidth))
)
when defined(debugChoose):
lines = @[$s] & lines
for l in lines:
stdout.writeLine l
cursorUp lines.len
flushFile stdout
s.buffer = ""
proc getSelections[T](state: State, things: openArray[T]): seq[T] =
if state.selections.len == 0:
result.add things[state.pos]
for i in state.selections:
result.add things[i]
proc choose*[T](things: openArray[T], height: Natural = 6): seq[T] =
illwillInit(fullscreen = false)
setControlCHook(quitProc)
hideCursor()
var state = newState(things, height)
while true:
var key = getKey()
pressed(state, key)
case key:
of Key.None: discard
of Key.Down, Key.J:
down state
of Key.Up, Key.K:
up state
of Key.Tab:
select state
of Key.Enter:
exitProc()
return getSelections(state, things)
else: discard
addThingsWindow(state, things)
draw state
sleep 20
when isMainModule:
let items = LowercaseLetters.toSeq()
let item = choose(items)
echo "selected: ", item

View file

@ -4,8 +4,10 @@
Adapter to add hwylterm colors to cligen output. Adapter to add hwylterm colors to cligen output.
]## ]##
import std/[tables] import std/[tables]
import cligen
import ./bbansi import ./bbansi
template canImport(x): bool = compiles: import x
when not canImport(cligen): {.fatal: "hwylterm/cli requires cligen>= 1.7.5".}
import cligen
type type

View file

@ -5,7 +5,10 @@ author = "Daylin Morgan"
description = "bringing some fun (hwyl) to the terminal" description = "bringing some fun (hwyl) to the terminal"
license = "MIT" license = "MIT"
srcDir = "../src" srcDir = "../src"
namedBin = {"hwylterm/bbansi":"bbansi"}.toTable namedBin = {
"hwylterm/bbansi" :"bbansi",
"hwylterm/chooser":"hwylchoose"
}.toTable
requires "nim >= 2.0.8" requires "nim >= 2.0.8"