Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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

This comment has been minimized.

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

This comment has been minimized.

Copy link

czipperz commented Apr 13, 2019

@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

This comment has been minimized.

Copy link

czipperz commented Apr 13, 2019

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

This comment has been minimized.

Copy link

czipperz commented Apr 13, 2019

These improvements and more are tracked in my emacs config

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.