Compare commits

...

2 commits

4 changed files with 118 additions and 51 deletions

View file

@ -3,5 +3,5 @@ format:
uv run ruff format . uv run ruff format .
check: check:
uv run ruff check . uv run ruff check . --fix
uv run ty check . uv run ty check .

View file

@ -31,3 +31,66 @@ dev = [
[tool.ruff.lint.isort] [tool.ruff.lint.isort]
force-single-line = true force-single-line = true
[tool.ruff]
target-version = "py312"
[tool.ruff.lint]
ignore = [
"E501",
"COM812", # flake8-commas
]
select = [
"F", # Pyflakes
"E", # Error
"W", # Warning
# "C90", # mccabe
"I", # isort
"N", # pep8-naming
# "D", # pydocstyle
# "UP", # pyupgrade
"YTT", # flake8-2020
# "ANN", # flake8-annotations
# "S", # flake8-bandit
# "BLE", # flake8-blind-except
# "FBT", # flake8-boolean-trap
"B", # flake8-bugbear
"A", # flake8-builtins
"COM", # flake8-commas
"C4", # flake8-comprehensions
"DTZ", # flake8-datetimez
"T10", # flake8-debugger
"DJ", # flake8-django
"EM", # flake8-errmsg
"EXE", # flake8-executable
"ISC", # flake8-implicit-str-concat
"ICN", # flake8-import-conventions
"G", # flake8-logging-format
# "INP", # flake8-no-pep420
"PIE", # flake8-pie
"T20", # flake8-print
"PYI", # flake8-pyi
"PT", # flake8-pytest-style
"Q", # flake8-quotes
"RSE", # flake8-raise
"RET", # flake8-return
# "SLF", # flake8-self
# "SIM", # flake8-simplify
"TID", # flake8-tidy-imports
"TCH", # flake8-type-checking
# "INT", # flake8-gettext
# "ARG", # flake8-unused-arguments
"PTH", # flake8-use-pathlib
# "ERA", # eradicate
"PD", # pandas-vet
"PGH", # pygrep-hooks
# "PL", # Pylint
# "PLC", # Convention
"PLE", # Error
# "PLR", # Refactor
"PLW", # Warning
# "TRY", # tryceratops
"NPY", # NumPy-specific rules
# "RUF", # Ruff-specific rules
]

View file

View file

@ -11,25 +11,28 @@
# /// # ///
from __future__ import annotations from __future__ import annotations
import os import os
import re import re
import shutil import shutil
import subprocess import subprocess
from dataclasses import dataclass from dataclasses import dataclass
from pathlib import Path from pathlib import Path
from typing import List, Optional, Tuple from typing import List
from typing import Optional
from typing import Tuple
import typer import typer
from pydantic import Field
from pydantic_settings import BaseSettings, SettingsConfigDict
from rich.console import Console
from rich.table import Table
from rich.prompt import Prompt, Confirm
from rich.syntax import Syntax
from rich.panel import Panel
from iterfzf import iterfzf from iterfzf import iterfzf
from pydantic import Field
from pydantic_settings import BaseSettings
from pydantic_settings import SettingsConfigDict
from rich.console import Console
from rich.panel import Panel
from rich.prompt import Confirm
from rich.prompt import Prompt
from rich.syntax import Syntax
from rich.table import Table
app = typer.Typer( app = typer.Typer(
help="Workspace management tool", help="Workspace management tool",
@ -94,8 +97,8 @@ def resolve_paths(workspaces_name: Optional[str]) -> Tuple[Settings, Path, Path]
""" """
base_settings = Settings.from_env_and_override(workspaces_name) base_settings = Settings.from_env_and_override(workspaces_name)
name = base_settings.workspaces_name name = base_settings.workspaces_name
repos_dir = Path(os.path.expanduser(f"~/{name}")).resolve() repos_dir = Path(f"~/{name}").expanduser().resolve()
workspaces_dir = Path(os.path.expanduser(f"~/{name}.workspaces")).resolve() workspaces_dir = Path(f"~/{name}.workspaces").expanduser().resolve()
return base_settings, repos_dir, workspaces_dir return base_settings, repos_dir, workspaces_dir
@ -209,17 +212,17 @@ def find_workspace_dir(workspaces_dir: Path, workspace_name: Optional[str]) -> P
cwd = Path.cwd().resolve() cwd = Path.cwd().resolve()
try: try:
cwd.relative_to(workspaces_dir) cwd.relative_to(workspaces_dir)
except ValueError: except ValueError as e:
workspace_root = pick_workspace_with_iterfzf(list_workspaces(workspaces_dir)) workspace_root = pick_workspace_with_iterfzf(list_workspaces(workspaces_dir))
if workspace_root is None: if workspace_root is None:
console.print("[red]No workspace selected. Exiting.[/red]") console.print("[red]No workspace selected. Exiting.[/red]")
raise typer.Exit(code=1) raise typer.Exit(code=1) from e
return workspace_root return workspace_root
# The top-level workspace directory is the first component under workspaces_dir # The top-level workspace directory is the first component under workspaces_dir
rel = cwd.relative_to(workspaces_dir) rel = cwd.relative_to(workspaces_dir)
workspace_root = workspaces_dir / rel.parts[0] workspace_root = workspaces_dir / rel.parts[0]
print(f"Using workspace: {workspace_root}") console.print(f"Using workspace: {workspace_root}")
return workspace_root return workspace_root
@ -1394,6 +1397,7 @@ def pick_project(base_dir: Path) -> Optional[str]:
["fzf", "--reverse", f"--header=Select project from {base_dir.name} >"], ["fzf", "--reverse", f"--header=Select project from {base_dir.name} >"],
input="\n".join(subdirs), input="\n".join(subdirs),
text=True, text=True,
check=False,
capture_output=True, capture_output=True,
) )
@ -1423,41 +1427,40 @@ def create_detached_session(
clear_tmux_env=True, clear_tmux_env=True,
) )
return r.returncode == 0 return r.returncode == 0
else: r = run_tmux(
r = run_tmux( ["new-session", "-Ad", "-s", session_name, "-c", str(path_name)],
["new-session", "-Ad", "-s", session_name, "-c", str(path_name)], clear_tmux_env=True,
clear_tmux_env=True, )
) if r.returncode != 0:
if r.returncode != 0: return False
return False
r = run_tmux( r = run_tmux(
[ [
"split-window", "split-window",
"-vb", "-vb",
"-t", "-t",
session_name, session_name,
"-c", "-c",
str(path_name), str(path_name),
"-p", "-p",
"70", "70",
], ],
clear_tmux_env=True, clear_tmux_env=True,
) )
if r.returncode != 0: if r.returncode != 0:
return False return False
r = run_tmux( r = run_tmux(
[ [
"send-keys", "send-keys",
"-t", "-t",
session_name, session_name,
"nvim '+Telescope find_files'", "nvim '+Telescope find_files'",
"Enter", "Enter",
], ],
clear_tmux_env=True, clear_tmux_env=True,
) )
return r.returncode == 0 return r.returncode == 0
def create_if_needed_and_attach( def create_if_needed_and_attach(
@ -1494,6 +1497,7 @@ def attach_to_first_session() -> None:
list_proc = subprocess.run( list_proc = subprocess.run(
["tmux", "list-sessions", "-F", "#{session_name}"], ["tmux", "list-sessions", "-F", "#{session_name}"],
text=True, text=True,
check=False,
capture_output=True, capture_output=True,
) )
if list_proc.returncode != 0 or not list_proc.stdout.strip(): if list_proc.returncode != 0 or not list_proc.stdout.strip():
@ -1552,12 +1556,12 @@ def tmux_cli_attach(
console.print(f"[red]Workspace '{workspace}' does not exist at {ws_dir}[/red]") console.print(f"[red]Workspace '{workspace}' does not exist at {ws_dir}[/red]")
raise typer.Exit(1) raise typer.Exit(1)
# pick repo in workspace # pick repo in workspace
repo = pick_repo_with_iterfzf([dir for dir in ws_dir.iterdir() if dir.is_dir()]) repo = pick_repo_with_iterfzf([directory for directory in ws_dir.iterdir() if directory.is_dir()])
if not repo: if not repo:
console.print("[red]No repo selected. Exiting.[/red]") console.print("[red]No repo selected. Exiting.[/red]")
raise typer.Exit(1) raise typer.Exit(1)
session_name = f"ω|{ws_dir.name}|{repo.name}" session_name = f"ω|{ws_dir.name}|{repo.name}"
print(f"Session name: {session_name}") console.print(f"Session name: {session_name}")
create_if_needed_and_attach(session_name, repo, False) create_if_needed_and_attach(session_name, repo, False)