summaryrefslogtreecommitdiffstats
path: root/emacs.d/nxhtml/util/mlinks.el
blob: 0f816541d82661431132748cb06b2a74c12502c1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
;;; mlinks.el --- Minor mode making major mode dependent links
;;
;; Author: Lennar Borgman
;; Created: Tue Jan 16 2007
(defconst mlinks:version "0.28") ;;Version:
;; Last-Updated: 2010-01-05 Tue
;; Keywords:
;; Compatibility:
;;
;; Fxeatures that might be required by this library:
;;
;;   `appmenu', `cl', `mail-prsvr', `mm-util', `ourcomments-util',
;;   `url-expand', `url-methods', `url-parse', `url-util',
;;   `url-vars'.
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;; Commentary:
;;
;; This file implements the minor mode `mlinks-mode' that create
;; hyperlinks for different major modes.  Such links can be visible or
;; invisible.  The meanings of the links are defined per mode.
;;
;; Examples:
;;
;; - In in html style modes the links are visible they can mean either
;;   open a file for editing, go to an achnor or view the link in a
;;   web browser etc.
;;
;; - In emacs lisp mode the links are invisible, but maybe highlighed
;;   when point or mouse is on them.  (Having them highlighted when
;;   point is on them can be a quick way to check that you have
;;   spelled a symbol correct.)  The meanings of the links in emacs
;;   lisp mode are go to definition.
;;
;; Common to links that open a buffer in Emacs is that you can the
;; buffer opened in the same window, the other window or in a new
;; frame.  The same key binding is used in all major modes for this.
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;; Change log:
;;
;; FIX-ME: url-hexify-string etc
;;
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program; see the file COPYING.  If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;; Code:

(eval-when-compile (require 'cl))
(eval-when-compile (require 'appmenu nil t))
(eval-when-compile (require 'mumamo nil t))
(eval-when-compile (require 'ourcomments-util nil t))

(require 'rx)
(require 'url-parse)
(require 'url-expand)

(defvar mlinks-point-hilighter-overlay nil)
(make-variable-buffer-local 'mlinks-point-hilighter-overlay)
(put 'mlinks-point-hilighter-overlay 'permanent-local t)

;;;###autoload
(defgroup mlinks nil
  "Customization group for `mlinks-mode'."
  :group 'nxhtml
  :group 'hypermedia)

(defvar mlinks-link-face 'mlinks-link-face)
(defface mlinks-link-face
  '((t (:inherit highlight)))
  "Face normally active links have on them."
  :group 'mlinks)

(defvar mlinks-hyperactive-link-face 'mlinks-hyperactive-link-face)
(defface mlinks-hyperactive-link-face
  '((t (:inherit isearch)))
  "Face hyper active links have on them."
  :group 'mlinks)

(defvar mlinks-font-lock-face 'mlinks-font-lock-face)
(defface mlinks-font-lock-face
  '((t :inherit link))
  "Default face for MLinks' links."
  :group 'mlinks)



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Mode function bindings

;;(customize-option mlinks-mode-functions)
(defcustom mlinks-mode-functions
  '(
    ;; For message buffer etc.
    (fundamental-mode
     ((goto mlinks-elisp-goto)
      (hili mlinks-elisp-hili)
      (hion t)
      )
     )
    (emacs-lisp-mode
     ((goto mlinks-elisp-goto)
      (hili mlinks-elisp-hili)
      (hion t)
      )
     )
    ;; *scractch*
    (lisp-interaction-mode
     ((goto mlinks-elisp-goto)
      (hili mlinks-elisp-hili)
      (hion t)
      )
     )
    (help-mode
     ((goto mlinks-elisp-goto)
      (hili mlinks-elisp-hili)
      (hion t)
      )
     )
    (Info-mode
     ((goto mlinks-elisp-goto)
      (hili mlinks-elisp-hili)
      (hion t)
      )
     )
    (Custom-mode
     ((goto mlinks-elisp-custom-goto)
      (hili mlinks-elisp-hili)
      (hion t)
      (fontify mlinks-custom-fontify)
      )
     )
    (text-mode
     ((goto mlinks-goto-plain-url)
      (hion t)
      (fontify mlinks-plain-urls-fontify)
      )
     )
    (nxhtml-mode
     ((hion t)
      (fontify mlinks-html-fontify)
      (goto mlinks-html-style-goto)
      )
     )
    (nxml-mode
     ((hion t)
      (fontify mlinks-html-fontify)
      (goto mlinks-html-style-goto)
      )
     )
    (sgml-mode
     ((hion t)
      (fontify mlinks-html-fontify)
      (goto mlinks-html-style-goto)
      )
     )
    (html-mode
     ((hion t)
      (fontify mlinks-html-fontify)
      (goto mlinks-html-style-goto)
      )
     )
    )
  "Defines MLinks hyperlinks for major modes.
"
  ;; Each element in the list is a list with two elements

  ;;   \(MAJOR-MODE SETTINGS)

  ;; where MAJOR-MODE is the major mode for which the settings SETTINGS should be used.
  ;; SETTINGS is an association list which can have the following element types

  ;;   \(hili HILIGHT-FUN)  ;; Mandatory
  ;;   \(goto GOTO-FUN)     ;; Mandatory
  ;;   \(hion HION-BOOL)    ;; Optional
  ;;   \(next NEXT-FUN)     ;; Optional
  ;;   \(prev PREV-FUN)     ;; Optional

  ;; Where
  ;; - HILIGHT-FUN is the function to hilight a link when point is
  ;;   inside the link. This is done when Emacs is idle.
  ;; - GOTO-FUN is the function to follow the link at point.
  ;; - HION-BOOL is t or nil depending on if hilighting should be on
  ;;   by default.
  ;; - NEXT-FUN is the function to go to the next link.
  ;; - PREV-FUN is the function to go to the previous link."
  ;;   ;;:type '(repeat (alist :key-type symbol :value-type (alist :key-type symbol :value symbol)))
  :type '(alist :key-type major-mode-function
                :value-type (list
                             (set
                              (const :tag "Enable MLinks in this major mode" hion)
                              (const :tag "Mark All Links" mark)
                              (list :tag "Enable" (const :tag "Hilighting" hili) function)
                              (list :tag "Enable" (const :tag "Follow Link" goto) function)
                              (list :tag "Enable" (const :tag "Goto Next Link" next) function)
                              (list :tag "Enable" (const :tag "Goto Previous Link" prev) function)
                              )))
  :group 'mlinks)


(defun mlinks-get-mode-value (which)
  (let* ((major major-mode)
         (mode-rec (assoc major mlinks-mode-functions)))
    (catch 'mode-rec
      (while (and major
                  (not mode-rec))
        (setq major (get major 'derived-mode-parent))
        (setq mode-rec (assoc major mlinks-mode-functions))
        (when mode-rec (throw 'mode-rec nil))))
    (when mode-rec
      (let* ((mode (car mode-rec))
             (funs-alist (cadr mode-rec))
             (funs (assoc which funs-alist)))
        (cdr funs)))))




;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Minor modes

;; (appmenu-dump-keymap mlinks-mode-map)
(defvar mlinks-mode-map
  (let ((m (make-sparse-keymap "mlinks")))
    (define-key m [(control ?c) ?\r ?\r]   'mlinks-goto)
    (define-key m [(control ?c) ?\r ?w]    'mlinks-goto-other-window)
    (define-key m [(control ?c) ?\r ?f]    'mlinks-goto-other-frame)
    (define-key m [(control ?c) ?\r ?n]    'mlinks-next-saved-position)
    (define-key m [(control ?c) ?\r ?p]    'mlinks-prev-saved-position)
    (define-key m [(control ?c) ?\r S-tab] 'mlinks-backward-link)
    (define-key m [(control ?c) ?\r tab]   'mlinks-forward-link)
    (define-key m [(control ?c) ?\r ?h]    'mlinks-toggle-hilight)
    (define-key m [(control ?c) ?\r ?c]    'mlinks-copy-link-text)
    m))

;;;###autoload
(define-minor-mode mlinks-mode
  "Recognizes certain parts of a buffer as hyperlinks.
The hyperlinks are created in different ways for different major
modes with the help of the functions in the list
`mlinks-mode-functions'.

The hyperlinks can be hilighted when point is over them.  Use
`mlinks-toggle-hilight' to toggle this feature for the current
buffer.

All keybindings in this mode are by default done under the prefi§x
key

  C-c RET

which is supposed to be a kind of mnemonic for link (alluding to
the RET key commonly used in web browser to follow a link).
\(Unfortunately this breaks the rules in info node `Key Binding
Conventions'.) Below are the key bindings defined by this mode:

\\{mlinks-mode-map}

For some major modes `mlinks-backward-link' and
`mlinks-forward-link' will take you to the previous/next link.
By default the link moved to will be active, see
`mlinks-active-links'.

"
  nil
  " L"
  nil
  :keymap mlinks-mode-map
  :group 'mlinks
  (if mlinks-mode
      (progn
        (mlinks-add-appmenu)
        (mlinks-start-point-hilighter)
        (mlinks-add-font-lock))
    (mlinks-stop-point-hilighter)
    (when mlinks-point-hilighter-overlay
      (when (overlayp mlinks-point-hilighter-overlay)
        (delete-overlay mlinks-point-hilighter-overlay))
      (setq mlinks-point-hilighter-overlay nil))
    (mlinks-remove-font-lock)))
(put 'mlinks-mode 'permanent-local t)

(defun mlinks-turn-on-in-buffer ()
  (let ((hion (unless (and (boundp 'mumamo-set-major-running)
                           mumamo-set-major-running)
                (mlinks-get-mode-value 'hion))))
    (when hion (mlinks-mode 1))))

;;;###autoload
(define-globalized-minor-mode mlinks-global-mode mlinks-mode
  mlinks-turn-on-in-buffer
  "Turn on `mlink-mode' in all buffer where it is specified.
This is specified in `mlinks-mode-functions'."
  :group 'mlinks)

;; The problem with global minor modes:
(when (and mlinks-global-mode
           (not (boundp 'define-global-minor-mode-bug)))
  (mlinks-global-mode 1))

;;(define-toggle mlinks-active-links t
(define-minor-mode mlinks-active-links
  "Use quick movement keys on active links if non-nil.
When moving to an mlink with `mlinks-forward-link' or
`mlinks-backward-link' the link moved to will be in an active
state.  This is marked with a new color \(the face `isearch').
When the new color is shown the following keys are active

\\{mlinks-hyperactive-point-hilighter-keymap}
Any command cancels this state."
  :global t
  :init-value t
  :group 'mlinks)



(defun mlinks-link-text-prop-range (pos)
  (let* ((link-here (get-text-property pos 'mlinks-link))
         (beg (when link-here (previous-single-char-property-change (+ pos 1) 'mlinks-link)))
         (end (when link-here (next-single-char-property-change (- pos 0) 'mlinks-link))))
    (when (and beg end)
      (cons beg end))))

(defun mlinks-link-range (pos)
  (or (mlinks-link-text-prop-range pos)
      (let ((funs-- (mlinks-get-mode-value 'hili)))
        (when funs--
          (save-match-data
            (run-hook-with-args-until-success 'funs--))))))

(defun mlinks-link-at-point ()
  "Get link at point."
  (mlinks-point-hilighter-1)
  (when (and mlinks-point-hilighter-overlay
             (overlay-buffer mlinks-point-hilighter-overlay))
    (let* ((ovl mlinks-point-hilighter-overlay)
           (beg (overlay-start ovl))
           (end (overlay-end ovl)))
      (buffer-substring-no-properties beg end))))


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; At point highligher

(defvar mlinks-point-hilighter-timer nil)

(defun mlinks-stop-point-hilighter ()
  (when (timerp mlinks-point-hilighter-timer)
    (cancel-timer mlinks-point-hilighter-timer)
    (setq mlinks-point-hilighter-timer nil)))

(defun mlinks-start-point-hilighter ()
  (mlinks-stop-point-hilighter)
  (setq mlinks-point-hilighter-timer
        (run-with-idle-timer 0.1 t 'mlinks-point-hilighter)))

(defvar mlinks-link-overlay-priority 100)

(defun mlinks-make-point-hilighter-overlay (bounds)
  (unless mlinks-point-hilighter-overlay
    (setq mlinks-point-hilighter-overlay
          (make-overlay (car bounds) (cdr bounds)))
    (overlay-put mlinks-point-hilighter-overlay 'priority mlinks-link-overlay-priority)
    (overlay-put mlinks-point-hilighter-overlay 'mouse-face 'highlight)
    (mlinks-set-normal-point-hilight)
    ))

(defun mlinks-point-hilighter ()
  "Mark link at point if any.
This moves the hilight point overlay to point or deletes it."
  ;; This runs in a timer, protect it.
  (condition-case err
      (let ((inhibit-point-motion-hooks t))
        (mlinks-point-hilighter-1))
    (error "mlinks-point-hilighter error: %s" (error-message-string err))))

(defun mlinks-point-hilighter-1 ()
  (when mlinks-mode
    (let ((bounds-- (mlinks-link-range (point))))
      (if bounds--
          (if mlinks-point-hilighter-overlay
              (move-overlay mlinks-point-hilighter-overlay (car bounds--) (cdr bounds--))
            (mlinks-make-point-hilighter-overlay bounds--))
        (when mlinks-point-hilighter-overlay
          (delete-overlay mlinks-point-hilighter-overlay))))))

(defvar mlinks-hyperactive-point-hilighter-keymap
  (let ((m (make-sparse-keymap "mlinks")))
    (define-key m [S-tab]   'mlinks-backward-link)
    (define-key m [tab]     'mlinks-forward-link)
    (define-key m "\t"      'mlinks-forward-link)
    (define-key m [?\r]     'mlinks-goto)
    (define-key m [?w]      'mlinks-goto-other-window)
    (define-key m [?f]      'mlinks-goto-other-frame)
    (define-key m [mouse-1] 'mlinks-goto)
    (set-keymap-parent m mlinks-mode-map)
    m))

(defvar mlinks-point-hilighter-keymap
  (let ((m (make-sparse-keymap "mlinks")))
    (define-key m [mouse-1] 'mlinks-goto)
    (set-keymap-parent m mlinks-mode-map)
    m))

(defun mlinks-point-hilighter-pre-command ()
  (condition-case err
      (unless (let ((map (overlay-get mlinks-point-hilighter-overlay 'keymap)))
                (where-is-internal this-command
                                   (list
                                    map)))
        (mlinks-set-normal-point-hilight)
        (unless mlinks-point-hilighter-timer
          (delete-overlay mlinks-point-hilighter-overlay)))
    (error (message "mlinks-point-hilighter-pre-command: %s" err))))
(put 'mlinks-point-hilighter-pre-command 'permanent-local t)

(defun mlinks-set-hyperactive-point-hilight ()
  "Make link hyper active, ie add some special key binding.
Used after jumping specifically to a link. The idea is that the
user may want to easily jump between links in this state."
  (add-hook 'pre-command-hook 'mlinks-point-hilighter-pre-command nil t)
  (mlinks-point-hilighter)
  (overlay-put mlinks-point-hilighter-overlay 'face mlinks-hyperactive-link-face)
  (overlay-put mlinks-point-hilighter-overlay 'keymap mlinks-hyperactive-point-hilighter-keymap))

(defun mlinks-set-normal-point-hilight ()
  "Make link normally active as if you happened to be on it."
  (remove-hook 'pre-command-hook 'mlinks-point-hilighter-pre-command t)
  (mlinks-point-hilighter)
  (overlay-put mlinks-point-hilighter-overlay 'face mlinks-link-face)
  (overlay-put mlinks-point-hilighter-overlay 'keymap mlinks-point-hilighter-keymap))

(defun mlinks-set-point-hilight-after-jump-to ()
  "Set hilight style after jump to link."
  (if mlinks-active-links
      (mlinks-set-hyperactive-point-hilight)
    (mlinks-set-normal-point-hilight)))



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Jumping around

(defvar mlinks-places nil)
(make-variable-buffer-local 'mlinks-placesn)
(put 'mlinks-places 'permanent-local t)

(defvar mlinks-places-n 0)
(make-variable-buffer-local 'mlinks-places-n)
(put 'mlinks-places-n 'permanent-local t)

(defun mlinks-has-links ()
  (or (mlinks-get-mode-value 'fontify)
      (when (and (boundp 'mumamo-multi-major-mode) mumamo-multi-major-mode)
        ;; Fix-me: just assume multi major has it... Need a list of
        ;; major modes. There is no way to get such a list for the
        ;; multi major mode (since you can't know what the chunk
        ;; functions will return.  However you can get a list of
        ;; current chunks major mode.
        t
        )))

(defun mlinks-backward-link ()
  "Go to previous `mlinks-mode' link in buffer."
  (interactive)
  (if (not (mlinks-has-links))
      (message "There is no way to go to previous link for this major mode")
    (let ((res (mlinks-prev-link)))
      (if res
          (progn
            (goto-char res)
            (mlinks-set-point-hilight-after-jump-to))
        (message "No previous link found")))))

(defun mlinks-forward-link ()
  "Go to next `mlinks-mode' link in buffer."
  (interactive)
  (if (not (mlinks-has-links))
      (message "There is no way to go to next link for this major mode")
    (let ((res (mlinks-next-link)))
      (if res
          (progn
            (goto-char res)
            (mlinks-set-point-hilight-after-jump-to))
        (message "No next link found")))))


(defun mlinks-goto ()
  "Follow `mlinks-mode' link at current point.
Save the current position so that they can be move to again by
`mlinks-prev-saved-position' and `mlinks-next-saved-position'.

Return non-nil if link was followed, otherewise nil."
  (interactive)
  (mlinks-goto-1 nil))

(defun mlinks-goto-other-window ()
  "Like `mlinks-goto' but opens in other window.
Uses `switch-to-buffer-other-window'."
  (interactive)
  (mlinks-goto-1 'other-window))

(defun mlinks-goto-other-frame ()
  "Like `mlinks-goto' but opens in other frame.
Uses `switch-to-buffer-other-frame'."
  (interactive)
  (mlinks-goto-1 'other-frame))

(defun mlinks-goto-1(where)
  (push-mark)
  (let* ((funs (mlinks-get-mode-value 'goto))
         (old (point-marker))
         (mlinks-temp-buffer-where where)
         (res (run-hook-with-args-until-success 'funs)))
    (if (not res)
        (progn
          (message "Don't know how to follow this MLink link")
          nil)
      (unless (= old (point-marker))
        (let* ((prev (car mlinks-places)))
          (when (or (not prev)
                    ;;(not (markerp prev))
                    (not (marker-buffer prev))
                    (/= old prev))
            (setq mlinks-places (cons old mlinks-places))
            (setq mlinks-places-n (length mlinks-places))))))))


(defun mlinks-prev-saved-position ()
  "Go to previous position saved by `mlinks-goto'."
  (interactive)
  (unless (mlinks-goto-n (1- mlinks-places-n))
    (message "No previous MLink position")))

(defun mlinks-next-saved-position ()
  "Go to next position saved by `mlinks-goto'."
  (interactive)
  (unless (mlinks-goto-n (1+ mlinks-places-n))
    (message "No next MLink position")))

(defun mlinks-goto-n (to)
  (if (not mlinks-places)
      (message "No saved MLinks positions")
    (let ((minp 1)
          (maxp (length mlinks-places)))
      (if (<= to minp)
          (progn
            (setq to minp)
            (message "Going to first MLinks position"))
        (if (>= to maxp)
            (progn
              (setq to maxp)
              (message "Going to last MLinks position"))))
      (setq mlinks-places-n to)
      (let ((n (- maxp to))
            (places mlinks-places)
            place
            buffer
            point)
        (while (> n 0)
          (setq places (cdr places))
          (setq n (1- n)))
        (setq place (car places))
        (mlinks-switch-to-buffer (marker-buffer place))
        (goto-char place)))))

(defvar mlinks-temp-buffer-where nil)
(defun mlinks-switch-to-buffer (buffer)
  (mlinks-switch-to-buffer-1 buffer mlinks-temp-buffer-where))

(defun mlinks-switch-to-buffer-1(buffer where)
  (cond
   ((null where)
    (switch-to-buffer buffer))
   ((eq where 'other-window)
    (switch-to-buffer-other-window buffer))
   ((eq where 'other-frame)
    (switch-to-buffer-other-frame buffer))
   (t
    (error "Invalid argument, where=%s" where))))

;; FIXME: face, var
(defun mlinks-custom (var)
  (customize-option var)
  )



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; AppMenu support

(defun mlinks-appmenu ()
  (when mlinks-mode
    ;; Fix-me: reverse the list
    (let ((link-val (mlinks-link-at-point))
          (map (make-sparse-keymap "mlinks"))
          (num 2))
      (when (mlinks-get-mode-value 'prev)
        (define-key map [mlinks-next-link]
          (list 'menu-item "Next Link" 'mlinks-forward-link)))
      (when (mlinks-get-mode-value 'next)
        (define-key map [mlinks-prev-link]
          (list 'menu-item "Previous Link" 'mlinks-backward-link)))
      (when link-val
        (let* ((possible (when (member major-mode '(html-mode nxhtml-mode nxml-mode sqml-mode text-mode))
                           (mlinks-html-possible-href-actions link-val)))
               (mailto (assoc 'mailto possible))
               (view-web (assoc 'view-web possible))
               (view-web-base (assoc 'view-web-base possible))
               (edit (assoc 'edit possible))
               (file (nth 1 edit))
               (anchor (nth 2 edit))
               (choices)
               (answer)
               )
          (when (> (length map) num)
            (define-key map [mlinks-href-sep] (list 'menu-item "--")))
          (setq num (length map))
          (when view-web
            (define-key map [mlinks-href-view-web]
              (list 'menu-item "Browse Link Web Url"
                    `(lambda () (interactive)
                       (browse-url ,link-val)))))
          (when view-web-base
            (define-key map [mlinks-href-view-web-based]
              (list 'menu-item "Browse Link Web Url (base URL found)"
                    `(lambda () (interactive)
                       (browse-url (cdr ,view-web-base))))))
          (when mailto
            (define-key map [mlinks-href-mail]
              (list 'menu-item (concat "&Mail to " (substring link-val 7))
                    `(lambda () (interactive)
                       (mlinks-html-mail-to ,link-val)))))
          (when edit
            (when (and (file-exists-p file)
                       (not anchor)
                       (assoc 'upload possible))
              (let ((abs-file (expand-file-name file)))
                (define-key map [mlinks-href-upload]
                  (list 'menu-item "Upload Linked File"
                        `(lambda () (interactive)
                           (html-upl-upload-file ,abs-file))))))
            (when (and (file-exists-p file)
                       (not anchor)
                       (assoc 'edit-gimp possible))
              (let ((abs-file (expand-file-name file)))
                (define-key map [mlinks-href-edit-gimp]
                  (list 'menu-item "Edit Linked File with GIMP"
                        `(lambda () (interactive)
                           (gimpedit-edit-file ,abs-file))))))
            (when (and (file-exists-p file)
                       (assoc 'view-local possible))
              (let ((url (concat "file:///" (expand-file-name file))))
                (when anchor
                  (let ((url-anchor (concat url "#" anchor)))
                    (define-key map [mlinks-href-view-file-at]
                      (list 'menu-item (concat "Browse Linked File URL at #" anchor)
                            `(lambda () (interactive)
                               (browse-url ,url-anchor))))))
                (define-key map [mlinks-href-view-file]
                  (list 'menu-item "&Browse Linked File URL"
                        `(lambda () (interactive)
                           (browse-url ,url))))))
            (when (> (length map) num)
              (define-key map [mlinks-href-sep-2] (list 'menu-item "--")))
            (setq num (length map))
            (unless (equal file (buffer-file-name))
              (define-key map [mlinks-href-edit]
                (list 'menu-item "&Open Linked File"
                      `(lambda () (interactive) (mlinks-goto))))
              (define-key map [mlinks-href-edit-window]
                (list 'menu-item "&Open Linked File in Other Window"
                      `(lambda () (interactive) (mlinks-goto-other-window))))
              (define-key map [mlinks-href-edit-frame]
                (list 'menu-item "&Open Linked File in New Frame"
                      `(lambda () (interactive) (mlinks-goto-other-frame))))
              )
            (when (and (file-exists-p file) anchor)
              (define-key map [mlinks-href-edit-at]
                (list 'menu-item (concat "Open Linked File &at #" anchor)
                      `(lambda () (interactive)
                         (mlinks-goto)))))
            )
          (when (> (length map) num)
            (define-key map [mlinks-href-sep-1] (list 'menu-item "--")))
          (setq num (length map))
          (when link-val
            (define-key map [mlinks-href-copy-link]
              (list 'menu-item "&Copy Link Text"
                    'mlinks-copy-link-text)))))
      (when (> (length map) 2)
        map))))

(defun mlinks-add-appmenu ()
  "Add entries for MLinks to AppMenu."
  (when (featurep 'appmenu)
    (appmenu-add 'mlinks 100 'mlinks-mode "Current MLink" 'mlinks-appmenu)))

(defun mlinks-copy-link-text ()
  "Copy text of `mlinks-mode' link at point to clipboard."
  (interactive)
  (mlinks-point-hilighter)
  (let ((ovl mlinks-point-hilighter-overlay))
    (if (and ovl
             (overlayp ovl)
             (overlay-buffer ovl)
             (eq (current-buffer)
                 (overlay-buffer ovl))
             (<= (overlay-start ovl)
                 (point))
             (>= (overlay-end ovl)
                 (point)))
        (let* ((beg (overlay-start ovl))
               (end (overlay-end ovl))
               (str (buffer-substring beg end)))
          (copy-region-as-kill beg end)
          (message "Copied %d chars to clipboard" (length str)))
      (message "No link here to copy"))))





;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;; text-mode etc ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defvar mlinks-plain-urls-regexp
  (rx-to-string `(or (submatch (optional "mailto:")
                               (regexp ,(concat
                                         ;;"[a-z0-9$%(*-=?[_][^<>\")!;:,{}]*"
                                         "[a-z0-9$%(*=?[_-][^<>\")!;:,{}]*"
                                         "\@"
                                         "\\(?:[a-z0-9\-]+\.\\)+[a-z0-9]\\{2,4\\}")))
                     (submatch (or (regexp "https?://")
                                   "www.")
                               (1+ (any ,url-get-url-filename-chars))
                               )
                     )))

(defun mlinks-plain-urls-fontify (bound)
  (mlinks-fontify bound mlinks-plain-urls-regexp 0))

(defun mlinks-goto-plain-url ()
  (let* ((range (mlinks-link-range (point)))
         (link  (when range (buffer-substring-no-properties (car range) (cdr range)))))
    ;;(mlinks-html-href-act-on link)
    (when (= 0 (string-match mlinks-plain-urls-regexp link))
      (let ((which (if (match-end 1) 1 2)))
        (cond
         ((= 1 which)
          (mlinks-html-mail-to link)
          t)
         ((= 2 which)
          (browse-url link)
          t)
         (t nil))))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;; nxhtml-mode ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defun mlinks-html-style-goto ()
  (mlinks-html-style-mode-fun t))

(defvar mlinks-html-link-regexp
  ;; This value takes care of nxhtml-strval-mode (and is therefore a little bit incorrect ...)
  ;;"\\(?:^\\|[[:space:]]\\)\\(?:href\\|src\\)[[:space:]]*=[[:space:]]*\"\\([^<«\"]*\\)\""
  (rx (or "^" space)
      (or "href" "src")
      (0+ space)
      "="
      (0+ space)
      (submatch
       (or
        (seq "\""
             (and
              (0+ (not (any "\""))))
             "\"")
        (seq "'"
             (and
              (0+ (not (any "\'"))))
             "'")))))

(defun mlinks-html-style-mode-fun (goto)
  (let (start
        end
        bounds)
    (save-excursion
      (forward-char)
      (when (< 0 (skip-chars-forward "^\"'" (line-end-position)))
        (forward-char)
        (save-match-data
          (when (looking-back
                 mlinks-html-link-regexp
                 (line-beginning-position -1))
            (let ((which (if (match-beginning 1) 1 2)))
              (setq start (1+ (match-beginning which)))
              (setq end   (1- (match-end which))))
            (setq bounds (cons start end))))))
    (when start
      (if (not goto)
          bounds
        (let ((href-val (buffer-substring-no-properties start end)))
          (mlinks-html-href-act-on href-val))
        t))))

(defun mlink-check-file-to-edit (file)
  (assert (file-name-absolute-p file))
  (let ((file-dir (file-name-directory file)))
    (unless (file-directory-p file-dir)
      (if (file-directory-p (file-name-directory file))
          (if (yes-or-no-p (format "Directory %s does not exist. Create it? " file-dir))
              (make-directory file-dir)
            (setq file nil))
        (if (yes-or-no-p (format "Directory %s and its parent does not exist. Create them? " file-dir))
            (make-directory file-dir t)
          (setq file nil))))
    file))

(defun mlinks-html-edit-at (file &optional anchor)
  (let ((abs-file (if (file-name-absolute-p file)
                      file
                    (expand-file-name file))))
    (if (or (file-directory-p abs-file)
            (string= abs-file
                     (file-name-as-directory abs-file)))
        (if (file-directory-p abs-file)
            (when (y-or-n-p (format "Do you want to edit the directory %s? : " abs-file))
              (dired abs-file))
          (message "Can't find directory %s" abs-file))
      (when (mlink-check-file-to-edit abs-file)
        (let ((b (find-file-noselect abs-file)))
          (mlinks-switch-to-buffer b))
        (when anchor
          (let ((here (point))
                (anchor-regexp (concat "\\(?:id\\|name\\)[[:space:]]*=[[:space:]]*\"" anchor "\"")))
            (goto-char (point-min))
            (if (search-forward-regexp anchor-regexp nil t)
                (backward-char 2)
              (message "Anchor \"%s\" not found" anchor)
              (goto-char here))))))))

(defun mlinks-html-mail-to (addr)
  (browse-url addr))

(defun mlinks-html-href-act-on (href-val)
  (if href-val
      (let* ((possible (mlinks-html-possible-href-actions href-val))
             (edit (assoc 'edit possible))
             (file (nth 1 edit))
             (anchor (nth 2 edit))
             )
        (cond (edit
               (mlinks-html-edit-at file anchor)
               t)
              ((assoc 'mailto possible)
               (when (y-or-n-p "This is a mail address.  Do you want to send a message to this mail address? ")
                 (mlinks-html-mail-to href-val)))
              ((assoc 'view-web possible)
               (when (y-or-n-p "Can't edit this URL, it is on the web.  View the URL in your web browser? ")
                 (browse-url href-val)))
              ((assoc 'view-web-base possible)
               (when (y-or-n-p "Can't edit, based URL is to the web.  View resulting URL in your web browser? ")
                 (browse-url (cdr (assoc 'view-web-base possible)))))
              (t
               (message "Do not know how to handle this URL"))
              ))
    (message "No value for href attribute")))

(defun mlinks-html-possible-href-actions (link)
  (let ((urlobj (url-generic-parse-url link))
        (edit nil)
        (possible nil))
    (cond ((member (url-type urlobj) '("http" "https"))
           (add-to-list 'possible (cons 'view-web link)))
          ((member (url-type urlobj) '("mailto"))
           (add-to-list 'possible (cons 'mailto link)))
          ((url-host urlobj)
           (message "Do not know how to handle this URL"))
          (t (setq edit t)))
    (when edit
      (let ((base-href (mlinks-html-find-base-href)))
        (when base-href
          (let ((baseobj (url-generic-parse-url base-href)))
            (setq edit nil)
            (cond ((member (url-type baseobj) '("http" "https"))
                   (add-to-list 'possible (cons 'view-web-base (url-expand-file-name link base-href))))
                  ((url-host urlobj)
                   (message "Do not know how to handle this URL"))
                  (t (setq edit t)))))
        (when edit
          (let* ((full (split-string (url-filename urlobj) "#"))
                 (file (nth 0 full))
                 (anchor (nth 1 full))
                 )
            (when (equal file "")
              (setq file (buffer-file-name)))
            (when base-href
              ;; We know at this point it is not a http url
              (setq file (expand-file-name file base-href)))
            (let ((ext (downcase (file-name-extension file))))
              (when (member ext '("htm" "html"))
                (add-to-list 'possible (cons 'view-local (list file anchor))))
              (when (and (featurep 'gimpedit)
                         (member ext '("gif" "png" "jpg" "jpeg")))
                (add-to-list 'possible (cons 'edit-gimp (list file anchor)))))
            (when (featurep 'html-upl)
              (add-to-list 'possible (cons 'upload (list file anchor))))
            (add-to-list 'possible (cons 'edit (list file anchor)))))))
    possible))

(defun mlinks-html-find-base-href ()
  "Return base href found in the current file."
  (let ((base-href))
    (save-excursion
      (goto-char (point-min))
      (while (and (not base-href)
                  (search-forward-regexp "<!--[^!]*-->\\|<base[[:space:]]" nil t))
        (when (equal " " (char-to-string (char-before)))
          (backward-char 6)
          (when (looking-at "<base [^>]*href *= *\"\\(.*?\\)\"")
            (setq base-href (match-string-no-properties 1))))))
    base-href))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;; Custom-mode ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun mlinks-elisp-custom-goto ()
  (mlinks-elisp-mode-fun 'custom))

(defvar mlinks-custom-link-regexp
  (rx "`"
      (group
       (1+ (not (any "'"))))
      "'"))

(defun mlinks-custom-fontify (bound)
  (mlinks-fontify bound mlinks-custom-link-regexp 0))


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;; emacs-lisp-mode ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun mlinks-elisp-goto ()
  (mlinks-elisp-mode-fun 'source))

(defun mlinks-elisp-hili ()
  (mlinks-elisp-mode-fun nil))

(defun mlinks-elisp-mode-fun (goto)
  (let ((symbol-name (thing-at-point 'symbol)))
    (when symbol-name
      (let ((bounds-- (bounds-of-thing-at-point 'symbol))
            ret--)
        (if (save-excursion
              (goto-char (cdr bounds--))
              (looking-back (concat "(\\(?:require\\|featurep\\)\s+'" symbol-name)
                            (line-beginning-position)))
            (progn
              (setq ret-- bounds--)
              (when goto
                (mlinks-elisp-mode-require symbol-name)))
          (when (mlinks-elisp-mode-symbol symbol-name goto)
            (setq ret-- bounds--)))
        ret--))))

(defun mlinks-elisp-function (symbol)
  "Go to an elisp function."
  (interactive "aElisp function: ")
  (mlinks-elisp-mode-symbol (symbol-name symbol) 'source))

(defun mlinks-elisp-mode-symbol (symbol-name-- goto--)
  ;; Fix-me: use uninterned variables (see mail from Miles)
  ;; Make these names a bit strange because they are boundp at the time of checking:
  (let ((symbol-- (intern-soft symbol-name--))
        defs--)
    (when (and symbol-- (boundp symbol--))
      (add-to-list 'defs-- 'variable))
    (when (fboundp symbol--)
      (add-to-list 'defs-- 'function))
    (when (facep symbol--)
      (add-to-list 'defs-- 'face))
    ;; Avoid some fails hits
    (when (memq symbol--
                '(goto t
                       bounds-- funs-- ret--
                       symbol-- defs-- symbol-name-- goto--))
      (setq defs-- nil))
    (let (defs-places
           def)
      (if (not goto--)
          (progn
            defs--)
        (if (not defs--)
            (progn
              (message "Could not find definition of '%s" symbol-name--)
              nil)
          (dolist (type (cond
                         ((eq goto-- 'source)
                          '(nil defvar defface))
                         ((eq goto-- 'custom)
                          '(defvar defface))
                         (t
                          (error "Bad goto-- value: %s" goto--))))
            (condition-case err
                (add-to-list 'defs-places
                             (cons
                              type
                              (save-excursion
                                (let* ((bp (find-definition-noselect symbol-- type))
                                       (b (car bp))
                                       (p (cdr bp)))
                                  (unless p
                                    (with-current-buffer b
                                      (save-restriction
                                        (widen)
                                        (setq bp (find-definition-noselect symbol-- type)))))
                                  bp))))
              (error
               ;;(lwarn '(mlinks) :error "%s" (error-message-string err))
               (when t
                 (cond
                  ((eq (car err) 'search-failed))
                  ((and (eq (car err) 'error)
                        (string= (error-message-string err)
                                 (format "Don't know where `%s' is defined" symbol--))))
                  (t
                   (message "%s: %s" (car err) (error-message-string err))))))))
          (if (= 1 (length defs-places))
              (setq def (car defs-places))
            (let ((many nil)
                  lnk)
              (dolist (d defs-places)
                (if (not lnk)
                    (setq lnk (cdr d))
                  (unless (equal lnk (cdr d))
                    (setq many t))))
              (if (not many)
                  (setq def (car defs-places))
                (let* ((alts (mapcar (lambda (elt)
                                       (let ((type (car elt))
                                             str)
                                         (setq str
                                               (cond
                                                ((not type)
                                                 "Function")
                                                ((eq type 'defvar)
                                                 "Variable")
                                                ((eq type 'defface)
                                                 "Face")))
                                         (cons str elt)))
                                     defs-places))
                       (stralts (mapcar (lambda (elt)
                                          (car elt))
                                        alts))
                       (completion-ignore-case t)
                       (stralt (completing-read "Type: " stralts nil t))
                       (alt (assoc stralt alts)))
                  (setq def (cdr alt))))))
          (when def
            (cond
             ((eq goto-- 'source)
              ;; Be sure to go to the real sources from CVS:
              (let* ((buf (car (cdr def)))
                     ;; Avoid going to source
                     ;;(file (find-source-lisp-file (with-current-buffer buf buffer-file-name)) )
                     (file (with-current-buffer buf buffer-file-name))
                     (orig-buf (find-file-noselect file)))
                (mlinks-switch-to-buffer orig-buf)
                (let ((p (cdr (cdr def))))
                  ;; Fix-me: Move this test to a more general place.
                  (if (or (< p (point-min))
                          (> p (point-max)))
                      ;; Check for cloned indirect buffers.
                      (progn
                        (setq orig-buf
                              (catch 'view-in-buf
                                (dolist (indirect-buf (buffer-list))
                                  ;;(message "base-buffer=%s, orig-buf=%s, eq => %s" (buffer-base-buffer indirect-buf) orig-buf (eq (buffer-base-buffer indirect-buf) orig-buf))
                                  (when (eq (buffer-base-buffer indirect-buf) orig-buf)
                                    (with-current-buffer indirect-buf
                                      ;;(message "indirect-buf=%s" indirect-buf)
                                      (unless (or (< p (point-min))
                                                  (> p (point-max)))
                                        ;;(message "switching")
                                        ;;(mlinks-switch-to-buffer indirect-buf)
                                        (message "mlinks: Switching to indirect buffer because of narrowing")
                                        (throw 'view-in-buf indirect-buf)
                                        ))
                                    ))))
                        (when orig-buf
                          (mlinks-switch-to-buffer orig-buf))
                        ;;(message "cb=%s" (current-buffer))
                        (if (or (< p (point-min))
                                (> p (point-max)))
                            (when (y-or-n-p (format "%s is invisible because of narrowing. Widen? " symbol--))
                              (widen)
                              (goto-char p))
                          (goto-char p)))
                    (goto-char p)))))
             ((eq goto-- 'custom)
              (mlinks-custom symbol--))
             (t
              (error "Back goto-- value again: %s" goto--)))))))))

(defun mlinks-elisp-mode-require (module)
  (let ((where mlinks-temp-buffer-where))
    (cond
     ((null where)
      (find-library module))
     ((eq where 'other-window)
      (other-window 1)
      (find-library module))
     ((eq where 'other-frame)
      (make-frame-command)
      (find-library module))
     (t
      (error "Invalid argument, where=%s" where)))))



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;; Helpers when adopting for modes ;;;;;;;;;;;;;;;;;

;;; Save this, do not delete this comment:

;; (defun mlinks-hit-test ()
;;   "Just a helper function for adding support for new modes."
;;   (let* (
;;          (s0 (if (match-string 0) (match-string 0) ""))
;;          (s1 (if (match-string 1) (match-string 1) ""))
;;          (s2 (if (match-string 2) (match-string 2) ""))
;;          (s3 (if (match-string 3) (match-string 3) ""))
;;          )
;;     (message "match0=%s, match1=%s, match2=%s, match3=%s" s0 s1 s2 s3)))

;; (defun mlinks-handle-reg-fun-list (reg-fun-list)
;;   "Just a helper function."
;;   (let (done
;;         regexp
;;         hitfun
;;         m
;;         p
;;         b
;;         )
;;     (dolist (rh reg-fun-list)
;;       (message "rh=%s" rh);(sit-for 2)
;;       (unless done
;;         (setq regexp (car rh))
;;         (setq hitfun (cadr rh))
;;         (message "regexp=%s, hitfun=%s" regexp hitfun);(sit-for 1)
;;         (when (and (save-match-data
;;                      (setq m (re-search-backward regexp (line-beginning-position) t))
;;                      (> p (match-beginning 0))))
;;           (setq done t)
;;           (setq b (match-beginning 0))
;;           (setq e (match-end 0))
;;           )
;;         (if (not (and b e
;;                       (< b p)
;;                       (< p e)))
;;             (message "MLinks Mode did not find any link here")
;;           (goto-char b)
;;           (if (not (looking-at regexp))
;;               (error "Internal error, regexp %s, no match looking-at" regexp)
;;             (let ((last (car mlinks-places))
;;                   (m (make-marker)))
;;               (set-marker m (line-beginning-position))
;;               (when (or (not last)
;;                         (/= m last))
;;                 (setq mlinks-places (cons m mlinks-places))))
;;             (funcall hitfun))
;;           )))))



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Font Lock use

(defvar mlinks-link-update-pos-max nil)
(make-variable-buffer-local 'mlinks-link-update-pos-max)
(put 'mlinks-link-update-pos-max 'permanent-local t)

(defun mlinks-remove-font-lock ()
  "Remove info from font-lock."
  (when (mlinks-want-font-locking)
    (mlink-font-lock nil)))

(defun mlinks-add-font-lock ()
  "Add info to font-lock."
  (when (mlinks-want-font-locking)
    (mlink-font-lock t)))

(defun mlinks-want-font-locking ()
  (or (mlinks-get-mode-value 'fontify)
      (mlinks-get-mode-value 'next-mark)))



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Font Lock integration

(defun mlink-font-lock (on)
  (let* ((add-or-remove (if on 'font-lock-add-keywords 'font-lock-remove-keywords))
         (fontify-fun (car (mlinks-get-mode-value 'fontify)))
         (args (list nil `(( ,fontify-fun ( 0 mlinks-font-lock-face t ))))))
    (when fontify-fun
      ;; Note: Had a lot of trouble with this which I modelled first
      ;; after dlink. Using hi-lock as a model made it work with
      ;; mumamo too.
      ;;
      ;; Next arg, HOW, is needed to get it to work with mumamo. This
      ;; adds it last, like hi-lock.
      (when on (setq args (append args (list t))))
      (apply add-or-remove args)
      (font-lock-mode -1)
      (font-lock-mode 1))))

(defun mlinks-html-fontify (bound)
  (mlinks-fontify bound mlinks-html-link-regexp 1))

(defun mlinks-fontify (bound regexp border)
  (let ((start (point))
        end-start
        stop next-stop
        (more t)
        old-beg old-end
        (wn 1)
        ret)
    ;; Note: we shouldnot use save-match-data here. Instead
    ;; set-match-data is called below!
    (if (not (re-search-forward regexp bound t))
        (setq end-start bound)
      (setq ret t)
      (setq end-start (- (point) 2))
      (let* ((which (if (match-beginning 1) 1 2))
             (beg (+ (match-beginning which) border))
             (end (- (match-end which) border)))
        (put-text-property beg end 'mlinks-link t)
        (set-match-data (list (copy-marker end) (copy-marker beg)))))
    (setq stop start)
    (setq next-stop -1)
    (while (and (> 100 (setq wn (1+ wn)))
                (setq next-stop (next-single-char-property-change stop 'mlinks-link nil end-start))
                (/= next-stop stop))
      (setq stop next-stop)
      (if (get-text-property stop 'mlinks-link)
          (setq old-beg stop)
        (when old-beg
          (remove-list-of-text-properties old-beg stop '(mlinks-link 'mouse-face)))))
    ret))

(defun mlinks-next-link ()
  "Find next link, fontify as necessary."
  (let* ((here (point))
         (prev-pos (point))
         (fontified-here (get-text-property (max (point-min) (1- prev-pos)) 'fontified))
         (fontified-to (next-single-char-property-change prev-pos 'fontified))
         (pos (next-single-char-property-change prev-pos 'mlinks-link nil
                                                (or fontified-to (point-max))))
         (fontified-all (and fontified-here (not fontified-to)))
         ready
         next-fontified-to)
    (while (not (or ready
                    (and fontified-all
                         (not pos))))
      (if pos
          (progn
            (unless (get-text-property pos 'mlinks-link)
              ;; Get to next link
              (setq prev-pos pos)
              (setq pos (next-single-char-property-change prev-pos 'mlinks-link nil
                                                          (or fontified-to (point-max)))))
            (when pos
              (setq ready (get-text-property pos 'mlinks-link))
              (setq prev-pos pos)
              (unless ready (setq pos nil))))
        (unless (or fontified-all fontified-to)
          (if (get-text-property prev-pos 'fontified)
              (setq fontified-all
                    (not (setq fontified-to
                               (next-single-char-property-change prev-pos 'fontified))))
            (setq fontified-to ( or (previous-single-char-property-change prev-pos 'fontified)
                                    1))))
        (setq next-fontified-to (min (+ fontified-to 5000)
                                     (point-max)))
        (mumamo-with-buffer-prepared-for-jit-lock
         (progn
           (put-text-property fontified-to next-fontified-to 'fontified t)
           (font-lock-fontify-region fontified-to next-fontified-to)))
        (setq fontified-to (next-single-char-property-change (1- next-fontified-to)
                                                             'fontified))
        (setq fontified-all (not fontified-to))
        (setq pos (next-single-char-property-change prev-pos 'mlinks-link nil
                                                    (or fontified-to (point-max))))))
    (when ready prev-pos)))

(defun mlinks-prev-link ()
  "Find previous link, fontify as necessary."
  (let* ((prev-pos (point))
         (fontified-from (previous-single-char-property-change prev-pos 'fontified))
         (fontified-here (get-text-property (max (point-min) (1- prev-pos)) 'fontified))
         (fontified-all (and fontified-here (not fontified-from)))
         (pos (when fontified-here
                (previous-single-char-property-change prev-pos 'mlinks-link nil
                                                      (or fontified-from 1))))
         ready
         next-fontified-from)
    (while (not (or ready
                    (and fontified-all
                         (not pos))))
      (assert (numberp prev-pos) t)
      (if pos
          (progn
            (when (and (> (1- pos) (point-min))
                       (get-text-property (1- pos) 'mlinks-link))
              ;; Get out of current link
              (setq prev-pos pos)
              (setq pos (previous-single-char-property-change prev-pos 'mlinks-link nil
                                                              (or fontified-from 1))))
            (when pos
              (setq prev-pos pos)
              (setq ready (and (get-text-property pos 'fontified)
                               (or (= 1 pos)
                                   (not (get-text-property (1- pos) 'mlinks-link)))
                               (get-text-property pos 'mlinks-link)))
              (unless ready (setq pos nil))))
        (setq next-fontified-from (max (- fontified-from 5000)
                                       (point-min)))
        (mumamo-with-buffer-prepared-for-jit-lock
         (progn
           (put-text-property next-fontified-from fontified-from 'fontified t)
           (font-lock-fontify-region next-fontified-from fontified-from)))
        (setq fontified-from (previous-single-char-property-change
                              (1+ next-fontified-from) 'fontified))
        (setq fontified-all (not fontified-from))
        (setq pos (previous-single-char-property-change prev-pos 'mlinks-link nil
                                                        (or fontified-from 1)))))
    (when ready pos)))


;;; This is for the problem reported by some Asian users:
;;;
;;;   Lisp error: (invalid-read-syntax "] in a list")
;;;
;; Local Variables:
;; coding: utf-8
;; End:

(provide 'mlinks)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; mlinks.el ends here