about summary refs log tree commit diff
path: root/emacs/.emacs.d/wpc/entr.el
blob: ac2a5812c3283c2ed011c4d240cfc11f5e416ccc (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
;;; entr.el --- Working with terminals and entr -*- lexical-binding: t -*-
;; Author: William Carroll <wpcarro@gmail.com>

;;; Commentary:
;; Help make watch commands easier.
;;
;; This should be entirely temporary because in reality we should be able to use
;; Emacs's buffer watching abilities to run commands.
;; TODO: Explore Emacs integration that obviates `entr`.

;;; Code:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Dependencies
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(require 'f)
(require 'buffer)
(require 'prelude)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Library
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; TODO: Support a generic file-watcher for commonly used languages.
(defconst entr/major-mode->save-handler
  '((python-mode . entr/handle-python3))
  "Mapping of language to the `after-save-hook' function it should register.")

(defun entr/shell-command-to-buffer (cmd name)
  "Run CMD in a shell and output to the buffer NAME.
The buffer is a find-or-create operation.
The buffer is erased between runs with `erase-buffer'."
  (let ((b (buffer/find-or-create name)))
    (with-current-buffer b (erase-buffer))
    (shell-command cmd b)
    (buffer/show b)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Python
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; TODO: This should be a top-level function.
(defconst entr/handle-python3
  (lambda ()
    (entr/shell-command-to-buffer
     (format "python3 %s" (buffer-file-name))
     "*python3*"))
  "Function that is registered as the `after-save-hook' for python3.")

(defun entr/register-python3 ()
  "Register a buffer-local `after-save-hook' for calling python3 with filename."
  (interactive)
  (add-hook 'after-save-hook entr/handle-python3 nil t))

(defun entr/deregister-python3 ()
  "Remove the buffer-local `after-save-hook' for python3."
  (interactive)
  (remove-hook 'after-save-hook entr/handle-python3 t))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Protobuf
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defun entr/format-protobuf ()
  "Formats a protobuf buffer."
  (call-interactively #'clang-format))

;; TODO: Run this automatically with .proto file extensions.  Do this after
;; verifying that `clang-format' complies with Google's style guide.
(defun entr/register-protobuf ()
  "Register a buffer-local `before-save-hook' for formatting protobuf buffers."
  (interactive)
  (add-hook
   'before-save-hook
   #'entr/format-protobuf
   nil
   t))

;; TODO: Is there an interactive way to remove hooks in Emacs?
(defun entr/deregister-protobuf ()
  "Remove the buffer-local `before-save-hook' for protobuf."
  (interactive)
  (remove-hook
   'before-save-hook
   #'entr/format-protobuf
   t))

;; TODO: Support this.  Currently the `intern' call is the problem.
;; (defun entr/ivy-remove-hook (hook)
;;   "Use Counsel to remove a handler from HOOK."
;;   (interactive)
;;   (ivy-read
;;    "Remove hook: "
;;    (intern (prelude/prompt "Hook name: "))
;;    :action (lambda (x) (message x))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Miscellaneous
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defun entr/command (command)
  "Create a terminal instance with entr running COMMAND.
COMMAND is a function that is called with the current filename."
  ;; Algorithm:
  ;; - Get buffer's filename.
  ;; - Open terminator running: `echo entr <filename> | entr <command>`.
  (interactive)
  (with-current-buffer (current-buffer)
      (let ((filename (buffer-file-name)))
        (prelude/inspect
         (format "echo %s | entr %s" filename (funcall command filename))))))

(provide 'entr)
;;; entr.el ends here