Emacs pra blogueirinho!¶
Como agora eu escrevo o blog usando arquivos .rst, escrevo usando o editor de texto normal que uso no dia-a-dia, e como uso o emacs, por que não fazer um esqueminha já pra me mostrar o post renderizado enquanto estou escrevendo? É sobre isso o post de hoje.
A ideia geral¶
A ideia principal é que quando eu salve o arquivo que estou editando eu veja html gerado pelo aphotoblog renderizado num navegador, dividindo minha tela no meio. Pra isso eu preciso buildar o html assim que eu salvar o arquivo, já ter um servidor servindo no diretório e aí abrir um navegador na página certa depois de buildar a coisa.
Pra fazer isso eu vou subir uma instância do tupi
assim que eu começar a escrever um post, quando eu salvar o arquivo, uso o
after-save-hook do emacs pra rodar uma função que vai rodar o comando de
build e por fim abrir a página html do post usando o
eaf-browser.
Voltando um pouco: .dir-locals.el¶
A minha configuração do emacs usa bastante o .dir-locals.el. Esse é um
arquivo que a gente coloca num diretório e quando um arquivo nesse
diretório (ou em seus sub-diretórios) esse arquivo vai ser lido e as
variáveis nesse arquivo estarão disponíveis no buffer.
O .dir-locals.el é algo assim:
((text-mode . ((some-var . "the-value")))
(prog-mode . ((other-var . "t"))))
E aí para acessar essas variáveis use-se o hack-local-variables:
(hack-local-variables)
(message some-var)
E assim eu seto variáveis de configuração para um projeto.
De volta ao caminho¶
Bom, com a coisa do .dir-locals.el explicada, já dá pra fazer o
que tem que ser feito. Pra repassar, a ideia é a seguinte:
Quando eu abrir um arquivo .rst que será um post, eu inicio uma instância do webserver e quando eu salvar o arquivo eu faço o build do post e abro um navegador mostrando o post já com o html renderizado.
Pra fazer isso a coisa é assim:
(require 'vterm)
(require 'eaf-browser)
(defun pdj:run-in-term-on-background (command &optional term-name)
"Run COMMAND in a new vterm buffer in background, without poping to buffer.
If TERM-NAME is provided, use it as the buffer name."
(interactive "P")
(let ((vterm-buffer-name (or term-name
(generate-new-buffer-name pdj:vterm-buffer-name))))
(vterm--internal #'pdj:--dummy-pop-to-buffer vterm-buffer-name)
(vterm-send-string command)
(vterm-send-return)))
(defcustom pdj:ablog-tupi-bin "tupi" "the tupi binary")
(defcustom pdj:ablog-tupi-buffer-name "ablog-tupi" "buffer name for the tupi process")
(defcustom pdj:ablog-build-dir "_website/build"
"build directory for the blog html")
(defcustom pdj:ablog-build-cmd "make build" "command to build the blog html")
(defcustom pdj:ablog-build-buffer-name "ablog-build" "buffer name for the blog build")
(defcustom pdj:ablog-tupi-url "http://localhost:8080" "url serving the blog html")
(setq pdj:--ablog-tupi-started nil)
(defun pdj:ablog-set-start-tupi-cmd ()
(hack-local-variables)
(setq pdj:ablog-start-tupi-cmd (concat pdj:ablog-tupi-bin " "
" -root "
pdj:project-directory
pdj:ablog-build-dir
" -default-to-index")))
(defun pdj:ablog-start-tupi()
"Starts a tupi instance in a vterm buffer"
(unless (equal pdj:--ablog-tupi-started t)
(pdj:run-in-term-on-project-directory-on-background
pdj:ablog-start-tupi-cmd
pdj:ablog-tupi-buffer-name)
(setq pdj:--ablog-tupi-started t)))
(defun pdj:ablog-setup-venv ()
(hack-local-variables)
(venv-workon pdj:venv-name))
(defun pdj:ablog-build ()
"Builds the blog html and calls the apropriate callback for success or error"
;; aqui a gente usa o esquema do vterm de executar comandos elisp a partir
;; do shell com o `vterm_cmd`
(setq pdj:--ablog-build-script (format" %s
if [[ \"$?\" == \"0\" ]]
then
vterm_cmd ablog-build-success %s
else
vterm_cmd ablog-build-fail
fi" pdj:ablog-build-cmd (pdj:ablog-web-path)))
(pdj:run-in-term-on-project-directory-on-background
pdj:--ablog-build-script
pdj:ablog-build-buffer-name))
(defun pdj:ablog-build-success (webpath)
"Open or refresh the web browser after the blog html is built"
(let ((kill-buffer-query-functions nil))
(kill-buffer pdj:ablog-build-buffer-name))
(setq pdj:--ablog-post-url (concat pdj:ablog-tupi-url webpath))
(eaf-open-browser-other-window pdj:--ablog-post-url)
(eaf-py-proxy-insert_or_refresh_page)
(other-window 1))
(defun pdj:ablog-build-fail ()
"Pops to the ablog build buffer"
(pop-to-buffer pdj:ablog-build-buffer-name))
(defun pdj:ablog-web-path ()
"Returns the path to be used in the url for the current post buffer"
(hack-local-variables)
(setq pdj--rel-dir (replace-regexp-in-string
pdj:project-directory "" (buffer-file-name)))
(setq pdj--rel-dir (replace-regexp-in-string
".rst" "/" pdj--rel-dir))
(setq pdj:--ablog-web-path (concat "/" pdj--rel-dir)))
(defun pdj:ablog-set-eval-cmds ()
"Adds pdj:ablog-build-success and pdj:ablog-build-fail to vterm-eval-cmds list"
(push (list "ablog-build-fail" 'pdj:ablog-build-fail) vterm-eval-cmds)
(push (list "ablog-build-success" 'pdj:ablog-build-success) vterm-eval-cmds))
(defun pdj:ablog-all-hooks()
(hack-local-variables)
(venv-workon pdj:venv-name)
(pdj:ablog-set-start-tupi-cmd)
(pdj:ablog-set-eval-cmds)
(pdj:ablog-start-tupi))
(defun pdj:ablog-setup()
(add-hook 'rst-mode-hook 'pdj:ablog-all-hooks)
(add-hook 'after-save-hook (lambda ()
(when (eq major-mode 'rst-mode)
(pdj:ablog-build)))))
E é basicamente isso!