1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3 ;; Emacs Muse Major Mode
5 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
7 ;; The Emacs Muse major mode is basically a hyped-up text-mode which
8 ;; knows a lot more about the apparent structure of the document.
11 (require 'muse-project
)
13 (autoload 'muse-use-font-lock
"muse-colors")
17 (require 'pcomplete
) ; load if available
22 (defgroup muse-mode nil
23 "Options controlling the behaviour of the Muse editing Mode.
24 See `muse-publish' for more information."
27 (defcustom muse-mode-highlight-p t
28 "If non-nil, highlight the content of Muse buffers."
33 (defcustom muse-mode-auto-p t
34 "If non-nil, automagically determine when Muse mode should be activated."
39 (add-hook 'find-file-hooks
'muse-mode-maybe
)
40 (remove-hook 'find-file-hooks
'muse-mode-maybe
))
44 (defcustom muse-mode-hook nil
45 "A hook that is run when Muse mode is entered."
47 :options
'(flyspell-mode footnote-mode turn-on-auto-fill
48 highlight-changes-mode
)
52 (let ((map (make-sparse-keymap)))
53 (define-key map
[(control ?c
) (control ?a
)] 'muse-index
)
54 (define-key map
[(control ?c
) (control ?b
)] 'muse-browse-result
)
55 (define-key map
[(control ?c
) (control ?c
)] 'muse-follow-name-at-point
)
56 (define-key map
[(control ?c
) (control ?e
)] 'muse-edit-link-at-point
)
57 (define-key map
[(control ?c
) (control ?t
)] 'muse-publish-this-file
)
58 (define-key map
[(control ?c
) (control ?v
)] 'muse-follow-name-at-point
)
60 (define-key map
[(control ?c
) (control ?l
)] 'font-lock-mode
)
62 (define-key map
[(control ?c
) ?
=]
65 (diff-backup buffer-file-name
)))
67 (define-key map
[tab] 'muse-next-reference)
68 (define-key map [(control ?i)] 'muse-next-reference)
70 (if (featurep 'xemacs)
72 (define-key map [(button2)] 'muse-follow-name-at-mouse)
73 (define-key map [(shift button2)]
74 'muse-follow-name-at-mouse-other-window))
75 (define-key map [(shift control ?m)]
76 'muse-follow-name-at-point-other-window)
77 (define-key map [mouse-2] 'muse-follow-name-at-mouse)
78 (define-key map [(shift mouse-2)]
79 'muse-follow-name-at-mouse-other-window))
81 (if (featurep 'xemacs)
82 (define-key map [(shift tab)] 'muse-previous-reference)
83 (define-key map [(shift iso-lefttab)] 'muse-previous-reference)
84 (define-key map [(shift control ?i)] 'muse-previous-reference))
86 (define-key map [(control ?c) (control ?f)] 'muse-project-find-file)
87 (define-key map [(control ?c) (control ?p)] 'muse-project-publish)
89 (when (featurep 'pcomplete)
90 (define-key map [(meta tab)] 'pcomplete)
91 (define-key map [(meta control ?i)] 'pcomplete))
94 "Keymap used by Emacs Muse mode.")
99 (define-derived-mode muse-mode text-mode "Muse"
100 "Muse is an Emacs mode for authoring and publishing documents.
102 ;; because we're not inheriting from normal-mode, we need to
103 ;; explicitly run file variables if the user wants to
105 (hack-local-variables)
106 (error (message "File local-variables error: %s"
107 (prin1-to-string err))))
108 (if muse-mode-highlight-p
109 (muse-use-font-lock))
110 (setq muse-current-project (muse-project-of-file))
111 (muse-project-set-variables)
112 (when (featurep 'pcomplete)
113 ;; if pcomplete is available, set it up!
114 (set (make-variable-buffer-local 'pcomplete-default-completion-function)
115 'muse-mode-completions)
116 (set (make-variable-buffer-local 'pcomplete-command-completion-function)
117 'muse-mode-completions)
118 (set (make-variable-buffer-local 'pcomplete-parse-arguments-function)
119 'muse-mode-current-word)))
121 (defun muse-mode-maybe ()
122 "Maybe turn Emacs Muse mode on for this file."
123 (let ((project (muse-project-of-file)))
125 (funcall (or (muse-get-keyword :major-mode (cadr project) t)
128 ;;; Support page name completion using pcomplete
130 (defun muse-completions ()
131 "Return a list of possible completions names for this buffer."
132 (let ((project (muse-project-of-file)))
134 (while (pcomplete-here
135 (mapcar 'car (muse-project-file-alist project)))))))
137 (defun muse-current-word ()
141 (skip-chars-backward "^\\[[:space:]")
142 (narrow-to-region (point) end))
143 (pcomplete-parse-buffer-arguments))))
145 ;;; Navigate/visit links or URLs. Use TAB, S-TAB and RET (or mouse-2).
147 (defun muse-link-at-point (&optional pos)
148 "Return link text if a URL or Muse link name is at point."
149 (let ((case-fold-search nil)
150 (here (or pos (point))))
152 (and (char-after pos)
153 (not (eq (char-syntax (char-after pos)) ?\ ))))
156 (skip-chars-backward "^'\"<>{}([:space:]")
157 (or (and (looking-at muse-url-regexp)
159 (and (or (looking-at muse-link-regexp)
160 (and (search-backward "[[" (line-beginning-position) t)
161 (looking-at muse-link-regexp)))
162 (<= here (match-end 0))
163 (match-string 1)))))))
165 (defun muse-make-link (link &optional name)
166 "Return a link to LINK with NAME as the text."
169 (not (string= name ""))
170 (not (string= link name)))
171 (concat "[[" (or link "") "][" name "]]")
172 (concat "[[" (or link "") "]]")))
174 (defun muse-edit-link-at-point ()
175 "Edit the current link.
176 Do not rename the page originally referred to."
179 (if (muse-link-at-point)
182 (read-string "Link: "
183 (match-string-no-properties 1))
184 (read-string "Text: "
185 (match-string-no-properties 2)))
187 (error "There is no valid link at point"))))
189 (defun muse-visit-link (link &optional other-window)
190 "Visit the URL or link named by LINK-NAME."
191 (let ((visit-link-function
192 (muse-get-keyword :visit-link (cadr (muse-project-of-file)) t)))
193 (if visit-link-function
194 (funcall visit-link-function link other-window)
195 (if (string-match muse-url-regexp link)
198 (if (string-match "#" link)
199 (setq anchor (substring link (match-beginning 0))
200 link (substring link 0 (match-beginning 0))))
201 (let ((project (muse-project-of-file)))
203 (muse-project-find-file link project
205 'find-file-other-window))
207 (find-file-other-window link)
210 (search-forward anchor nil t)))))))
212 (defun muse-browse-result (style &optional other-window)
213 "Visit the current page's published result."
214 (interactive (list (muse-publish-get-style) current-prefix-arg))
215 (setq style (muse-style style))
217 (muse-publish-output-file buffer-file-name
218 (muse-style-element :path style) style)))
219 (if (not (file-readable-p result-path))
220 (error "Cannot open output file '%s" result-path)
222 (find-file-other-window result-path)
223 (let ((func (muse-style-element :browser style t)))
225 (funcall func result-path)
226 (message "The publishing style %s does not support browsing."
229 (defun muse-follow-name-at-point (&optional other-window)
230 "Visit the link at point, or insert a newline if none."
232 (let ((link (muse-link-at-point)))
234 (muse-visit-link link other-window)
235 (error "There is no valid link at point"))))
237 (defun muse-follow-name-at-point-other-window ()
238 "Visit the link at point in other window."
240 (muse-follow-name-at-point t))
242 (defun muse-follow-name-at-mouse (event &optional other-window)
243 "Visit the link at point, or yank text if none."
246 (cond ((fboundp 'event-window) ; XEmacs
247 (set-buffer (window-buffer (event-window event)))
248 (and (funcall (symbol-function 'event-point) event)
249 (goto-char (funcall (symbol-function 'event-point) event))))
250 ((fboundp 'posn-window) ; Emacs
251 (set-buffer (window-buffer (posn-window (event-start event))))
252 (goto-char (posn-point (event-start event)))))
253 (muse-follow-name-at-point other-window)))
255 (defun muse-follow-name-at-mouse-other-window (event)
256 "Visit the link at point"
258 ;; throw away the old window position, since other-window will
260 (select-window (car (cadr event)))
261 (muse-follow-name-at-mouse event t))
263 (defun muse-next-reference ()
264 "Move forward to next Muse link or URL, cycling if necessary."
266 (let ((case-fold-search nil)
269 (if (muse-link-at-point)
270 (goto-char (match-end 0)))
272 (if (re-search-forward
273 (concat "\\(" muse-link-regexp "\\|"
274 muse-url-regexp "\\)") nil t)
275 (setq pos (match-beginning 0)
277 (goto-char (point-min))
278 (setq cycled (1+ cycled)))))
282 (defun muse-previous-reference ()
283 "Move backward to the next Muse link or URL, cycling if necessary.
284 This function is not entirely accurate, but it's close enough."
286 (let ((case-fold-search nil)
290 (if (re-search-backward
291 (concat "\\(" muse-link-regexp "\\|"
292 muse-url-regexp "\\)") nil t)
295 (goto-char (point-max))
296 (setq cycled (1+ cycled)))))
300 ;;; Generate an index of all known Muse pages
302 (defun muse-generate-index (&optional as-list exclude-private)
303 "Generate an index of all Muse pages."
304 (let ((files (sort (copy-alist (muse-project-file-alist))
307 (string-lessp (car l) (car r))))))
309 (with-current-buffer (get-buffer-create "*Muse Index*")
312 (unless (and exclude-private
313 (muse-project-private-p (cdar files)))
314 (insert (if as-list "- " "") "[[" (caar files) "]]\n"))
315 (setq files (cdr files)))
319 "Display an index of all known Muse pages."
321 (message "Generating Muse index...")
322 (let ((project (muse-project)))
323 (with-current-buffer (muse-generate-index)
324 (goto-char (point-min))
326 (setq muse-current-project project)
327 (pop-to-buffer (current-buffer))))
328 (message "Generating Muse index...done"))
332 ;;; muse-mode.el ends here