├── .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 | 
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 |
--------------------------------------------------------------------------------