-
Notifications
You must be signed in to change notification settings - Fork 32
/
mmm-utils.el
162 lines (130 loc) · 5.49 KB
/
mmm-utils.el
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
;;; mmm-utils.el --- Coding Utilities for MMM Mode -*- lexical-binding: t; -*-
;; Copyright (C) 2000-2003, 2011-2013, 2020 Free Software Foundation, Inc.
;; Author: Michael Abraham Shulman <viritrilbia@gmail.com>
;;{{{ GPL
;; This file 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 file 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 GNU Emacs; see the file COPYING. If not, write to
;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;}}}
;;; Commentary:
;; This file provides a number of macros and other coding utilities
;; for MMM Mode.
;;; Code:
;;{{{ Valid Buffer
;; We used to wrap almost everything in this, but I realized that
;; only `mmm-mode-on' really needs it. Kept it as a macro, though,
;; for modularity and in case we need it somewhere else.
(defmacro mmm-valid-buffer (&rest body)
"Execute BODY if in a valid buffer for MMM Mode to be enabled. This
means not hidden, not a minibuffer, not in batch mode, and not in of
`mmm-never-modes'."
`(unless (or (eq (aref (buffer-name) 0) ?\ )
(window-minibuffer-p (selected-window))
(memq major-mode mmm-never-modes)
noninteractive
mmm-in-temp-buffer)
,@body))
;;;(def-edebug-spec mmm-valid-buffer t)
;;}}}
;;{{{ Save Everything
;; Never trust callback functions to preserve anything.
(defmacro mmm-save-all (&rest body)
"Execute BODY forms, then restoring point, mark, current buffer,
restrictions, and match data."
(declare (indent 1) (debug t))
`(save-excursion
(save-restriction
(save-match-data
,@body))))
;;;(def-edebug-spec mmm-save-all t)
;;}}}
;;{{{ String Formatting
(defun mmm-format-string (string arg-pairs)
"Format STRING by replacing arguments as specified by ARG-PAIRS.
Each element of ARG-PAIRS is \(REGEXP . STR) where each STR is to be
substituted for the corresponding REGEXP wherever it matches."
(let ((case-fold-search nil))
(save-match-data
(dolist (pair arg-pairs)
(while (string-match (car pair) string)
(setq string (replace-match
(if (fboundp 'format-mode-line)
(format-mode-line (cdr pair))
(cdr pair))
t t string))))))
string)
(defun mmm-format-matches (string &optional on-string)
"Format STRING by matches from the current match data.
Strings like ~N are replaced by the Nth subexpression from the last
global match. Does nothing if STRING is not a string.
ON-STRING, if supplied, means to use the match data from a
`string-match' on that string, rather than the global match data."
(when (stringp string)
(let ((old-data (match-data))
subexp)
(save-match-data
(while (string-match "~\\([0-9]\\)" string)
(setq subexp (string-to-number (match-string-no-properties 1 string))
string (replace-match
(save-match-data
(set-match-data old-data)
(match-string-no-properties subexp on-string))
t t string))))))
string)
;;}}}
;;{{{ Save Keywords
(defmacro mmm-save-keyword (param)
"Return the list (:PARAM (symbol-value PARAM)) if PARAM is non-nil.
Return nil if PARAM is not a variable (lexical or dynamic) or has the
value nil. Best used only when it is important that nil values
disappear."
;; Would be nice to use `ignore-error' here, but that wasn't
;; introduced until Emacs 27.1. This condition-case does the same thing.
`(condition-case nil
(if ,param (list ,(intern (format ":%s" param)) ,param))
((void-variable ,param) nil)))
(defmacro mmm-save-keywords (&rest params)
"Return a list saving the non-nil elements of PARAMS.
For instance
\(let \(\(a 1) \(c 2) (d 3)) \(mmm-save-keywords a b c)) => \(:a 1 :c 2).
Use of this macro can make code more readable when there are a lot of
PARAMS, but less readable when there are only a few. Also best used
only when it is important that nil values disappear."
`(append ,@(mapcar (lambda (param) `(mmm-save-keyword ,param)) params)))
;;}}}
;;{{{ Looking Back At
(defun mmm-looking-back-at (regexp &optional bound)
"Return t if text before point matches REGEXP.
Modifies the match data. If supplied, BOUND means not to look farther
back that that many characters before point. Otherwise, it defaults to
\(length REGEXP), which is good enough when REGEXP is a simple
string."
(eq (point)
(save-excursion
(and (re-search-backward regexp
(- (point) (or bound (length regexp)))
t)
(match-end 0)))))
;;}}}
;;{{{ Markers
;; Mostly for remembering interactively made regions
(defun mmm-make-marker (pos beg-p sticky-p)
"Make, and return, a marker at POS that is or isn't sticky.
BEG-P represents whether the marker delimits the beginning of a
region \(or the end of it). STICKY-P is whether it should be sticky,
i.e. whether text inserted at the marker should be inside the region."
(let ((mkr (set-marker (make-marker) pos)))
(set-marker-insertion-type mkr (if beg-p (not sticky-p) sticky-p))
mkr))
;;}}}
(provide 'mmm-utils)
;;; mmm-utils.el ends here