├── .gitignore ├── README.org └── org2elcomment.el /.gitignore: -------------------------------------------------------------------------------- 1 | *.elc 2 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | #+TITLE: org2elcomment 2 | 3 | Convert =org-mode= file to Elisp comments. 4 | 5 | * Overview 6 | This simple package is mainly used for Elisp package writers. After you've 7 | written the =README.org= for your package, you can use =org2elcomment= to 8 | convert the org file to Elisp comments in the corresponding source code file. 9 | 10 | * Usage 11 | Make sure your source code file has =;;; Commentary:= and =;;; Code:= lines. 12 | The generated comments will be put between these two lines. If you use 13 | =auto-insert=, it will take care of generating a standard file header that 14 | contains these two lines in your source code. 15 | 16 | ** Command =org2elcomment= 17 | In your Org file, invoke =org2elcomment=, select the source code file, and 18 | done! Now take a look at your source code file, you can see your Org file has 19 | been converted to the comments in your source code file. 20 | 21 | ** Command =org2elcomment-anywhere= 22 | You can invoke this command anywhere in Emacs. It requires two parameters. 23 | You need to select the source code file as well as the org file. After 24 | selecting the org file, you can optionally save the org file location as the 25 | file-local variable in the source code file so that you don't need to select 26 | the org file again for the same source code file. 27 | 28 | If you want to automate the process of converting the org to the commentary 29 | section in Elisp file in your project, you can consider using this command. 30 | 31 | * Customization 32 | ** Org Export Backend 33 | Behind the scenes, this package uses =org-export-as= function and the default 34 | backend is =ascii=. You can change to whatever backend that your org-mode 35 | export engine supports, such as =md= (for markdown): 36 | 37 | : (setq org2elcomment-backend 'md) 38 | 39 | ** Exporter Function 40 | In fact, it is even possible to use your own export function instead of the 41 | exporter of org-mode. Write a function which accepts a file name of an org 42 | file and returns the string as the export result. Here is how the default 43 | exporter that we use in this package looks like: 44 | 45 | #+BEGIN_SRC elisp 46 | (defun org2elcomment-default-exporter (org-file) 47 | (with-temp-buffer 48 | (insert-file-contents org-file) 49 | (org-export-as org2elcomment-backend))) 50 | #+END_SRC 51 | 52 | After defining your own export function, say, =my-exporter=, change the value 53 | of =org2elcomment-exporter=: 54 | 55 | : (setq org2elcomment-exporter 'my-exporter) 56 | -------------------------------------------------------------------------------- /org2elcomment.el: -------------------------------------------------------------------------------- 1 | ;;; org2elcomment.el --- Convert Org file to Elisp comments -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2016 Junpeng Qiu 4 | 5 | ;; Author: Junpeng Qiu 6 | ;; Package-Requires: ((org "8.3.4")) 7 | ;; Keywords: extensions 8 | 9 | ;; This program is free software; you can redistribute it and/or modify 10 | ;; it under the terms of the GNU General Public License as published by 11 | ;; the Free Software Foundation, either version 3 of the License, or 12 | ;; (at your option) any later version. 13 | 14 | ;; This program is distributed in the hope that it will be useful, 15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ;; GNU General Public License for more details. 18 | 19 | ;; You should have received a copy of the GNU General Public License 20 | ;; along with this program. If not, see . 21 | 22 | ;;; Commentary: 23 | 24 | ;; _______________ 25 | 26 | ;; ORG2ELCOMMENT 27 | 28 | ;; Junpeng Qiu 29 | ;; _______________ 30 | 31 | 32 | ;; Table of Contents 33 | ;; _________________ 34 | 35 | ;; 1 Overview 36 | ;; 2 Usage 37 | ;; .. 2.1 Command `org2elcomment' 38 | ;; .. 2.2 Command `org2elcomment-anywhere' 39 | ;; 3 Customization 40 | ;; .. 3.1 Org Export Backend 41 | ;; .. 3.2 Exporter Function 42 | 43 | 44 | ;; Convert `org-mode' file to Elisp comments. 45 | 46 | 47 | ;; 1 Overview 48 | ;; ========== 49 | 50 | ;; This simple package is mainly used for Elisp package writers. After 51 | ;; you've written the `README.org' for your package, you can use 52 | ;; `org2elcomment' to convert the org file to Elisp comments in the 53 | ;; corresponding source code file. 54 | 55 | 56 | ;; 2 Usage 57 | ;; ======= 58 | 59 | ;; Make sure your source code file has `;;; Commentary:' and `;;; Code:' 60 | ;; lines. The generated comments will be put between these two lines. 61 | ;; If you use `auto-insert', it will take care of generating a standard 62 | ;; file header that contains these two lines in your source code. 63 | 64 | 65 | ;; 2.1 Command `org2elcomment' 66 | ;; ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 67 | 68 | ;; In your Org file, invoke `org2elcomment', select the source code file, 69 | ;; and done! Now take a look at your source code file, you can see your 70 | ;; Org file has been converted to the comments in your source code file. 71 | 72 | 73 | ;; 2.2 Command `org2elcomment-anywhere' 74 | ;; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 75 | 76 | ;; You can invoke this command anywhere in Emacs. It requires two 77 | ;; parameters. You need to select the source code file as well as the 78 | ;; org file. After selecting the org file, you can optionally save the 79 | ;; org file location as the file-local variable in the source code file 80 | ;; so that you don't need to select the org file again for the same 81 | ;; source code file. 82 | 83 | ;; If you want to automate the process of converting the org to the 84 | ;; commentary section in Elisp file in your project, you can consider 85 | ;; using this command. 86 | 87 | 88 | ;; 3 Customization 89 | ;; =============== 90 | 91 | ;; 3.1 Org Export Backend 92 | ;; ~~~~~~~~~~~~~~~~~~~~~~ 93 | 94 | ;; Behind the scenes, this package uses `org-export-as' function and the 95 | ;; default backend is `ascii'. You can change to whatever backend that 96 | ;; your org-mode export engine supports, such as `md' (for markdown): 97 | 98 | ;; ,---- 99 | ;; | (setq org2elcomment-backend 'md) 100 | ;; `---- 101 | 102 | 103 | ;; 3.2 Exporter Function 104 | ;; ~~~~~~~~~~~~~~~~~~~~~ 105 | 106 | ;; In fact, it is even possible to use your own export function instead 107 | ;; of the exporter of org-mode. Write a function which accepts a file 108 | ;; name of an org file and returns the string as the export result. Here 109 | ;; is how the default exporter that we use in this package looks like: 110 | 111 | ;; ,---- 112 | ;; | (defun org2elcomment-default-exporter (org-file) 113 | ;; | (with-temp-buffer 114 | ;; | (insert-file-contents org-file) 115 | ;; | (org-export-as org2elcomment-backend))) 116 | ;; `---- 117 | 118 | ;; After defining your own export function, say, `my-exporter', change 119 | ;; the value of `org2elcomment-exporter': 120 | 121 | ;; ,---- 122 | ;; | (setq org2elcomment-exporter 'my-exporter) 123 | ;; `---- 124 | 125 | ;;; Code: 126 | 127 | (require 'org) 128 | (require 'pulse nil t) 129 | 130 | (defvar org2elcomment-backend 'ascii 131 | "Org export backend used by the default exporter of `org2elcomment'.") 132 | (defvar org2elcomment-exporter 'org2elcomment-default-exporter 133 | "Export function used by `org2elcomment'.") 134 | 135 | (defvar org2elcomment-last-source nil) 136 | (make-variable-buffer-local 'org2elcomment-last-source) 137 | 138 | (defvar org2elcomment-anywhere-last-source nil) 139 | 140 | (defvar org2elcomment-anywhere-org-file nil) 141 | 142 | (defun org2elcomment--find-bounds (buffer) 143 | (let (beg end) 144 | (with-current-buffer buffer 145 | (save-excursion 146 | (goto-char (point-min)) 147 | (when (re-search-forward "^;;;[[:blank:]]+Commentary:[[:blank:]]*$" nil t) 148 | (setq beg (line-beginning-position 2)) 149 | (when (re-search-forward "^;;;[[:blank:]]+Code:[[:blank:]]*$") 150 | (setq end (line-beginning-position)) 151 | (if (and beg end) 152 | (cons beg end) 153 | (message "org2elcomment: No \";;; Commentary:\" or \";;; Code:\" found.") 154 | nil))))))) 155 | 156 | (defun org2elcomment--get-prompt (initial default-value) 157 | (if default-value 158 | (format "%s (default \"%s\"): " initial default-value) 159 | (format "%s: " initial))) 160 | 161 | (defmacro org2elcomment--interactive-form (name) 162 | `(list (let ((prompt (org2elcomment--get-prompt "Elisp file" ,name))) 163 | (setq ,name (read-file-name prompt nil ,name t))))) 164 | 165 | (defun org2elcomment-default-exporter (org-file) 166 | (with-temp-buffer 167 | (insert-file-contents org-file) 168 | (goto-char (point-min)) 169 | (while (search-forward ". " nil t) 170 | (replace-match ". ")) 171 | (let ((sentence-end-double-space t)) 172 | (org-export-as org2elcomment-backend)))) 173 | 174 | (defun org2elcomment--save-org-to-el (buffer value) 175 | "Set current file's local variable `org2elcomment-anywhere-org-file'." 176 | (when (yes-or-no-p "Save org file location to Elisp file? ") 177 | (with-current-buffer buffer 178 | (add-file-local-variable 179 | 'org2elcomment-anywhere-org-file value)))) 180 | 181 | (defun org2elcomment--read-org-from-el (buffer el-file-dir) 182 | "Get org file location from current buffer's context. 183 | 184 | `org2elcomment-anywhere-org-file' only save the relative path of 185 | the org file. We need to return its abosolute path based 186 | directory EL-FILE-DIR." 187 | (with-current-buffer buffer 188 | (hack-local-variables) 189 | (when (assoc 'org2elcomment-anywhere-org-file 190 | file-local-variables-alist) 191 | (let ((org-file 192 | (concat (file-name-as-directory el-file-dir) 193 | org2elcomment-anywhere-org-file))) 194 | (and org-file (file-exists-p org-file) org-file))))) 195 | 196 | (defun org2elcomment--update-comment (el-file org-file &optional pre-handler 197 | post-handler) 198 | (if (file-locked-p el-file) 199 | (message "org2elcomment: File %S has been locked. Operation denied. " el-file) 200 | (with-temp-buffer 201 | (insert-file-contents el-file) 202 | 203 | (emacs-lisp-mode) 204 | 205 | ;; change `org-file' if necessary 206 | (when pre-handler 207 | (setq org-file (funcall pre-handler el-file org-file))) 208 | 209 | ;; now it's good to update `org2elcomment-anywhere-org-file' 210 | (setq org2elcomment-anywhere-org-file org-file) 211 | 212 | (let ((bounds (org2elcomment--find-bounds (current-buffer))) 213 | output beg end) 214 | (when (and bounds (setq output (funcall org2elcomment-exporter org-file))) 215 | (kill-region (car bounds) (cdr bounds)) 216 | (goto-char (car bounds)) 217 | (insert "\n") 218 | (setq beg (point)) 219 | (insert output) 220 | (comment-region beg (point)) 221 | (insert "\n") 222 | (setq end (point)) 223 | (setq output (buffer-string)) 224 | (let ((buffer (get-file-buffer el-file))) 225 | (if buffer 226 | (with-current-buffer buffer 227 | (let ((point (point))) 228 | (erase-buffer) 229 | (insert output) 230 | (goto-char point)) 231 | (message "org2elcomment: The commentary in buffer %S has been updated." 232 | (buffer-name buffer))) 233 | (write-region (point-min) 234 | (point-max) el-file) 235 | (message "org2elcomment: The commentary of file %S has been updated." el-file)) 236 | ;; some visual effect if necessary 237 | (when post-handler 238 | (funcall post-handler (find-file-noselect el-file) beg end)))))))) 239 | 240 | (defun org2elcomment--pre-handler (el-file org-file) 241 | (goto-char (point-min)) 242 | (let ((el-file-dir (file-name-directory el-file))) 243 | (setq org-file 244 | (or (if (and org-file (file-exists-p org-file)) 245 | org-file) 246 | (org2elcomment--read-org-from-el (current-buffer) el-file-dir) 247 | (read-file-name 248 | (org2elcomment--get-prompt "Org file" org2elcomment-anywhere-org-file) 249 | el-file-dir 250 | org2elcomment-anywhere-org-file 251 | t))) 252 | 253 | (unless (org2elcomment--read-org-from-el (current-buffer) el-file-dir) 254 | (org2elcomment--save-org-to-el 255 | (current-buffer) (file-relative-name org-file el-file-dir))) 256 | org-file)) 257 | 258 | (defun org2elcomment--post-handler (el-buf beg end) 259 | (switch-to-buffer el-buf) 260 | (push-mark) 261 | (goto-char beg) 262 | (recenter 0) 263 | (when (featurep 'pulse) 264 | (pulse-momentary-highlight-region beg end))) 265 | 266 | ;;;###autoload 267 | (defun org2elcomment-anywhere (el-file &optional org-file) 268 | "Convert ORG-FILE to the commentary section in EL-FILE. 269 | This command can be invoked anywhere inside Emacs." 270 | (interactive (org2elcomment--interactive-form org2elcomment-anywhere-last-source)) 271 | (org2elcomment--update-comment el-file org-file #'org2elcomment--pre-handler)) 272 | 273 | ;;;###autoload 274 | (defun org2elcomment (el-file) 275 | "Conver the current org file to the commentary section in EL-FILE. 276 | This command must be invoked when the current buffer is in `org-mode'." 277 | (interactive (org2elcomment--interactive-form org2elcomment-last-source)) 278 | (org2elcomment--update-comment el-file (buffer-file-name) nil #'org2elcomment--post-handler)) 279 | 280 | (provide 'org2elcomment) 281 | ;;; org2elcomment.el ends here 282 | --------------------------------------------------------------------------------