Compare commits

..

2 commits

4 changed files with 118 additions and 51 deletions

View file

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

View file

@ -31,3 +31,66 @@ dev = [
[tool.ruff.lint.isort]
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
import os
import re
import shutil
import subprocess
from dataclasses import dataclass
from pathlib import Path
from typing import List, Optional, Tuple
from typing import List
from typing import Optional
from typing import Tuple
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 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(
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)
name = base_settings.workspaces_name
repos_dir = Path(os.path.expanduser(f"~/{name}")).resolve()
workspaces_dir = Path(os.path.expanduser(f"~/{name}.workspaces")).resolve()
repos_dir = Path(f"~/{name}").expanduser().resolve()
workspaces_dir = Path(f"~/{name}.workspaces").expanduser().resolve()
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()
try:
cwd.relative_to(workspaces_dir)
except ValueError:
except ValueError as e:
workspace_root = pick_workspace_with_iterfzf(list_workspaces(workspaces_dir))
if workspace_root is None:
console.print("[red]No workspace selected. Exiting.[/red]")
raise typer.Exit(code=1)
raise typer.Exit(code=1) from e
return workspace_root
# The top-level workspace directory is the first component under workspaces_dir
rel = cwd.relative_to(workspaces_dir)
workspace_root = workspaces_dir / rel.parts[0]
print(f"Using workspace: {workspace_root}")
console.print(f"Using workspace: {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} >"],
input="\n".join(subdirs),
text=True,
check=False,
capture_output=True,
)
@ -1423,41 +1427,40 @@ def create_detached_session(
clear_tmux_env=True,
)
return r.returncode == 0
else:
r = run_tmux(
["new-session", "-Ad", "-s", session_name, "-c", str(path_name)],
clear_tmux_env=True,
)
if r.returncode != 0:
return False
r = run_tmux(
["new-session", "-Ad", "-s", session_name, "-c", str(path_name)],
clear_tmux_env=True,
)
if r.returncode != 0:
return False
r = run_tmux(
[
"split-window",
"-vb",
"-t",
session_name,
"-c",
str(path_name),
"-p",
"70",
],
clear_tmux_env=True,
)
if r.returncode != 0:
return False
r = run_tmux(
[
"split-window",
"-vb",
"-t",
session_name,
"-c",
str(path_name),
"-p",
"70",
],
clear_tmux_env=True,
)
if r.returncode != 0:
return False
r = run_tmux(
[
"send-keys",
"-t",
session_name,
"nvim '+Telescope find_files'",
"Enter",
],
clear_tmux_env=True,
)
return r.returncode == 0
r = run_tmux(
[
"send-keys",
"-t",
session_name,
"nvim '+Telescope find_files'",
"Enter",
],
clear_tmux_env=True,
)
return r.returncode == 0
def create_if_needed_and_attach(
@ -1494,6 +1497,7 @@ def attach_to_first_session() -> None:
list_proc = subprocess.run(
["tmux", "list-sessions", "-F", "#{session_name}"],
text=True,
check=False,
capture_output=True,
)
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]")
raise typer.Exit(1)
# 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:
console.print("[red]No repo selected. Exiting.[/red]")
raise typer.Exit(1)
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)