mirror of
https://github.com/daylinmorgan/hwylterm.git
synced 2024-12-21 18:50:44 -06:00
implement an interactive "chooser"
This commit is contained in:
parent
398b77cc2b
commit
38b1854c12
5 changed files with 153 additions and 5 deletions
|
@ -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"
|
||||||
|
|
|
@ -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
139
src/hwylterm/chooser.nim
Normal 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
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
|
|
Loading…
Reference in a new issue