Add Fish module

This commit is contained in:
Joscha 2026-01-06 20:00:31 +01:00
parent fe97d02469
commit 13012429d1
2 changed files with 109 additions and 0 deletions

View file

@ -1,9 +1,11 @@
from .echo import Echo from .echo import Echo
from .files import Files from .files import Files
from .fish import Fish
from .pacman import Pacman from .pacman import Pacman
__all__: list[str] = [ __all__: list[str] = [
"Echo", "Echo",
"Files", "Files",
"Fish",
"Pacman", "Pacman",
] ]

107
pasch/modules/fish.py Normal file
View file

@ -0,0 +1,107 @@
import os
from dataclasses import dataclass
from pasch.file.text import TextFile
from pasch.modules.files import Files
from pasch.modules.pacman import Pacman
from pasch.orchestrator import Module, Orchestrator
from pasch.util import prompt, run_execute
def escape(s: str) -> str:
# The only meaningful escape sequences in single quotes are `\'`, which
# escapes a single quote and `\\`, which escapes the backslash symbol.
ESCAPES = {"'": "\\'", "\\": "\\\\"}
escaped = "".join(ESCAPES.get(c, c) for c in s)
return f"'{escaped}'"
@dataclass
class Raw:
string: str
type FishStr = str | Raw
def fescape(s: FishStr) -> str:
if isinstance(s, Raw):
return s.string
return escape(s)
class Fish(Module):
def __init__(
self,
orchestrator: Orchestrator,
files: Files,
pacman: Pacman,
) -> None:
super().__init__(orchestrator)
self._files = files
self._pacman = pacman
self.path: list[FishStr] = []
self.abbrs: dict[str, FishStr] = {}
self.env_vars: dict[str, FishStr] = {}
self.commands: list[str] = []
self.interactive_commands: list[str] = []
def add_to_path(self, value: FishStr) -> None:
self.path.append(value)
def add_abbr(self, name: str, replacement: FishStr) -> None:
self.abbrs[name] = replacement
def add_env_var(self, name: str, value: FishStr) -> None:
self.env_vars[name] = value
def add_command(self, command: str) -> None:
self.commands.append(command)
def add_interactive(self, command: str) -> None:
self.interactive_commands.append(command)
def configure(self) -> None:
file = TextFile()
file.tag(comment="#")
# Commands set by the user should always appear after generated commands
commands = self.commands
interactive_commands = self.interactive_commands
self.commands = []
self.interactive_commands = []
if self.path:
segments = " ".join(fescape(s) for s in self.path)
self.add_command(f"set PATH $PATH {segments}")
for name, replacement in sorted(self.abbrs.items()):
self.add_interactive(f"'abbr' {escape(name)} {fescape(replacement)}")
for name, value in sorted(self.env_vars.items()):
self.add_command(f"set -gx {escape(name)} {fescape(value)}")
self.commands.extend(commands)
self.interactive_commands.extend(interactive_commands)
for command in self.commands:
file.append(command)
if self.commands and self.interactive_commands:
file.append("")
if self.interactive_commands:
file.append("if status is-interactive")
for command in self.interactive_commands:
file.append(f" {command}")
file.append("end")
self._files.add(".config/fish/config.fish", file)
self._pacman.install("fish")
def execute(self) -> None:
shell = os.environ.get("SHELL")
if shell == "/usr/bin/fish":
return
fix = prompt("Your shell is not fish. Set it to fish?", default=False)
if not fix:
return
run_execute("sudo", "usermod", "--shell", "/usr/bin/fish", self.o.user)