feat: handle duplicates/and term improvements

This commit is contained in:
Daylin Morgan 2023-10-03 13:06:34 -05:00
parent 1d08c01e6e
commit 8673532e2a
Signed by: daylin
GPG key ID: C1E52E7DD81DF79F
4 changed files with 77 additions and 31 deletions

View file

@ -1,7 +1,5 @@
import std/[algorithm, os, sequtils, sets, strutils, tables, times]
import bbansi
import utils
import std/[algorithm, os, sequtils, sets, strutils, sugar, tables, times]
import tmuxutils, term
type
Project* = object
@ -13,22 +11,45 @@ type
proc pathToName(path: string): string = splitPath(path)[1].replace(".", "_")
proc newProject(path: string, open: bool): Project =
proc newProject(path: string, open: bool, name = ""): Project =
result.location = path
result.name = path.pathToName()
result.name =
if name != "": name
else: path.pathToName()
result.updated = getLastModificationTime(path)
result.open = open
proc newUnknownProject(name: string): Project =
result.name = name
result.open = true
proc getTsmDirs(): seq[string] =
let tsmDirs = getEnv("TSM_DIRS")
if tsmDirs == "":
bbEcho "[red]Please set [cyan]$TSM_DIRS[/] to a colon-delimited list of paths"
quit QuitFailure
termQuit "Please set [yellow]$TSM_DIRS[/] to a colon-delimited list of paths"
result = tsmDirs.split(":")
proc findDuplicateProjects(paths: seq[string],
sessions: var HashSet[string]): seq[Project] =
var candidates: Table[string, seq[string]]
for p in paths:
candidates[p] = p.split(DirSep)
let maxExtra = min(candidates.values.toSeq.mapIt(it.len))
for i in 2..maxExtra:
let deduplicated = collect:
for path, pathSplit in candidates.pairs:
(name: pathSplit[^i..^1].joinPath, path: path)
if deduplicated.mapIt(it[0]).toHashSet.len == candidates.len:
for (name, path) in deduplicated:
let open = name in sessions
result.add newProject(path, open, name)
if open: sessions.excl name
break
if result.len == 0:
termQuit "failed to deduplicate these paths:" & paths.join(", ")
proc findProjects*(open: bool = false): seq[Project] =
var candidates: Table[string, seq[string]]
var sessions = tmux.sessions.toHashSet()
@ -36,20 +57,19 @@ proc findProjects*(open: bool = false): seq[Project] =
for devDir in getTsmDirs():
for path in walkDir(devDir):
if ({path.kind} * {pcFile, pcLinkToFile}).len > 0: continue
let name = path.path.tailDir()
if name in candidates:
let name = path.path.splitPath.tail
if candidates.hasKeyOrPut(name, @[path.path]):
candidates[name].add path.path
else:
candidates[name] = @[path.path]
# TODO: improve this to handle duplicate entries by appending parent?
for name, paths in candidates:
for name, paths in candidates.pairs:
if len(paths) == 1:
let path = paths[0]
let open = path.pathToName in sessions
let
path = paths[0]
open = path.pathToName in sessions
result.add newProject(path, open)
if open:
sessions.excl toHashSet([path.pathToName])
if open: sessions.excl path.pathToName
else:
result &= findDuplicateProjects(paths, sessions)
if open:
result = result.filterIt(it.open)
@ -64,7 +84,8 @@ proc findProjects*(open: bool = false): seq[Project] =
result = sessions.toSeq().mapIt(newUnknownProject(it)) & result
if len(result) == 0:
echo "nothing to select"
quit 1
termError "nothing to select, check your [yellow]$TSM_DIRS"
termEcho "searched these directories: "
echo getTsmDirs().mapIt(" " & it).join("\n")
quit QuitFailure

17
src/term.nim Normal file
View file

@ -0,0 +1,17 @@
import std/strutils
import bbansi
const
sep = " [magenta]|[/] "
prefix = "[cyan]tsm[/]" & sep
errPrefix = prefix & "[red]error[/]" & sep
proc termEcho*(x: varargs[string, `$`]) =
bbEcho prefix, x.join(" ")
proc termError*(x: varargs[string, `$`]) =
bbEcho errPrefix, x.join(" ")
proc termQuit*(x: varargs[string, `$`]) =
termError x
quit QuitFailure

View file

@ -1,5 +1,7 @@
import std/[os, osproc, strformat, strutils]
import term
type
Tmux = object
active: bool
@ -8,24 +10,31 @@ type
proc checkExe(names: varargs[string]) =
for name in names:
if findExe(name) == "":
echo "tsm requires " & name
termError "tsm requires " & name
checkExe "tmux"
proc cmdGet(tmux: Tmux, args: string): string =
let (output, code) = execCmdEx("tmux " & args)
if code != 0:
echo "ERROR: failed to run: tmux ", args, "see below for error"
proc tmuxError(args: string, output: string = "") =
termError "failed to run: [bold]tmux", args
if output != "":
termError "see below for error"
echo output
quit QuitFailure
return output
proc cmdGet(tmux: Tmux, args: string): string =
let (output, code) = execCmdEx("tmux " & args)
if code == 0: return output
tmuxError args, output
template cmd(tmux: Tmux, args: string) =
discard execCmd("tmux " & args)
let code = execCmd "tmux " & args
if code != 0: tmuxError(args)
# discard tmux.cmdGet args
proc newTmux(): Tmux =
result.active = existsEnv("TMUX")
# check if server is active?
# check if server is active
if execCmdEx("tmux run").exitCode == 0:
result.sessions = (
result.cmdGet "list-sessions -F '#S'"
@ -44,5 +53,4 @@ proc new*(t: Tmux, session: string, loc: string) =
else:
t.cmd fmt"new-session -s {session} -c {loc}"
let tmux* = newTmux()

View file

@ -1,6 +1,6 @@
import std/[tables]
import selector, project, utils
import selector, project, tmuxutils
proc tsm(open: bool = false) =
let