mirror of
https://github.com/daylinmorgan/tsm.git
synced 2024-12-22 05:00:44 -06:00
Compare commits
3 commits
5ca52175f6
...
62ca20fd9b
Author | SHA1 | Date | |
---|---|---|---|
62ca20fd9b | |||
093446e4a6 | |||
97cac40a11 |
8 changed files with 80 additions and 56 deletions
|
@ -3,12 +3,12 @@
|
||||||
"packages": {
|
"packages": {
|
||||||
"hwylterm": {
|
"hwylterm": {
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"vcsRevision": "cbeefd675c0884feebad4dc62910092519f8b2ed",
|
"vcsRevision": "f1cc95f86edcc00665fc8280f57edc0e83d461f9",
|
||||||
"url": "https://github.com/daylinmorgan/hwylterm",
|
"url": "https://github.com/daylinmorgan/hwylterm",
|
||||||
"downloadMethod": "git",
|
"downloadMethod": "git",
|
||||||
"dependencies": [],
|
"dependencies": [],
|
||||||
"checksums": {
|
"checksums": {
|
||||||
"sha1": "4d043352ad07388d0181c0fd4cf325317e9306d0"
|
"sha1": "433522bac3b8f3caae252a1a42867ed8dc91f4d2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"illwill": {
|
"illwill": {
|
||||||
|
|
|
@ -36,11 +36,11 @@
|
||||||
},
|
},
|
||||||
"nixpkgs_2": {
|
"nixpkgs_2": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1730958623,
|
"lastModified": 1731531548,
|
||||||
"narHash": "sha256-JwQZIGSYnRNOgDDoIgqKITrPVil+RMWHsZH1eE1VGN0=",
|
"narHash": "sha256-sz8/v17enkYmfpgeeuyzniGJU0QQBfmAjlemAUYhfy8=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "85f7e662eda4fa3a995556527c87b2524b691933",
|
"rev": "24f0d4acd634792badd6470134c387a3b039dace",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
|
@ -43,7 +43,7 @@
|
||||||
pname = "tsm";
|
pname = "tsm";
|
||||||
version = "2024.1001-unstable";
|
version = "2024.1001-unstable";
|
||||||
src = ../.;
|
src = ../.;
|
||||||
nimbleDepsHash = "sha256-5xK3KsLLFZ72qWreIHW0SBL3+5H6Ll6o0spMfW1iPX8=";
|
nimbleDepsHash = "sha256-Z+KX1r4tpLQc3hTp2HYI6aLM2cwUCFlWIU8QP/Jx7h4=";
|
||||||
nimFlags = [
|
nimFlags = [
|
||||||
"-d:TsmVersion=v${version}"
|
"-d:TsmVersion=v${version}"
|
||||||
];
|
];
|
||||||
|
|
|
@ -9,6 +9,7 @@ type
|
||||||
updated*: Time
|
updated*: Time
|
||||||
open*: bool
|
open*: bool
|
||||||
matched*: bool
|
matched*: bool
|
||||||
|
tmuxinfo*: string
|
||||||
|
|
||||||
proc pathToName*(path: string): string =
|
proc pathToName*(path: string): string =
|
||||||
splitPath(path)[1].replace(".", "_")
|
splitPath(path)[1].replace(".", "_")
|
||||||
|
@ -16,10 +17,8 @@ proc pathToName*(path: string): string =
|
||||||
proc newProject*(path: string, open: bool, name = "", named: bool = false): Project =
|
proc newProject*(path: string, open: bool, name = "", named: bool = false): Project =
|
||||||
result.location = path
|
result.location = path
|
||||||
result.name =
|
result.name =
|
||||||
if name != "":
|
if name != "": name
|
||||||
name
|
else: pathToName(path)
|
||||||
else:
|
|
||||||
pathToName(path)
|
|
||||||
result.updated = getLastModificationTime(path)
|
result.updated = getLastModificationTime(path)
|
||||||
result.open = open
|
result.open = open
|
||||||
result.named = named
|
result.named = named
|
||||||
|
@ -65,10 +64,18 @@ proc `<-`(candidates: var Table[string, seq[string]], path: string) =
|
||||||
if candidates.hasKeyOrPut(name, @[path]):
|
if candidates.hasKeyOrPut(name, @[path]):
|
||||||
candidates[name].add 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] =
|
proc findProjects*(open: bool = false): seq[Project] =
|
||||||
let tsmConfig = loadTsmConfig()
|
let tsmConfig = loadTsmConfig()
|
||||||
var candidates: Table[string, seq[string]]
|
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 devDir in tsmConfig.paths:
|
||||||
for (kind, path) in walkDir(devDir):
|
for (kind, path) in walkDir(devDir):
|
||||||
|
@ -109,7 +116,8 @@ proc findProjects*(open: bool = false): seq[Project] =
|
||||||
else: cmp(y.updated, x.updated)
|
else: cmp(y.updated, x.updated)
|
||||||
|
|
||||||
if sessions.len > 0:
|
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:
|
if len(result) == 0:
|
||||||
termError "nothing to select, check your [yellow]$TSM_PATHS"
|
termError "nothing to select, check your [yellow]$TSM_PATHS"
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import std/[enumerate, os, strformat, strutils, terminal]
|
import std/[enumerate, os, strformat, strutils, terminal]
|
||||||
|
import hwylterm
|
||||||
from illwill import illwillDeinit, illwillInit, getKey, Key
|
from illwill import illwillDeinit, illwillInit, getKey, Key
|
||||||
import term, project
|
import term, project
|
||||||
|
|
||||||
|
@ -34,9 +35,40 @@ type
|
||||||
cursor: Cursor
|
cursor: Cursor
|
||||||
projectIdx: Natural
|
projectIdx: Natural
|
||||||
projects: seq[Project]
|
projects: seq[Project]
|
||||||
|
selected: seq[Natural]
|
||||||
|
|
||||||
var state = State()
|
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) =
|
func addLine(b: var Buffer, text: string) =
|
||||||
b.buffer.add (" " & text).alignLeft(b.width) & "\n"
|
b.buffer.add (" " & text).alignLeft(b.width) & "\n"
|
||||||
|
|
||||||
|
@ -54,14 +86,6 @@ proc addInput(b: var Buffer) =
|
||||||
func numLines(b: Buffer): int =
|
func numLines(b: Buffer): int =
|
||||||
b.buffer.count '\n'
|
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) =
|
proc draw(b: var Buffer) =
|
||||||
while b.numLines < b.height:
|
while b.numLines < b.height:
|
||||||
b.addLine ""
|
b.addLine ""
|
||||||
|
@ -130,26 +154,6 @@ proc getProject(): Project =
|
||||||
var idx = state.cursor.y - state.cursor.min + state.projectIdx
|
var idx = state.cursor.y - state.cursor.min + state.projectIdx
|
||||||
return projects[idx]
|
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) =
|
proc addInfoLine(b: var Buffer) =
|
||||||
let
|
let
|
||||||
|
@ -162,15 +166,16 @@ proc addInfoLine(b: var Buffer) =
|
||||||
|
|
||||||
proc addProjects(b: var Buffer) =
|
proc addProjects(b: var Buffer) =
|
||||||
let
|
let
|
||||||
projects = sortProjects()
|
# NOTE: is a bounds error possible?
|
||||||
maxNumProjects = state.buffer.height - state.buffer.inputPad
|
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):
|
||||||
for (i, project) in enumerate(projects[state.projectIdx..^1]):
|
let cursorArrow =
|
||||||
b.addProject(project, state.cursor.y == numProjects)
|
if state.cursor.y == i + 1: "> "
|
||||||
inc numProjects
|
else: " "
|
||||||
if numProjects > maxNumProjects:
|
b.addLine(cursorArrow & $display(state, project))
|
||||||
break
|
|
||||||
|
|
||||||
proc reset() =
|
proc reset() =
|
||||||
state.cursor.y = state.cursor.min
|
state.cursor.y = state.cursor.min
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
import std/[os, osproc, strformat, strutils]
|
import std/[os, osproc, strformat, strutils, sequtils]
|
||||||
|
|
||||||
import term
|
import term
|
||||||
|
|
||||||
type
|
type
|
||||||
Tmux = object
|
TmuxSession* = object
|
||||||
active: bool
|
name*: string
|
||||||
sessions*: seq[string]
|
info*: string
|
||||||
|
Tmux* = object
|
||||||
|
active*: bool
|
||||||
|
sessions*: seq[TmuxSession]
|
||||||
|
|
||||||
proc checkExe(names: varargs[string]) =
|
proc checkExe(names: varargs[string]) =
|
||||||
for name in names:
|
for name in names:
|
||||||
if findExe(name) == "":
|
if findExe(name) == "":
|
||||||
|
@ -33,11 +36,18 @@ template cmd(tmux: Tmux, args: string) =
|
||||||
tmuxError(args)
|
tmuxError(args)
|
||||||
# discard tmux.cmdGet 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 =
|
proc newTmux(): Tmux =
|
||||||
result.active = existsEnv("TMUX")
|
result.active = existsEnv("TMUX")
|
||||||
# check if server is active
|
# check if server is active
|
||||||
if execCmdEx("tmux run").exitCode == 0:
|
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) =
|
proc attach*(t: Tmux, session: string) =
|
||||||
let args = if t.active: "switch-client -t" else: "attach -t"
|
let args = if t.active: "switch-client -t" else: "attach -t"
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import std/sequtils
|
||||||
import ./[selector, project, tmuxutils]
|
import ./[selector, project, tmuxutils]
|
||||||
import hwylterm, hwylterm/hwylcli
|
import hwylterm, hwylterm/hwylcli
|
||||||
|
|
||||||
|
@ -7,7 +8,7 @@ proc tsm(open: bool = false) =
|
||||||
project = selectProject projects
|
project = selectProject projects
|
||||||
selected = project.name
|
selected = project.name
|
||||||
|
|
||||||
if selected notin tmux.sessions:
|
if selected notin tmux.sessions.mapIt(it.name):
|
||||||
tmux.new(project.name, project.location)
|
tmux.new(project.name, project.location)
|
||||||
else:
|
else:
|
||||||
tmux.attach project.name
|
tmux.attach project.name
|
||||||
|
|
|
@ -12,6 +12,6 @@ binDir = "bin"
|
||||||
|
|
||||||
requires "nim >= 2.0.0"
|
requires "nim >= 2.0.0"
|
||||||
requires "illwill >= 0.4.1"
|
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"
|
requires "https://github.com/usu-dev/usu-nim"
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue