| 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 |
(eval-when-compile (require 'cl)) |
|---|
| 57 |
|
|---|
| 58 |
(defun easy-mmode-pretty-mode-name (mode &optional lighter) |
|---|
| 59 |
"Turn the symbol MODE into a string intended for the user. |
|---|
| 60 |
If provided, LIGHTER will be used to help choose capitalization by, |
|---|
| 61 |
replacing its case-insensitive matches with the literal string in LIGHTER." |
|---|
| 62 |
(let* ((case-fold-search t) |
|---|
| 63 |
|
|---|
| 64 |
(name (concat (replace-regexp-in-string |
|---|
| 65 |
|
|---|
| 66 |
|
|---|
| 67 |
|
|---|
| 68 |
"-Minor" " minor" |
|---|
| 69 |
|
|---|
| 70 |
(capitalize (replace-regexp-in-string |
|---|
| 71 |
|
|---|
| 72 |
"-mode\\'" "" (symbol-name mode)))) |
|---|
| 73 |
" mode"))) |
|---|
| 74 |
(if (not (stringp lighter)) name |
|---|
| 75 |
|
|---|
| 76 |
(setq lighter (replace-regexp-in-string "\\`\\s-+\\|\\s-+\\'" "" |
|---|
| 77 |
lighter)) |
|---|
| 78 |
|
|---|
| 79 |
|
|---|
| 80 |
|
|---|
| 81 |
|
|---|
| 82 |
|
|---|
| 83 |
|
|---|
| 84 |
(replace-regexp-in-string (regexp-quote lighter) lighter name t t)))) |
|---|
| 85 |
|
|---|
| 86 |
|
|---|
| 87 |
(defalias 'easy-mmode-define-minor-mode 'define-minor-mode) |
|---|
| 88 |
|
|---|
| 89 |
(defmacro define-minor-mode (mode doc &optional init-value lighter keymap &rest body) |
|---|
| 90 |
"Define a new minor mode MODE. |
|---|
| 91 |
This function defines the associated control variable MODE, keymap MODE-map, |
|---|
| 92 |
and toggle command MODE. |
|---|
| 93 |
|
|---|
| 94 |
DOC is the documentation for the mode toggle command. |
|---|
| 95 |
Optional INIT-VALUE is the initial value of the mode's variable. |
|---|
| 96 |
Optional LIGHTER is displayed in the modeline when the mode is on. |
|---|
| 97 |
Optional KEYMAP is the default (defvar) keymap bound to the mode keymap. |
|---|
| 98 |
If it is a list, it is passed to `easy-mmode-define-keymap' |
|---|
| 99 |
in order to build a valid keymap. It's generally better to use |
|---|
| 100 |
a separate MODE-map variable than to use this argument. |
|---|
| 101 |
The above three arguments can be skipped if keyword arguments are |
|---|
| 102 |
used (see below). |
|---|
| 103 |
|
|---|
| 104 |
BODY contains code to execute each time the mode is activated or deactivated. |
|---|
| 105 |
It is executed after toggling the mode, |
|---|
| 106 |
and before running the hook variable `MODE-hook'. |
|---|
| 107 |
Before the actual body code, you can write keyword arguments (alternating |
|---|
| 108 |
keywords and values). These following keyword arguments are supported (other |
|---|
| 109 |
keywords will be passed to `defcustom' if the minor mode is global): |
|---|
| 110 |
:group GROUP Custom group name to use in all generated `defcustom' forms. |
|---|
| 111 |
Defaults to MODE without the possible trailing \"-mode\". |
|---|
| 112 |
Don't use this default group name unless you have written a |
|---|
| 113 |
`defgroup' to define that group properly. |
|---|
| 114 |
:global GLOBAL If non-nil specifies that the minor mode is not meant to be |
|---|
| 115 |
buffer-local, so don't make the variable MODE buffer-local. |
|---|
| 116 |
By default, the mode is buffer-local. |
|---|
| 117 |
:init-value VAL Same as the INIT-VALUE argument. |
|---|
| 118 |
:lighter SPEC Same as the LIGHTER argument. |
|---|
| 119 |
:keymap MAP Same as the KEYMAP argument. |
|---|
| 120 |
:require SYM Same as in `defcustom'. |
|---|
| 121 |
|
|---|
| 122 |
For example, you could write |
|---|
| 123 |
(define-minor-mode foo-mode \"If enabled, foo on you!\" |
|---|
| 124 |
:lighter \" Foo\" :require 'foo :global t :group 'hassle :version \"27.5\" |
|---|
| 125 |
...BODY CODE...)" |
|---|
| 126 |
(declare (debug (&define name stringp |
|---|
| 127 |
[&optional [¬ keywordp] sexp |
|---|
| 128 |
&optional [¬ keywordp] sexp |
|---|
| 129 |
&optional [¬ keywordp] sexp] |
|---|
| 130 |
[&rest [keywordp sexp]] |
|---|
| 131 |
def-body))) |
|---|
| 132 |
|
|---|
| 133 |
|
|---|
| 134 |
(cond |
|---|
| 135 |
((keywordp init-value) |
|---|
| 136 |
(setq body (list* init-value lighter keymap body) |
|---|
| 137 |
init-value nil lighter nil keymap nil)) |
|---|
| 138 |
((keywordp lighter) |
|---|
| 139 |
(setq body (list* lighter keymap body) lighter nil keymap nil)) |
|---|
| 140 |
((keywordp keymap) (push keymap body) (setq keymap nil))) |
|---|
| 141 |
|
|---|
| 142 |
(let* ((last-message (make-symbol "last-message")) |
|---|
| 143 |
(mode-name (symbol-name mode)) |
|---|
| 144 |
(pretty-name (easy-mmode-pretty-mode-name mode lighter)) |
|---|
| 145 |
(globalp nil) |
|---|
| 146 |
(set nil) |
|---|
| 147 |
(initialize nil) |
|---|
| 148 |
(group nil) |
|---|
| 149 |
(type nil) |
|---|
| 150 |
(extra-args nil) |
|---|
| 151 |
(extra-keywords nil) |
|---|
| 152 |
(require t) |
|---|
| 153 |
(hook (intern (concat mode-name "-hook"))) |
|---|
| 154 |
(hook-on (intern (concat mode-name "-on-hook"))) |
|---|
| 155 |
(hook-off (intern (concat mode-name "-off-hook"))) |
|---|
| 156 |
keyw keymap-sym) |
|---|
| 157 |
|
|---|
| 158 |
|
|---|
| 159 |
(while (keywordp (setq keyw (car body))) |
|---|
| 160 |
(setq body (cdr body)) |
|---|
| 161 |
(case keyw |
|---|
| 162 |
(:init-value (setq init-value (pop body))) |
|---|
| 163 |
(:lighter (setq lighter (pop body))) |
|---|
| 164 |
(:global (setq globalp (pop body))) |
|---|
| 165 |
(:extra-args (setq extra-args (pop body))) |
|---|
| 166 |
(:set (setq set (list :set (pop body)))) |
|---|
| 167 |
(:initialize (setq initialize (list :initialize (pop body)))) |
|---|
| 168 |
(:group (setq group (nconc group (list :group (pop body))))) |
|---|
| 169 |
(:type (setq type (list :type (pop body)))) |
|---|
| 170 |
(:require (setq require (pop body))) |
|---|
| 171 |
(:keymap (setq keymap (pop body))) |
|---|
| 172 |
(t (push keyw extra-keywords) (push (pop body) extra-keywords)))) |
|---|
| 173 |
|
|---|
| 174 |
(setq keymap-sym (if (and keymap (symbolp keymap)) keymap |
|---|
| 175 |
(intern (concat mode-name "-map")))) |
|---|
| 176 |
|
|---|
| 177 |
(unless set (setq set '(:set 'custom-set-minor-mode))) |
|---|
| 178 |
|
|---|
| 179 |
(unless initialize |
|---|
| 180 |
(setq initialize '(:initialize 'custom-initialize-default))) |
|---|
| 181 |
|
|---|
| 182 |
(unless group |
|---|
| 183 |
|
|---|
| 184 |
(setq group |
|---|
| 185 |
`(:group ',(intern (replace-regexp-in-string |
|---|
| 186 |
"-mode\\'" "" mode-name))))) |
|---|
| 187 |
|
|---|
| 188 |
(unless type (setq type '(:type 'boolean))) |
|---|
| 189 |
|
|---|
| 190 |
`(progn |
|---|
| 191 |
|
|---|
| 192 |
,(if (not globalp) |
|---|
| 193 |
`(progn |
|---|
| 194 |
(defvar ,mode ,init-value ,(format "Non-nil if %s is enabled. |
|---|
| 195 |
Use the command `%s' to change this variable." pretty-name mode)) |
|---|
| 196 |
(make-variable-buffer-local ',mode)) |
|---|
| 197 |
|
|---|
| 198 |
(let ((base-doc-string |
|---|
| 199 |
(concat "Non-nil if %s is enabled. |
|---|
| 200 |
See the command `%s' for a description of this minor mode." |
|---|
| 201 |
(if body " |
|---|
| 202 |
Setting this variable directly does not take effect; |
|---|
| 203 |
either customize it (see the info node `Easy Customization') |
|---|
| 204 |
or call the function `%s'.")))) |
|---|
| 205 |
`(defcustom ,mode ,init-value |
|---|
| 206 |
,(format base-doc-string pretty-name mode mode) |
|---|
| 207 |
,@set |
|---|
| 208 |
,@initialize |
|---|
| 209 |
,@group |
|---|
| 210 |
,@type |
|---|
| 211 |
,@(unless (eq require t) `(:require ,require)) |
|---|
| 212 |
,@(nreverse extra-keywords)))) |
|---|
| 213 |
|
|---|
| 214 |
|
|---|
| 215 |
(defun ,mode (&optional arg ,@extra-args) |
|---|
| 216 |
,(or doc |
|---|
| 217 |
(format (concat "Toggle %s on or off. |
|---|
| 218 |
Interactively, with no prefix argument, toggle the mode. |
|---|
| 219 |
With universal prefix ARG turn mode on. |
|---|
| 220 |
With zero or negative ARG turn mode off. |
|---|
| 221 |
\\{%s}") pretty-name keymap-sym)) |
|---|
| 222 |
|
|---|
| 223 |
|
|---|
| 224 |
(interactive (list (or current-prefix-arg 'toggle))) |
|---|
| 225 |
(let ((,last-message (current-message))) |
|---|
| 226 |
(setq ,mode |
|---|
| 227 |
(cond |
|---|
| 228 |
((eq arg 'toggle) (not ,mode)) |
|---|
| 229 |
(arg (> (prefix-numeric-value arg) 0)) |
|---|
| 230 |
(t |
|---|
| 231 |
(if (null ,mode) t |
|---|
| 232 |
(message |
|---|
| 233 |
"Toggling %s off; better pass an explicit argument." |
|---|
| 234 |
',mode) |
|---|
| 235 |
nil)))) |
|---|
| 236 |
,@body |
|---|
| 237 |
|
|---|
| 238 |
(run-hooks ',hook (if ,mode ',hook-on ',hook-off)) |
|---|
| 239 |
(if (called-interactively-p) |
|---|
| 240 |
(progn |
|---|
| 241 |
,(if globalp `(customize-mark-as-set ',mode)) |
|---|
| 242 |
|
|---|
| 243 |
|
|---|
| 244 |
(unless (and (current-message) |
|---|
| 245 |
(not (equal ,last-message |
|---|
| 246 |
(current-message)))) |
|---|
| 247 |
(message ,(format "%s %%sabled" pretty-name) |
|---|
| 248 |
(if ,mode "en" "dis")))))) |
|---|
| 249 |
(force-mode-line-update) |
|---|
| 250 |
|
|---|
| 251 |
,mode) |
|---|
| 252 |
|
|---|
| 253 |
|
|---|
| 254 |
|
|---|
| 255 |
:autoload-end |
|---|
| 256 |
|
|---|
| 257 |
|
|---|
| 258 |
,(unless (symbolp keymap) |
|---|
| 259 |
`(defvar ,keymap-sym |
|---|
| 260 |
(let ((m ,keymap)) |
|---|
| 261 |
(cond ((keymapp m) m) |
|---|
| 262 |
((listp m) (easy-mmode-define-keymap m)) |
|---|
| 263 |
(t (error "Invalid keymap %S" ,keymap)))) |
|---|
| 264 |
,(format "Keymap for `%s'." mode-name))) |
|---|
| 265 |
|
|---|
| 266 |
(add-minor-mode ',mode ',lighter |
|---|
| 267 |
,(if keymap keymap-sym |
|---|
| 268 |
`(if (boundp ',keymap-sym) |
|---|
| 269 |
(symbol-value ',keymap-sym))))))) |
|---|
| 270 |
|
|---|
| 271 |
|
|---|
| 272 |
|
|---|
| 273 |
|
|---|
| 274 |
|
|---|
| 275 |
|
|---|
| 276 |
(defalias 'easy-mmode-define-global-mode 'define-globalized-minor-mode) |
|---|
| 277 |
|
|---|
| 278 |
(defalias 'define-global-minor-mode 'define-globalized-minor-mode) |
|---|
| 279 |
|
|---|
| 280 |
(defmacro define-globalized-minor-mode (global-mode mode turn-on &rest keys) |
|---|
| 281 |
"Make a global mode GLOBAL-MODE corresponding to buffer-local minor MODE. |
|---|
| 282 |
TURN-ON is a function that will be called with no args in every buffer |
|---|
| 283 |
and that should try to turn MODE on if applicable for that buffer. |
|---|
| 284 |
KEYS is a list of CL-style keyword arguments. As the minor mode |
|---|
| 285 |
defined by this function is always global, any :global keyword is |
|---|
| 286 |
ignored. Other keywords have the same meaning as in `define-minor-mode', |
|---|
| 287 |
which see. In particular, :group specifies the custom group. |
|---|
| 288 |
The most useful keywords are those that are passed on to the |
|---|
| 289 |
`defcustom'. It normally makes no sense to pass the :lighter |
|---|
| 290 |
or :keymap keywords to `define-globalized-minor-mode', since these |
|---|
| 291 |
are usually passed to the buffer-local version of the minor mode. |
|---|
| 292 |
|
|---|
| 293 |
If MODE's set-up depends on the major mode in effect when it was |
|---|
| 294 |
enabled, then disabling and reenabling MODE should make MODE work |
|---|
| 295 |
correctly with the current major mode. This is important to |
|---|
| 296 |
prevent problems with derived modes, that is, major modes that |
|---|
| 297 |
call another major mode in their body." |
|---|
| 298 |
|
|---|
| 299 |
(let* ((global-mode-name (symbol-name global-mode)) |
|---|
| 300 |
(pretty-name (easy-mmode-pretty-mode-name mode)) |
|---|
| 301 |
(pretty-global-name (easy-mmode-pretty-mode-name global-mode)) |
|---|
| 302 |
(group nil) |
|---|
| 303 |
(extra-keywords nil) |
|---|
| 304 |
(MODE-buffers (intern (concat global-mode-name "-buffers"))) |
|---|
| 305 |
(MODE-enable-in-buffers |
|---|
| 306 |
(intern (concat global-mode-name "-enable-in-buffers"))) |
|---|
| 307 |
(MODE-check-buffers |
|---|
| 308 |
(intern (concat global-mode-name "-check-buffers"))) |
|---|
| 309 |
(MODE-cmhh (intern (concat global-mode-name "-cmhh"))) |
|---|
| 310 |
(MODE-major-mode (intern (concat (symbol-name mode) "-major-mode"))) |
|---|
| 311 |
keyw) |
|---|
| 312 |
|
|---|
| 313 |
|
|---|
| 314 |
(while (keywordp (setq keyw (car keys))) |
|---|
| 315 |
(setq keys (cdr keys)) |
|---|
| 316 |
(case keyw |
|---|
| 317 |
(:group (setq group (nconc group (list :group (pop keys))))) |
|---|
| 318 |
(:global (setq keys (cdr keys))) |
|---|
| 319 |
(t (push keyw extra-keywords) (push (pop keys) extra-keywords)))) |
|---|
| 320 |
|
|---|
| 321 |
(unless group |
|---|
| 322 |
|
|---|
| 323 |
(setq group |
|---|
| 324 |
`(:group ',(intern (replace-regexp-in-string |
|---|
| 325 |
"-mode\\'" "" (symbol-name mode)))))) |
|---|
| 326 |
|
|---|
| 327 |
`(progn |
|---|
| 328 |
(defvar ,MODE-major-mode nil) |
|---|
| 329 |
(make-variable-buffer-local ',MODE-major-mode) |
|---|
| 330 |
|
|---|
| 331 |
(define-minor-mode ,global-mode |
|---|
| 332 |
,(format "Toggle %s in every possible buffer. |
|---|
| 333 |
With prefix ARG, turn %s on if and only if ARG is positive. |
|---|
| 334 |
%s is enabled in all buffers where `%s' would do it. |
|---|
| 335 |
See `%s' for more information on %s." |
|---|
| 336 |
pretty-name pretty-global-name pretty-name turn-on |
|---|
| 337 |
mode pretty-name) |
|---|
| 338 |
:global t ,@group ,@(nreverse extra-keywords) |
|---|
| 339 |
|
|---|
| 340 |
|
|---|
| 341 |
(if ,global-mode |
|---|
| 342 |
(progn |
|---|
| 343 |
(add-hook 'after-change-major-mode-hook |
|---|
| 344 |
',MODE-enable-in-buffers) |
|---|
| 345 |
(add-hook 'find-file-hook ',MODE-check-buffers) |
|---|
| 346 |
(add-hook 'change-major-mode-hook ',MODE-cmhh)) |
|---|
| 347 |
(remove-hook 'after-change-major-mode-hook ',MODE-enable-in-buffers) |
|---|
| 348 |
(remove-hook 'find-file-hook ',MODE-check-buffers) |
|---|
| 349 |
(remove-hook 'change-major-mode-hook ',MODE-cmhh)) |
|---|
| 350 |
|
|---|
| 351 |
|
|---|
| 352 |
(dolist (buf (buffer-list)) |
|---|
| 353 |
(with-current-buffer buf |
|---|
| 354 |
(if ,global-mode (,turn-on) (when ,mode (,mode -1)))))) |
|---|
| 355 |
|
|---|
| 356 |
|
|---|
| 357 |
|
|---|
| 358 |
:autoload-end |
|---|
| 359 |
|
|---|
| 360 |
|
|---|
| 361 |
(defvar ,MODE-buffers nil) |
|---|
| 362 |
|
|---|
| 363 |
|
|---|
| 364 |
(defun ,MODE-enable-in-buffers () |
|---|
| 365 |
(dolist (buf ,MODE-buffers) |
|---|
| 366 |
(when (buffer-live-p buf) |
|---|
| 367 |
(with-current-buffer buf |
|---|
| 368 |
(if ,mode |
|---|
| 369 |
(unless (eq ,MODE-major-mode major-mode) |
|---|
| 370 |
(,mode -1) |
|---|
| 371 |
(,turn-on) |
|---|
| 372 |
(setq ,MODE-major-mode major-mode)) |
|---|
| 373 |
(,turn-on) |
|---|
| 374 |
(setq ,MODE-major-mode major-mode)))))) |
|---|
| 375 |
(put ',MODE-enable-in-buffers 'definition-name ',global-mode) |
|---|
| 376 |
|
|---|
| 377 |
(defun ,MODE-check-buffers () |
|---|
| 378 |
(,MODE-enable-in-buffers) |
|---|
| 379 |
(setq ,MODE-buffers nil) |
|---|
| 380 |
(remove-hook 'post-command-hook ',MODE-check-buffers)) |
|---|
| 381 |
(put ',MODE-check-buffers 'definition-name ',global-mode) |
|---|
| 382 |
|
|---|
| 383 |
|
|---|
| 384 |
(defun ,MODE-cmhh () |
|---|
| 385 |
(add-to-list ',MODE-buffers (current-buffer)) |
|---|
| 386 |
(add-hook 'post-command-hook ',MODE-check-buffers)) |
|---|
| 387 |
(put ',MODE-cmhh 'definition-name ',global-mode)))) |
|---|
| 388 |
|
|---|
| 389 |
|
|---|
| 390 |
|
|---|
| 391 |
|
|---|
| 392 |
|
|---|
| 393 |
(if (fboundp 'set-keymap-parents) |
|---|
| 394 |
(defalias 'easy-mmode-set-keymap-parents 'set-keymap-parents) |
|---|
| 395 |
(defun easy-mmode-set-keymap-parents (m parents) |
|---|
| 396 |
(set-keymap-parent |
|---|
| 397 |
m |
|---|
| 398 |
(cond |
|---|
| 399 |
((not (consp parents)) parents) |
|---|
| 400 |
((not (cdr parents)) (car parents)) |
|---|
| 401 |
(t (let ((m (copy-keymap (pop parents)))) |
|---|
| 402 |
(easy-mmode-set-keymap-parents m parents) |
|---|
| 403 |
m)))))) |
|---|
| 404 |
|
|---|
| 405 |
|
|---|
| 406 |
(defun easy-mmode-define-keymap (bs &optional name m args) |
|---|
| 407 |
"Return a keymap built from bindings BS. |
|---|
| 408 |
BS must be a list of (KEY . BINDING) where |
|---|
| 409 |
KEY and BINDINGS are suitable for `define-key'. |
|---|
| 410 |
Optional NAME is passed to `make-sparse-keymap'. |
|---|
| 411 |
Optional map M can be used to modify an existing map. |
|---|
| 412 |
ARGS is a list of additional keyword arguments." |
|---|
| 413 |
(let (inherit dense) |
|---|
| 414 |
(while args |
|---|
| 415 |
(let ((key (pop args)) |
|---|
| 416 |
(val (pop args))) |
|---|
| 417 |
(case key |
|---|
| 418 |
(:name (setq name val)) |
|---|
| 419 |
(:dense (setq dense val)) |
|---|
| 420 |
(:inherit (setq inherit val)) |
|---|
| 421 |
(:group) |
|---|
| 422 |
(t (message "Unknown argument %s in defmap" key))))) |
|---|
| 423 |
(unless (keymapp m) |
|---|
| 424 |
(setq bs (append m bs)) |
|---|
| 425 |
(setq m (if dense (make-keymap name) (make-sparse-keymap name)))) |
|---|
| 426 |
(dolist (b bs) |
|---|
| 427 |
(let ((keys (car b)) |
|---|
| 428 |
(binding (cdr b))) |
|---|
| 429 |
(dolist (key (if (consp keys) keys (list keys))) |
|---|
| 430 |
(cond |
|---|
| 431 |
((symbolp key) |
|---|
| 432 |
(substitute-key-definition key binding m global-map)) |
|---|
| 433 |
((null binding) |
|---|
| 434 |
(unless (keymapp (lookup-key m key)) (define-key m key binding))) |
|---|
| 435 |
((let ((o (lookup-key m key))) |
|---|
| 436 |
(or (null o) (numberp o) (eq o 'undefined))) |
|---|
| 437 |
(define-key m key binding)))))) |
|---|
| 438 |
(cond |
|---|
| 439 |
((keymapp inherit) (set-keymap-parent m inherit)) |
|---|
| 440 |
((consp inherit) (easy-mmode-set-keymap-parents m inherit))) |
|---|
| 441 |
m)) |
|---|
| 442 |
|
|---|
| 443 |
|
|---|
| 444 |
(defmacro easy-mmode-defmap (m bs doc &rest args) |
|---|
| 445 |
`(defconst ,m |
|---|
| 446 |
(easy-mmode-define-keymap ,bs nil (if (boundp ',m) ,m) ,(cons 'list args)) |
|---|
| 447 |
,doc)) |
|---|
| 448 |
|
|---|
| 449 |
|
|---|
| 450 |
|
|---|
| 451 |
|
|---|
| 452 |
|
|---|
| 453 |
|
|---|
| 454 |
(defun easy-mmode-define-syntax (css args) |
|---|
| 455 |
(let ((st (make-syntax-table (plist-get args :copy))) |
|---|
| 456 |
(parent (plist-get args :inherit))) |
|---|
| 457 |
(dolist (cs css) |
|---|
| 458 |
(let ((char (car cs)) |
|---|
| 459 |
(syntax (cdr cs))) |
|---|
| 460 |
(if (sequencep char) |
|---|
| 461 |
(mapcar (lambda (c) (modify-syntax-entry c syntax st)) char) |
|---|
| 462 |
(modify-syntax-entry char syntax st)))) |
|---|
| 463 |
(if parent (set-char-table-parent |
|---|
| 464 |
st (if (symbolp parent) (symbol-value parent) parent))) |
|---|
| 465 |
st)) |
|---|
| 466 |
|
|---|
| 467 |
|
|---|
| 468 |
(defmacro easy-mmode-defsyntax (st css doc &rest args) |
|---|
| 469 |
"Define variable ST as a syntax-table. |
|---|
| 470 |
CSS contains a list of syntax specifications of the form (CHAR . SYNTAX)." |
|---|
| 471 |
`(progn |
|---|
| 472 |
(autoload 'easy-mmode-define-syntax "easy-mmode") |
|---|
| 473 |
(defconst ,st (easy-mmode-define-syntax ,css ,(cons 'list args)) ,doc))) |
|---|
| 474 |
|
|---|
| 475 |
|
|---|
| 476 |
|
|---|
| 477 |
|
|---|
| 478 |
|
|---|
| 479 |
|
|---|
| 480 |
|
|---|
| 481 |
(defmacro easy-mmode-define-navigation (base re &optional name endfun narrowfun) |
|---|
| 482 |
"Define BASE-next and BASE-prev to navigate in the buffer. |
|---|
| 483 |
RE determines the places the commands should move point to. |
|---|
| 484 |
NAME should describe the entities matched by RE. It is used to build |
|---|
| 485 |
the docstrings of the two functions. |
|---|
| 486 |
BASE-next also tries to make sure that the whole entry is visible by |
|---|
| 487 |
searching for its end (by calling ENDFUN if provided or by looking for |
|---|
| 488 |
the next entry) and recentering if necessary. |
|---|
| 489 |
ENDFUN should return the end position (with or without moving point). |
|---|
| 490 |
NARROWFUN non-nil means to check for narrowing before moving, and if |
|---|
| 491 |
found, do `widen' first and then call NARROWFUN with no args after moving." |
|---|
| 492 |
(let* ((base-name (symbol-name base)) |
|---|
| 493 |
(prev-sym (intern (concat base-name "-prev"))) |
|---|
| 494 |
(next-sym (intern (concat base-name "-next"))) |
|---|
| 495 |
(check-narrow-maybe |
|---|
| 496 |
(when narrowfun |
|---|
| 497 |
'(setq was-narrowed |
|---|
| 498 |
(prog1 (or (< (- (point-max) (point-min)) (buffer-size))) |
|---|
| 499 |
(widen))))) |
|---|
| 500 |
(re-narrow-maybe (when narrowfun |
|---|
| 501 |
`(when was-narrowed (,narrowfun))))) |
|---|
| 502 |
(unless name (setq name base-name)) |
|---|
| 503 |
`(progn |
|---|
| 504 |
(add-to-list 'debug-ignored-errors |
|---|
| 505 |
,(concat "^No \\(previous\\|next\\) " (regexp-quote name))) |
|---|
| 506 |
(defun ,next-sym (&optional count) |
|---|
| 507 |
,(format "Go to the next COUNT'th %s." name) |
|---|
| 508 |
(interactive "p") |
|---|
| 509 |
(unless count (setq count 1)) |
|---|
| 510 |
(if (< count 0) (,prev-sym (- count)) |
|---|
| 511 |
(if (looking-at ,re) (setq count (1+ count))) |
|---|
| 512 |
(let (was-narrowed) |
|---|
| 513 |
,check-narrow-maybe |
|---|
| 514 |
(if (not (re-search-forward ,re nil t count)) |
|---|
| 515 |
(if (looking-at ,re) |
|---|
| 516 |
(goto-char (or ,(if endfun `(,endfun)) (point-max))) |
|---|
| 517 |
(error "No next %s" ,name)) |
|---|
| 518 |
(goto-char (match-beginning 0)) |
|---|
| 519 |
(when (and (eq (current-buffer) (window-buffer (selected-window))) |
|---|
| 520 |
(interactive-p)) |
|---|
| 521 |
(let ((endpt (or (save-excursion |
|---|
| 522 |
,(if endfun `(,endfun) |
|---|
| 523 |
`(re-search-forward ,re nil t 2))) |
|---|
| 524 |
(point-max)))) |
|---|
| 525 |
(unless (pos-visible-in-window-p endpt nil t) |
|---|
| 526 |
(recenter '(0)))))) |
|---|
| 527 |
,re-narrow-maybe))) |
|---|
| 528 |
(put ',next-sym 'definition-name ',base) |
|---|
| 529 |
(defun ,prev-sym (&optional count) |
|---|
| 530 |
,(format "Go to the previous COUNT'th %s" (or name base-name)) |
|---|
| 531 |
(interactive "p") |
|---|
| 532 |
(unless count (setq count 1)) |
|---|
| 533 |
(if (< count 0) (,next-sym (- count)) |
|---|
| 534 |
(let (was-narrowed) |
|---|
| 535 |
,check-narrow-maybe |
|---|
| 536 |
(unless (re-search-backward ,re nil t count) |
|---|
| 537 |
(error "No previous %s" ,name)) |
|---|
| 538 |
,re-narrow-maybe))) |
|---|
| 539 |
(put ',prev-sym 'definition-name ',base)))) |
|---|
| 540 |
|
|---|
| 541 |
|
|---|
| 542 |
(provide 'easy-mmode) |
|---|
| 543 |
|
|---|
| 544 |
|
|---|
| 545 |
|
|---|
| 546 |
|
|---|