~ubuntu-branches/ubuntu/oneiric/muse-el/oneiric

« back to all changes in this revision

Viewing changes to lisp/muse-mode.el

  • Committer: Bazaar Package Importer
  • Author(s): Michael W. Olson (GNU address)
  • Date: 2007-06-25 08:17:44 UTC
  • mto: This revision was merged to the branch mainline in revision 5.
  • Revision ID: james.westby@ubuntu.com-20070625081744-h7ajew6icqoiltfp
Tags: upstream-3.03
ImportĀ upstreamĀ versionĀ 3.03

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
;;; muse-mode.el --- mode for editing Muse files; has font-lock support
2
2
 
3
 
;; Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc.
 
3
;; Copyright (C) 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
4
4
 
5
5
;; This file is part of Emacs Muse.  It is not part of GNU Emacs.
6
6
 
27
27
;;; Contributors:
28
28
 
29
29
;; Andrea Riciputi (ariciputi AT pito DOT com) gave an initial
30
 
;; implementation for tag completion by means of the
31
 
;; `muse-insert-tag' function.
 
30
;; implementation for tag completion by means of the `muse-insert-tag'
 
31
;; function.
 
32
 
 
33
;; Per B. Sederberg (per AT med DOT upenn DOT edu) contributed the
 
34
;; insertion of relative links and list items, backlink searching, and
 
35
;; other things as well.
 
36
 
 
37
;; Stefan Schlee (stefan_schlee AT yahoo DOT com) fixed a bug in
 
38
;; muse-next-reference and muse-previous-reference involving links
 
39
;; that begin at point 1.
32
40
 
33
41
;;; Code:
34
42
 
38
46
;;
39
47
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
40
48
 
 
49
(provide 'muse-mode)
 
50
 
41
51
(require 'muse)
42
52
(require 'muse-regexps)
43
53
(require 'muse-project)
44
 
(require 'muse-publish)
45
54
 
46
55
(autoload 'muse-use-font-lock "muse-colors")
 
56
(autoload 'muse-publish-this-file "muse-publish")
 
57
(autoload 'muse-publish-get-style "muse-publish")
 
58
(autoload 'muse-publish-output-file "muse-publish")
47
59
 
48
60
(require 'derived)
49
61
(eval-when-compile
74
86
          (set sym value)))
75
87
  :group 'muse-mode)
76
88
 
 
89
(defun muse-mode-maybe-after-init ()
 
90
  (when muse-mode-auto-p
 
91
    (add-hook 'find-file-hooks 'muse-mode-maybe)))
 
92
 
 
93
;; If the user sets this value in their init file, make sure that
 
94
;; it takes effect
 
95
(add-hook 'after-init-hook 'muse-mode-maybe-after-init)
 
96
 
77
97
(defcustom muse-mode-intangible-links nil
78
98
  "If non-nil, use the intangible property on links.
79
99
This can cause problems with flyspell (and potentially fill-mode),
86
106
  :type 'hook
87
107
  :options '(flyspell-mode footnote-mode turn-on-auto-fill
88
108
             highlight-changes-mode)
89
 
  :set #'(lambda (sym val)
90
 
           (when (featurep 'muse-wiki)
91
 
             (add-to-list 'val 'muse-wiki-update-custom-values))
92
 
           (set sym val))
93
 
  :group 'muse-mode)
 
109
  :group 'muse-mode)
 
110
 
 
111
(defcustom muse-grep-command
 
112
  "find %D -type f ! -name '*~' | xargs -I {} echo \\\"{}\\\" | xargs egrep -n -e \"%W\""
 
113
  "The command to use when grepping for backlinks and other
 
114
searches through the muse projects.  The string %D is replaced by
 
115
the directories from muse-project-alist, space-separated.  The
 
116
string %W is replaced with the name of the muse page or whatever
 
117
else you are searching for.  This command has been modified to
 
118
handle spaces in filenames, which were giving egrep a problem.
 
119
 
 
120
Note: We highly recommend using glimpse to search large projects.
 
121
To use glimpse, install and edit a file called .glimpse_exclude
 
122
in your home directory.  Put a list of glob patterns in that file
 
123
to exclude Emacs backup files, etc.  Then, run the indexer using:
 
124
 
 
125
  glimpseindex -o <list of Wiki directories>
 
126
 
 
127
Once that's completed, customize this variable to have the
 
128
following value:
 
129
 
 
130
  glimpse -nyi \"%W\"
 
131
 
 
132
Your searches will go much, much faster, especially for very
 
133
large projects.  Don't forget to add a user cronjob to update the
 
134
index at intervals."
 
135
  :type 'string
 
136
  :group 'muse-mode)
 
137
 
 
138
(defvar muse-insert-map
 
139
  (let ((map (make-sparse-keymap)))
 
140
    (define-key map "l" 'muse-insert-relative-link-to-file)
 
141
    (define-key map "t" 'muse-insert-tag)
 
142
    (define-key map "u" 'muse-insert-url)
 
143
 
 
144
    map))
94
145
 
95
146
(defvar muse-mode-map
96
147
  (let ((map (make-sparse-keymap)))
97
148
    (define-key map [(control ?c) (control ?a)] 'muse-index)
98
 
    (define-key map [(control ?c) (control ?b)] 'muse-browse-result)
99
 
    (define-key map [(control ?c) (control ?c)] 'muse-follow-name-at-point)
100
149
    (define-key map [(control ?c) (control ?e)] 'muse-edit-link-at-point)
101
 
    (define-key map [(control ?c) (control ?t)] 'muse-publish-this-file)
102
 
    (define-key map [(control ?c) (control ?v)] 'muse-follow-name-at-point)
103
 
 
104
150
    (define-key map [(control ?c) (control ?l)] 'font-lock-mode)
 
151
    (define-key map [(control ?c) (control ?t)]
 
152
      'muse-project-publish-this-file)
 
153
    (define-key map [(control ?c) (control ?T)] 'muse-publish-this-file)
 
154
    (define-key map [(control ?c) (meta control ?t)] 'muse-publish-this-file)
 
155
    (define-key map [(control ?c) (control ?v)] 'muse-browse-result)
105
156
 
106
157
    (define-key map [(control ?c) ?=]           'muse-what-changed)
107
158
 
127
178
    (define-key map [(control ?c) (control ?f)] 'muse-project-find-file)
128
179
    (define-key map [(control ?c) (control ?p)] 'muse-project-publish)
129
180
 
130
 
    (define-key map [(control ?c) tab] 'muse-insert-tag)
131
 
    (define-key map [(control ?c) (control ?i)] 'muse-insert-tag)
 
181
    (define-key map [(control ?c) (control ?i)] 'muse-insert-thing)
 
182
    (define-key map [(control ?c) tab] 'muse-insert-thing)
 
183
 
 
184
    ;; Searching functions
 
185
    (define-key map [(control ?c) (control ?b)] 'muse-find-backlinks)
 
186
    (define-key map [(control ?c) (control ?s)] 'muse-search)
 
187
 
 
188
    ;; Enhanced list functions
 
189
    (define-key map [(meta return)] 'muse-insert-list-item)
 
190
    (define-key map [(control ?>)] 'muse-increase-list-item-indentation)
 
191
    (define-key map [(control ?<)] 'muse-decrease-list-item-indentation)
132
192
 
133
193
    (when (featurep 'pcomplete)
134
194
      (define-key map [(meta tab)] 'pcomplete)
155
215
    (set (make-local-variable 'inhibit-point-motion-hooks) t))
156
216
  (setq muse-current-project (muse-project-of-file))
157
217
  (muse-project-set-variables)
158
 
  ;; Make sure several variables get updated if the user has changed
159
 
  ;; them without using the customize interface.
160
 
  (muse-update-ignored-extensions-regexp 'muse-ignored-extensions
161
 
                                         muse-ignored-extensions)
162
 
  (muse-update-url-regexp 'muse-url-protocols muse-url-protocols)
163
218
  ;; Make fill not split up links
164
219
  (when (boundp 'fill-nobreak-predicate)
165
220
    (make-local-variable 'fill-nobreak-predicate)
171
226
                   'muse-mode-fill-nobreak-p)))
172
227
  ;; Make fill work nicely with item lists
173
228
  (set (make-local-variable 'adaptive-fill-regexp)
174
 
       "\\s-+\\(-\\|[0-9]+\\.\\)\\s-+\\|\\[[0-9]+\\]\\s-*\\|\\s-*")
 
229
       (concat "\\s-+\\(-\\|[0-9]+\\.\\)\\s-+\\|\\[[0-9]+\\]\\s-*"
 
230
               "\\|.*\\s-*::\\s-+\\|\\s-*"))
175
231
  (set (make-local-variable 'paragraph-start)
176
 
       (concat paragraph-start "\\|\\s-+\\(-\\|[0-9]+\\.\\)\\s-+"
177
 
               "\\|\\[[0-9]+\\]\\s-*"))
 
232
       (concat paragraph-start
 
233
               "\\|\\s-+\\(-\\|[0-9]+\\.\\)\\s-+\\|\\[[0-9]+\\]\\s-*"
 
234
               "\\|.*\\s-*::\\s-+"))
178
235
  ;; Comment syntax is `; comment'
179
236
  (set (make-local-variable 'comment-start)
180
237
       "; ")
192
249
         'muse-mode-completions)
193
250
    (set (make-local-variable 'pcomplete-parse-arguments-function)
194
251
         'muse-mode-current-word))
 
252
  ;; Initialize any auto-generated variables
 
253
  (run-hooks 'muse-update-values-hook)
195
254
  (when muse-mode-highlight-p
196
255
    (muse-use-font-lock)))
197
256
 
217
276
Otherwise return nil.
218
277
 
219
278
This is used to keep links from being improperly colorized by flyspell."
220
 
  (save-match-data
221
 
    (null (muse-link-at-point))))
 
279
  (and (not (get-text-property (if (bobp) (point) (1- (point)))
 
280
                               'muse-link))
 
281
       (save-match-data
 
282
         (null (muse-link-at-point)))))
222
283
 
 
284
;;;###autoload
223
285
(defun muse-mode-choose-mode ()
224
286
  "Turn the proper Emacs Muse related mode on for this file."
225
287
  (let ((project (muse-project-of-file)))
233
295
         (funcall (or (muse-get-keyword :major-mode (cadr project) t)
234
296
                      'muse-mode)))))
235
297
 
 
298
;;; Enhanced list editing
 
299
 
 
300
(defun muse-on-blank-line ()
 
301
  "See if point is on a blank line"
 
302
  (save-excursion
 
303
    (beginning-of-line)
 
304
    (looking-at (concat "[" muse-regexp-blank "]?[\n]+"))))
 
305
 
 
306
(defun muse-get-paragraph-start ()
 
307
  "Return the start of the current paragraph. This function will
 
308
return nil if there are no prior paragraphs and the beginning of
 
309
the line if point is on a blank line."
 
310
  (let ((para-start (concat "[\n]+[" muse-regexp-blank "]?[\n]+")))
 
311
    ;; search back to start of paragraph
 
312
    (save-excursion
 
313
      (save-match-data
 
314
        (if (not (muse-on-blank-line))
 
315
            (re-search-backward para-start nil t)
 
316
          (line-beginning-position))))))
 
317
 
 
318
(defun muse-insert-thing ()
 
319
  "Prompt for something to insert into the current buffer."
 
320
  (interactive)
 
321
  (message "Insert:\nl  link\nt  Muse tag\nu  URL")
 
322
  (let (key cmd)
 
323
    (let ((overriding-local-map muse-insert-map))
 
324
      (setq key (read-key-sequence nil)))
 
325
    (if (commandp (setq cmd (lookup-key muse-insert-map key)))
 
326
        (progn (message "")
 
327
               (call-interactively cmd))
 
328
      (message "Not inserting anything"))))
 
329
 
 
330
;;;###autoload
 
331
(defun muse-insert-list-item ()
 
332
  "Insert a list item at the current point, taking into account
 
333
your current list type and indentation level."
 
334
  (interactive)
 
335
  (let ((newitem " - ")
 
336
        (itemno nil)
 
337
        (pstart (muse-get-paragraph-start))
 
338
        (list-item (format muse-list-item-regexp
 
339
                           (concat "[" muse-regexp-blank "]*"))))
 
340
    ;; search backwards for start of current item
 
341
    (save-excursion
 
342
      (when (re-search-backward list-item pstart t)
 
343
        ;; save the matching item
 
344
        (setq newitem (match-string 0))
 
345
        ;; see what type it is
 
346
        (if (string-match "::" (match-string 0))
 
347
            ;; is a definition, replace the term
 
348
            (setq newitem (concat " "
 
349
                                  (read-string "Term: ")
 
350
                                  " :: "))
 
351
          ;; see if it's a numbered list
 
352
          (when (string-match "[0-9]+" newitem)
 
353
            ;; is numbered, so increment
 
354
            (setq itemno (1+
 
355
                          (string-to-number
 
356
                           (match-string 0 newitem))))
 
357
            (setq newitem (replace-match
 
358
                           (number-to-string itemno)
 
359
                           nil nil newitem))))))
 
360
    ;; insert the new item
 
361
    (insert (concat "\n" newitem))))
 
362
 
 
363
(defun muse-alter-list-item-indentation (operation)
 
364
  "Alter the indentation of the current list item.
 
365
Valid values of OPERATION are 'increase and 'decrease."
 
366
  (let ((pstart (muse-get-paragraph-start))
 
367
        (list-item (format muse-list-item-regexp
 
368
                           (concat "[" muse-regexp-blank "]*")))
 
369
        beg move-func indent)
 
370
    ;; search backwards until start of paragraph to see if we are on a
 
371
    ;; current item
 
372
    (save-excursion
 
373
      (if (or (progn (goto-char (muse-line-beginning-position))
 
374
                     ;; we are on an item
 
375
                     (looking-at list-item))
 
376
              ;; not on item, so search backwards
 
377
              (re-search-backward list-item pstart t))
 
378
          (let ((beg (point)))
 
379
            ;; we are on an item
 
380
            (setq indent (buffer-substring (match-beginning 0)
 
381
                                           (match-beginning 1)))
 
382
            (muse-forward-list-item (muse-list-item-type (match-string 1))
 
383
                                    (concat "[" muse-regexp-blank "]*")
 
384
                                    t)
 
385
            (save-restriction
 
386
              (narrow-to-region beg (point))
 
387
              (goto-char (point-min))
 
388
              (let ((halt nil))
 
389
                (while (< (point) (point-max))
 
390
                  ;; increase or decrease the indentation
 
391
                  (unless halt
 
392
                    (cond ((eq operation 'increase)
 
393
                           (insert "  "))
 
394
                          ((eq operation 'decrease)
 
395
                           (if (looking-at "  ")
 
396
                               ;; we have enough space, so delete it
 
397
                               (delete-region (match-beginning 0)
 
398
                                              (match-end 0))
 
399
                             (setq halt t)))))
 
400
                  (forward-line 1)))))
 
401
        ;; we are not on an item, so warn
 
402
        (message "You are not on a list item.")))))
 
403
 
 
404
;;;###autoload
 
405
(defun muse-increase-list-item-indentation ()
 
406
  "Increase the indentation of the current list item."
 
407
  (interactive)
 
408
  (muse-alter-list-item-indentation 'increase))
 
409
 
 
410
;;;###autoload
 
411
(defun muse-decrease-list-item-indentation ()
 
412
  "Decrease the indentation of the current list item."
 
413
  (interactive)
 
414
  (muse-alter-list-item-indentation 'decrease))
 
415
 
236
416
;;; Support page name completion using pcomplete
237
417
 
238
 
(defun muse-completions ()
 
418
(defun muse-mode-completions ()
239
419
  "Return a list of possible completions names for this buffer."
240
420
  (let ((project (muse-project-of-file)))
241
421
    (if project
242
422
        (while (pcomplete-here
243
423
                (mapcar 'car (muse-project-file-alist project)))))))
244
424
 
245
 
(defun muse-current-word ()
 
425
(defun muse-mode-current-word ()
246
426
  (let ((end (point)))
247
427
    (save-excursion
248
428
      (save-restriction
257
437
  (let ((case-fold-search nil)
258
438
        (inhibit-point-motion-hooks t)
259
439
        (here (or pos (point))))
260
 
    (when (or (null pos)
261
 
              (and (char-after pos)
262
 
                   (not (eq (char-syntax (char-after pos)) ?\ ))))
263
 
      (save-excursion
264
 
        (goto-char here)
265
 
        ;; Check for explicit link here or before point
266
 
        (if (or (looking-at muse-explicit-link-regexp)
267
 
                (and
268
 
                 (re-search-backward "\\[\\[\\|\\]\\]"
269
 
                                     (muse-line-beginning-position)
270
 
                                     t)
271
 
                 (string= (or (match-string 0) "") "[[")
272
 
                 (looking-at muse-explicit-link-regexp)))
273
 
            (progn
274
 
              (goto-char (match-beginning 1))
275
 
              (muse-handle-explicit-link))
 
440
    ;; if we are using muse-colors, we can just use link properties to
 
441
    ;; determine whether we are on a link
 
442
    (if (featurep 'muse-colors)
 
443
        (when (get-text-property here 'muse-link)
 
444
          (save-excursion
 
445
            (when (and (not (bobp))
 
446
                       (get-text-property (1- here) 'muse-link))
 
447
              (goto-char (or (previous-single-property-change here 'muse-link)
 
448
                             (point-min))))
 
449
            (if (looking-at muse-explicit-link-regexp)
 
450
                (progn
 
451
                  (goto-char (match-beginning 1))
 
452
                  (muse-handle-explicit-link))
 
453
              (muse-handle-implicit-link))))
 
454
      ;; use fallback method to find a link
 
455
      (when (or (null pos)
 
456
                (and (char-after pos)
 
457
                     (not (eq (char-syntax (char-after pos)) ?\ ))))
 
458
        (save-excursion
276
459
          (goto-char here)
277
 
          ;; Check for bare URL or other link type
278
 
          (skip-chars-backward (concat "^'\"<>{}(\n" muse-regexp-blank))
279
 
          (and (looking-at muse-implicit-link-regexp)
280
 
               (muse-handle-implicit-link)))))))
 
460
          ;; check for explicit link here or before point
 
461
          (if (or (looking-at muse-explicit-link-regexp)
 
462
                  (and
 
463
                   (re-search-backward "\\[\\[\\|\\]\\]"
 
464
                                       (muse-line-beginning-position)
 
465
                                       t)
 
466
                   (string= (or (match-string 0) "") "[[")
 
467
                   (looking-at muse-explicit-link-regexp)))
 
468
              (progn
 
469
                (goto-char (match-beginning 1))
 
470
                (muse-handle-explicit-link))
 
471
            (goto-char here)
 
472
            ;; check for bare URL or other link type
 
473
            (skip-chars-backward (concat "^'\"<>{}(\n" muse-regexp-blank))
 
474
            (and (looking-at muse-implicit-link-regexp)
 
475
                 (muse-handle-implicit-link))))))))
281
476
 
282
 
(defun muse-make-link (link &optional name)
283
 
  "Return a link to LINK with NAME as the text."
 
477
(defun muse-make-link (link &optional desc)
 
478
  "Return a link to LINK with DESC as the description."
284
479
  (when (string-match muse-explicit-link-regexp link)
285
 
    (unless name (setq name (match-string 2 link)))
286
 
    (setq link (match-string 1 link)))
287
 
  (if (and name
 
480
    (unless desc (setq desc (muse-get-link-desc link)))
 
481
    (setq link (muse-get-link link)))
 
482
  (if (and desc
288
483
           link
289
 
           (not (string= name ""))
290
 
           (not (string= link name)))
291
 
      (concat "[[" (muse-link-escape link) "][" (muse-link-escape name) "]]")
292
 
    (concat "[[" (muse-link-escape link) "]]")))
 
484
           (not (string= desc ""))
 
485
           (not (string= link desc)))
 
486
      (concat "[[" (muse-link-escape link) "][" (muse-link-escape desc) "]]")
 
487
    (concat "[[" (or (muse-link-escape link) "") "]]")))
 
488
 
 
489
;;;###autoload
 
490
(defun muse-insert-relative-link-to-file ()
 
491
  "Insert a relative link to a file, with optional description, at point."
 
492
  ;; Perhaps the relative location should be configurable, so that the
 
493
  ;; file search would start in the publishing directory and then
 
494
  ;; insert the link relative to the publishing directory
 
495
  (interactive)
 
496
  (insert
 
497
   (muse-make-link (file-relative-name (read-file-name "Link: "))
 
498
                   (read-string "Text: "))))
 
499
 
 
500
(defun muse-insert-url ()
 
501
  "Insert a URL, with optional description, at point."
 
502
  (interactive)
 
503
  (insert
 
504
   (muse-make-link (read-string "URL: ")
 
505
                   (read-string "Text: "))))
293
506
 
294
507
;;;###autoload
295
508
(defun muse-edit-link-at-point ()
297
510
Do not rename the page originally referred to."
298
511
  (interactive)
299
512
  (if (muse-link-at-point)
300
 
      (replace-match
301
 
       (save-match-data
302
 
         (muse-make-link
303
 
          (read-string "Link: "
304
 
                       (muse-match-string-no-properties 1))
305
 
          (read-string "Text: "
306
 
                       (muse-match-string-no-properties 2))))
307
 
       t t)
 
513
      (let ((link (muse-link-unescape (muse-get-link)))
 
514
            (desc (muse-link-unescape (muse-get-link-desc))))
 
515
        (replace-match
 
516
         (save-match-data
 
517
           (muse-make-link
 
518
            (read-string "Link: " link)
 
519
            (read-string "Text: " desc)))
 
520
         t t))
308
521
    (error "There is no valid link at point")))
309
522
 
310
523
(defun muse-visit-link-default (link &optional other-window)
360
573
;;;###autoload
361
574
(defun muse-browse-result (style &optional other-window)
362
575
  "Visit the current page's published result."
363
 
  (interactive (list (muse-publish-get-style) current-prefix-arg))
 
576
  (interactive
 
577
   (list (muse-project-get-applicable-style buffer-file-name
 
578
                                            (cddr muse-current-project))
 
579
         current-prefix-arg))
364
580
  (setq style (muse-style style))
365
581
  (let ((result-path
366
582
         (muse-publish-output-file buffer-file-name
377
593
 
378
594
;;;###autoload
379
595
(defun muse-follow-name-at-point (&optional other-window)
380
 
  "Visit the link at point, or insert a newline if none is found."
 
596
  "Visit the link at point."
381
597
  (interactive "P")
382
598
  (let ((link (muse-link-at-point)))
383
599
    (if link
393
609
(defun muse-follow-name-at-mouse (event &optional other-window)
394
610
  "Visit the link at point, or yank text if none is found."
395
611
  (interactive "eN")
396
 
  (save-excursion
397
 
    (cond ((fboundp 'event-window)      ; XEmacs
398
 
           (set-buffer (window-buffer (event-window event)))
399
 
           (and (funcall (symbol-function 'event-point) event)
400
 
                (goto-char (funcall (symbol-function 'event-point) event))))
401
 
          ((fboundp 'posn-window)       ; Emacs
402
 
           (set-buffer (window-buffer (posn-window (event-start event))))
403
 
           (goto-char (posn-point (event-start event)))))
404
 
    (let ((link (muse-link-at-point)))
405
 
      (if link
406
 
          (muse-visit-link link other-window)
407
 
        ;; Fall back to normal binding for this event
408
 
        (call-interactively
409
 
         (lookup-key (current-global-map) (this-command-keys)))))))
 
612
  (unless
 
613
      (save-excursion
 
614
        (cond ((fboundp 'event-window)      ; XEmacs
 
615
               (set-buffer (window-buffer (event-window event)))
 
616
               (and (funcall (symbol-function 'event-point) event)
 
617
                    (goto-char (funcall (symbol-function 'event-point)
 
618
                                        event))))
 
619
              ((fboundp 'posn-window)       ; Emacs
 
620
               (set-buffer (window-buffer (posn-window (event-start event))))
 
621
               (goto-char (posn-point (event-start event)))))
 
622
        (let ((link (muse-link-at-point)))
 
623
          (when link
 
624
            (muse-visit-link link other-window)
 
625
            t)))
 
626
    ;; Fall back to normal binding for this event
 
627
    (call-interactively
 
628
     (lookup-key (current-global-map) (this-command-keys)))))
410
629
 
411
630
(defun muse-follow-name-at-mouse-other-window (event)
412
631
  "Visit the link at point"
420
639
(defun muse-next-reference ()
421
640
  "Move forward to next Muse link or URL, cycling if necessary."
422
641
  (interactive)
423
 
  (let ((cycled 0) pos)
 
642
  (let ((pos))
424
643
    (save-excursion
425
 
      (when (memq (get-text-property (point) 'face)
426
 
                  '(muse-link-face muse-bad-link-face))
427
 
        (goto-char (or (next-single-property-change (point) 'face)
 
644
      (when (get-text-property (point) 'muse-link)
 
645
        (goto-char (or (next-single-property-change (point) 'muse-link)
428
646
                       (point-max))))
429
 
      (while (< cycled 2)
430
 
        (let ((next (point)))
431
 
          (if (while (and (null pos)
432
 
                          (setq next
433
 
                                (next-single-property-change
434
 
                                 next 'face)))
435
 
                (when (memq (get-text-property next 'face)
436
 
                            '(muse-link-face muse-bad-link-face))
437
 
                  (setq pos next)))
438
 
              (setq cycled 2)
439
 
            (goto-char (point-min))
440
 
            (setq cycled (1+ cycled))))))
441
 
    (if pos
442
 
        (goto-char pos))))
 
647
 
 
648
      (setq pos (next-single-property-change (point) 'muse-link))
 
649
 
 
650
      (when (not pos)
 
651
        (if (get-text-property (point-min) 'muse-link)
 
652
            (setq pos (point-min))
 
653
          (setq pos (next-single-property-change (point-min) 'muse-link)))))
 
654
 
 
655
    (when pos
 
656
      (goto-char pos))))
443
657
 
444
658
;;;###autoload
445
659
(defun muse-previous-reference ()
446
660
  "Move backward to the next Muse link or URL, cycling if necessary.
 
661
In case of Emacs x <= 21 and ignoring of intangible properties (see
 
662
`muse-mode-intangible-links').
 
663
 
447
664
This function is not entirely accurate, but it's close enough."
448
665
  (interactive)
449
 
  (let ((cycled 0) pos)
 
666
  (let ((pos))
450
667
    (save-excursion
451
 
      (while (< cycled 2)
452
 
        (let ((prev (point)))
453
 
          (if (while (and (null pos)
454
 
                          (setq prev
455
 
                                (previous-single-property-change
456
 
                                 prev 'face)))
457
 
              (when (memq (get-text-property prev 'face)
458
 
                          '(muse-link-face muse-bad-link-face))
459
 
                (setq pos prev)))
460
 
              (setq cycled 2)
461
 
            (goto-char (point-max))
462
 
            (setq cycled (1+ cycled))))))
463
 
    (if pos
464
 
        (goto-char pos))))
 
668
 
 
669
      ;; Hack: The user perceives the two cases of point ("|")
 
670
      ;; position (1) "|[[" and (2) "[[|" or "][|" as "point is at
 
671
      ;; start of link".  But in the sense of the function
 
672
      ;; "previous-single-property-change" these two cases are
 
673
      ;; different.  The following code aligns these two cases.  Emacs
 
674
      ;; 21: If the intangible property is ignored case (2) is more
 
675
      ;; complicate and this hack only solves the problem partially.
 
676
      ;;
 
677
      (when (and (get-text-property (point) 'muse-link)
 
678
                 (muse-looking-back "\\[\\|\\]"))
 
679
        (goto-char (or (previous-single-property-change (point) 'muse-link)
 
680
                       (point-min))))
 
681
 
 
682
      (when (eq (point) (point-min))
 
683
        (goto-char (point-max)))
 
684
 
 
685
      (setq pos (previous-single-property-change (point) 'muse-link))
 
686
 
 
687
      (when (not pos)
 
688
        (if (get-text-property (point-min) 'muse-link)
 
689
            (setq pos (point-min))
 
690
          (setq pos (previous-single-property-change (point-max)
 
691
                                                     'muse-link)))))
 
692
 
 
693
    (when pos
 
694
      (if (get-text-property pos 'muse-link)
 
695
          (goto-char pos)
 
696
        (goto-char (or (previous-single-property-change pos 'muse-link)
 
697
                       (point-min)))))))
465
698
 
466
699
;;;###autoload
467
700
(defun muse-what-changed ()
469
702
  (interactive)
470
703
  (diff-backup buffer-file-name))
471
704
 
 
705
 
 
706
;;; Find text in project pages, or pages referring to the current page
 
707
 
 
708
(defvar muse-search-history nil)
 
709
 
 
710
(defun muse-grep (string &optional grep-command-no-shadow)
 
711
  "Grep for STRING in the project directories.
 
712
GREP-COMMAND if passed will supplant `muse-grep-command'."
 
713
  ;; careful - grep-command leaks into compile, so we call it
 
714
  ;; -no-shadow instead
 
715
  (require 'compile)
 
716
  (let* ((str (or grep-command-no-shadow muse-grep-command))
 
717
         (muse-directories (mapcar
 
718
                            (lambda (thing)
 
719
                              (car (cadr thing)))
 
720
                            muse-project-alist))
 
721
         (dirs (mapconcat (lambda (dir)
 
722
                            (shell-quote-argument
 
723
                             (expand-file-name dir)))
 
724
                          muse-directories " ")))
 
725
    (while (string-match "%W" str)
 
726
      (setq str (replace-match string t t str)))
 
727
    (while (string-match "%D" str)
 
728
      (setq str (replace-match dirs t t str)))
 
729
    (if (fboundp 'compilation-start)
 
730
        (compilation-start str nil (lambda (&rest args) "*search*")
 
731
                           grep-regexp-alist)
 
732
      (and (fboundp 'compile-internal)
 
733
           (compile-internal str "No more search hits" "search"
 
734
                             nil grep-regexp-alist)))))
 
735
 
 
736
;;;###autoload
 
737
(defun muse-search-with-command (text)
 
738
  "Search for the given TEXT string in the project directories
 
739
using the specified command."
 
740
  (interactive
 
741
   (list (let ((str (concat muse-grep-command)) pos)
 
742
           (when (string-match "%W" str)
 
743
             (setq pos (match-beginning 0))
 
744
             (unless (featurep 'xemacs)
 
745
               (setq pos (1+ pos)))
 
746
             (setq str (replace-match "" t t str)))
 
747
           (read-from-minibuffer "Search command: "
 
748
                                 (cons str pos) nil nil
 
749
                                 'muse-search-history))))
 
750
  (muse-grep nil text))
 
751
 
 
752
;;;###autoload
 
753
(defun muse-search ()
 
754
  "Search for the given TEXT using the default grep command."
 
755
  (interactive)
 
756
  (muse-grep (read-string "Search: ")))
 
757
 
 
758
;;;###autoload
 
759
(defun muse-find-backlinks ()
 
760
  "Grep for the current pagename in all the project directories."
 
761
  (interactive)
 
762
  (muse-grep (muse-page-name)))
 
763
 
 
764
 
472
765
;;; Generate an index of all known Muse pages
473
766
 
474
767
(defun muse-generate-index (&optional as-list exclude-private)
511
804
        (setq files (cdr files)))
512
805
      (buffer-string))))
513
806
 
514
 
;;; Insert tags interactively on C-c TAB
 
807
;;; Insert tags interactively on C-c TAB t
515
808
 
516
809
(defvar muse-tag-history nil
517
810
  "List of recently-entered tags; used by `muse-insert-tag'.
532
825
     (concat "Tag: "
533
826
             (when muse-tag-history
534
827
               (concat "(default: " (car muse-tag-history) ") ")))
535
 
     (mapcar 'list (nconc (mapcar 'car muse-publish-markup-tags)
536
 
                          muse-custom-tags))
 
828
     (progn
 
829
       (require 'muse-publish)
 
830
       (mapcar 'list (nconc (mapcar 'car muse-publish-markup-tags)
 
831
                            muse-custom-tags)))
537
832
     nil nil nil 'muse-tag-history
538
833
     (car muse-tag-history))))
539
834
  (when (equal tag "")
540
835
    (setq tag (car muse-tag-history)))
 
836
  (unless (interactive-p)
 
837
    (require 'muse-publish))
541
838
  (let ((tag-entry (assoc tag muse-publish-markup-tags))
542
839
        (options ""))
543
840
    ;; Add to custom list if no entry exists
554
851
      (insert (concat "\n\n</" tag ">\n"))
555
852
      (forward-line -2))))
556
853
 
557
 
(provide 'muse-mode)
558
 
 
559
854
;;; muse-mode.el ends here