diff --git a/nimble.lock b/nimble.lock index 9cb81ce..dd77401 100644 --- a/nimble.lock +++ b/nimble.lock @@ -3,12 +3,12 @@ "packages": { "hwylterm": { "version": "0.1.0", - "vcsRevision": "cbeefd675c0884feebad4dc62910092519f8b2ed", + "vcsRevision": "f1cc95f86edcc00665fc8280f57edc0e83d461f9", "url": "https://github.com/daylinmorgan/hwylterm", "downloadMethod": "git", "dependencies": [], "checksums": { - "sha1": "4d043352ad07388d0181c0fd4cf325317e9306d0" + "sha1": "433522bac3b8f3caae252a1a42867ed8dc91f4d2" } }, "illwill": { diff --git a/src/project.nim b/src/project.nim index c446cd6..10414f2 100644 --- a/src/project.nim +++ b/src/project.nim @@ -9,6 +9,7 @@ type updated*: Time open*: bool matched*: bool + tmuxinfo*: string proc pathToName*(path: string): string = splitPath(path)[1].replace(".", "_") @@ -16,10 +17,8 @@ proc pathToName*(path: string): string = proc newProject*(path: string, open: bool, name = "", named: bool = false): Project = result.location = path result.name = - if name != "": - name - else: - pathToName(path) + if name != "": name + else: pathToName(path) result.updated = getLastModificationTime(path) result.open = open result.named = named @@ -65,10 +64,18 @@ proc `<-`(candidates: var Table[string, seq[string]], path: string) = if candidates.hasKeyOrPut(name, @[path]): candidates[name].add path + +func projectFromSession(s: TmuxSession): Project = + result.name = s.name + result.open = true + result.tmuxinfo = s.info + + + proc findProjects*(open: bool = false): seq[Project] = let tsmConfig = loadTsmConfig() var candidates: Table[string, seq[string]] - var sessions = tmux.sessions.toHashSet() + var sessions = tmux.sessions.mapIt(it.name).toHashSet() for devDir in tsmConfig.paths: for (kind, path) in walkDir(devDir): @@ -109,7 +116,8 @@ proc findProjects*(open: bool = false): seq[Project] = else: cmp(y.updated, x.updated) if sessions.len > 0: - result = sessions.toSeq().mapIt(newUnknownProject(it)) & result + result = tmux.sessions.filterIt(it.name in sessions).mapIt(projectFromSession(it)) & result + # result = sessions.toSeq().mapIt(newUnknownProject(it)) & result if len(result) == 0: termError "nothing to select, check your [yellow]$TSM_PATHS" diff --git a/src/selector.nim b/src/selector.nim index 29541cd..47c6791 100644 --- a/src/selector.nim +++ b/src/selector.nim @@ -1,4 +1,5 @@ import std/[enumerate, os, strformat, strutils, terminal] +import hwylterm from illwill import illwillDeinit, illwillInit, getKey, Key import term, project @@ -34,9 +35,40 @@ type cursor: Cursor projectIdx: Natural projects: seq[Project] + selected: seq[Natural] var state = State() +func clip( + s: string, + width: int = state.buffer.width - 3 +): string = + result = + if s.len > width: s[0..width] + else: s + +func highlight(p: Project): string = + if p.location == "": "green" + elif p.open: "bold yellow" + elif p.named: "cyan" + else: "default" + +proc display(s: State, p: Project): Bbstring = + let + name = p.name.clip + input = s.input.clip + + if p.matched: + result.add input.bb("red") + if input.len < name.len: + result.add ($name[input.len..^1]).bb(p.highlight) + else: + result.add name.bb(p.highlight) + + if p.tmuxinfo != "": + # will fail without clip! + result.add p.tmuxinfo.bb("faint") + func addLine(b: var Buffer, text: string) = b.buffer.add (" " & text).alignLeft(b.width) & "\n" @@ -54,14 +86,6 @@ proc addInput(b: var Buffer) = func numLines(b: Buffer): int = b.buffer.count '\n' -func clip( - s: string, - width: int = state.buffer.width - 3 -): string = - result = - if s.len > width: s[0..width] - else: s - proc draw(b: var Buffer) = while b.numLines < b.height: b.addLine "" @@ -130,26 +154,6 @@ proc getProject(): Project = var idx = state.cursor.y - state.cursor.min + state.projectIdx return projects[idx] -func highlight(p: Project): string = - if p.location == "": "green" - elif p.open: "bold yellow" - elif p.named: "cyan" - else: "default" - -proc addProject(b: var Buffer, project: Project, selected: bool) = - let - name = project.name.clip - input = state.input.clip - cur = (if selected: "> " else: " ") - - if project.matched: - var displayName = fmt"[red]{input}[/]" - if input.len < name.len: - displayName &= - fmt"[{project.highlight}]{name[input.len..^1]}[/{project.highlight}]" - b.addLine(cur & $displayName.bb) - else: - b.addLine(cur & $name.bb(project.highlight)) proc addInfoLine(b: var Buffer) = let @@ -162,15 +166,16 @@ proc addInfoLine(b: var Buffer) = proc addProjects(b: var Buffer) = let - projects = sortProjects() - maxNumProjects = state.buffer.height - state.buffer.inputPad + # NOTE: is a bounds error possible? + nProjects = min(state.buffer.height - state.buffer.inputPad, state.projects.len() - state.projectIdx) + slice = state.projectIdx..<(state.projectIdx + nProjects) + projects = sortProjects()[slice] - var numProjects = 1 - for (i, project) in enumerate(projects[state.projectIdx..^1]): - b.addProject(project, state.cursor.y == numProjects) - inc numProjects - if numProjects > maxNumProjects: - break + for (i, project) in enumerate(projects): + let cursorArrow = + if state.cursor.y == i + 1: "> " + else: " " + b.addLine(cursorArrow & $display(state, project)) proc reset() = state.cursor.y = state.cursor.min diff --git a/src/tmuxutils.nim b/src/tmuxutils.nim index b2d7ecb..4010c2a 100644 --- a/src/tmuxutils.nim +++ b/src/tmuxutils.nim @@ -1,12 +1,15 @@ -import std/[os, osproc, strformat, strutils] +import std/[os, osproc, strformat, strutils, sequtils] import term type - Tmux = object - active: bool - sessions*: seq[string] - + TmuxSession* = object + name*: string + info*: string + Tmux* = object + active*: bool + sessions*: seq[TmuxSession] + proc checkExe(names: varargs[string]) = for name in names: if findExe(name) == "": @@ -33,11 +36,18 @@ template cmd(tmux: Tmux, args: string) = tmuxError(args) # discard tmux.cmdGet args +proc toSession(s: string): TmuxSession = + let ss = s.split(":", 1) + if ss.len != 2: + tmuxError "failed to parse session info from: " & s + result.name = ss[0] + result.info = ss[1] + proc newTmux(): Tmux = result.active = existsEnv("TMUX") # check if server is active if execCmdEx("tmux run").exitCode == 0: - result.sessions = (result.cmdGet "list-sessions -F '#S'").strip().split("\n") + result.sessions = (result.cmdGet "list-sessions").strip().split("\n").mapIt(toSession(it)) proc attach*(t: Tmux, session: string) = let args = if t.active: "switch-client -t" else: "attach -t" diff --git a/src/tsm.nim b/src/tsm.nim index 81326df..452f229 100644 --- a/src/tsm.nim +++ b/src/tsm.nim @@ -1,3 +1,4 @@ +import std/sequtils import ./[selector, project, tmuxutils] import hwylterm, hwylterm/hwylcli @@ -7,7 +8,7 @@ proc tsm(open: bool = false) = project = selectProject projects selected = project.name - if selected notin tmux.sessions: + if selected notin tmux.sessions.mapIt(it.name): tmux.new(project.name, project.location) else: tmux.attach project.name diff --git a/tsm.nimble b/tsm.nimble index c9b8fba..93ab95a 100644 --- a/tsm.nimble +++ b/tsm.nimble @@ -12,6 +12,6 @@ binDir = "bin" requires "nim >= 2.0.0" requires "illwill >= 0.4.1" -requires "https://github.com/daylinmorgan/hwylterm#cbeefd67" +requires "https://github.com/daylinmorgan/hwylterm#f1cc95f8" requires "https://github.com/usu-dev/usu-nim"