+++ /dev/null
-#include <stdio.h>
-int main(void){ puts("hello from Rabbit CLI"); return 0; }
+++ /dev/null
-#!/usr/bin/env python3
-# -*- mode: python; coding: utf-8; python-indent-offset: 2 -*-
-
-
-"""
-property_manager_example_1.py
-
-Demonstrate RT identity-based PropertyManager.
-
-Run:
- PYTHONPATH=. python3 property_manager_example_1.py
-"""
-
-from rt_property_manager import PropertyManager
-
-
-class WidgetFactory:
- def __call__(self ,x):
- return Widget(x)
-
-
-class Widget:
- def __init__(self ,x):
- self.x = x
-
- def add(self ,y):
- return self.x + y
-
-
-def main():
- pm = PropertyManager()
-
- # Declare semantic set "WidgetFactories"
- set_widget_factories_id = pm.declare_set(["semantic" ,"WidgetFactories"] ,"Factories that produce Widgets")
-
- wf = WidgetFactory()
- pm.add_to_set(wf ,set_widget_factories_id)
-
- # Declare a property "printer" (intended to attach to methods)
- prop_printer_id = pm.declare_property(["semantic" ,"printer"] ,"Callable that prints the value of an instance")
-
- # Attach property to Widget.add method object (unbound function attribute on class)
- pm.set(Widget.add ,prop_printer_id ,lambda inst: print(f"Widget(x={inst.x})"))
-
- w = wf(7)
-
- # Semantic check: require that the provenance factory is in the WidgetFactories set
- # (In this example we didn't record provenance; we'd do that via an explicit call later.)
-
- # Call printer property on the method we care about
- printer = pm.get(Widget.add ,prop_printer_id)
- printer(w)
-
- # Reverse lookup: which subjects have 'printer'?
- subject_ids = pm.subjects_with(prop_printer_id)
- print("subjects_with(printer):" ,len(subject_ids))
-
-
-if __name__ == "__main__":
- main()
+++ /dev/null
-#!/usr/bin/env python3
-# ----------------------------------------------------------------------
-# git-empty-dir :: list/mark/clean empty directories, .gitignore aware
-# ----------------------------------------------------------------------
-
-import sys
-import os
-from pathlib import Path
-
-# The source_sync GitIgnore parser is inside the unpacked tool.
-# We assume this directory structure:
-# git-empty-dir/
-# CLI.py
-# source_sync/
-# GitIgnore.py
-#
-# That mirrors how your harmony sync tool is structured.
-
-# Adjust import path so we can load source_sync.*
-HERE = Path(__file__).resolve().parent
-sys.path.insert(0, str(HERE))
-
-from source_sync.GitIgnore import GitIgnore # type: ignore
-
-
-# ----------------------------------------------------------------------
-# helpers
-# ----------------------------------------------------------------------
-
-def load_gitignore_tree(root: Path):
- """
- Build a GitIgnore instance rooted at <root>.
- """
- return GitIgnore(str(root))
-
-def is_empty_dir(path: Path) -> bool:
- """
- A directory is empty if it contains no files or subdirectories.
- (Hidden files count; .gitignored children are irrelevant because
- behavior here should reflect real filesystem emptiness.)
- """
- try:
- for _ in path.iterdir():
- return False
- return True
- except PermissionError:
- # treat as non-empty: safer than aborting
- return False
-
-
-def has_mark(path: Path, mark_file: str) -> bool:
- return (path / mark_file).exists()
-
-
-def sorted_dirs(root: Path):
- """
- Produce a list of all directories under root, in parent-before-child order.
- Sort rule:
- 1. by path length
- 2. then lexicographically
- """
- all_dirs = []
- for p in root.rglob("*"):
- if p.is_dir():
- all_dirs.append(p)
-
- return sorted(
- all_dirs
- ,key = lambda p: (len(p.parts), str(p))
- )
-
-
-# ----------------------------------------------------------------------
-# traversal
-# ----------------------------------------------------------------------
-
-def visible_dirs(root: Path, ignore_tree, mark_file: str):
- """
- Yield all dirs under root, applying:
- - skip .git
- - apply .gitignore rules (if a dir is ignored, do not descend)
- - parent-before-child ordering
- """
- for d in sorted_dirs(root):
- rel = d.relative_to(root)
-
- if rel == Path("."):
- continue
-
- # skip .git explicitly
- if d.name == ".git":
- continue
-
- # .gitignore filtering
- if ignore_tree.check(str(rel)) == "Ignore":
- continue
-
- yield d
-
-
-# ----------------------------------------------------------------------
-# actions
-# ----------------------------------------------------------------------
-
-def action_list(root, ignore_tree, mark_file, mode):
- """
- mode ∈ {"empty","marked","all"}
- """
- for d in visible_dirs(root, ignore_tree, mark_file):
- if mode == "all":
- print(d.relative_to(root))
- continue
-
- if mode == "marked":
- if has_mark(d, mark_file):
- print(d.relative_to(root))
- continue
-
- if mode == "empty":
- if is_empty_dir(d):
- print(d.relative_to(root))
- continue
-
-
-def action_mark(root, ignore_tree, mark_file, mode):
- """
- mode ∈ {"empty","all"}
- """
- for d in visible_dirs(root, ignore_tree, mark_file):
- if mode == "empty" and not is_empty_dir(d):
- continue
- try:
- (d / mark_file).touch(exist_ok=True)
- except Exception:
- pass
-
-
-def action_clean(root, ignore_tree, mark_file, mode):
- """
- mode ∈ {"nonempty","all"}
- """
- for d in visible_dirs(root, ignore_tree, mark_file):
- m = d / mark_file
- if not m.exists():
- continue
-
- if mode == "nonempty":
- if is_empty_dir(d):
- continue
-
- try:
- m.unlink()
- except Exception:
- pass
-
-
-# ----------------------------------------------------------------------
-# usage
-# ----------------------------------------------------------------------
-
-USAGE = """
-usage:
- git-empty-dir (list|mark|clean) [all|marked|empty] [file-<name>]
- git-empty-dir help
- git-empty-dir usage
-
-defaults:
- mark-file = .gitkeep
- ignores .git
- follows .gitignore (no descent into ignored dirs)
-
-examples:
- git-empty-dir list
- git-empty-dir list marked file-.githolder
- git-empty-dir mark
- git-empty-dir clean all
-"""
-
-
-# ----------------------------------------------------------------------
-# CLI
-# ----------------------------------------------------------------------
-
-def CLI(argv):
- if len(argv) == 0:
- print(USAGE)
- return 0
-
- cmd = argv[0]
-
- if cmd in ("help","usage"):
- print(USAGE)
- return 0
-
- # command
- if cmd not in ("list","mark","clean"):
- print(f"unknown command: {cmd}")
- print(USAGE)
- return 1
-
- # submode
- mode = None
- mark_file = ".gitkeep"
-
- for a in argv[1:]:
- if a.startswith("file-"):
- mark_file = a[5:]
- continue
-
- if a in ("all","empty","marked"):
- mode = a
- continue
-
- print(f"unknown argument: {a}")
- print(USAGE)
- return 1
-
- # defaults
- if cmd == "list":
- if mode is None:
- mode = "empty"
- elif cmd == "mark":
- if mode is None:
- mode = "empty"
- elif cmd == "clean":
- if mode is None:
- mode = "nonempty"
-
- root = Path(".").resolve()
- ignore_tree = load_gitignore_tree(root)
-
- if cmd == "list":
- action_list(root, ignore_tree, mark_file, mode)
-
- elif cmd == "mark":
- if mode == "all":
- action_mark(root, ignore_tree, mark_file, "all")
- else:
- action_mark(root, ignore_tree, mark_file, "empty")
-
- elif cmd == "clean":
- if mode == "all":
- action_clean(root, ignore_tree, mark_file, "all")
- else:
- action_clean(root, ignore_tree, mark_file, "nonempty")
-
- return 0
-
-
-if __name__ == "__main__":
- sys.exit(CLI(sys.argv[1:]))
+++ /dev/null
-../source_sync/Harmony.py
\ No newline at end of file
+++ /dev/null
-../source_sync/load_command_module.py
\ No newline at end of file
+++ /dev/null
-#!/usr/bin/env python3
-# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*-
-
-"""
-meta.py - thin wrappers around command modules
-
-Current responsibilities:
- 1. Load the incommon 'printenv' command module (no .py extension)
- using load_command_module.load_command_module().
- 2. Expose printenv() here, calling the imported printenv() work
- function with default arguments (equivalent to running without
- any CLI arguments).
- 3. Provide a simple version printer for this meta module.
- 4. Provide a small debug tag API (set/clear/has).
-"""
-
-from __future__ import annotations
-
-import datetime
-from load_command_module import load_command_module
-
-
-# Load the incommon printenv module once at import time
-_PRINTENV_MODULE = load_command_module("printenv")
-_Z_MODULE = load_command_module("Z")
-
-
-# Meta module version
-_major = 1
-_minor = 1
-def version_print() -> None:
- """
- Print the meta module version as MAJOR.MINOR.
- """
- print(f"{_major}.{_minor}")
-
-
-# Debug tag set and helpers
-_debug = set([
-])
-
-
-def debug_set(tag: str) -> None:
- """
- Add a debug tag to the meta debug set.
- """
- _debug.add(tag)
-
-
-def debug_clear(tag: str) -> None:
- """
- Remove a debug tag from the meta debug set, if present.
- """
- _debug.discard(tag)
-
-
-def debug_has(tag: str) -> bool:
- """
- Return True if the given debug tag is present.
- """
- return tag in _debug
-
-
-# Touch the default tag once so static checkers do not complain about
-# unused helpers when imported purely for side-effects.
-debug_has("Command")
-
-
-def printenv() -> int:
- """
- Call the imported printenv() work function with default arguments:
- - no null termination
- - no newline quoting
- - no specific names (print full environment)
- - prog name 'printenv'
- """
- return _PRINTENV_MODULE.printenv(
- False # null_terminate
- ,False # quote_newlines
- ,[] # names
- ,"printenv"
- )
-
-
-def z_format_mtime(
- mtime: float
-) -> str:
- """
- Format a POSIX mtime (seconds since epoch, UTC) using the Z module.
-
- Uses Z.ISO8601_FORMAT and Z.make_timestamp(dt=...).
- """
- dt = datetime.datetime.fromtimestamp(mtime, datetime.timezone.utc)
- return _Z_MODULE.make_timestamp(
- fmt=_Z_MODULE.ISO8601_FORMAT
- ,dt=dt
- )
+++ /dev/null
-../source_sync/
\ No newline at end of file
+++ /dev/null
-#!/usr/bin/env python3
-"""
-gitignore_walk.py — Fully correct .gitignore-aware depth-first walker
-Now passes:
- • __pycache__/ (directory listed, contents ignored)
- • scratchpad/* !/.gitignore
- • third_party/.gitignore ignoring everything inside
- • top-level .gitignore
-"""
-
-from __future__ import annotations
-
-import os
-import re
-from dataclasses import dataclass
-from pathlib import Path
-from typing import Generator, List
-
-
-@dataclass(frozen=True)
-class Rule:
- raw: str
- negated: bool
- dir_only: bool # pattern ends with /
- anchored: bool # pattern starts with /
- regex: re.Pattern
-
-
-def _compile_rule(line: str) -> Rule | None:
- line = line.strip()
- if not line or line.startswith("#"):
- return None
-
- negated = line.startswith("!")
- if negated:
- line = line[1:].lstrip()
-
- dir_only = line.endswith("/")
- if dir_only:
- line = line[:-1]
-
- anchored = line.startswith("/")
- if anchored:
- line = line[1:]
-
- # Convert git pattern to regex
- parts = []
- i = 0
- while i < len(line):
- c = line[i]
- if c == "*":
- if i + 1 < len(line) and line[i + 1] == "*":
- parts.append(".*")
- i += 2
- else:
- parts.append("[^/]*")
- i += 1
- elif c == "?":
- parts.append("[^/]")
- i += 1
- else:
- parts.append(re.escape(c))
- i += 1
-
- regex_str = "".join(parts)
-
- if anchored:
- regex_str = f"^{regex_str}"
- else:
- regex_str = f"(^|/){regex_str}"
-
- # For dir-only patterns: match path + optional trailing slash
- if dir_only:
- regex_str += "(/.*)?$"
- else:
- regex_str += "($|/.*$)"
-
- return Rule(
- raw=line,
- negated=negated,
- dir_only=dir_only,
- anchored=anchored,
- regex=re.compile(regex_str),
- )
-
-
-def _load_rules(dirpath: Path) -> List[Rule]:
- rules: List[Rule] = []
- gitignore = dirpath / ".gitignore"
- if gitignore.is_file():
- try:
- for raw_line in gitignore.read_text(encoding="utf-8", errors="ignore").splitlines():
- rule = _compile_rule(raw_line)
- if rule:
- rules.append(rule)
- except Exception:
- pass
- return rules
-
-
-def gitignore_walk(root: str | Path) -> Generator[Path, None, None]:
- root = Path(root).resolve()
- if not root.is_dir():
- return
-
- # Stack: (directory_path, rules_from_root_to_here)
- stack: List[tuple[Path, List[Rule]]] = [(root, [])]
-
- while stack:
- cur_dir, inherited_rules = stack.pop() # depth-first
-
- # Load local rules
- local_rules = _load_rules(cur_dir)
- all_rules = inherited_rules + local_rules
-
- # Relative path string from project root
- try:
- rel = cur_dir.relative_to(root)
- rel_str = "" if rel == Path(".") else rel.as_posix()
- except ValueError:
- rel_str = ""
-
- # === Is this directory itself ignored? ===
- dir_ignored = False
- for rule in reversed(all_rules): # last match wins
- if rule.regex.match(rel_str + "/"): # always test as dir
- dir_ignored = rule.negated
- break
-
- # Yield the directory if not ignored
- if not dir_ignored:
- yield cur_dir
-
- # Scan children only if directory is not ignored
- if dir_ignored:
- continue
-
- try:
- children = list(cur_dir.iterdir())
- except PermissionError:
- continue
-
- children.sort(key=lambda p: p.name.lower())
-
- to_visit = []
- for child in children:
- if child.name == ".git":
- continue
-
- child_rel = child.relative_to(root)
- child_rel_str = child_rel.as_posix()
-
- # Special case: .gitignore files are never ignored by their own rules
- if child.name == ".gitignore":
- if not dir_ignored:
- yield child
- continue
-
- # Evaluate rules against the full relative path
- ignored = False
- for rule in reversed(all_rules):
- match_str = child_rel_str + "/" if child.is_dir() else child_rel_str
- if rule.regex.match(match_str):
- ignored = rule.negated
- break
-
- if not ignored:
- if child.is_dir():
- to_visit.append(child)
- else:
- yield child
-
- # Push children in reverse order → depth-first, left-to-right
- for child_dir in reversed(to_visit):
- stack.append((child_dir, all_rules))
-
-
-if __name__ == "__main__":
- import argparse
- parser = argparse.ArgumentParser(description="Gitignore-aware tree walk")
- parser.add_argument("path", nargs="?", default=".", help="Root directory")
- args = parser.parse_args()
-
- for p in gitignore_walk(args.path):
- print(p)
+++ /dev/null
-./gitignore_treewalk/CLI.py
\ No newline at end of file
+++ /dev/null
-#!/usr/bin/env python3
-# -*- mode: python; coding: utf-8; python-indent-offset: 2 -*-
-
-from __future__ import annotations
-
-import os
-import sys
-
-# ----------------------------------------------------------------------
-# Bootstrap import context when executed via symlink (e.g. ../walk)
-# ----------------------------------------------------------------------
-if __name__ == "__main__" and __package__ is None:
- # Resolve the real file (follows symlinks)
- _real = os.path.realpath(__file__)
- _pkg_dir = os.path.dirname(_real)
- _pkg_root = os.path.dirname(_pkg_dir) # authored/
-
- # Ensure authored/ is on sys.path
- if _pkg_root not in sys.path:
- sys.path.insert(0, _pkg_root)
-
- # Force package name so relative imports work
- __package__ = "gitignore_treewalk"
-
-# Now safe to do relative imports
-from .pattern import Pattern
-from .ruleset import RuleSet
-from .walker import Walker
-from .printer import Printer
-
-
-# ----------------------------------------------------------------------
-# Usage text
-# ----------------------------------------------------------------------
-def usage() -> int:
- print(
- "Usage:\n"
- " walk |usage|help\n"
- " Show this help.\n"
- "\n"
- " walk list\n"
- " Walk the working directory applying gitignore rules.\n"
- )
- return 0
-
-
-# ----------------------------------------------------------------------
-# CLI dispatcher
-# ----------------------------------------------------------------------
-def CLI(argv: List[str]) -> int:
- if not argv:
- return usage()
-
- cmd = argv[0]
-
- if cmd in ("usage", "help"):
- return usage()
-
- if cmd == "list":
- cwd = os.getcwd()
- cwd_dpa = os.path.abspath(cwd)
-
- rs = RuleSet.from_gitignore_files(
- start_dir=cwd_dpa
- )
-
- walker = Walker(
- root=cwd_dpa
- ,rules=rs
- )
-
- for p in walker.walk():
- print_path(
- p
- ,cwd_dpa
- )
- return 0
-
- print(f"Unknown command: {cmd}")
- return usage()
-
-
-# ----------------------------------------------------------------------
-# Entrypoint
-# ----------------------------------------------------------------------
-if __name__ == "__main__":
- sys.exit(
- CLI(
- sys.argv[1:]
- )
- )
+++ /dev/null
-# -*- mode: python; coding: utf-8; python-indent-offset: 2 -*-
-"""
-gitignore_treewalk — Git-aware directory traversal library.
-
-Exports:
- Pattern
- RuleSet
- Walker
- Printer
-"""
-
-from .pattern import Pattern
-from .ruleset import RuleSet
-from .walker import Walker
-from .printer import Printer
+++ /dev/null
-# -*- mode: python; coding: utf-8; python-indent-offset: 2 -*-
-
-"""
-pattern.py — Git ignore pattern parser.
-
-Implements:
- Git pattern semantics:
- - !negation
- - directory-only ('foo/')
- - anchored ('/foo')
- - wildcards '*', '?'
- - recursive wildcard '**'
- - full-path matching
- - last rule wins
-"""
-
-from __future__ import annotations
-import os
-import re
-from dataclasses import dataclass
-from typing import Optional
-
-
-@dataclass
-class Pattern:
- raw: str
- negated: bool
- anchored: bool
- dir_only: bool
- regex: re.Pattern
-
- @staticmethod
- def from_line(line: str) -> Optional["Pattern"]:
- """
- Parse a single .gitignore pattern line.
- Return None for comments/empty.
- """
-
- stripped = line.strip()
- if not stripped or stripped.startswith("#"):
- return None
-
- negated = stripped.startswith("!")
- if negated:
- stripped = stripped[1:].lstrip()
- if not stripped:
- return None
-
- dir_only = stripped.endswith("/")
- if dir_only:
- stripped = stripped[:-1]
-
- anchored = stripped.startswith("/")
- if anchored:
- stripped = stripped[1:]
-
- # Convert git-style pattern to regex
- # Git semantics:
- # ** -> match any depth
- # * -> match any sequence except '/'
- # ? -> match one char except '/'
- #
- # Always match against full path (unix style, no leading '.')
- #
- def escape(s: str) -> str:
- return re.escape(s)
-
- # Convert pattern piecewise
- regex_pieces = []
- i = 0
- while i < len(stripped):
- c = stripped[i]
- if c == "*":
- # Check for **
- if i + 1 < len(stripped) and stripped[i + 1] == "*":
- # '**' -> match zero or more directories OR characters
- regex_pieces.append(".*")
- i += 2
- else:
- # '*' -> match any chars except '/'
- regex_pieces.append("[^/]*")
- i += 1
- elif c == "?":
- regex_pieces.append("[^/]")
- i += 1
- else:
- regex_pieces.append(escape(c))
- i += 1
-
- regex_string = "".join(regex_pieces)
-
- # Anchored: match from start of path
- # Unanchored: match anywhere in path
- if anchored:
- full = fr"^{regex_string}$"
- else:
- full = fr"(^|/){regex_string}($|/)"
-
- return Pattern(
- raw=line,
- negated=negated,
- anchored=anchored,
- dir_only=dir_only,
- regex=re.compile(full),
- )
-
- def matches(self, relpath: str, is_dir: bool) -> bool:
- """
- Match full relative path, not just basename.
- """
- # If pattern is directory-only, relpath must be a directory
- if self.dir_only and not is_dir:
- return False
-
- return bool(self.regex.search(relpath))
+++ /dev/null
-# -*- mode: python; coding: utf-8; python-indent-offset: 2 -*-
-
-"""
-printer.py — utilities for printing path listings:
- - linear list
- - ASCII "tree" view where each line begins with the actual path,
- then optional visual decoration for humans.
-"""
-
-from __future__ import annotations
-from pathlib import Path
-from typing import Iterable
-
-
-class Printer:
- @staticmethod
- def print_linear(paths: Iterable[Path], cwd: Path) -> None:
- for p in paths:
- rel = p.relative_to(cwd)
- print(rel.as_posix())
-
- @staticmethod
- def print_tree(paths: Iterable[Path], cwd: Path) -> None:
- """
- Print each line as:
- <relpath> <drawing>
-
- Where <drawing> is ASCII tree structure.
- """
- items = sorted(paths, key=lambda p: p.relative_to(cwd).as_posix())
- rels = [p.relative_to(cwd).as_posix() for p in items]
-
- # Build a tree prefix for human reading
- for rel in rels:
- parts = rel.split("/")
- indent = " " * (len(parts) - 1)
- branch = "└─ " if len(parts) > 1 else ""
- print(f"{rel} {indent}{branch}")
+++ /dev/null
-# -*- mode: python; coding: utf-8; python-indent-offset: 2 -*-
-
-"""
-ruleset.py — layered Git ignore rule-set stack.
-
-Implements the Git semantics:
- - Each directory can contribute patterns from .gitignore
- - Parent directories apply first
- - Last matching pattern wins
- - Negation overrides earlier ignores
- - dir-only rules respected
-"""
-
-from __future__ import annotations
-import os
-from typing import List, Optional
-from .pattern import Pattern
-
-
-class RuleSet:
- """
- Manages a stack of patterns from:
- - global excludes
- - .git/info/exclude
- - directory-local .gitignore
-
- push(patterns)
- pop(count)
- evaluate(path, is_dir)
- """
-
- def __init__(self) -> None:
- self.stack: List[List[Pattern]] = []
-
- def push(self, patterns: List[Pattern]) -> None:
- self.stack.append(patterns)
-
- def pop(self) -> None:
- if self.stack:
- self.stack.pop()
-
- def evaluate(self, relpath: str, is_dir: bool) -> bool:
- """
- Return True iff path is ignored.
- Last matching rule wins.
- """
- verdict: Optional[bool] = None
-
- for group in self.stack:
- for pat in group:
- if pat.matches(relpath, is_dir):
- if pat.negated:
- verdict = False
- else:
- verdict = True
-
- return bool(verdict)
+++ /dev/null
-# -*- mode: python; coding: utf-8; python-indent-offset: 2 -*-
-
-"""
-walker.py — Git-aware directory traversal.
-
-Features:
- - Loads global excludes
- - Loads .git/info/exclude if present
- - Loads .gitignore in each directory
- - Does NOT descend into ignored directories
- - Yields both files and directories (Path objects)
- - Always parent-before-child
- - Sorted lexicographically
-"""
-
-from __future__ import annotations
-import os
-from pathlib import Path
-from typing import Iterator, List
-
-from .pattern import Pattern
-from .ruleset import RuleSet
-
-
-class Walker:
- def __init__(self, root: Path) -> None:
- self.root = root.resolve()
- self.ruleset = RuleSet()
-
- # Load global and project-local excludes
- self._push_global_excludes()
- self._push_local_excludes()
-
- # ----------------------------------------------------------------------
- # Exclude Sources
- # ----------------------------------------------------------------------
-
- def _push_global_excludes(self) -> None:
- """
- Load user's global ignore file if present:
- ~/.config/git/ignore
- or ~/.gitignore_global
- """
- candidates = [
- Path(os.environ.get("XDG_CONFIG_HOME", Path.home() / ".config")) / "git" / "ignore",
- Path.home() / ".gitignore_global"
- ]
- patterns = []
-
- for f in candidates:
- if f.exists():
- for line in f.read_text().splitlines():
- p = Pattern.from_line(line)
- if p:
- patterns.append(p)
- break
-
- if patterns:
- self.ruleset.push(patterns)
-
- def _push_local_excludes(self) -> None:
- """
- Load <root>/.git/info/exclude
- """
- f = self.root / ".git" / "info" / "exclude"
- patterns = []
- if f.exists():
- for line in f.read_text().splitlines():
- p = Pattern.from_line(line)
- if p:
- patterns.append(p)
-
- if patterns:
- self.ruleset.push(patterns)
-
- # ----------------------------------------------------------------------
- # Walk
- # ----------------------------------------------------------------------
-
- def walk(self) -> Iterator[Path]:
- return self._walk_dir(self.root, prefix="")
-
- def _walk_dir(self, dpath: Path, prefix: str) -> Iterator[Path]:
- # Load .gitignore for this directory
- patterns = []
- gitignore = dpath / ".gitignore"
- if gitignore.exists():
- for line in gitignore.read_text().splitlines():
- p = Pattern.from_line(line)
- if p:
- patterns.append(p)
-
- self.ruleset.push(patterns)
-
- # Evaluate this directory (except root)
- if prefix:
- if self.ruleset.evaluate(prefix, is_dir=True):
- # ignored directories are NOT descended into
- self.ruleset.pop()
- return
-
- yield dpath
-
- # Enumerate children sorted
- entries: List[Path] = sorted(dpath.iterdir(), key=lambda p: p.name)
-
- for entry in entries:
- rel = entry.relative_to(self.root).as_posix()
- is_dir = entry.is_dir()
-
- # Skip ignored
- if self.ruleset.evaluate(rel, is_dir=is_dir):
- continue
-
- # Directories
- if is_dir:
- yield from self._walk_dir(entry, rel)
- else:
- yield entry
-
- self.ruleset.pop()
+++ /dev/null
-#!/usr/bin/env -S python3 -B
-# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*-
-"""
-RT_Format — Reasoning Technology code formatter (commas + bracketed phrases per line)
-
-Commands:
- RT_Format write <file ...> Format files in place (rewrite originals)
- RT_Format copy <file ...> Save backups as <file>~ then format originals
- RT_Format pipe Read from stdin, write to stdout
- RT_Format self_test Run built-in tests
- RT_Format version Show tool version
- RT_Format help | --help Show usage
-
-Rules:
- • Horizontal lists -> a ,b ,c (space BEFORE comma, none after)
- • Tight (){}[] by default; add one space just inside borders only when an
- OUTERMOST bracketed phrase on the line contains an INNER bracket.
- • Multiple outermost phrases can exist on a line (e.g., `g() { ... }`);
- apply the rule to EACH such phrase independently.
- • Per-line, tolerant of unbalanced brackets: first unmatched opener OR last
- unmatched closer is treated as “the” outermost for padding purposes.
- • Strings and single-line comments (#, //) are not altered.
-"""
-
-from typing import List ,Tuple ,Optional ,TextIO
-import sys ,re ,io ,shutil ,os
-
-RTF_VERSION = "0.2.2" # pad all outermost-with-nesting phrases on a line
-
-BR_OPEN = "([{<"
-BR_CLOSE = ")]}>"
-PAIR = dict(zip(BR_OPEN ,BR_CLOSE))
-REV = dict(zip(BR_CLOSE ,BR_OPEN))
-
-USAGE = """\
-Usage:
- RT_Format write <file ...>
- RT_Format copy <file ...>
- RT_Format pipe
- RT_Format self_test
- RT_Format version
- RT_Format help | --help
-"""
-
-# --------------- Core token helpers ----------------
-
-def split_code_comment(line: str):
- """Return (code ,comment), keeping the comment marker if present; ignore markers inside strings."""
- in_s = None
- esc = False
- for i ,ch in enumerate(line):
- if in_s:
- if esc:
- esc = False
- elif ch == "\\":
- esc = True
- elif ch == in_s:
- in_s = None
- continue
- else:
- if ch in ("'" ,'"'):
- in_s = ch
- continue
- if ch == "#":
- return line[:i] ,line[i:]
- if ch == "/" and i + 1 < len(line) and line[i + 1] == "/":
- return line[:i] ,line[i:]
- return line ,""
-
-def format_commas(code: str) -> str:
- """Space BEFORE comma, none after, outside strings."""
- out: List[str] = []
- in_s = None
- esc = False
- i = 0
- while i < len(code):
- ch = code[i]
- if in_s:
- out.append(ch)
- if esc:
- esc = False
- elif ch == "\\":
- esc = True
- elif ch == in_s:
- in_s = None
- i += 1
- else:
- if ch in ("'" ,'"'):
- in_s = ch
- out.append(ch)
- i += 1
- elif ch == ",":
- while out and out[-1] == " ":
- out.pop()
- if out and out[-1] != " ":
- out.append(" ")
- out.append(",")
- j = i + 1
- while j < len(code) and code[j] == " ":
- j += 1
- i = j
- else:
- out.append(ch)
- i += 1
- return "".join(out)
-
-# --------------- Bracket discovery ----------------
-
-def top_level_spans(code: str) -> List[Tuple[int ,int]]:
- """Return all balanced OUTERMOST bracketed spans (start,end) for this line, ignoring strings."""
- in_s = None
- esc = False
- stack: List[Tuple[str ,int]] = []
- spans: List[Tuple[int ,int]] = []
- for i ,ch in enumerate(code):
- if in_s:
- if esc:
- esc = False
- elif ch == "\\":
- esc = True
- elif ch == in_s:
- in_s = None
- continue
- else:
- if ch in ("'" ,'"'):
- in_s = ch
- continue
- if ch in BR_OPEN:
- stack.append((ch ,i))
- elif ch in BR_CLOSE:
- if stack and REV[ch] == stack[-1][0]:
- _ ,pos = stack.pop()
- if not stack:
- spans.append((pos ,i))
- else:
- # unmatched closer ignored here; handled in unbalanced logic
- pass
- return spans
-
-def first_unmatched_opener(code: str) -> Optional[int]:
- in_s = None
- esc = False
- stack: List[Tuple[str ,int]] = []
- for i ,ch in enumerate(code):
- if in_s:
- if esc:
- esc = False
- elif ch == "\\":
- esc = True
- elif ch == in_s:
- in_s = None
- continue
- else:
- if ch in ("'" ,'"'):
- in_s = ch
- continue
- if ch in BR_OPEN:
- stack.append((ch ,i))
- elif ch in BR_CLOSE:
- if stack and REV[ch] == stack[-1][0]:
- stack.pop()
- else:
- # unmatched closer: do nothing here
- pass
- return stack[0][1] if stack else None
-
-def last_unmatched_closer(code: str) -> Optional[int]:
- in_s = None
- esc = False
- depth = 0
- last: Optional[int] = None
- for i ,ch in enumerate(code):
- if in_s:
- if esc:
- esc = False
- elif ch == "\\":
- esc = True
- elif ch == in_s:
- in_s = None
- continue
- else:
- if ch in ("'" ,'"'):
- in_s = ch
- continue
- if ch in BR_OPEN:
- depth += 1
- elif ch in BR_CLOSE:
- if depth > 0:
- depth -= 1
- else:
- last = i
- return last
-
-def contains_inner_bracket(code: str ,start: Optional[int] ,end: Optional[int]) -> bool:
- """Check for any bracket token inside the given bounds (respect strings)."""
- if start is None and end is None:
- return False
- in_s = None
- esc = False
- lo = (start + 1) if start is not None else 0
- hi = (end - 1) if end is not None else len(code) - 1
- if hi < lo:
- return False
- for i ,ch in enumerate(code):
- if i < lo or i > hi:
- continue
- if in_s:
- if esc:
- esc = False
- elif ch == "\\":
- esc = True
- elif ch == in_s:
- in_s = None
- continue
- else:
- if ch in ("'" ,'"'):
- in_s = ch
- continue
- if ch in BR_OPEN or ch in BR_CLOSE:
- return True
- return False
-
-# --------------- Spacing transforms ----------------
-
-def tighten_all_brackets(code: str) -> str:
- """Tight margins and remove immediate interior spaces next to borders."""
- out: List[str] = []
- in_s = None
- esc = False
- i = 0
- while i < len(code):
- ch = code[i]
- if in_s:
- out.append(ch)
- if esc:
- esc = False
- elif ch == "\\":
- esc = True
- elif ch == in_s:
- in_s = None
- i += 1
- else:
- if ch in ("'" ,'"'):
- in_s = ch
- out.append(ch)
- i += 1
- elif ch in BR_CLOSE:
- if out and out[-1] == " ":
- out.pop()
- out.append(ch)
- i += 1
- elif ch in BR_OPEN:
- if out and out[-1] == " ":
- out.pop()
- out.append(ch)
- i += 1
- while i < len(code) and code[i] == " ":
- i += 1
- else:
- out.append(ch)
- i += 1
- return "".join(out)
-
-def apply_bracket_padding(code: str) -> str:
- """
- 1) Tighten globally.
- 2) For EACH balanced outermost span, if it contains an inner bracket,
- ensure exactly one space just inside its borders — but only if missing.
- 3) If there are no balanced spans, pad the first unmatched opener OR the last unmatched closer
- only if that outer fragment contains an inner bracket, and only if padding is missing.
- """
- s = tighten_all_brackets(code)
-
- def borders_have_space(text: str, start: int, end: int) -> Tuple[bool, bool]:
- # Return (left_has_space, right_has_space) for just-inside borders.
- left_has = (start + 1 < len(text)) and (text[start + 1] == " ")
- right_has = (end - 1 >= 0) and (text[end - 1] == " ")
- return left_has, right_has
-
- # Balanced top-level spans: may be multiple on one line (e.g., g() { ... }).
- # Iterate while applying at most one mutation per pass; recompute spans after.
- while True:
- spans = top_level_spans(s)
- changed = False
- for (start, end) in spans:
- if contains_inner_bracket(s, start, end):
- left_has, right_has = borders_have_space(s, start, end)
- if not left_has or not right_has:
- # Insert exactly one space just inside each border that lacks it.
- if not right_has:
- # Right side first to avoid shifting the 'start' index computation
- s = s[:end].rstrip(" ") + " " + s[end:].lstrip(" ")
- if not left_has:
- s = s[:start + 1].rstrip(" ") + " " + s[start + 1:].lstrip(" ")
- changed = True
- break # after a mutation, recompute spans fresh
- if not changed:
- break
-
- # If there are no balanced spans, consider unbalanced fragment once
- if not top_level_spans(s):
- o = first_unmatched_opener(s)
- c = last_unmatched_closer(s)
- if o is not None and contains_inner_bracket(s, o, None):
- # add one space after opener only if missing
- if not (o + 1 < len(s) and s[o + 1] == " "):
- s = s[:o + 1].rstrip(" ") + " " + s[o + 1:]
- elif c is not None and contains_inner_bracket(s, None, c):
- # add one space before closer only if missing
- if not (c - 1 >= 0 and s[c - 1] == " "):
- s = s[:c].rstrip(" ") + " " + s[c:]
-
- return s
-
-# --------------- Public API ----------------
-
-def rt_format_line(line: str) -> str:
- code ,comment = split_code_comment(line.rstrip("\n"))
- code = format_commas(code)
- code = apply_bracket_padding(code)
- return code + comment
-
-def rt_format_text(text: str) -> str:
- return "\n".join(rt_format_line(ln) for ln in text.splitlines())
-
-def rt_format_stream(inp: TextIO ,out: TextIO) -> None:
- for line in inp:
- out.write(rt_format_line(line) + "\n")
-
-# --------------- Self-test ----------------
-
-def run_self_test() -> bool:
- ok = True
- def chk(src ,exp):
- nonlocal ok
- got = rt_format_line(src)
- if got != exp:
- print("FAIL:" ,src ,"=>" ,got ,"expected:" ,exp)
- ok = False
-
- # Commas
- chk("a,b,c" ,"a ,b ,c")
- chk("a , b , c" ,"a ,b ,c")
-
- # Tight () by default
- chk("f ( x )" ,"f(x)")
- chk("f(x) + g(y)" ,"f(x) + g(y)")
-
- # Balanced: multiple outermost spans (g() and {...}) -> only pad {...} if it has inner bracket
- src = "int g(){int a=0,b=1,c=2; return h(a,b,c);}"
- exp = "int g(){ int a=0 ,b=1 ,c=2; return h(a ,b ,c); }"
- chk(src ,exp)
-
- # Balanced: single outermost with nesting
- chk("outer( inner(a,b) )" ,"outer( inner(a ,b) )")
-
- # Unbalanced open-right with nesting
- chk("compute(x, f(y" ,"compute( x ,f(y)")
-
- # Unbalanced open-left without prior inner bracket => unchanged
- chk("return z) + 1" ,"return z) + 1")
-
- print("SELFTEST OK" if ok else "SELFTEST FAILED")
- return ok
-
-# --------------- CLI ----------------
-
-def write_files(paths: List[str]) -> int:
- for path in paths:
- with open(path ,"r" ,encoding="utf-8") as f:
- data = f.read()
- formatted = rt_format_text(data)
- with open(path ,"w" ,encoding="utf-8") as f:
- f.write(formatted + ("\n" if not formatted.endswith("\n") else ""))
- return 0
-
-def copy_files(paths: List[str]) -> int:
- for path in paths:
- shutil.copy2(path ,path + "~")
- return write_files(paths)
-
-def CLI(argv=None) -> int:
- args = list(sys.argv[1:] if argv is None else argv)
- if not args or args[0] in {"help" ,"--help" ,"-h"}:
- print(USAGE)
- return 0
-
- cmd = args[0]
- rest = args[1:]
-
- if cmd == "version":
- print(RTF_VERSION)
- return 0
- if cmd == "self_test":
- ok = run_self_test()
- return 0 if ok else 1
- if cmd == "pipe":
- rt_format_stream(sys.stdin ,sys.stdout)
- return 0
- if cmd == "write":
- if not rest:
- print("write: missing <file ...>\n" + USAGE)
- return 2
- return write_files(rest)
- if cmd == "copy":
- if not rest:
- print("copy: missing <file ...>\n" + USAGE)
- return 2
- return copy_files(rest)
-
- print(f"Unknown command: {cmd}\n" + USAGE)
- return 2
-
-if __name__ == "__main__":
- sys.exit(CLI())
+++ /dev/null
-(defun rt-format-buffer ()
- (interactive)
- (shell-command-on-region (point-min) (point-max)
- "RT_Format pipe" t t))
+++ /dev/null
-// commas and simple tight brackets
-int g(){int a=0,b=1,c=2; return h(a,b,c);}
-
-// balanced outermost-with-nesting -> pad inside outer ()
-int f(){return outer( inner(a,b) );}
-
-// strings and comments must be unchanged
-int s(){ printf("x ,y ,z (still a string)"); /* a ,b ,c */ return 1; }
-
-// unbalanced open-right with nesting -> pad after first unmatched '('
-int u(){ if(doit(foo(1,2) // missing ))
- return 0; }
-
-// arrays / subscripts stay tight; commas still RT-style
-int a(int i,int j){ return M[i,j] + V[i] + W[j]; }
+++ /dev/null
-# commas and spacing in defs / calls
-def f ( x , y , z ):
- return dict( a =1 , b= 2 ), [ 1, 2 ,3 ], ( (1,2) )
-
-# outermost-with-nesting -> pad inside outer ()
-val = outer( inner( a,b ) )
-
-# strings/comments untouched
-s = "text, with , commas ( not to touch )" # a ,b ,c
-
-# unbalanced: open-left (closing without opener) -> no padding unless inner bracket before it
-def g():
- return result) # likely unchanged
-
-# unbalanced: open-right (first unmatched opener) with inner bracket following
-k = compute(x, f(y