ruby_learner 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +58 -0
- data/LICENSE.txt +21 -0
- data/README.md +65 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/new_terminal +25 -0
- data/bin/setup +8 -0
- data/docs/happy_ruby/RussOlsen_EloquentRuby_c1.pdf +0 -0
- data/docs/happy_ruby/RussOlsen_EloquentRuby_c5.pdf +0 -0
- data/docs/happy_ruby/TanoshiiRuby_v3_c23.pdf +0 -0
- data/docs/happy_ruby/TanoshiiRuby_v5_c1.pdf +0 -0
- data/docs/happy_ruby/TanoshiiRuby_v5_c2-3.pdf +0 -0
- data/docs/happy_ruby/c2.ipynb +479 -0
- data/docs/happy_ruby/c3_4.ipynb +237 -0
- data/docs/seminar/8-1.org +18 -0
- data/exe/ruby_learner +5 -0
- data/lib/ruby_learner/h.rb +14 -0
- data/lib/ruby_learner/methods.rb +131 -0
- data/lib/ruby_learner/random_h.rb +16 -0
- data/lib/ruby_learner/ruby_learner.rb +43 -0
- data/lib/ruby_learner/sequential_h.rb +15 -0
- data/lib/ruby_learner/typing_practice.rb +21 -0
- data/lib/ruby_learner/version.rb +3 -0
- data/questions/random_check/.rspec +1 -0
- data/questions/random_check/random_h.rb +16 -0
- data/questions/random_check/section_1/.rspec +1 -0
- data/questions/random_check/section_1/lib/answer.rb +15 -0
- data/questions/random_check/section_1/lib/sentence.org +9 -0
- data/questions/random_check/section_1/lib/workplace.rb +5 -0
- data/questions/random_check/section_1/spec/spec_helper.rb +100 -0
- data/questions/random_check/section_1/spec/workplace_spec.rb +10 -0
- data/questions/random_check/section_2/.rspec +1 -0
- data/questions/random_check/section_2/lib/answer.rb +17 -0
- data/questions/random_check/section_2/lib/sentence.org +12 -0
- data/questions/random_check/section_2/lib/workplace.rb +5 -0
- data/questions/random_check/section_2/spec/.rspec +1 -0
- data/questions/random_check/section_2/spec/spec_helper.rb +100 -0
- data/questions/random_check/section_2/spec/workplace_spec.rb +11 -0
- data/questions/sequential_check/section_1/part_1/lib/answer.rb +9 -0
- data/questions/sequential_check/section_1/part_1/lib/sentence.org +9 -0
- data/questions/sequential_check/section_1/part_1/lib/workplace.rb +5 -0
- data/questions/sequential_check/section_1/part_1/spec/spec_helper.rb +100 -0
- data/questions/sequential_check/section_1/part_1/spec/workplace_spec.rb +10 -0
- data/questions/sequential_check/section_1/part_2/lib/answer.rb +16 -0
- data/questions/sequential_check/section_1/part_2/lib/sentence.org +12 -0
- data/questions/sequential_check/section_1/part_2/lib/workplace.rb +5 -0
- data/questions/sequential_check/section_1/part_2/spec/.rspec +1 -0
- data/questions/sequential_check/section_1/part_2/spec/spec_helper.rb +100 -0
- data/questions/sequential_check/section_1/part_2/spec/workplace_spec.rb +11 -0
- data/ruby_learner.gemspec +41 -0
- data/takahashi/docs/README.org +139 -0
- data/takahashi/docs/drill.html +875 -0
- data/takahashi/docs/drill.html~ +877 -0
- data/takahashi/docs/drill.org +446 -0
- data/takahashi/docs/ruby_for_beginner.html +2642 -0
- data/takahashi/docs/ruby_for_beginner.org +1430 -0
- data/takahashi/sample_prog/answer/10_1.rb +5 -0
- data/takahashi/sample_prog/answer/11_1.rb +5 -0
- data/takahashi/sample_prog/answer/11_2.rb +4 -0
- data/takahashi/sample_prog/answer/1_1.rb +1 -0
- data/takahashi/sample_prog/answer/1_2.rb +1 -0
- data/takahashi/sample_prog/answer/1_3.rb +1 -0
- data/takahashi/sample_prog/answer/2_1.rb +5 -0
- data/takahashi/sample_prog/answer/2_2.rb +12 -0
- data/takahashi/sample_prog/answer/3_1.rb +10 -0
- data/takahashi/sample_prog/answer/4_1.rb +7 -0
- data/takahashi/sample_prog/answer/5_1.rb +6 -0
- data/takahashi/sample_prog/answer/5_2.rb +3 -0
- data/takahashi/sample_prog/answer/6_1.rb +3 -0
- data/takahashi/sample_prog/answer/6_2.rb +5 -0
- data/takahashi/sample_prog/answer/6_3.rb +5 -0
- data/takahashi/sample_prog/answer/6_4.rb +7 -0
- data/takahashi/sample_prog/answer/7_1.rb +3 -0
- data/takahashi/sample_prog/answer/7_2.rb +8 -0
- data/takahashi/sample_prog/answer/9_1.rb +3 -0
- data/takahashi/sample_prog/answer/9_2.rb +5 -0
- data/takahashi/sample_prog/answer/9_3.rb +10 -0
- data/takahashi/sample_prog/answer/hello.rb +3 -0
- data/workshop/.rspec +1 -0
- data/workshop/emacs.d/ac-comphist.dat +50 -0
- data/workshop/emacs.d/cp5022x.el +156 -0
- data/workshop/emacs.d/elpa/archives/gnu/archive-contents +1240 -0
- data/workshop/emacs.d/elpa/archives/melpa/archive-contents +2 -0
- data/workshop/emacs.d/elpa/auto-complete-20170124.1845/auto-complete-autoloads.el +65 -0
- data/workshop/emacs.d/elpa/auto-complete-20170124.1845/auto-complete-config.el +551 -0
- data/workshop/emacs.d/elpa/auto-complete-20170124.1845/auto-complete-config.elc +0 -0
- data/workshop/emacs.d/elpa/auto-complete-20170124.1845/auto-complete-pkg.el +6 -0
- data/workshop/emacs.d/elpa/auto-complete-20170124.1845/auto-complete.el +2164 -0
- data/workshop/emacs.d/elpa/auto-complete-20170124.1845/auto-complete.elc +0 -0
- data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/ada-mode +72 -0
- data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/c++-mode +99 -0
- data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/c-mode +55 -0
- data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/caml-mode +231 -0
- data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/clojure-mode +580 -0
- data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/clojurescript-mode +475 -0
- data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/coq-mode +278 -0
- data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/css-mode +874 -0
- data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/erlang-mode +216 -0
- data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/ess-julia-mode +37 -0
- data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/go-mode +25 -0
- data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/haskell-mode +679 -0
- data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/java-mode +53 -0
- data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/js-mode +148 -0
- data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/julia-mode +37 -0
- data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/lua-mode +21 -0
- data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/nim-mode +70 -0
- data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/objc-mode +161 -0
- data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/octave-mode +46 -0
- data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/php-mode +6144 -0
- data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/python-mode +379 -0
- data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/qml-mode +183 -0
- data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/ruby-mode +181 -0
- data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/scala-mode +1347 -0
- data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/scheme-mode +216 -0
- data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/sclang-mode +1481 -0
- data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/sh-mode +182 -0
- data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/swift-mode +87 -0
- data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/tcl-mode +172 -0
- data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/ts-mode +797 -0
- data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/tuareg-mode +231 -0
- data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/verilog-mode +313 -0
- data/workshop/emacs.d/elpa/better-defaults-20170613.2104/better-defaults-autoloads.el +16 -0
- data/workshop/emacs.d/elpa/better-defaults-20170613.2104/better-defaults-pkg.el +2 -0
- data/workshop/emacs.d/elpa/better-defaults-20170613.2104/better-defaults.el +90 -0
- data/workshop/emacs.d/elpa/better-defaults-20170613.2104/better-defaults.elc +0 -0
- data/workshop/emacs.d/elpa/haml-mode-20170923.2153/haml-mode-autoloads.el +26 -0
- data/workshop/emacs.d/elpa/haml-mode-20170923.2153/haml-mode-pkg.el +2 -0
- data/workshop/emacs.d/elpa/haml-mode-20170923.2153/haml-mode.el +877 -0
- data/workshop/emacs.d/elpa/haml-mode-20170923.2153/haml-mode.elc +0 -0
- data/workshop/emacs.d/elpa/haml-mode-readme.txt +8 -0
- data/workshop/emacs.d/elpa/material-theme-20171123.1040/material-light-theme.el +918 -0
- data/workshop/emacs.d/elpa/material-theme-20171123.1040/material-light-theme.elc +0 -0
- data/workshop/emacs.d/elpa/material-theme-20171123.1040/material-theme-autoloads.el +32 -0
- data/workshop/emacs.d/elpa/material-theme-20171123.1040/material-theme-pkg.el +8 -0
- data/workshop/emacs.d/elpa/material-theme-20171123.1040/material-theme.el +912 -0
- data/workshop/emacs.d/elpa/material-theme-20171123.1040/material-theme.elc +0 -0
- data/workshop/emacs.d/elpa/ox-bibtex-chinese-readme.txt +21 -0
- data/workshop/emacs.d/elpa/popup-20160709.729/popup-autoloads.el +15 -0
- data/workshop/emacs.d/elpa/popup-20160709.729/popup-pkg.el +2 -0
- data/workshop/emacs.d/elpa/popup-20160709.729/popup.el +1432 -0
- data/workshop/emacs.d/elpa/popup-20160709.729/popup.elc +0 -0
- data/workshop/emacs.d/elpa/yaml-mode-20180212.1556/yaml-mode-autoloads.el +33 -0
- data/workshop/emacs.d/elpa/yaml-mode-20180212.1556/yaml-mode-pkg.el +2 -0
- data/workshop/emacs.d/elpa/yaml-mode-20180212.1556/yaml-mode.el +470 -0
- data/workshop/emacs.d/elpa/yaml-mode-20180212.1556/yaml-mode.elc +0 -0
- data/workshop/emacs.d/elpa/yaml-mode-readme.txt +25 -0
- data/workshop/emacs.d/haml-mode-master/.gitignore +1 -0
- data/workshop/emacs.d/haml-mode-master/.mailmap +2 -0
- data/workshop/emacs.d/haml-mode-master/MIT-LICENSE +20 -0
- data/workshop/emacs.d/haml-mode-master/README.md +47 -0
- data/workshop/emacs.d/haml-mode-master/haml-mode.el +887 -0
- data/workshop/emacs.d/iceberg_theme.el +202 -0
- data/workshop/emacs.d/init-open-recentf.el +133 -0
- data/workshop/emacs.d/init.el +229 -0
- data/workshop/emacs.d/inits/line-num.el +264 -0
- data/workshop/emacs.d/install-elisp.el +366 -0
- data/workshop/emacs.d/markdown-mode/markdown-mode.el +5978 -0
- data/workshop/emacs.d/notes +12 -0
- data/workshop/emacs.d/processing-mode/processing-mode.el +275 -0
- data/workshop/emacs.d/recentf +31 -0
- data/workshop/emacs.d/ruby-mode/inf-ruby.el +416 -0
- data/workshop/emacs.d/ruby-mode/rdoc-mode.el +130 -0
- data/workshop/emacs.d/ruby-mode/ruby-electric.el +205 -0
- data/workshop/emacs.d/ruby-mode/ruby-mode.el +1496 -0
- data/workshop/emacs.d/ruby-mode/ruby-style.el +78 -0
- data/workshop/emacs.d/ruby-mode/rubydb2x.el +104 -0
- data/workshop/emacs.d/ruby-mode/rubydb3x.el +115 -0
- data/workshop/emacs.d/ruby_learner_init.el +244 -0
- data/workshop/emacs.d/themes/dracula-theme.el +431 -0
- data/workshop/emacs.d/themes/iceberg-theme.el +205 -0
- data/workshop/emacs.d/themes/my-misterioso-theme.el +109 -0
- data/workshop/emacs.d/themes/my-wombat-theme.el +121 -0
- data/workshop/emacs.d/wiki-mode/wiki.el +976 -0
- data/workshop/emacs_help.org +34 -0
- data/workshop/lib/answer.rb +1 -0
- data/workshop/lib/sentence.org +1 -0
- data/workshop/lib/workplace.rb +1 -0
- data/workshop/restore/empty.rb +0 -0
- data/workshop/spec/spec_helper.rb +100 -0
- data/workshop/spec/workplace_spec.rb +1 -0
- data/workshop/training_data.txt +3 -0
- 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
|
+
'("<" . "<")
|
146
|
+
'(">" . ">")
|
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
|
+
'(\"<\" . \"<\")
|
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
|