Skip to content

Instantly share code, notes, and snippets.

@fast-90
Last active February 1, 2024 11:44
Show Gist options
  • Save fast-90/3d7ce3252fff59b0f6d6d1bcadcf4938 to your computer and use it in GitHub Desktop.
Save fast-90/3d7ce3252fff59b0f6d6d1bcadcf4938 to your computer and use it in GitHub Desktop.
My setup for Quarto in Emacs
(use-package python
:init
(defun python-comint-filter (output)
"Filter out '__PYTHON_EL_' when sending region to inferior Python shell.
See also: https://stackoverflow.com/questions/75103221/emacs-remove-python-el-eval-message"
(let* ((regexp "^.*__PYTHON_EL_\\(.*\\)\\(.*\\)[[:space:]]*$")
(lines (split-string output "\n"))
(filtered-lines (cl-remove-if (lambda (line)
(or (string-match-p regexp line)
(string-match-p "^\\s-*$" line)))
lines)))
(if (equal (length lines) (length filtered-lines))
output
(mapconcat 'identity filtered-lines "\n"))))
:hook
(comint-preoutput-filter-functions . python-comint-filter)
:custom
(python-indent-guess-indent-offset-verbose nil)
(python-shell-completion-native-enable nil))
(use-package pyvenv
:init
(pyvenv-mode t))
(use-package pet
:hook
(python-base-mode-hook . dn/python-pet-hook)
:init
(defun dn/python-pet-hook ()
"Activate pet mode, and set the local variables to identify the correct executables."
(pet-mode)
(setq-local pyvenv-activate (pet-virtualenv-root))
(setq-local python-shell-interpreter (pet-executable-find "python"))
(setq-local python-shell-interpreter-args "-i")
(when-let ((black-executable (pet-executable-find "black")))
(setq-local python-black-command black-executable))))
(use-package quarto-mode
:init
;; (defvar dn/quarto-preview-port nil
;; "Port which was last used for `dn/quarto-preview'.")
(defun dn/quarto-preview ()
"Start quarto preview process via shell command."
(interactive)
;; (setq dn/quarto-preview-port (number-to-string (+ 3000 (random 5000))))
(let ((shell-command-switch "-ic"))
(start-process-shell-command
"quarto-preview"
"*quarto-preview*"
(concat "quarto preview " buffer-file-name " --no-browser"))
)
(with-current-buffer (process-buffer (get-process "quarto-preview"))
(display-buffer (current-buffer))))
(defun dn/quarto-preview-xwidget ()
"Open the preview file with xwidget-webkit."
(interactive)
(if (get-process "quarto-preview")
(let ((previous-buffer (buffer-name))
(port (with-current-buffer (process-buffer (get-process "quarto-preview"))
(goto-char (point-max))
(search-backward-regexp "\\(Browse at.*\\)\\(localhost:\\)\\([0-9]\\{4\\}\\)" nil nil)
(match-string 3))))
(xwidget-webkit-browse-url (concat "http://localhost:" port))
(switch-to-buffer-other-window previous-buffer))
(message "No preview server running! Run M-x dn/quarto-preview first.")))
(defun poly-python-eval-region (beg end msg)
(python-shell-send-region beg end nil msg t))
(defun poly-python-mode-setup ()
(setq-local polymode-eval-region-function #'poly-python-eval-region))
(defun dn/quarto-insert-python-code-blocks (count)
"Inserts `count` number of Python code blocks starting from the line below the cursor."
(interactive "nEnter the number of Python code blocks to insert: ")
(save-excursion
(forward-line) ; Move to the line below the cursor
(dotimes (_ count)
(insert "```{python}\n\n```\n\n")
(forward-line 3)))
(forward-line))
(defun dn/quarto-next-block ()
"Move the cursor to the beginning of the line under the next code block."
(interactive)
(goto-char (line-beginning-position))
(search-forward-regexp "^```{.*}$" nil t)
(forward-line))
(defun dn/quarto-previous-block ()
"Move the cursor to the beginning of the line under the next code block."
(interactive)
(search-backward-regexp "^```$" nil t)
(previous-line)
(search-backward "```{" nil t)
(forward-line))
(defun dn/quarto-eval-block ()
"Evaluate current code block.
If cursor is not in a code block, evaluate the previous code block."
(interactive)
(let* ((current (save-excursion (point)))
(beg (save-excursion
(search-backward-regexp "^```{.*}" nil t)
(forward-line)
(point)))
(end (save-excursion
(goto-char beg)
(search-forward-regexp "```$" nil t)
(previous-line)(end-of-line)
(point))))
;; Don't send to Python shell if the block is empty.
(unless (eq beg end)
(push-mark beg)
(goto-char end)
(activate-mark)
(elpy-shell-send-region-or-buffer-and-step)
(deactivate-mark)
(goto-char current))))
(defun dn/quarto-eval-blocks-below ()
"Evaluate all blocks below point."
(interactive)
(let ((current (save-excursion (point))))
(while (progn
(dn/quarto-next-block)
(dn/quarto-eval-block)
(search-forward-regexp "^```{.*?}$" nil t)))
(goto-char current)))
(defun dn/quarto-eval-all-blocks ()
"Evaluate all blocks in notebook."
(interactive)
(let ((current (save-excursion (point))))
(goto-char (point-min))
(dn/quarto-eval-blocks-below)
(goto-char current)))
(defhydra dn/quarto-block-hydra ()
"Code blocks"
("n" dn/quarto-next-block "Next block")
("p" dn/quarto-previous-block "Previous block")
("e" dn/quarto-eval-block "Evaluate block")
("E" (lambda() (interactive) (dn/quarto-eval-block) (dn/quarto-next-block)) "Eval and next")
("a" dn/quarto-eval-blocks-below "Eval blocks below")
("A" dn/quarto-eval-all-blocks "Eval notebook"))
:hook
(python-base-mode-hook . poly-python-mode-setup)
:bind (:map dn/prog-map
("o" . dn/quarto-block-hydra/body)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment