├── .gitignore ├── snapshots └── el2org.gif ├── README.md └── el2org.el /.gitignore: -------------------------------------------------------------------------------- 1 | /*~ 2 | /*.html 3 | /*.elc 4 | /el2org.org 5 | /index.org 6 | -------------------------------------------------------------------------------- /snapshots/el2org.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tumashu/el2org/HEAD/snapshots/el2org.gif -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Note: this file is auto converted from el2org.el by [el2org](https://github.com/tumashu/el2org), please do not edit it by hand!!! 2 | 3 | 4 | # 目录 5 | 6 | 1. [What is el2org](#orgfbaf885) 7 | 1. [Installation](#orgf6eded7) 8 | 2. [Configure](#org27e2b16) 9 | 3. [Usage](#org1242b25) 10 | 11 | 12 | 13 | 14 | # What is el2org 15 | 16 | el2org is a simple tool, which can convert a emacs-lisp file to org file. 17 | You can write code and document in a elisp file with its help. 18 | 19 | (convert to) (export to) 20 | elisp -----------------> org (internal) --------------> other formats 21 | 22 | Note: el2org.el file may be a good example. 23 | 24 | ![img](./snapshots/el2org.gif) 25 | 26 | 27 | 28 | 29 | ## Installation 30 | 31 | 1. Config melpa source, please read: 32 | 2. M-x package-install RET el2org RET 33 | 3. M-x package-install RET ox-gfm RET 34 | 35 | ox-gfm is needed by \`el2org-generate-readme', if ox-gfm can not be found, 36 | ox-md will be used as fallback. 37 | 38 | 39 | 40 | 41 | ## Configure 42 | 43 | (require 'el2org) 44 | (require 'ox-gfm) 45 | 46 | 47 | 48 | 49 | ## Usage 50 | 51 | 1. \`el2org-generate-file' can convert an elisp file to other file format 52 | which org's exporter support. 53 | 2. \`el2org-generate-readme' can generate README.md from elisp's "Commentary" 54 | section. 55 | 3. \`el2org-generate-html' can generate a html file from current elisp file 56 | and browse it. 57 | 4. \`el2org-generate-org' can generate a org file from current elisp file. 58 | 59 | -------------------------------------------------------------------------------- /el2org.el: -------------------------------------------------------------------------------- 1 | ;;; el2org.el --- Convert elisp file to org file -*- lexical-binding: t; -*- 2 | 3 | ;; * Header 4 | ;; #+begin_example 5 | ;; Copyright (C) 2017-2019 Feng Shu 6 | 7 | ;; Author: Feng Shu 8 | ;; Homepage: https://github.com/tumashu/el2org 9 | ;; Keywords: convenience 10 | ;; Package-Requires: ((emacs "25.1")) 11 | ;; Version: 0.10 12 | 13 | ;; This program is free software; you can redistribute it and/or modify 14 | ;; it under the terms of the GNU General Public License as published by 15 | ;; the Free Software Foundation, either version 3 of the License, or 16 | ;; (at your option) any later version. 17 | 18 | ;; This program is distributed in the hope that it will be useful, 19 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | ;; GNU General Public License for more details. 22 | 23 | ;; You should have received a copy of the GNU General Public License 24 | ;; along with this program. If not, see . 25 | ;; #+end_example 26 | 27 | ;;; Commentary: 28 | 29 | ;; * What is el2org :README: 30 | ;; el2org is a simple tool, which can convert a emacs-lisp file to org file. 31 | ;; You can write code and document in a elisp file with its help. 32 | 33 | ;; #+begin_example 34 | ;; (convert to) (export to) 35 | ;; elisp -----------------> org (internal) --------------> other formats 36 | ;; #+end_example 37 | 38 | ;; Note: el2org.el file may be a good example. 39 | 40 | ;; [[./snapshots/el2org.gif]] 41 | 42 | ;; ** Installation 43 | 44 | ;; 1. Config melpa source, please read: [[http://melpa.org/#/getting-started]] 45 | ;; 2. M-x package-install RET el2org RET 46 | ;; 3. M-x package-install RET ox-gfm RET 47 | 48 | ;; ox-gfm is needed by `el2org-generate-readme', if ox-gfm can not be found, 49 | ;; ox-md will be used as fallback. 50 | 51 | ;; ** Configure 52 | 53 | ;; #+begin_src emacs-lisp 54 | ;; (require 'el2org) 55 | ;; (require 'ox-gfm) 56 | ;; #+end_src 57 | 58 | ;; ** Usage 59 | 60 | ;; 1. `el2org-generate-file' can convert an elisp file to other file format 61 | ;; which org's exporter support. 62 | ;; 2. `el2org-generate-readme' can generate README.md from elisp's "Commentary" 63 | ;; section. 64 | ;; 3. `el2org-generate-html' can generate a html file from current elisp file 65 | ;; and browse it. 66 | ;; 4. `el2org-generate-org' can generate a org file from current elisp file. 67 | 68 | ;;; Code: 69 | 70 | ;; * el2org's code :code: 71 | (require 'lisp-mode) 72 | (require 'thingatpt) 73 | (require 'org) 74 | (require 'ox-org) 75 | (require 'ox-md) 76 | 77 | (defgroup el2org nil 78 | "A tool, which can convert a emacs-lisp file to org file" 79 | :group 'tools 80 | :prefix "el2org-") 81 | 82 | (defcustom el2org-default-backend 'gfm 83 | "Default backend for README file." 84 | :group 'el2org 85 | :type '(choice (const :tag "Org" org) 86 | (const :tag "GitHub Markdown" gfm) 87 | (const :tag "Org Markdown" md))) 88 | 89 | (defcustom el2org-add-notification 'begin 90 | "Add a notification, which mention el2org as file generator in README." 91 | :group 'el2org 92 | :type '(choice (const :tag "Beginning of file" begin) 93 | (const :tag "End of file" end) 94 | (const :tag "Off" nil))) 95 | 96 | (defvar el2org-backend-settings 97 | `((gfm 98 | :require ox-gfm 99 | :fallback md 100 | :filename-extension "md" 101 | :notification 102 | ,(concat "Note: this file is auto converted from %elisp-file by " 103 | "[el2org](https://github.com/tumashu/el2org), " 104 | "please do not edit it by hand!!!")) 105 | (md 106 | :filename-extension "md" 107 | :notification 108 | ,(concat "Note: this file is auto converted from %elisp-file by " 109 | "[el2org](https://github.com/tumashu/el2org), " 110 | "please do not edit it by hand!!!")) 111 | (org 112 | :filename-extension "org" 113 | :notification 114 | ,(concat "Note: this file is auto converted from %elisp-file by " 115 | "[[https://github.com/tumashu/el2org][el2org]], " 116 | "please do not edit it by hand!!!"))) 117 | "The settings of el2org backends.") 118 | 119 | (defun el2org-in-src-block-p () 120 | "If the current point is in BEGIN/end_src block, return t." 121 | (let ((begin1 (save-excursion 122 | (if (re-search-backward ";; #[+]begin_src emacs-lisp" nil t) 123 | (point) 124 | (point-min)))) 125 | (begin2 (save-excursion 126 | (if (re-search-backward ";; #[+]end_src" nil t) 127 | (point) 128 | (point-min)))) 129 | (end1 (save-excursion 130 | (if (re-search-forward "\n;; #[+]begin_src emacs-lisp" nil t) 131 | (point) 132 | (point-max)))) 133 | (end2 (save-excursion 134 | (if (re-search-forward "\n;; #[+]end_src" nil t) 135 | (point) 136 | (point-max))))) 137 | (and (> begin1 begin2) (> end1 end2)))) 138 | 139 | (defun el2org-generate-file (el-file tags backend output-file &optional force with-tags) 140 | (when (and (string-match-p "\\.el$" el-file) 141 | (file-exists-p el-file) 142 | (or force 143 | (not (file-exists-p output-file)) 144 | (file-newer-than-file-p el-file output-file))) 145 | (with-temp-buffer 146 | (insert-file-contents el-file) 147 | (emacs-lisp-mode) 148 | (let ((case-fold-search t) 149 | (buffer-end-of-newline-p 150 | (save-excursion (goto-char (point-max)) 151 | (forward-line 0) 152 | (if (string-match 153 | "^$" 154 | (buffer-substring-no-properties (point) (point-max))) 155 | t 156 | nil)))) 157 | ;; Protect existing "begin_src emacs-lisp" 158 | (goto-char (point-min)) 159 | (while (re-search-forward "#[+]begin_src[ ]+emacs-lisp" nil t) 160 | (replace-match "&&&el2org-begin-src-emacs-lisp&&&" nil t)) 161 | ;; Protect existing "#+end_src" 162 | (goto-char (point-min)) 163 | (while (re-search-forward "#[+]end_src" nil t) 164 | (replace-match "&&&el2org-end-src&&&" nil t)) 165 | ;; Add "#+end_src" 166 | (goto-char (point-min)) 167 | (let ((status t)) 168 | (while status 169 | (thing-at-point--end-of-sexp) 170 | (end-of-line) 171 | (if (< (point) (point-max)) 172 | (insert "\n;; #+end_src") 173 | (setq status nil) 174 | (unless buffer-end-of-newline-p 175 | (insert "\n;; #+end_src"))))) 176 | ;; Add "#+begin_src" 177 | (goto-char (point-max)) 178 | (let ((status t)) 179 | (while status 180 | (thing-at-point--beginning-of-sexp) 181 | (if (> (point) (point-min)) 182 | (insert ";; #+begin_src emacs-lisp\n") 183 | (setq status nil)))) 184 | ;; Deal with first line if it prefix with ";;;" 185 | (goto-char (point-min)) 186 | (while (re-search-forward "^;;;.*---[ ]+" (line-end-position) t) 187 | (replace-match ";; #+TITLE: " nil t)) 188 | ;; Remove lexical-binding string 189 | (goto-char (point-min)) 190 | (while (re-search-forward "[ ]*-\\*-.*-\\*-[ ]*$" 191 | (line-end-position) t) 192 | (replace-match "" nil t)) 193 | ;; Indent the buffer, so ";;" and ";;;" in sexp will not be removed. 194 | (indent-region (point-min) (point-max)) 195 | ;; Add protect-mask to the beginning of "^;;[;]+" in string. 196 | (goto-char (point-min)) 197 | (while (not (eobp)) 198 | (beginning-of-line) 199 | (let ((content (buffer-substring 200 | (line-beginning-position) 201 | (line-end-position)))) 202 | (when (and (el2org-in-src-block-p) 203 | (string-match-p "^;;[; ]" content)) 204 | (goto-char (line-beginning-position)) 205 | (insert "&&&el2org;;;&&&") 206 | (goto-char (line-beginning-position)))) 207 | (forward-line)) 208 | ;; Deal with ";; Local Variables:" and ";; End:" 209 | (goto-char (point-min)) 210 | (while (re-search-forward "^;;+[ ]+Local[ ]+Variables: *" nil t) 211 | (replace-match ";; #+begin_example\n;; Local Variables:" nil t)) 212 | (goto-char (point-min)) 213 | (while (re-search-forward "^;;+[ ]+End: *" nil t) 214 | (replace-match ";; End:\n;; #+end_example" nil t)) 215 | ;; Deal with ";;;" 216 | (goto-char (point-min)) 217 | (while (re-search-forward "^;;;" nil t) 218 | (replace-match "# ;;;" nil t)) 219 | ;; Un-protect existing "begin_src emacs-lisp" 220 | (goto-char (point-min)) 221 | (while (re-search-forward "&&&el2org-begin-src-emacs-lisp&&&" nil t) 222 | (replace-match "#+begin_src emacs-lisp" nil t)) 223 | ;; Un-Protect existing "#+end_src" 224 | (goto-char (point-min)) 225 | (while (re-search-forward "&&&el2org-end-src&&&" nil t) 226 | (replace-match "#+end_src" nil t)) 227 | ;; Deal with ";;" 228 | (goto-char (point-min)) 229 | (while (re-search-forward "^;;[ ]?" nil t) 230 | (replace-match "" nil t) 231 | (goto-char (line-end-position))) 232 | ;; Delete useless "begin_src/end_src" 233 | (goto-char (point-min)) 234 | (while (re-search-forward "^#[+]begin_src[ ]+emacs-lisp\n+#[+]end_src\n*" nil t) 235 | (replace-match "" nil t)) 236 | (goto-char (point-min)) 237 | (while (re-search-forward "^#[+]end_src\n#[+]begin_src[ ]+emacs-lisp\n" nil t) 238 | (replace-match "" nil t)) 239 | ;; Remove protect-mark. 240 | (goto-char (point-min)) 241 | (while (re-search-forward "^&&&el2org;;;&&&" nil t) 242 | (replace-match "" nil t))) 243 | ;; Export 244 | (org-mode) 245 | (let ((org-export-select-tags tags) 246 | (org-export-with-tags with-tags) 247 | (indent-tabs-mode nil) 248 | (tab-width 4)) 249 | (org-export-to-file backend output-file)) 250 | output-file))) 251 | 252 | (defun el2org--get-valid-backend (backend) 253 | (let* ((setting (cdr (assq backend el2org-backend-settings))) 254 | (feature (plist-get setting :require)) 255 | (fallback (plist-get setting :fallback))) 256 | (if (or (not feature) 257 | (and feature (featurep feature))) 258 | backend 259 | (message "el2org: backend '%s' is invalid, fallback to '%s'." backend fallback) 260 | fallback))) 261 | 262 | ;;;###autoload 263 | (defun el2org-generate-readme (&optional backend file-ext) 264 | "Generate README from current emacs-lisp file. 265 | 266 | If BACKEND is set then use-it else use `el2org-default-backend'. 267 | If FILE-EXT is nil deduce it from BACKEND." 268 | (interactive) 269 | (let* ((backend (el2org--get-valid-backend 270 | (or backend el2org-default-backend))) 271 | (file-ext 272 | (or file-ext 273 | (plist-get (cdr (assq backend el2org-backend-settings)) 274 | :filename-extension))) 275 | (el-file (or (buffer-file-name) 276 | (error "el2org: No emacs-lisp file is found."))) 277 | (readme-file (concat (file-name-directory el-file) "README" "." file-ext)) 278 | (notification (replace-regexp-in-string 279 | "%elisp-file" 280 | (file-name-nondirectory el-file) 281 | (or (plist-get (cdr (assq 'md el2org-backend-settings)) 282 | :notification) 283 | "")))) 284 | (el2org-generate-file el-file '("README") backend readme-file t) 285 | (when el2org-add-notification 286 | (with-temp-buffer 287 | (insert-file-contents readme-file) 288 | (if (eq el2org-add-notification 'end) 289 | (progn (goto-char (point-max)) 290 | (insert "\n\n") 291 | (insert notification)) 292 | (goto-char (point-min)) 293 | (insert notification) 294 | (insert "\n\n")) 295 | (write-file readme-file))))) 296 | 297 | ;;;###autoload 298 | (defun el2org-generate-html () 299 | "Generate html file from current elisp file and browse it." 300 | (interactive) 301 | (let* ((file (or (buffer-file-name) 302 | (error "el2org: No emacs-lisp file is found."))) 303 | (html-file (concat (file-name-sans-extension file) "_el2org.html")) 304 | (tags (when (yes-or-no-p "Convert README tag only? ") 305 | '("README")))) 306 | (el2org-generate-file file tags 'html html-file t) 307 | (browse-url html-file))) 308 | 309 | ;;;###autoload 310 | (defun el2org-generate-org () 311 | "Generate org file from current elisp file." 312 | (interactive) 313 | (let* ((file (or (buffer-file-name) 314 | (error "el2org: No emacs-lisp file is found."))) 315 | (org-file (concat (file-name-sans-extension file) ".org"))) 316 | (el2org-generate-file file nil 'org org-file t t))) 317 | 318 | (provide 'el2org) 319 | 320 | ;; * Footer 321 | 322 | ;; Local Variables: 323 | ;; coding: utf-8-unix 324 | ;; End: 325 | 326 | ;;; el2org.el ends here 327 | --------------------------------------------------------------------------------