clickable minor mode

# 概要
Emacs のテキスト内の URL 文字列をマウスクリックするとブラウザで開くためのものです.多分 Windows Emacs 限定です.

探せばたくさんありそうですし, デフォルトでもある機能ですが (自分は ido-use-url-at-point を使っています) お手軽に連続チェックしたいとき用のものです.

# インストールなど
[このページの下](#clickable-modeel)にある `clickable-mode.el` をファイルにして load-path の通ったところにおいて init.el でレクワイアしておき
```emacs-lisp:init.el
(require 'clickable-mode)
```


使いたいときに起動します(必要ならキーバインドします).
```emacs-lisp:
M-x clickable-mode
```


起動中は url 文字列がリンク・フェイスになりマウスオーバーでハイライトし,左クリックするとブラウザでそのアドレスを開きます.
`C-return` でも開き `tab`, `backtab` で url 文字列を横断できます. 

# 応用
常用するときは, 例えば html-mode ならinit.el に以下のように書きます.

```emacs-lisp:init.el
(add-hook 'html-mode-hook #'clickable-mode)
```


menu に設定しておけば名前やバインドを忘れても安心です.

```emacs-lisp:init.el
(easy-menu-add-item
 global-map '(menu-bar)
 '("Private" ["Clickable mode" clickable-mode
              :style toggle :selected clickable-mode]))
```

# clickable-mode.el
```emacs-lisp:clickable-mode.el
;;; clickable-mode.el --- Clickable minor mode.
;; $Id: clickable-mode.qiita,v 1.1 2024-08-10 09:06:18+09 fubuki Exp fubuki $

;;; Commentary:

;; Make the URL string clickable.

;;; Install:

;; (require 'clickable-mode)
;;
;; (easy-menu-add-item
;;  global-map '(menu-bar)
;;  '("Private" ["Clickable" clickable-mode
;;               :style toggle :selected clickable-mode]))

;;; fix:

;; Ctrl + mouse (C-mouse-1) 等で動かなかったのを修正.

;; (add-hook 'clickable-mode-hook
;;           (lambda ()
;;             (define-key clickable-mode-mouse-map [C-mouse-1] #'clickable-open)
;;             (define-key clickable-mode-mouse-map [C-down-mouse-1] #'clickable-open)))

;;; Code:

(defvar clickable-mode-mouse-map
  (let ((map (make-sparse-keymap)))
    (define-key map [mouse-1]  #'clickable-open)
    (define-key map [C-return] #'clickable-open)
    map))

(defvar clickable-mode-map
  (let ((map (make-sparse-keymap)))
    (define-key map [tab]   #'clickable-next)
    (define-key map [S-tab] #'clickable-previous)
    map))

(defvar-local clickable-ov nil)
(defvar clickable-url "https?://[^\"> \n]+")

(defun clickable-make ()
  (let (ov)
    (save-excursion
      (goto-char (point-min))
      (while (re-search-forward clickable-url nil t)
        (push (make-overlay (match-beginning 0) (match-end 0)) ov)))
    (dolist (o ov)
      (overlay-put o 'category 'clickable)
      (overlay-put o 'face 'link)
      (overlay-put o 'mouse-face 'highlight)
      (overlay-put o 'uri (buffer-substring (overlay-start o) (overlay-end o)))
      (overlay-put o 'keymap clickable-mode-mouse-map))
    (setq clickable-ov ov)))

(defun clickable-clear ()
  (setq clickable-ov nil)
  (remove-overlays (point-min) (point-max) 'category 'clickable))

(defun clickable-open ()
  (interactive)
  (let ((pos (posn-point (event-start last-command-event))))
    (and pos (goto-char pos))
    (dolist (o (overlays-at (point)))
      (if (eq (overlay-get o 'category) 'clickable)
          (w32-shell-execute "open" (overlay-get o 'uri))))))

(defun clickable-next (arg)
  (interactive "p")
  (dotimes (i arg) (clickable--next)))

(defun clickable-previous (arg)
  (interactive "p")
  (dotimes (i arg) (clickable--previous)))

(defun clickable--next ()
  (let ((pos (next-single-char-property-change (point) 'category)))
    (goto-char pos)
    (or (overlays-at pos) (clickable--next))))

(defun clickable--previous ()
  (let ((pos (previous-single-char-property-change (point) 'category)))
    (goto-char pos)
    (or (overlays-at pos) (clickable--previous))))

;;;###autoload
(define-minor-mode clickable-mode
  "Mouse click url open mode."
  :init-value nil
  :lighter " Clickable"
  (if clickable-mode
      (clickable-make)
    (clickable-clear)))

;;;###autoload
(define-derived-mode clickable-text-mode text-mode  "CText"
  "Mouse click url open text mode."
  (clickable-mode 1))

(provide 'clickable-mode)
;;; clickable-mode.el ends here
```

# 更新情報 (Sep  7 2024)
github にその後機能強化した版をアップしました.
表示中に `C-c @ c (clickable-change)` でマッチ対象や生成リンクの変更ができるようになっています.
ルールはカスタム変数 `clickable-url` で指定します.

[clickable-mode](https://https://github.com/s-fubuki/clickable-mode)