Skip to content

Instantly share code, notes, and snippets.

@antifuchs
Last active January 17, 2020 02:44
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save antifuchs/aa9fa4c3d1354ea163bc13e63d32db1a to your computer and use it in GitHub Desktop.
Save antifuchs/aa9fa4c3d1354ea163bc13e63d32db1a to your computer and use it in GitHub Desktop.
This sets up a key binding for C-c ' in rust-mode, which acts very similarly to org-src's key binding, except the other direction: If you use it on a rustdoc comment, it'll open another buffer with the comment in it, in markdown-mode.
;;; Copyright (C) 2018, 2019 Andreas Fuchs <asf@boinkor.net>
;;; Made available to you under the terms of GPLv2 or later: https://choosealicense.com/licenses/gpl-2.0/
(use-package edit-indirect
:straight t)
(defconst asf--rustdoc-line-prefixes (concat "^\\([\t ]*" (regexp-opt '("///" "//!")) "\\)"))
(defun asf--rustdoc-current-comment-block-region ()
(save-excursion
(forward-line 0)
(when (looking-at asf--rustdoc-line-prefixes)
;; determine the current line's prefix & extract all the lines
;; with matching prefixes that surround it (we assume that's the
;; extent of the rustdoc block):
(let ((prefix (match-string-no-properties 1))
(initial-position (point))
start end)
;; look back:
(while (and (looking-at prefix)
(not (eql (point) (point-min))))
(setq start (point))
(forward-line -1))
(when (looking-at prefix)
;; we're at the beginning of the buffer; make sure we got that:
(setq start (point)))
(goto-char initial-position)
(while (and (not (eql (point) (point-max)))
(looking-at prefix))
(forward-line 1)
(setq end (point)))
(list start end)))))
(defvar asf--rustdoc-prefix)
(defun asf--rustdoc-prefix-without-space (prefix)
(replace-regexp-in-string "[\t ]*$" "" prefix))
(defun asf--rustdoc-strip-prefix ()
(save-excursion
(goto-char (point-min))
;; infer prefix from first line:
(let* ((prefix (when (looking-at (concat asf--rustdoc-line-prefixes "[\t ]*"))
(match-string 0)))
(prefix-re (concat "^" prefix))
(empty-line-prefix-re (concat "^" (asf--rustdoc-prefix-without-space prefix))))
(while (< (point) (point-max))
(when (or (looking-at prefix-re)
(looking-at empty-line-prefix-re))
(replace-match "")
(forward-line 1)))
prefix)))
(defun asf--rustdoc-apply-prefix ()
(let ((prefix asf--rustdoc-prefix))
(save-excursion
(goto-char (point-min))
(while (and (re-search-forward "^" nil t)
(not (eql (point) (point-max))))
(replace-match prefix " "))
(goto-char (point-max))
;; non-empty lines at the end must end in a newline:
(unless (looking-at "^$")
(insert "\n")))))
(defun asf--rustdoc-setup-buffer ()
;; Strip the comment prefix from lines & set up the buffer's major mode:
(let ((prefix (asf--rustdoc-strip-prefix)))
(markdown-mode)
(setq-local asf--rustdoc-prefix prefix)
(setq-local edit-indirect-before-commit-hook '(asf--rustdoc-apply-prefix))))
(defun asf-rustdoc-edit ()
(interactive)
(let ((guess (asf--rustdoc-current-comment-block-region)))
(unless guess
(error "Not in a rustdoc comment that I can guess!"))
(destructuring-bind (start end) guess
(let ((edit-indirect-after-creation-hook 'asf--rustdoc-setup-buffer))
(edit-indirect-region start end t)))))
(add-hook 'rust-mode-hook
(defun asf--rustdoc-setup-hook ()
(bind-key "C-c '" 'asf-rustdoc-edit rust-mode-map)))
@czipperz
Copy link

@shepmaster I think instead of (bind-key "C-c '" 'asf-rustdoc-edit rust-mode-map) you should use (define-key rust-mode-map (kbd "C-c '") 'asf-rustdoc-edit)

@czipperz
Copy link

On line 57, if you insert inside the while loop the following code it prevents empty lines from ending in spaces:

        (when (eolp)
          (delete-horizontal-space))

Making it

      (while (and (re-search-forward "^" nil t)
                  (not (eql (point) (point-max))))
        (replace-match prefix " ")
        (when (eolp)
          (delete-horizontal-space)))

@czipperz
Copy link

These improvements and more are tracked in my emacs config

@condy0919
Copy link

condy0919 commented Jan 16, 2020

@antifuchs
Copy link
Author

Huh, that's really cool - glad somebody a real package that did what this was intended to do.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment