comint-ledger/comint-ledger.el

110 lines
3.9 KiB
EmacsLisp

;;; comint-ledger.el --- Command interpreter for the Ledger CLI -*- lexical-binding: t; -*-
;; Author: Bram Schoenmakers
;; URL: https://apps.bram85.nl/git/bram/comint-ledger
;; Version: 0.1
;; Keywords: processes
;; Package-Requires: ((emacs "28.1"))
;;; This file is NOT part of GNU Emacs
;;; Commentary:
;;
;; Command interpreter (comint) for the Ledger CLI tool.
;; Starting point for this mode is Mickey Petersen's article on writing comint modes.
;;
;; https://www.masteringemacs.org/article/comint-writing-command-interpreter
;;
;;; Code:
(require 'comint)
(defvar comint-ledger-cli-path (executable-find "ledger")
"Path to the program used by `run-ledger'.")
(defvar comint-ledger-file-path nil
"Path to the ledger data file.")
(defvar comint-ledger-cli-arguments '()
"Commandline arguments to pass to `ledger'.")
(defvar comint-ledger-mode-map
(let ((map (nconc (make-sparse-keymap) comint-mode-map)))
;; example definition
(define-key map "\t" 'completion-at-point)
map)
"Basic mode map for `run-ledger'.")
(defvar comint-ledger-prompt-regexp (rx bol "] ")
"Prompt for `run-ledger'.")
(defvar comint-ledger-buffer-name "*Ledger*"
"Name of the buffer to use for the `run-ledger' comint instance.")
;;;###autoload
(defun run-ledger (ledger-file)
"Run an inferior instance of `ledger' inside Emacs, using LEDGER-FILE.
The LEDGER-FILE can be configured through the `comint-ledger-file-path'
variable, otherwise a prompt will appear to select a file."
(interactive (list (or comint-ledger-file-path
(read-file-name "Ledger file: "))))
(let* (;; The ledger command interpreter ignores --no-pager, so if
;; no pager is set, less(1) is used. This is suboptimal
;; inside an Emacs buffer, so set the pager to cat(1)
;; instead.
(cat-executable (executable-find "cat"))
(process-environment (append process-environment
(list (concat "PAGER=" cat-executable))))
(ledger-program comint-ledger-cli-path)
(ledger-file-arguments (list "-f" ledger-file))
(ledger-arguments (append ledger-file-arguments comint-ledger-cli-arguments))
(buffer (get-buffer-create comint-ledger-buffer-name))
(proc-alive (comint-check-proc buffer)))
;; if the process is dead then re-create the process and reset the
;; mode.
(unless proc-alive
(with-current-buffer buffer
(apply 'make-comint-in-buffer "Ledger" buffer
ledger-program nil ledger-arguments)
(comint-ledger-mode)))
;; Regardless, provided we have a valid buffer, we pop to it.
(when buffer
(pop-to-buffer buffer))))
(defun comint-ledger--initialize ()
"Helper function to initialize ledger."
(setq comint-process-echoes t)
(setq comint-use-prompt-regexp t))
(define-derived-mode comint-ledger-mode comint-mode "comint-ledger"
"Major mode for `run-ledger'.
\\<ledger-mode-map>"
;; this sets up the prompt so it matches things like: [foo@bar]
(setq comint-prompt-regexp comint-ledger-prompt-regexp)
;; this makes it read only; a contentious subject as some prefer the
;; buffer to be overwritable.
(setq comint-prompt-read-only t)
;; this makes it so commands like M-{ and M-} work.
(set (make-local-variable 'paragraph-separate) "\\'")
(set (make-local-variable 'font-lock-defaults) '(comint-ledger-font-lock-keywords t))
(set (make-local-variable 'paragraph-start) comint-ledger-prompt-regexp))
(add-hook 'ledger-mode-hook 'comint-ledger--initialize)
(defconst comint-ledger-keywords
'()
"List of keywords to highlight in `comint-ledger-font-lock-keywords'.")
(defvar comint-ledger-font-lock-keywords
(list
;; highlight all the reserved commands.
`(,(concat "\\_<" (regexp-opt comint-ledger-keywords) "\\_>") . font-lock-keyword-face))
"Additional expressions to highlight in `comint-ledger-mode'.")
(provide 'comint-ledger)
;;; comint-ledger.el ends here