Skip to content

Instantly share code, notes, and snippets.

@antifuchs
Last active January 17, 2020 02:44
Show Gist options
  • 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)))
@shepmaster
Copy link

shepmaster commented Mar 22, 2019

This is (would be) wonderful, but there's a handful of non-standard (?) functions used here:

  • use-package
  • bind-key
  • destructuring-bind (this is where I stoped to post this comment 😄 )

At least, these all seemed to be missing in my Emacs 26.1 install.

Is there any chance of some modifications to list the dependencies or work in "stock" Emacs?


I'm using this for now:

- (use-package edit-indirect
-  :straight t)
+ (require 'edit-indirect)
+ (require 'cl)
-             (bind-key "C-c '" 'asf-rustdoc-edit rust-mode-map)))
+             (local-set-key "\C-c'" 'asf-rustdoc-edit)))

Once in the Markdown mode, I use polymode to edit the inline examples as Rust again (very meta).

@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