ruby_learner 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (188) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +5 -0
  5. data/CODE_OF_CONDUCT.md +74 -0
  6. data/Gemfile +6 -0
  7. data/Gemfile.lock +58 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +65 -0
  10. data/Rakefile +6 -0
  11. data/bin/console +14 -0
  12. data/bin/new_terminal +25 -0
  13. data/bin/setup +8 -0
  14. data/docs/happy_ruby/RussOlsen_EloquentRuby_c1.pdf +0 -0
  15. data/docs/happy_ruby/RussOlsen_EloquentRuby_c5.pdf +0 -0
  16. data/docs/happy_ruby/TanoshiiRuby_v3_c23.pdf +0 -0
  17. data/docs/happy_ruby/TanoshiiRuby_v5_c1.pdf +0 -0
  18. data/docs/happy_ruby/TanoshiiRuby_v5_c2-3.pdf +0 -0
  19. data/docs/happy_ruby/c2.ipynb +479 -0
  20. data/docs/happy_ruby/c3_4.ipynb +237 -0
  21. data/docs/seminar/8-1.org +18 -0
  22. data/exe/ruby_learner +5 -0
  23. data/lib/ruby_learner/h.rb +14 -0
  24. data/lib/ruby_learner/methods.rb +131 -0
  25. data/lib/ruby_learner/random_h.rb +16 -0
  26. data/lib/ruby_learner/ruby_learner.rb +43 -0
  27. data/lib/ruby_learner/sequential_h.rb +15 -0
  28. data/lib/ruby_learner/typing_practice.rb +21 -0
  29. data/lib/ruby_learner/version.rb +3 -0
  30. data/questions/random_check/.rspec +1 -0
  31. data/questions/random_check/random_h.rb +16 -0
  32. data/questions/random_check/section_1/.rspec +1 -0
  33. data/questions/random_check/section_1/lib/answer.rb +15 -0
  34. data/questions/random_check/section_1/lib/sentence.org +9 -0
  35. data/questions/random_check/section_1/lib/workplace.rb +5 -0
  36. data/questions/random_check/section_1/spec/spec_helper.rb +100 -0
  37. data/questions/random_check/section_1/spec/workplace_spec.rb +10 -0
  38. data/questions/random_check/section_2/.rspec +1 -0
  39. data/questions/random_check/section_2/lib/answer.rb +17 -0
  40. data/questions/random_check/section_2/lib/sentence.org +12 -0
  41. data/questions/random_check/section_2/lib/workplace.rb +5 -0
  42. data/questions/random_check/section_2/spec/.rspec +1 -0
  43. data/questions/random_check/section_2/spec/spec_helper.rb +100 -0
  44. data/questions/random_check/section_2/spec/workplace_spec.rb +11 -0
  45. data/questions/sequential_check/section_1/part_1/lib/answer.rb +9 -0
  46. data/questions/sequential_check/section_1/part_1/lib/sentence.org +9 -0
  47. data/questions/sequential_check/section_1/part_1/lib/workplace.rb +5 -0
  48. data/questions/sequential_check/section_1/part_1/spec/spec_helper.rb +100 -0
  49. data/questions/sequential_check/section_1/part_1/spec/workplace_spec.rb +10 -0
  50. data/questions/sequential_check/section_1/part_2/lib/answer.rb +16 -0
  51. data/questions/sequential_check/section_1/part_2/lib/sentence.org +12 -0
  52. data/questions/sequential_check/section_1/part_2/lib/workplace.rb +5 -0
  53. data/questions/sequential_check/section_1/part_2/spec/.rspec +1 -0
  54. data/questions/sequential_check/section_1/part_2/spec/spec_helper.rb +100 -0
  55. data/questions/sequential_check/section_1/part_2/spec/workplace_spec.rb +11 -0
  56. data/ruby_learner.gemspec +41 -0
  57. data/takahashi/docs/README.org +139 -0
  58. data/takahashi/docs/drill.html +875 -0
  59. data/takahashi/docs/drill.html~ +877 -0
  60. data/takahashi/docs/drill.org +446 -0
  61. data/takahashi/docs/ruby_for_beginner.html +2642 -0
  62. data/takahashi/docs/ruby_for_beginner.org +1430 -0
  63. data/takahashi/sample_prog/answer/10_1.rb +5 -0
  64. data/takahashi/sample_prog/answer/11_1.rb +5 -0
  65. data/takahashi/sample_prog/answer/11_2.rb +4 -0
  66. data/takahashi/sample_prog/answer/1_1.rb +1 -0
  67. data/takahashi/sample_prog/answer/1_2.rb +1 -0
  68. data/takahashi/sample_prog/answer/1_3.rb +1 -0
  69. data/takahashi/sample_prog/answer/2_1.rb +5 -0
  70. data/takahashi/sample_prog/answer/2_2.rb +12 -0
  71. data/takahashi/sample_prog/answer/3_1.rb +10 -0
  72. data/takahashi/sample_prog/answer/4_1.rb +7 -0
  73. data/takahashi/sample_prog/answer/5_1.rb +6 -0
  74. data/takahashi/sample_prog/answer/5_2.rb +3 -0
  75. data/takahashi/sample_prog/answer/6_1.rb +3 -0
  76. data/takahashi/sample_prog/answer/6_2.rb +5 -0
  77. data/takahashi/sample_prog/answer/6_3.rb +5 -0
  78. data/takahashi/sample_prog/answer/6_4.rb +7 -0
  79. data/takahashi/sample_prog/answer/7_1.rb +3 -0
  80. data/takahashi/sample_prog/answer/7_2.rb +8 -0
  81. data/takahashi/sample_prog/answer/9_1.rb +3 -0
  82. data/takahashi/sample_prog/answer/9_2.rb +5 -0
  83. data/takahashi/sample_prog/answer/9_3.rb +10 -0
  84. data/takahashi/sample_prog/answer/hello.rb +3 -0
  85. data/workshop/.rspec +1 -0
  86. data/workshop/emacs.d/ac-comphist.dat +50 -0
  87. data/workshop/emacs.d/cp5022x.el +156 -0
  88. data/workshop/emacs.d/elpa/archives/gnu/archive-contents +1240 -0
  89. data/workshop/emacs.d/elpa/archives/melpa/archive-contents +2 -0
  90. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/auto-complete-autoloads.el +65 -0
  91. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/auto-complete-config.el +551 -0
  92. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/auto-complete-config.elc +0 -0
  93. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/auto-complete-pkg.el +6 -0
  94. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/auto-complete.el +2164 -0
  95. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/auto-complete.elc +0 -0
  96. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/ada-mode +72 -0
  97. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/c++-mode +99 -0
  98. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/c-mode +55 -0
  99. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/caml-mode +231 -0
  100. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/clojure-mode +580 -0
  101. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/clojurescript-mode +475 -0
  102. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/coq-mode +278 -0
  103. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/css-mode +874 -0
  104. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/erlang-mode +216 -0
  105. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/ess-julia-mode +37 -0
  106. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/go-mode +25 -0
  107. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/haskell-mode +679 -0
  108. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/java-mode +53 -0
  109. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/js-mode +148 -0
  110. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/julia-mode +37 -0
  111. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/lua-mode +21 -0
  112. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/nim-mode +70 -0
  113. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/objc-mode +161 -0
  114. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/octave-mode +46 -0
  115. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/php-mode +6144 -0
  116. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/python-mode +379 -0
  117. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/qml-mode +183 -0
  118. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/ruby-mode +181 -0
  119. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/scala-mode +1347 -0
  120. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/scheme-mode +216 -0
  121. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/sclang-mode +1481 -0
  122. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/sh-mode +182 -0
  123. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/swift-mode +87 -0
  124. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/tcl-mode +172 -0
  125. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/ts-mode +797 -0
  126. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/tuareg-mode +231 -0
  127. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/verilog-mode +313 -0
  128. data/workshop/emacs.d/elpa/better-defaults-20170613.2104/better-defaults-autoloads.el +16 -0
  129. data/workshop/emacs.d/elpa/better-defaults-20170613.2104/better-defaults-pkg.el +2 -0
  130. data/workshop/emacs.d/elpa/better-defaults-20170613.2104/better-defaults.el +90 -0
  131. data/workshop/emacs.d/elpa/better-defaults-20170613.2104/better-defaults.elc +0 -0
  132. data/workshop/emacs.d/elpa/haml-mode-20170923.2153/haml-mode-autoloads.el +26 -0
  133. data/workshop/emacs.d/elpa/haml-mode-20170923.2153/haml-mode-pkg.el +2 -0
  134. data/workshop/emacs.d/elpa/haml-mode-20170923.2153/haml-mode.el +877 -0
  135. data/workshop/emacs.d/elpa/haml-mode-20170923.2153/haml-mode.elc +0 -0
  136. data/workshop/emacs.d/elpa/haml-mode-readme.txt +8 -0
  137. data/workshop/emacs.d/elpa/material-theme-20171123.1040/material-light-theme.el +918 -0
  138. data/workshop/emacs.d/elpa/material-theme-20171123.1040/material-light-theme.elc +0 -0
  139. data/workshop/emacs.d/elpa/material-theme-20171123.1040/material-theme-autoloads.el +32 -0
  140. data/workshop/emacs.d/elpa/material-theme-20171123.1040/material-theme-pkg.el +8 -0
  141. data/workshop/emacs.d/elpa/material-theme-20171123.1040/material-theme.el +912 -0
  142. data/workshop/emacs.d/elpa/material-theme-20171123.1040/material-theme.elc +0 -0
  143. data/workshop/emacs.d/elpa/ox-bibtex-chinese-readme.txt +21 -0
  144. data/workshop/emacs.d/elpa/popup-20160709.729/popup-autoloads.el +15 -0
  145. data/workshop/emacs.d/elpa/popup-20160709.729/popup-pkg.el +2 -0
  146. data/workshop/emacs.d/elpa/popup-20160709.729/popup.el +1432 -0
  147. data/workshop/emacs.d/elpa/popup-20160709.729/popup.elc +0 -0
  148. data/workshop/emacs.d/elpa/yaml-mode-20180212.1556/yaml-mode-autoloads.el +33 -0
  149. data/workshop/emacs.d/elpa/yaml-mode-20180212.1556/yaml-mode-pkg.el +2 -0
  150. data/workshop/emacs.d/elpa/yaml-mode-20180212.1556/yaml-mode.el +470 -0
  151. data/workshop/emacs.d/elpa/yaml-mode-20180212.1556/yaml-mode.elc +0 -0
  152. data/workshop/emacs.d/elpa/yaml-mode-readme.txt +25 -0
  153. data/workshop/emacs.d/haml-mode-master/.gitignore +1 -0
  154. data/workshop/emacs.d/haml-mode-master/.mailmap +2 -0
  155. data/workshop/emacs.d/haml-mode-master/MIT-LICENSE +20 -0
  156. data/workshop/emacs.d/haml-mode-master/README.md +47 -0
  157. data/workshop/emacs.d/haml-mode-master/haml-mode.el +887 -0
  158. data/workshop/emacs.d/iceberg_theme.el +202 -0
  159. data/workshop/emacs.d/init-open-recentf.el +133 -0
  160. data/workshop/emacs.d/init.el +229 -0
  161. data/workshop/emacs.d/inits/line-num.el +264 -0
  162. data/workshop/emacs.d/install-elisp.el +366 -0
  163. data/workshop/emacs.d/markdown-mode/markdown-mode.el +5978 -0
  164. data/workshop/emacs.d/notes +12 -0
  165. data/workshop/emacs.d/processing-mode/processing-mode.el +275 -0
  166. data/workshop/emacs.d/recentf +31 -0
  167. data/workshop/emacs.d/ruby-mode/inf-ruby.el +416 -0
  168. data/workshop/emacs.d/ruby-mode/rdoc-mode.el +130 -0
  169. data/workshop/emacs.d/ruby-mode/ruby-electric.el +205 -0
  170. data/workshop/emacs.d/ruby-mode/ruby-mode.el +1496 -0
  171. data/workshop/emacs.d/ruby-mode/ruby-style.el +78 -0
  172. data/workshop/emacs.d/ruby-mode/rubydb2x.el +104 -0
  173. data/workshop/emacs.d/ruby-mode/rubydb3x.el +115 -0
  174. data/workshop/emacs.d/ruby_learner_init.el +244 -0
  175. data/workshop/emacs.d/themes/dracula-theme.el +431 -0
  176. data/workshop/emacs.d/themes/iceberg-theme.el +205 -0
  177. data/workshop/emacs.d/themes/my-misterioso-theme.el +109 -0
  178. data/workshop/emacs.d/themes/my-wombat-theme.el +121 -0
  179. data/workshop/emacs.d/wiki-mode/wiki.el +976 -0
  180. data/workshop/emacs_help.org +34 -0
  181. data/workshop/lib/answer.rb +1 -0
  182. data/workshop/lib/sentence.org +1 -0
  183. data/workshop/lib/workplace.rb +1 -0
  184. data/workshop/restore/empty.rb +0 -0
  185. data/workshop/spec/spec_helper.rb +100 -0
  186. data/workshop/spec/workplace_spec.rb +1 -0
  187. data/workshop/training_data.txt +3 -0
  188. metadata +343 -0
@@ -0,0 +1,976 @@
1
+ ;;; wiki.el --- hypertext authoring the WikiWay
2
+
3
+ ;; Copyright (C) 2001, 2002, 2012 Alex Schroeder <alex@gnu.org>
4
+
5
+ ;; Emacs Lisp Archive Entry
6
+ ;; Filename: wiki.el
7
+ ;; Version: 2.1.10
8
+ ;; Keywords: hypermedia
9
+ ;; Author: Alex Schroeder <alex@gnu.org>
10
+ ;; Maintainer: Alex Schroeder <alex@gnu.org>
11
+ ;; Description: Hypertext authoring the WikiWay
12
+ ;; URL: http://www.emacswiki.org/cgi-bin/wiki.pl?WikiMode
13
+
14
+ ;; This file is not part of GNU Emacs.
15
+
16
+ ;; This program is free software: you can redistribute it and/or modify it under
17
+ ;; the terms of the GNU General Public License as published by the Free Software
18
+ ;; Foundation, either version 3 of the License, or (at your option) any later
19
+ ;; version.
20
+ ;;
21
+ ;; This program is distributed in the hope that it will be useful, but WITHOUT
22
+ ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
23
+ ;; FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
24
+ ;; details.
25
+ ;;
26
+ ;; You should have received a copy of the GNU General Public License along with
27
+ ;; GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
28
+
29
+ ;;; Commentary:
30
+
31
+ ;; Wiki is a hypertext and a content management system: Normal users are
32
+ ;; encouraged to enhance the hypertext by editing and refactoring
33
+ ;; existing pages and by adding more pages. This is made easy by
34
+ ;; requiring a certain way of writing pages. It is not as complicated
35
+ ;; as a markup language such as HTML. The general idea is to write
36
+ ;; plain ASCII. Word with mixed case such as ThisOne are WikiNames --
37
+ ;; they may be a Link or they may not. If they are, clicking them will
38
+ ;; take you to the page with that WikiName; if they are not, clicking
39
+ ;; them will create an empty page for you to fill out.
40
+
41
+ ;; This mode does all of this for you without using a web browser, cgi
42
+ ;; scripts, databases, etc. All you need is Emacs! In order to
43
+ ;; install, put wiki.el on you load-path, and add the following to your
44
+ ;; .emacs file:
45
+
46
+ ;; (require 'wiki)
47
+
48
+ ;; This will activate WikiMode for all files in `wiki-directories' as soon
49
+ ;; as they are opened. This works by adding `wiki-maybe' to
50
+ ;; `find-file-hooks'.
51
+
52
+ ;; Emacs provides the functionality usually found on Wiki web sites
53
+ ;; automatically: To find out how many pages have links to your page,
54
+ ;; use `grep' or `dired-do-search'. To get an index of all wikis, use
55
+ ;; `dired'. To keep old versions around, use `version-control' or use
56
+ ;; `vc-next-action'. To edit wikis, use Emacs!
57
+
58
+ ;; You can publish a wiki using `wiki-publish', or you can use
59
+ ;; `dired-do-wiki-publish' to publish marked wikis from dired, or you
60
+ ;; can use `wiki-publish-all' to publish all wikis and write an index
61
+ ;; file. This will translate your plain text wikis into HTML according
62
+ ;; to the rules defined in `wiki-pub-rules'.
63
+
64
+ ;; Find out more: Take a look at http://c2.com/cgi/wiki?StartingPoints
65
+
66
+ ;;; XEmacs
67
+
68
+ ;; XEmacs users will have to get easy-mmode.el, I'm afraid. You should
69
+ ;; be able to get easy-mmode.el from a web site carrying the Emacs
70
+ ;; sources. It loads fine, so that shouldn't be a problem. Put
71
+ ;; `easy-mmode' somewhere in your load-path and install as follows:
72
+
73
+ ;; (require 'easy-mmode)
74
+ ;; (require 'wiki)
75
+
76
+ ;;; What about a Major Mode?
77
+
78
+ ;; By default, wiki files will be in `fundamental-mode'. I prefer to be
79
+ ;; in `text-mode', instead. You can do this either for all files that
80
+ ;; have WikiNames by changing `auto-mode-alist', or you can make
81
+ ;; text-mode the default mode instead of fundamental mode. Example:
82
+
83
+ ;; (setq default-major-mode 'text-mode)
84
+
85
+ ;; This puts wiki files in `text-mode'. One problem remains, however.
86
+ ;; Text mode usually means that the apostrophe is considered to be part
87
+ ;; of words, and some WikiNames will not be highlighted correctly, such
88
+ ;; as "WikiName''''s". In that case, change the syntax table, if you
89
+ ;; don't mind the side effects. Example:
90
+
91
+ ;; (modify-syntax-entry ?' "." text-mode-syntax-table)
92
+
93
+ ;;; Thanks
94
+
95
+ ;; Frank Gerhardt <Frank.Gerhardt@web.de>, author of the original wiki-mode.
96
+ ;; His latest version is here: http://www.s.netic.de/fg/wiki-mode/wiki.el
97
+ ;; Thomas Link <t.link@gmx.at>
98
+ ;; John Wiegley <johnw@gnu.org>, author of emacs-wiki.el.
99
+ ;; His latest version is here: http://www.emacswiki.org/emacs/EmacsWikiMode
100
+ ;; and evolved into Emacs Muse: http://www.emacswiki.org/emacs/EmacsMuse
101
+
102
+
103
+
104
+ ;;; Code:
105
+
106
+ (require 'easy-mmode); for easy-mmode-define-minor-mode
107
+ (require 'info); for info-xref face
108
+ (require 'thingatpt); for thing-at-point-looking-at and other things
109
+ (require 'compile); for grep-command
110
+ (load "goto-addr" t t); optional, for goto-address-mail-regexp
111
+
112
+ ;; Options
113
+
114
+ (defgroup wiki nil
115
+ "Options controlling the behaviour of Wiki Mode.
116
+ See `wiki-mode' for more information.")
117
+
118
+ (defcustom wiki-directories (list (expand-file-name "~/Wiki/"))
119
+ "List of directories where all wiki files are stored.
120
+ The directories should contain fully expanded directory names and they
121
+ should end with a slash on most systems, because each element in the
122
+ list is compared to the return value of `file-name-directory'. And that
123
+ function returns trailing slashes. Use `expand-file-name' to expand
124
+ directory names if necessary."
125
+ :group 'wiki
126
+ :type '(repeat directory))
127
+
128
+ (defcustom wiki-extension nil
129
+ "Extension to use for saved files.
130
+ A typical extension to use would be \"txt\"."
131
+ :group 'wiki
132
+ :type '(choice (const :tag "none" nil)
133
+ (const :tag "Text" "txt")))
134
+
135
+ (defcustom wiki-pub-directory "~/WebWiki"
136
+ "Directory where all wikis are published to.
137
+ If set to nil, publishing is disabled."
138
+ :group 'wiki
139
+ :type '(choice directory
140
+ (const :tag "Disable publishing" nil)))
141
+
142
+ (defcustom wiki-pub-rules
143
+ (list
144
+ ;; inhibit HTML
145
+ '("<" . "&lt;")
146
+ '(">" . "&gt;")
147
+ ;; strong, WardWiki uses italics instead of strong
148
+ '("''\\(\\(\\|.\\)*\\)''" . "<strong>\\1</strong>")
149
+ ;; empty lines at the beginning of the buffer
150
+ '("\\`\n*" . "<p>\n")
151
+ ;; plain paragraphs
152
+ '("\n\n+" . "\n\n<p>\n")
153
+ ;; list items
154
+ '("^\\*[ \t]*" . "<li>")
155
+ ;; list paragraphs
156
+ '("<p>\n<li>\\(\\([^\n]\n?\\)+\\)" . "<p>\n<ul>\n<li>\\1</ul>\n")
157
+ ;; preformatted paragraphs
158
+ '("<p>\n\\([ \t]+\\([^\n]\n?\\)+\\)" . "<p>\n<pre>\n\\1</pre>\n")
159
+ ;; quoted/indented paragraphs
160
+ '("<p>\n:\\(\\([^\n]\n?\\)+\\)" . "<blockquote>\n<p>\n\\1</blockquote>\n")
161
+ ;; URL
162
+ (cons thing-at-point-url-regexp
163
+ "<a href=\"\\&\">\\&</a>")
164
+ ;; email addresses without mailto
165
+ (cons (if (boundp 'goto-address-mail-regexp)
166
+ goto-address-mail-regexp
167
+ nil)
168
+ "<a href=\"mailto:\\&\">\\&</a>")
169
+ ;; wiki names
170
+ 'wiki-replace-links
171
+ ;; header
172
+ '(beginning-of-buffer . "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\">
173
+ <html>
174
+ <head>
175
+ <title><?name></title>
176
+ </head>
177
+ <body>
178
+ <h1><?name></h1>
179
+ ")
180
+ '("<\\?name>" . wiki-page-name)
181
+ ;; footer
182
+ '(end-of-buffer . "<hr>
183
+ <p>
184
+ Last change: <?date>
185
+ </body>
186
+ </html>")
187
+ '("<\\?date>" . wiki-current-date))
188
+ "List of rules to apply to a wiki page when publishing.
189
+ A RULE has one of the following forms:
190
+
191
+ FUNC
192
+
193
+ FUNC is a function that will be called once, without any parameters.
194
+ The function can do any search and replace it wants.
195
+
196
+ \(ACTION . REPLACEMENT)
197
+
198
+ ACTION is either a regular expression to search for using
199
+ `re-search-forward', or a function that will be called once, without any
200
+ parameters.
201
+
202
+ REPLACEMENT is either a string or a function returning a string. This
203
+ string will be used as a replacement using `replace-match' if ACTION is
204
+ a regular expression, or it will be inserted if ACTION is a function.
205
+
206
+ Some examples:
207
+
208
+ 'wiki-replace-links
209
+ '(\"<\" . \"&lt;\")
210
+ '(end-of-buffer . \"</body></html>\")
211
+ '(\"#NAME#\" . wiki-page-name)
212
+
213
+ All this is done in `wiki-pub-apply-rules'. The rules are
214
+ applied in order, one rule at a time. Note that case is never ignored.
215
+ `case-fold-search' will allways be bound to nil."
216
+ :group 'wiki
217
+ :type '(repeat (choice :value ("regexp" . "newtext")
218
+ (cons :tag "Rule"
219
+ (choice
220
+ (regexp :tag "Search a regexp")
221
+ (function :tag "Call a function to place point"
222
+ :value end-of-buffer))
223
+ (choice
224
+ (string :tag "Insert or replace a string"
225
+ :value "newtext")
226
+ (function :tag "Insert or replace a function"
227
+ :value current-time-string)))
228
+ (function :tag "Function"
229
+ :value current-time-string))))
230
+
231
+ (defcustom wiki-maintainer "mailto:Unknown Maintainer"
232
+ "URL where the maintainer can be reached."
233
+ :group 'wiki
234
+ :type 'string)
235
+
236
+ (defcustom wiki-date-format "%Y-%m-%d"
237
+ "Format of current date for `wiki-current-date'.
238
+ This string must be a valid argument to `format-time-string'."
239
+ :group 'wiki
240
+ :type 'string)
241
+
242
+ (defcustom wiki-pub-file-name-suffix ".html"
243
+ "This suffix will be appended to all wiki names when publishing."
244
+ :group 'wiki
245
+ :type 'string)
246
+
247
+ (defcustom wiki-index-file-name "index"
248
+ "Filename of the Wiki Index page.
249
+ `wiki-pub-file-name-suffix' will be appended."
250
+ :group 'wiki
251
+ :type 'string)
252
+
253
+ (defcustom wiki-highlight-buffer-hook '(wiki-highlight-wiki-names)
254
+ "Hook with functions to call when a buffer is highlighted."
255
+ :group 'wiki
256
+ :type 'hook)
257
+
258
+ (defgroup wiki-link nil
259
+ "Options controlling links in Wiki Mode."
260
+ :group 'wiki)
261
+
262
+ (defcustom wiki-name-regexp "\\<[A-Z][a-z]+\\([A-Z][a-z]+\\)+\\>"
263
+ "Regexp matching WikiNames.
264
+ Whenever the regexp is searched for, case is never ignored:
265
+ `case-fold-search' will allways be bound to nil.
266
+
267
+ See `wiki-no-name-p' if you want to exclude certain matches.
268
+ See `wiki-name-no-more' if highlighting is not removed correctly."
269
+ :group 'wiki-link
270
+ :type 'regexp)
271
+
272
+ (defcustom wiki-name-no-more "[A-Za-z]+"
273
+ "Regexp matching things that might once have been WikiNames.
274
+ Usually that amounts to all legal characters in `wiki-name-regexp'.
275
+ This is used to remove highlighting from former WikiNames."
276
+ :group 'wiki-link
277
+ :type 'regexp)
278
+
279
+ (defcustom wiki-highlight-name-exists 'wiki-name-exists-p
280
+ "Function to call in order to determine wether a WikiName exists already.
281
+ This is used when highlighting words using `wiki-highlight-match': If
282
+ the word is a non-existing wiki-name, a question mark is appended.
283
+
284
+ See `wiki-name-regexp' for possible names considered a WikiName."
285
+ :group 'wiki-link
286
+ :type 'function)
287
+
288
+ (defcustom wiki-follow-name-action 'find-file
289
+ "Function to use when following references.
290
+ The function should accept a string parameter, the WikiName.
291
+ If the WikiName exists as a file in `wiki-directories', the
292
+ fully qualified filename will be passed to the function."
293
+ :group 'wiki-link
294
+ :type 'function)
295
+
296
+ (defgroup wiki-parse nil
297
+ "Options controlling parsing of the wiki files.
298
+ These function only come in handy if you want to do complex things such
299
+ as find clusters in the graph or generate a structured table of contents."
300
+ :group 'wiki)
301
+
302
+ (defcustom wiki-include-function t
303
+ "Function to decide wether to include a file in the `wiki-filter', or t.
304
+ If t, then all pages will be included.
305
+ The function should accept a filename and a wiki structure as returned
306
+ by `wiki-parse-files' as arguments and return non-nil if the file is to
307
+ be part of the graph."
308
+ :group 'wiki-parse
309
+ :type '(choice (const :tag "All pages" t)
310
+ (const :tag "Significant fan out" wiki-significant-fan-out)
311
+ function))
312
+
313
+ (defcustom wiki-significant-fan-out 3
314
+ "Pages with a fan out higher than this are significant.
315
+ This is used by `wiki-significant-fan-out' which is a
316
+ possible value for `wiki-include-function'."
317
+ :group 'wiki-parse
318
+ :type 'integer)
319
+
320
+ ;; Starting up
321
+
322
+ (defsubst wiki-page-name ()
323
+ "Return page name."
324
+ (file-name-nondirectory (file-name-sans-extension buffer-file-name)))
325
+
326
+ (defun wiki-no-name-p ()
327
+ "Return non-nil if point is within a URL.
328
+ This function is faster than checking using `thing-at-point-looking-at'
329
+ and `thing-at-point-url-regexp'. Override this function if you do not
330
+ like it."
331
+ (let ((pos (point)))
332
+ (and (re-search-backward "[]\t\n \"'()<>[^`{}]" nil t)
333
+ (goto-char (match-end 0))
334
+ (looking-at thing-at-point-url-regexp)
335
+ (<= pos (match-end 0)))))
336
+
337
+ (defun wiki-name-p (&optional shortcut)
338
+ "Return non-nil when `point' is at a true wiki name.
339
+ A true wiki name matches `wiki-name-regexp' and doesn't trigger
340
+ `wiki-no-name-p'. In addition to that, it may not be equal to the
341
+ current filename. This modifies the data returned by `match-data'.
342
+
343
+ If optional argument SHORTCUT is non-nil, we assume that
344
+ `wiki-name-regexp' has just been searched for. Note that the potential
345
+ wiki name must be available via `match-string'."
346
+ (let ((case-fold-search nil))
347
+ (and (or shortcut (thing-at-point-looking-at wiki-name-regexp))
348
+ (or (not buffer-file-name)
349
+ (not (string-equal (wiki-page-name) (match-string 0))))
350
+ (not (save-match-data
351
+ (save-excursion
352
+ (wiki-no-name-p)))))))
353
+
354
+ (defun wiki-maybe ()
355
+ "Maybe turn `wiki-mode' on for this file.
356
+ This happens when the file's directory is a member of
357
+ `wiki-directories'."
358
+ (if (member (file-name-directory buffer-file-name)
359
+ wiki-directories)
360
+ (wiki-mode 1)
361
+ (wiki-mode 0)))
362
+
363
+ (add-hook 'find-file-hooks 'wiki-maybe)
364
+
365
+ (defun wiki-install ()
366
+ "Install `wiki-highlight-word-wrapper'."
367
+ (make-local-variable 'after-change-functions)
368
+ (add-to-list 'after-change-functions 'wiki-highlight-word-wrapper))
369
+
370
+ (defun wiki-deinstall ()
371
+ "Deinstall `wiki-highlight-word-wrapper'."
372
+ (setq after-change-functions (delq 'wiki-highlight-word-wrapper
373
+ after-change-functions)))
374
+
375
+ ;; The minor mode (this is what you get)
376
+
377
+ (defvar wiki-local-map
378
+ (let ((map (make-sparse-keymap)))
379
+ (define-key map (kbd "RET") 'wiki-follow-name-at-point)
380
+ (if (featurep 'xemacs)
381
+ (define-key map (kbd "<button2>") 'wiki-follow-name-at-mouse)
382
+ (define-key map (kbd "<mouse-2>") 'wiki-follow-name-at-mouse))
383
+ map)
384
+ "Local keymap used by wiki minor mode while on a WikiName.")
385
+
386
+ (defvar wiki-mode-map
387
+ (let ((map (make-sparse-keymap)))
388
+ (define-key map (kbd "C-c C-l") 'wiki-highlight-buffer)
389
+ (define-key map (kbd "C-c C-p") 'wiki-publish)
390
+ (define-key map (kbd "C-c C-v") 'wiki-view-published)
391
+ (define-key map (kbd "C-c C-b") 'wiki-backlink)
392
+ (define-key map (kbd "C-c =") 'wiki-backup)
393
+ (define-key map (kbd "<tab>") 'wiki-next-reference)
394
+ (define-key map (kbd "M-n") 'wiki-next-reference)
395
+ (define-key map (kbd "M-p") 'wiki-previous-reference)
396
+ map)
397
+ "Keymap used by wiki minor mode.")
398
+
399
+ (easy-mmode-define-minor-mode
400
+ wiki-mode
401
+ "Wiki mode transform all WikiNames into links.
402
+
403
+ Wiki is a hypertext and a content management system: Normal users are
404
+ encouraged to enhance the hypertext by editing and refactoring existing
405
+ wikis and by adding more. This is made easy by requiring a certain way
406
+ of writing the wikis. It is not as complicated as a markup language
407
+ such as HTML. The general idea is to write plain ASCII.
408
+
409
+ Words with mixed case such as ThisOne are WikiNames. WikiNames are
410
+ links you can follow. If a wiki with that name exists, you will be
411
+ taken there. If such a does not exist, following the link will create a
412
+ new wiki for you to fill. WikiNames for non-existing wikis have a `?'
413
+ appended so that you can see wether following the link will give you any
414
+ informatin or not.
415
+
416
+ In order to follow a link, hit RET when point is on the link, or use
417
+ mouse-2.
418
+
419
+ All wikis reside in `wiki-directories'.
420
+
421
+ \\{wiki-mode-map}"
422
+ nil
423
+ " Wiki"
424
+ wiki-mode-map)
425
+
426
+ (add-hook 'wiki-mode-on-hook 'wiki-install)
427
+ (add-hook 'wiki-mode-on-hook 'wiki-highlight-buffer)
428
+ (add-hook 'wiki-mode-on-hook (lambda () (setq indent-tabs-mode nil)))
429
+
430
+ (add-hook 'wiki-mode-off-hook 'wiki-deinstall)
431
+ (add-hook 'wiki-mode-off-hook 'wiki-delete-extents)
432
+
433
+ (when (fboundp 'goto-address)
434
+ (add-hook 'wiki-highlight-buffer-hook 'goto-address))
435
+
436
+ ;; List of known wiki files
437
+
438
+ (defvar wiki-last-update nil
439
+ "Time when the `wiki-file-alist' was last updated.")
440
+
441
+ (defvar wiki-file-alist nil
442
+ "List of existing WikiNames.
443
+ This is used by `wiki-existing-names' as a cache.")
444
+
445
+ (defsubst wiki-existing-page-names ()
446
+ "Return all page names from `wiki-existing-names'."
447
+ (mapcar (lambda (f) (car f)) (wiki-existing-names)))
448
+
449
+ (defsubst wiki-existing-file-names ()
450
+ "Return all file names from `wiki-existing-names'."
451
+ (mapcar (lambda (f) (cdr f)) (wiki-existing-names)))
452
+
453
+ (defun wiki-existing-names ()
454
+ "Return wiki filenames in `wiki-directories' as an alist.
455
+ Wiki filenames match `wiki-name-regexp'. The result is cached and
456
+ updated when necessary based upon directory modification dates. The car
457
+ of each element is the page name, the cdr of each element is the fully
458
+ qualified filename. Use `wiki-existing-page-names' and
459
+ `wiki-existing-file-names' to get lists of page names or file names."
460
+ (let* ((dirs wiki-directories)
461
+ last-mod)
462
+ (while dirs
463
+ (let ((mod-time (nth 5 (file-attributes (car dirs)))))
464
+ (if (or (null last-mod)
465
+ (time-less-p last-mod mod-time))
466
+ (setq last-mod mod-time)))
467
+ (setq dirs (cdr dirs)))
468
+ (if (not (or (null wiki-last-update)
469
+ (null last-mod)
470
+ (time-less-p wiki-last-update last-mod)))
471
+ wiki-file-alist
472
+ (setq wiki-last-update last-mod
473
+ wiki-file-alist (wiki-read-directories)))))
474
+
475
+ (defun wiki-read-directories ()
476
+ "Return list of all files in `wiki-directories'.
477
+ Each element in the list is a cons cell. The car holds the pagename,
478
+ the cdr holds the fully qualified filename. If set, `wiki-extension'
479
+ is appended to the filenames."
480
+ (let ((dirs wiki-directories)
481
+ (regexp (concat "^" wiki-name-regexp
482
+ (if wiki-extension (concat "\\." wiki-extension) "") "$"))
483
+ result)
484
+ (setq dirs wiki-directories)
485
+ (while dirs
486
+ (let ((files (mapcar (lambda (f)
487
+ (cons (file-name-nondirectory
488
+ (file-name-sans-extension f)) f))
489
+ (directory-files (car dirs) t regexp t))))
490
+ (setq result (nconc files result)))
491
+ (setq dirs (cdr dirs)))
492
+ result))
493
+
494
+ (defun wiki-name-exists-p (name)
495
+ "Return non-nil when NAME is an existing wiki-name."
496
+ (assoc name (wiki-existing-names)))
497
+
498
+ (defun wiki-expand-name (name)
499
+ "Return the expanded filename for NAME.
500
+ This relies on `wiki-existing-names'."
501
+ (cdr (assoc name (wiki-existing-names))))
502
+
503
+ ;; Following hyperlinks
504
+
505
+ (defun wiki-follow-name (name)
506
+ "Follow the link NAME by invoking `wiki-follow-name-action'.
507
+ If NAME is part a key in the alist returned by `wiki-existing-names',
508
+ then the corresponding filename is used instead of NAME."
509
+ (let ((file (cdr (assoc name (wiki-existing-names)))))
510
+ (if file
511
+ (funcall wiki-follow-name-action file)
512
+ (funcall wiki-follow-name-action
513
+ (concat name (if wiki-extension (concat "." wiki-extension) ""))))))
514
+
515
+ (defun wiki-follow-name-at-point ()
516
+ "Find wiki name at point.
517
+ See `wiki-name-p' and `wiki-follow-name'."
518
+ (interactive)
519
+ (if (wiki-name-p)
520
+ (wiki-follow-name (match-string 0))
521
+ (error "Point is not at a WikiName")))
522
+
523
+ (defun wiki-follow-name-at-mouse (event)
524
+ "Find wiki name at the mouse position.
525
+ See `wiki-follow-name-at-point'."
526
+ (interactive "e")
527
+ (save-excursion
528
+ (mouse-set-point event)
529
+ (wiki-follow-name-at-point)))
530
+
531
+ (defun wiki-next-reference ()
532
+ "Jump to next wiki name.
533
+ This modifies the data returned by `match-data'.
534
+ Returns the new position of point or nil.
535
+ See `wiki-name-p'."
536
+ (interactive)
537
+ (let ((case-fold-search nil)
538
+ found match)
539
+ (save-excursion
540
+ (condition-case nil
541
+ ;; will cause an error in empty buffers
542
+ (forward-char 1)
543
+ (error))
544
+ (when (re-search-forward wiki-name-regexp nil t)
545
+ (setq found (match-beginning 0)
546
+ match (match-data)))
547
+ (while (and found (not (wiki-name-p 'shortcut)))
548
+ (forward-char 1)
549
+ (if (re-search-forward wiki-name-regexp nil t)
550
+ (setq found (match-beginning 0)
551
+ match (match-data))
552
+ (setq found nil
553
+ match nil))))
554
+ (set-match-data match)
555
+ (when found
556
+ (goto-char found))))
557
+
558
+ (defun wiki-previous-reference ()
559
+ "Jump to previous wiki name.
560
+ See `wiki-name-p'."
561
+ (interactive)
562
+ (let ((case-fold-search nil)
563
+ found)
564
+ (save-excursion
565
+ (save-match-data
566
+ (setq found (re-search-backward wiki-name-regexp nil t))
567
+ (while (and found (not (wiki-name-p 'shortcut)))
568
+ (forward-char -1)
569
+ (setq found (re-search-backward wiki-name-regexp nil t)))))
570
+ (when found
571
+ (goto-char found))))
572
+
573
+ ;; Backlink and other searches
574
+
575
+ (defun wiki-backlink ()
576
+ "Return all backlinks to the current page using `grep'."
577
+ (interactive)
578
+ (when (not grep-command)
579
+ (grep-compute-defaults))
580
+ (grep (concat grep-command
581
+ (wiki-page-name)
582
+ " *"))
583
+ (set-buffer "*grep*"))
584
+
585
+ (defun wiki-backup ()
586
+ "Run `diff-backup' on the current file."
587
+ (interactive)
588
+ (diff-backup buffer-file-name))
589
+
590
+ ;; Highlighting hyperlinks
591
+
592
+ (defun wiki-highlight-buffer ()
593
+ "Highlight the buffer.
594
+ Delete all existing wiki highlighting using `wiki-delete-extents' and
595
+ call all functions in `wiki-highlight-buffer-hook'."
596
+ (interactive)
597
+ (wiki-delete-extents)
598
+ (run-hooks 'wiki-highlight-buffer-hook))
599
+
600
+ (defun wiki-highlight-wiki-names ()
601
+ "Highlight all WikiNames in the buffer.
602
+ This uses `wiki-highlight-match' to do the job.
603
+ The list of existing names is recomputed using `wiki-existing-names'."
604
+ (interactive)
605
+ (wiki-delete-extents)
606
+ (save-excursion
607
+ (goto-char (point-min))
608
+ (when (wiki-name-p)
609
+ (wiki-highlight-match))
610
+ (while (wiki-next-reference)
611
+ (wiki-highlight-match))))
612
+
613
+ (defun wiki-highlight-match ()
614
+ "Highlight the latest match as a WikiName.
615
+ `wiki-name-p' is not called again to verify the latest match.
616
+ Existing WikiNames are highlighted using face `info-xref'."
617
+ (save-match-data
618
+ (let ((with-glyph (not (funcall wiki-highlight-name-exists
619
+ (match-string 0)))))
620
+ (wiki-make-extent (match-beginning 0)
621
+ (match-end 0)
622
+ wiki-local-map
623
+ with-glyph))))
624
+
625
+ (defun wiki-highlight-word-wrapper (&optional start end len)
626
+ "Highlight the current word if it is a WikiName.
627
+ This function can be put on `after-change-functions'.
628
+ It calls `wiki-highlight-word' to do the job."
629
+ (when start
630
+ (wiki-highlight-word start))
631
+ (when (= 0 len); for insertions
632
+ (wiki-highlight-word end)))
633
+
634
+ (defun wiki-highlight-word (pos)
635
+ "Highlight the current word if it is a WikiName.
636
+ This uses `wiki-highlight-match' to do the job. POS specifies a buffer
637
+ position."
638
+ (save-excursion
639
+ (goto-char pos)
640
+ (save-match-data
641
+ (cond ((wiki-name-p); found a wiki name
642
+ (wiki-delete-extents (match-beginning 0) (match-end 0))
643
+ (wiki-highlight-match))
644
+ ;; The following code makes sure that when a WikiName is
645
+ ;; edited such that is no longer is a wiki name, the
646
+ ;; extent/overlay is removed.
647
+ ((thing-at-point-looking-at wiki-name-no-more)
648
+ (wiki-delete-extents (match-beginning 0) (match-end 0)))))))
649
+
650
+ ;; Parsing all files into a directed graph
651
+
652
+ (defun wiki-parse-files ()
653
+ "Return all pages and the links they contain in an alist.
654
+ Each element in the alist has the form
655
+ \(NAME LINK1 LINK2 ...)
656
+ See `wiki-parse-file'. The list of existing names is recomputed using
657
+ `wiki-existing-file-names'."
658
+ (mapcar (function wiki-parse-file) (wiki-existing-file-names)))
659
+
660
+ (defun wiki-parse-file (file)
661
+ "Build a list of links for FILE.
662
+ Returns a list of the form
663
+ \(NAME LINK1 LINK2 ...)
664
+ See `wiki-parse-files'."
665
+ (message "Parsing %s" file)
666
+ (let ((page (list (file-name-nondirectory file))))
667
+ (with-temp-buffer
668
+ ;; fake an existing buffer-file-name in the temp buffer
669
+ (let ((buffer-file-name file))
670
+ (insert-file-contents file)
671
+ (goto-char (point-min))
672
+ (while (wiki-next-reference)
673
+ (let ((this (match-string 0)))
674
+ (when (and (wiki-name-exists-p this)
675
+ (not (member this page)))
676
+ (setq page (cons this page)))))))
677
+ (reverse page)))
678
+
679
+ ;; Filtering the directed graph
680
+
681
+ (defun wiki-filter (structure)
682
+ "Filter STRUCTURE according to `wiki-include-function'."
683
+ (if (eq wiki-include-function t)
684
+ structure
685
+ (wiki-filter-links
686
+ (wiki-filter-pages
687
+ (copy-alist structure)))))
688
+
689
+ (defun wiki-filter-pages (structure)
690
+ "Filter pages structure according to `wiki-include-function'."
691
+ (let ((pages structure)
692
+ page)
693
+ (while pages
694
+ (setq page (car pages)
695
+ pages (cdr pages))
696
+ (if (funcall wiki-include-function
697
+ (car page) structure)
698
+ (message "Keeping %s" (car page))
699
+ (message "Filtering %s" (car page))
700
+ (setq structure (delete page structure))
701
+ ;; restart!
702
+ (setq pages structure)))
703
+ structure))
704
+
705
+ (defun wiki-filter-links (structure)
706
+ "Filter links to nonexisting pages from structure."
707
+ (let ((pages structure)
708
+ page)
709
+ (while pages
710
+ (setq page (car pages)
711
+ pages (cdr pages))
712
+ (setcdr page (delq nil (mapcar (lambda (link)
713
+ (if (assoc link structure)
714
+ link
715
+ nil))
716
+ (cdr page)))))
717
+ structure))
718
+
719
+ ;; Example filtering functions
720
+
721
+ (defun wiki-significant-fan-out (name structure)
722
+ "Return non-nil when `wiki-fan-out' is significant.
723
+ This is determined by `wiki-significant-fan-out'."
724
+ (> (wiki-fan-out name structure) wiki-significant-fan-out))
725
+
726
+ (defun wiki-fan-out (name structure)
727
+ "Return number of links pointing away from NAME.
728
+ This is calculated from STRUCTURE as returned by `wiki-parse-files'."
729
+ (length (cdr (assoc name structure))))
730
+
731
+ ;; Example applications of parsing and filtering
732
+
733
+ (defun wiki-list-by-fan-out ()
734
+ "List the wiki site structure by fan-out."
735
+ (interactive)
736
+ (let ((graph (mapcar (lambda (page)
737
+ (cons (car page) (length (cdr page))))
738
+ (wiki-parse-files))))
739
+ (message "Preparing...")
740
+ (setq graph (sort graph
741
+ (lambda (p1 p2)
742
+ (< (cdr p1) (cdr p2)))))
743
+ (let ((buf (get-buffer-create "*wiki*")))
744
+ (set-buffer buf)
745
+ (erase-buffer)
746
+ (pp graph buf)
747
+ (emacs-lisp-mode)
748
+ (wiki-mode 1)
749
+ (switch-to-buffer buf)
750
+ (message "Preparing...done"))))
751
+
752
+ ;; Publishing
753
+
754
+ (defun wiki-publish-all (&optional arg)
755
+ "Publish all wikis.
756
+ If the published wiki already exists, it is only overwritten if the wiki
757
+ is newer than the published copy. When given the optional argument ARG,
758
+ all wikis are rewritten, no matter how recent they are. The index file
759
+ is rewritten no matter what.
760
+ The list of existing names is recomputed using `wiki-existing-names'."
761
+ (interactive "P")
762
+ (save-some-buffers)
763
+ (wiki-publish-files (wiki-existing-file-names) arg)
764
+ (wiki-publish-index))
765
+
766
+ (defun dired-do-wiki-publish ()
767
+ "Publish all marked files in a dired buffer."
768
+ (interactive)
769
+ (wiki-publish-files (dired-get-marked-files) t))
770
+
771
+ (defun wiki-publish-files (files force)
772
+ "Publish all files in list FILES.
773
+ If the argument FORCE is nil, each file is only published
774
+ if it is newer than the published version. If the argument
775
+ FORCE is non-nil, the file is published no matter what."
776
+ (let (wiki-current-date file page)
777
+ (while files
778
+ (setq file (car files)
779
+ files (cdr files)
780
+ wiki-current-date (nth 5 (file-attributes file))
781
+ page (wiki-write-file-name
782
+ (concat (file-name-nondirectory (file-name-sans-extension file))
783
+ wiki-pub-file-name-suffix)))
784
+ (when (or force (file-newer-than-file-p file page))
785
+ (with-temp-buffer
786
+ (insert-file-contents file t)
787
+ (wiki-publish))))))
788
+
789
+ (defun wiki-publish ()
790
+ "Publish current wiki buffer as an HTML file.
791
+ The file will be created in `wiki-pub-directory'. You can
792
+ publish several files at once from a dired buffer using
793
+ `dired-do-wiki-publish', or you can publish all files using
794
+ `wiki-publish-all'."
795
+ (interactive)
796
+ (let ((file-name (file-name-sans-extension buffer-file-name))
797
+ (content (buffer-substring (point-min) (point-max))))
798
+ (message "Publishing %s" file-name)
799
+ (with-temp-buffer
800
+ ;; fake an existing buffer-file-name in the temp buffer
801
+ (let ((buffer-file-name file-name))
802
+ (let ((start (point))
803
+ end)
804
+ (insert content)
805
+ (setq end (point-marker))
806
+ (wiki-pub-apply-rules start end))
807
+ (wiki-write-file (concat (wiki-page-name)
808
+ wiki-pub-file-name-suffix))))))
809
+
810
+ ;; Index page
811
+
812
+ (defun wiki-publish-index ()
813
+ "Publish an index of all wikis."
814
+ (interactive)
815
+ (with-temp-buffer
816
+ (let ((buffer-file-name wiki-index-file-name))
817
+ (wiki-insert-index (wiki-existing-page-names))
818
+ (wiki-publish))))
819
+
820
+ (defun wiki-insert-index (files)
821
+ "Insert a list of all FILES."
822
+ (let (file)
823
+ (setq files (sort files 'string-lessp))
824
+ (while files
825
+ (setq file (car files)
826
+ files (cdr files))
827
+ (insert "* " file "\n"))))
828
+
829
+ ;; Writing files
830
+
831
+ (defun wiki-write-file (name)
832
+ "Write current buffer to file NAME in `wiki-pub-directory'."
833
+ (let ((backup-inhibited t))
834
+ (write-file (wiki-write-file-name name))))
835
+
836
+ (defun wiki-write-file-name (name)
837
+ "Expand file name NAME in `wiki-pub-directory'."
838
+ (if wiki-pub-directory
839
+ (expand-file-name name wiki-pub-directory)
840
+ (error "Publishing is disabled")))
841
+
842
+ ;; Publishing: HTML Tags helper functions
843
+
844
+ (defvar wiki-current-date nil
845
+ "The modification time of the file being published.
846
+ This is bound by `wiki-publish-files' to the modification time returned
847
+ by `file-attributes'. Note that publishing the current buffer using
848
+ `wiki-publish' will not bind the variable `wiki-current-date', therefore
849
+ the function `wiki-current-date' will return the current date.")
850
+
851
+ (defun wiki-current-date ()
852
+ "Insert the current date using `wiki-date-format'.
853
+ If bound, the variable `wiki-current-date' will be used instead of the
854
+ current date. It is usually the modification time of the file."
855
+ (format-time-string wiki-date-format wiki-current-date))
856
+
857
+ (defun wiki-replace-links ()
858
+ "Replace wiki names with HTML links."
859
+ (while (wiki-next-reference)
860
+ (let ((this (match-string 0)))
861
+ (if (assoc this (wiki-existing-names))
862
+ (replace-match
863
+ (concat "<a href=\"" this wiki-pub-file-name-suffix
864
+ "\">" this "</a>") t)
865
+ (replace-match
866
+ (concat this "<a href=\"" wiki-maintainer "\">?</a>") t)))))
867
+
868
+ (defun wiki-pub-apply-rules (start end)
869
+ "Replace wiki markup with publishing markup.
870
+ The standard publishing markup is HTML, but this can be changed. The
871
+ markup is produced by applying all the rules in the variable
872
+ `wiki-pub-rules'."
873
+ (save-restriction
874
+ (narrow-to-region start end)
875
+ (let ((case-fold-search nil)
876
+ (rules wiki-pub-rules)
877
+ rule)
878
+ (while rules
879
+ (setq rule (car rules)
880
+ rules (cdr rules))
881
+ (goto-char (point-min))
882
+ (wiki-pub-apply-rule rule)))))
883
+
884
+ (defun wiki-pub-apply-rule (rule)
885
+ "Apply RULE.
886
+ See `wiki-pub-rules'."
887
+ (if (functionp rule)
888
+ (funcall rule)
889
+ (let ((action (car rule)))
890
+ (cond ((functionp action)
891
+ (funcall action)
892
+ (insert (wiki-pub-rule-effect rule)))
893
+ ((stringp action)
894
+ (while (re-search-forward action nil t)
895
+ (replace-match (wiki-pub-rule-effect rule) t)))))))
896
+
897
+ (defun wiki-pub-rule-effect (rule)
898
+ "Return the string to use for RULE.
899
+ See `wiki-pub-rules'."
900
+ (let ((effect (cdr rule)))
901
+ (if (functionp effect)
902
+ (funcall effect)
903
+ effect)))
904
+
905
+ ;; Viewing the result
906
+
907
+ (defun wiki-view-published ()
908
+ "Switch to the published version of the current buffer."
909
+ (interactive)
910
+ (find-file (wiki-write-file-name
911
+ (concat (wiki-page-name)
912
+ wiki-pub-file-name-suffix))))
913
+
914
+ ;; Emacs/XEmacs compatibility layer
915
+
916
+ (defun wiki-make-extent (from to map with-glyph)
917
+ "Make an extent for the range [FROM, TO) in the current buffer.
918
+ MAP is the local keymap to use, if any.
919
+ WITH-GLYPH non-nil will add a question-mark after the extent.
920
+ XEmacs uses `make-extent', Emacs uses `make-overlay'."
921
+ ;; I don't use (fboundp 'make-extent) because of (require 'lucid)
922
+ (if (featurep 'xemacs)
923
+ ;; Extents for XEmacs
924
+ (let ((extent (make-extent from to)))
925
+ (set-extent-property extent 'face 'info-xref)
926
+ (set-extent-property extent 'mouse-face 'highlight)
927
+ (when map
928
+ (set-extent-property extent 'keymap map))
929
+ (set-extent-property extent 'evaporate t)
930
+ (set-extent-property extent 'wikiname t)
931
+ (when with-glyph
932
+ (set-extent-property extent 'end-glyph (make-glyph '("?"))))
933
+ extent)
934
+ ;; Overlays for Emacs
935
+ (let ((overlay (make-overlay from to)))
936
+ (overlay-put overlay 'face 'info-xref)
937
+ (overlay-put overlay 'mouse-face 'highlight)
938
+ (when map
939
+ (overlay-put overlay 'local-map map))
940
+ (overlay-put overlay 'evaporate t)
941
+ (overlay-put overlay 'wikiname t)
942
+ (when with-glyph
943
+ (overlay-put overlay 'after-string "?"))
944
+ overlay)))
945
+
946
+ (defun wiki-delete-extents (&optional start end)
947
+ "Delete all extents/overlays created by `wiki-make-extent'.
948
+ If optional arguments START and END are given, only the overlays in that
949
+ region will be deleted. XEmacs uses extents, Emacs uses overlays."
950
+ (if (featurep 'xemacs)
951
+ (let ((extents (extent-list nil start end))
952
+ extent)
953
+ (while extents
954
+ (setq extent (car extents)
955
+ extents (cdr extents))
956
+ (when (extent-property extent 'wikiname)
957
+ (delete-extent extent))))
958
+ (let ((overlays (overlays-in (or start (point-min))
959
+ (or end (point-max))))
960
+ overlay)
961
+ (while overlays
962
+ (setq overlay (car overlays)
963
+ overlays (cdr overlays))
964
+ (when (overlay-get overlay 'wikiname)
965
+ (delete-overlay overlay))))))
966
+
967
+ (unless (fboundp 'time-less-p)
968
+ (defun time-less-p (t1 t2)
969
+ "Say whether time T1 is less than time T2."
970
+ (or (< (car t1) (car t2))
971
+ (and (= (car t1) (car t2))
972
+ (< (nth 1 t1) (nth 1 t2))))))
973
+
974
+ (provide 'wiki)
975
+
976
+ ;;; wiki.el ends here