diff options
| -rwxr-xr-x | bin/project-ctags | 86 | ||||
| -rw-r--r-- | config.org | 11 | ||||
| -rw-r--r-- | lisp/yggdrasil-ui.el | 54 |
3 files changed, 117 insertions, 34 deletions
diff --git a/bin/project-ctags b/bin/project-ctags index 4fccd90..f0216d0 100755 --- a/bin/project-ctags +++ b/bin/project-ctags @@ -30,6 +30,7 @@ IGNORED_DIRECTORIES = { "tests", } PYTHON_SUFFIXES = {".py", ".pyi"} +RUST_SUFFIXES = {".rs"} C_SOURCE_SUFFIXES = {".c", ".cc", ".cpp", ".cxx", ".m", ".mm"} C_HEADER_SUFFIXES = {".h", ".hh", ".hpp", ".hxx"} NIX_CTAGS_OPTIONS = [ @@ -79,8 +80,10 @@ def parse_arguments(): parser.add_argument("--ctags") parser.add_argument("--python") parser.add_argument("--python-command") + parser.add_argument("--cargo") parser.add_argument("--source", action="append", default=[]) parser.add_argument("--no-python-deps", action="store_true") + parser.add_argument("--no-rust-deps", action="store_true") parser.add_argument("--no-c-deps", action="store_true") return parser.parse_args() @@ -228,6 +231,81 @@ def python_dependency_files(args, root, files): return sorted(dependency_files), shlex.join(command), unresolved +def cargo_command(args, root): + value = args.cargo or os.environ.get("PROJECT_CTAGS_CARGO") + if value: + path = Path(os.path.expandvars(os.path.expanduser(value))) + if not path.is_absolute(): + path = root / path + return str(path.absolute()) + return shutil.which("cargo") + + +def rust_host_target(root): + rustc = shutil.which("rustc") + if rustc is None: + return None + result = subprocess.run( + [rustc, "-vV"], + cwd=root, + text=True, + stdout=subprocess.PIPE, + stderr=subprocess.DEVNULL, + check=False, + ) + if result.returncode == 0: + for line in result.stdout.splitlines(): + if line.startswith("host: "): + return line.removeprefix("host: ") + return None + + +def rust_dependency_files(args, root): + manifest = root / "Cargo.toml" + if not manifest.is_file(): + return [], None + cargo = cargo_command(args, root) + if cargo is None: + return [], None + command = [ + cargo, + "metadata", + "--format-version=1", + "--locked", + "--offline", + "--manifest-path", + str(manifest), + ] + target = rust_host_target(root) + if target: + command.extend(["--filter-platform", target]) + result = subprocess.run( + command, + cwd=root, + text=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + check=False, + ) + if result.returncode != 0: + if result.stderr: + print(result.stderr.rstrip(), file=sys.stderr) + return [], None + try: + metadata = json.loads(result.stdout) + except json.JSONDecodeError: + print("Could not read Cargo metadata", file=sys.stderr) + return [], None + workspace_members = set(metadata.get("workspace_members", [])) + files = set() + for package in metadata.get("packages", []): + if package.get("id") in workspace_members: + continue + package_root = Path(package["manifest_path"]).resolve().parent + files.update(walk_files(package_root, RUST_SUFFIXES)) + return sorted(files), cargo + + def compilation_database(root): path = root / "compile_commands.json" if not path.is_file(): @@ -378,11 +456,15 @@ def main(): unresolved = [] if not args.no_python_deps: python_files, python, unresolved = python_dependency_files(args, root, local_files) + rust_files = [] + cargo = None + if not args.no_rust_deps: + rust_files, cargo = rust_dependency_files(args, root) c_headers = [] if not args.no_c_deps: c_headers = c_dependency_files(root, local_files) extra_files = extra_source_files(args, root) - all_files = sorted(set(local_files + python_files + c_headers + extra_files)) + all_files = sorted(set(local_files + python_files + rust_files + c_headers + extra_files)) if not all_files: raise SystemExit("No source files found") @@ -392,6 +474,8 @@ def main(): print(f"python: {python}; {len(python_files)} dependency files") if unresolved: print(f"python unresolved: {', '.join(unresolved)}") + if cargo: + print(f"rust: {cargo}; {len(rust_files)} dependency files") if c_headers: print(f"c: {len(c_headers)} dependency headers") if extra_files: @@ -2606,6 +2606,7 @@ Auto switch silos (different workspaces/folders) based on preconfigured director (helm-move-to-line-cycle-in-source nil) (helm-split-window-inside-p t) (helm-autoresize-mode 1) + (helm-buffers-show-icons t) (helm-boring-buffer-regexp-list '("\\` " "\\`\\*helm" @@ -2797,6 +2798,16 @@ Built-in tree-sitter mode (Emacs 29+). The =c= grammar ships via =treesit-gramma (c-ts-mode-indent-style 'k&r)) #+end_src +** rust-ts-mode + +#+begin_src emacs-lisp +(use-package rust-ts-mode + :ensure nil + :mode "\\.rs\\'" + :custom + (rust-ts-indent-offset 4)) +#+end_src + ** python-ts-mode Because I have to and not want to. diff --git a/lisp/yggdrasil-ui.el b/lisp/yggdrasil-ui.el index 1d4d1c0..17db379 100644 --- a/lisp/yggdrasil-ui.el +++ b/lisp/yggdrasil-ui.el @@ -34,16 +34,6 @@ :type 'string :group 'yggdrasil-ui) -(defcustom yggdrasil-ui-service-program "systemctl" - "Program used to query the Yggdrasil service state." - :type 'string - :group 'yggdrasil-ui) - -(defcustom yggdrasil-ui-service-name "yggdrasil" - "System service name for Yggdrasil." - :type 'string - :group 'yggdrasil-ui) - (defcustom yggdrasil-ui-known-hosts '(("chiefsosa" . "201:6904:7fcb:6fa6:b869:48cc:a863:e9a6") ("md" . "202:5e97:a5ba:a207:ce38:ac4a:ca3:8f86") @@ -153,36 +143,34 @@ Return (EXIT OUTPUT)." "Run yggdrasilctl ARGS and decode JSON. Return (ok VALUE) or (error MESSAGE)." (pcase-let ((`(,code ,out) (apply #'yggdrasil-ui--run args))) - (if (zerop code) - (condition-case err - (list 'ok (json-parse-string out - :object-type 'alist - :array-type 'list - :null-object nil - :false-object nil)) - (error (list 'error (error-message-string err)))) + (cond + ((not (zerop code)) (list 'error (if (string-empty-p out) (format "%s exited %s" yggdrasil-ui-control-program code) - out))))) - -(defun yggdrasil-ui--service-state () - "Return the local service state as a string." - (with-temp-buffer - (let ((code (condition-case nil - (call-process yggdrasil-ui-service-program nil t nil - "is-active" yggdrasil-ui-service-name) - (error 127))) - (out (string-trim (buffer-string)))) - (cond ((zerop code) out) - ((string-empty-p out) "unknown") - (t out))))) + out))) + ((string-empty-p out) + (list 'error (format "%s returned no output" + yggdrasil-ui-control-program))) + ((not (member (substring out 0 1) '("{" "["))) + ;; Yggdrasil 0.5.14 can print a fatal connection error to stdout and + ;; still exit successfully. Preserve that useful error instead of + ;; reporting a misleading JSON parser failure. + (list 'error out)) + (t + (condition-case err + (list 'ok (json-parse-string out + :object-type 'alist + :array-type 'list + :null-object nil + :false-object nil)) + (error (list 'error (error-message-string err)))))))) (defun yggdrasil-ui--snapshot () "Collect current Yggdrasil status." (let ((self (yggdrasil-ui--json "getSelf")) (peers (yggdrasil-ui--json "getPeers"))) (list :updated (format-time-string "%Y-%m-%d %H:%M:%S") - :service (yggdrasil-ui--service-state) + :service (if (eq (car self) 'ok) "active" "unavailable") :self (and (eq (car self) 'ok) (cadr self)) :self-error (and (eq (car self) 'error) (cadr self)) :peers (and (eq (car peers) 'ok) (alist-get 'peers (cadr peers))) @@ -237,7 +225,7 @@ Return (ok VALUE) or (error MESSAGE)." 'yggdrasil-ui-salient) ((or (null state) (eq state 'down) - (member state '("inactive" "Down"))) + (member state '("inactive" "Down" "unavailable"))) 'yggdrasil-ui-critical) (t 'yggdrasil-ui-warning))) |
