Support XDG-based init directory
[sxemacs] / lisp / simple.el
1 ;;; simple.el --- basic editing commands for SXEmacs
2
3 ;; Copyright (C) 1985-7, 1993-5, 1997 Free Software Foundation, Inc.
4 ;; Copyright (C) 1995 Tinker Systems and INS Engineering Corp.
5 ;; Copyright (C) 2000, 2001, 2002, 2003 Ben Wing.
6 ;; Copyright (C) 2006, Steve Youngs.
7
8 ;; Maintainer: SXEmacs Development Team
9 ;; Keywords: lisp, extensions, internal, dumped
10
11 ;; This file is part of SXEmacs.
12
13 ;; SXEmacs is free software: you can redistribute it and/or modify
14 ;; it under the terms of the GNU General Public License as published by
15 ;; the Free Software Foundation, either version 3 of the License, or
16 ;; (at your option) any later version.
17
18 ;; SXEmacs is distributed in the hope that it will be useful,
19 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 ;; GNU General Public License for more details.
22
23 ;; You should have received a copy of the GNU General Public License
24 ;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
25
26 ;;; Synched up with: FSF 19.34 [But not very closely].
27
28 ;;; Commentary:
29
30 ;; This file is dumped with SXEmacs.
31
32 ;; A grab-bag of basic SXEmacs commands not specifically related to some
33 ;; major mode or to file-handling.
34
35 ;; Changes for zmacs-style active-regions:
36 ;;
37 ;; beginning-of-buffer, end-of-buffer, count-lines-region,
38 ;; count-lines-buffer, what-line, what-cursor-position, set-goal-column,
39 ;; set-fill-column, prefix-arg-internal, and line-move (which is used by
40 ;; next-line and previous-line) set zmacs-region-stays to t, so that they
41 ;; don't affect the current region-hilighting state.
42 ;;
43 ;; mark-whole-buffer, mark-word, exchange-point-and-mark, and
44 ;; set-mark-command (without an argument) call zmacs-activate-region.
45 ;;
46 ;; mark takes an optional arg like the new Fmark_marker() does.  When
47 ;; the region is not active, mark returns nil unless the optional arg is true.
48 ;;
49 ;; push-mark, pop-mark, exchange-point-and-mark, and set-marker, and
50 ;; set-mark-command use (mark t) so that they can access the mark whether
51 ;; the region is active or not.
52 ;;
53 ;; shell-command, shell-command-on-region, yank, and yank-pop (which all
54 ;; push a mark) have been altered to call exchange-point-and-mark with an
55 ;; argument, meaning "don't activate the region".  These commands  only use
56 ;; exchange-point-and-mark to position the newly-pushed mark correctly, so
57 ;; this isn't a user-visible change.  These functions have also been altered
58 ;; to use (mark t) for the same reason.
59
60 ;; 97/3/14 Jareth Hein (jhod@po.iijnet.or.jp) added kinsoku processing (support
61 ;; for filling of Asian text) into the fill code. This was ripped bleeding from
62 ;; Mule-2.3, and could probably use some feature additions (like additional wrap
63 ;; styles, etc)
64
65 ;; 97/06/11 Steve Baur (steve@xemacs.org) Convert use of
66 ;;  (preceding|following)-char to char-(after|before).
67
68 ;;; Code:
69
70 (defgroup editing-basics nil
71   "Most basic editing variables."
72   :group 'editing)
73
74 (defgroup killing nil
75   "Killing and yanking commands."
76   :group 'editing)
77
78 (defgroup fill-comments nil
79   "Indenting and filling of comments."
80   :prefix "comment-"
81   :group 'fill)
82
83 (defgroup paren-matching nil
84   "Highlight (un)matching of parens and expressions."
85   :prefix "paren-"
86   :group 'matching)
87
88 (defgroup log-message nil
89   "Messages logging and display customizations."
90   :group 'minibuffer)
91
92 (defgroup warnings nil
93   "Warnings customizations."
94   :group 'minibuffer)
95
96
97 (defcustom search-caps-disable-folding t
98   "*If non-nil, upper case chars disable case fold searching.
99 This does not apply to \"yanked\" strings."
100   :type 'boolean
101   :group 'editing-basics)
102
103 ;; This is stolen (and slightly modified) from FSF emacs's
104 ;; `isearch-no-upper-case-p'.
105 (defun no-upper-case-p (string &optional regexp-flag)
106   "Return t if there are no upper case chars in STRING.
107 If REGEXP-FLAG is non-nil, disregard letters preceded by `\\' (but not `\\\\')
108 since they have special meaning in a regexp."
109   (let ((case-fold-search nil))
110     (not (string-match (if regexp-flag
111                            #r"\(^\|\\\\\|[^\]\)[A-Z]"
112                          "[A-Z]")
113                        string))
114     ))
115
116 (defmacro with-search-caps-disable-folding (string regexp-flag &rest body)
117   "Eval BODY with `case-fold-search' let to nil if `search-caps-disable-folding'
118 is non-nil, and if STRING (either a string or a regular expression according
119 to REGEXP-FLAG) contains uppercase letters."
120   `(let ((case-fold-search
121           (if (and case-fold-search search-caps-disable-folding)
122               (no-upper-case-p ,string ,regexp-flag)
123             case-fold-search)))
124      ,@body))
125 (put 'with-search-caps-disable-folding 'lisp-indent-function 2)
126 (put 'with-search-caps-disable-folding 'edebug-form-spec
127      '(sexp sexp &rest form))
128
129 (defmacro with-interactive-search-caps-disable-folding (string regexp-flag
130                                                                &rest body)
131   "Same as `with-search-caps-disable-folding', but only in the case of a
132 function called interactively."
133   `(let ((case-fold-search
134           (if (and (interactive-p)
135                    case-fold-search search-caps-disable-folding)
136               (no-upper-case-p ,string ,regexp-flag)
137             case-fold-search)))
138      ,@body))
139 (put 'with-interactive-search-caps-disable-folding 'lisp-indent-function 2)
140 (put 'with-interactive-search-caps-disable-folding 'edebug-form-spec
141      '(sexp sexp &rest form))
142
143 (defun newline (&optional n)
144   "Insert a newline, and move to left margin of the new line if it's blank.
145 The newline is marked with the text-property `hard'.
146 With optional arg N, insert that many newlines.
147 In Auto Fill mode, if no numeric arg, break the preceding line if it's long."
148   (interactive "*P")
149   (barf-if-buffer-read-only nil (point))
150   ;; Inserting a newline at the end of a line produces better redisplay in
151   ;; try_window_id than inserting at the beginning of a line, and the textual
152   ;; result is the same.  So, if we're at beginning of line, pretend to be at
153   ;; the end of the previous line.
154   ;; #### Does this have any relevance in XEmacs?
155   (let ((flag (and (not (bobp))
156                    (bolp)
157                    ;; Make sure the newline before point isn't intangible.
158                    (not (get-char-property (1- (point)) 'intangible))
159                    ;; Make sure the newline before point isn't read-only.
160                    (not (get-char-property (1- (point)) 'read-only))
161                    ;; Make sure the newline before point isn't invisible.
162                    (not (get-char-property (1- (point)) 'invisible))
163                    ;; This should probably also test for the previous char
164                    ;;  being the *last* character too.
165                    (not (get-char-property (1- (point)) 'end-open))
166                    ;; Make sure the newline before point has the same
167                    ;; properties as the char before it (if any).
168                    (< (or (previous-extent-change (point)) -2)
169                       (- (point) 2))))
170         (was-page-start (and (bolp)
171                              (looking-at page-delimiter)))
172         (beforepos (point)))
173     (if flag (backward-char 1))
174     ;; Call self-insert so that auto-fill, abbrev expansion etc. happens.
175     ;; Set last-command-char to tell self-insert what to insert.
176     (let ((last-command-char ?\n)
177           ;; Don't auto-fill if we have a numeric argument.
178           ;; Also not if flag is true (it would fill wrong line);
179           ;; there is no need to since we're at BOL.
180           (auto-fill-function (if (or n flag) nil auto-fill-function)))
181       (unwind-protect
182           (self-insert-command (prefix-numeric-value n))
183         ;; If we get an error in self-insert-command, put point at right place.
184         (if flag (forward-char 1))))
185     ;; If we did *not* get an error, cancel that forward-char.
186     (if flag (backward-char 1))
187     ;; Mark the newline(s) `hard'.
188     (if use-hard-newlines
189         (let* ((from (- (point) (if n (prefix-numeric-value n) 1)))
190                (sticky (get-text-property from 'end-open))) ; XEmacs
191           (put-text-property from (point) 'hard 't)
192           ;; If end-open is not "t", add 'hard to end-open list
193           (if (and (listp sticky) (not (memq 'hard sticky)))
194               (put-text-property from (point) 'end-open ; XEmacs
195                                  (cons 'hard sticky)))))
196     ;; If the newline leaves the previous line blank,
197     ;; and we have a left margin, delete that from the blank line.
198     (or flag
199         (save-excursion
200           (goto-char beforepos)
201           (beginning-of-line)
202           (and (looking-at "[ \t]$")
203                (> (current-left-margin) 0)
204                (delete-region (point) (progn (end-of-line) (point))))))
205     (if flag (forward-char 1))
206     ;; Indent the line after the newline, except in one case:
207     ;; when we added the newline at the beginning of a line
208     ;; which starts a page.
209     (or was-page-start
210         (move-to-left-margin nil t)))
211   nil)
212
213 (defun set-hard-newline-properties (from to)
214   (let ((sticky (get-text-property from 'rear-nonsticky)))
215     (put-text-property from to 'hard 't)
216     ;; If rear-nonsticky is not "t", add 'hard to rear-nonsticky list
217     (if (and (listp sticky) (not (memq 'hard sticky)))
218         (put-text-property from (point) 'rear-nonsticky
219                            (cons 'hard sticky)))))
220
221 (defun open-line (n)
222   "Insert a newline and leave point before it.
223 If there is a fill prefix and/or a left-margin, insert them on the new line
224 if the line would have been blank.
225 With arg N, insert N newlines."
226   (interactive "*p")
227   (let* ((do-fill-prefix (and fill-prefix (bolp)))
228          (do-left-margin (and (bolp) (> (current-left-margin) 0)))
229          (loc (point)))
230     (newline n)
231     (goto-char loc)
232     (while (> n 0)
233       (cond ((bolp)
234              (if do-left-margin (indent-to (current-left-margin)))
235              (if do-fill-prefix (insert fill-prefix))))
236       (forward-line 1)
237       (setq n (1- n)))
238     (goto-char loc)
239     (end-of-line)))
240
241 (defun split-line ()
242   "Split current line, moving portion beyond point vertically down."
243   (interactive "*")
244   (skip-chars-forward " \t")
245   (let ((col (current-column))
246         (pos (point)))
247     (newline 1)
248     (indent-to col 0)
249     (goto-char pos)))
250
251 (defun quoted-insert (arg)
252   "Read next input character and insert it.
253 This is useful for inserting control characters.
254 You may also type up to 3 octal digits, to insert a character with that code.
255
256 In overwrite mode, this function inserts the character anyway, and
257 does not handle octal digits specially.  This means that if you use
258 overwrite as your normal editing mode, you can use this function to
259 insert characters when necessary.
260
261 In binary overwrite mode, this function does overwrite, and octal
262 digits are interpreted as a character code.  This is supposed to make
263 this function useful in editing binary files."
264   (interactive "*p")
265   (let ((char (if (or (not overwrite-mode)
266                       (eq overwrite-mode 'overwrite-mode-binary))
267                   (read-quoted-char)
268                 ;; read-char obeys C-g, so we should protect.  FSF
269                 ;; doesn't have the protection here, but it's a bug in
270                 ;; FSF.
271                 (let ((inhibit-quit t))
272                   (read-char)))))
273     (if (> arg 0)
274         (if (eq overwrite-mode 'overwrite-mode-binary)
275             (delete-char arg)))
276     (while (> arg 0)
277       (insert char)
278       (setq arg (1- arg)))))
279
280 (defun delete-indentation (&optional arg)
281   "Join this line to previous and fix up whitespace at join.
282 If there is a fill prefix, delete it from the beginning of this line.
283 With argument, join this line to following line."
284   (interactive "*P")
285   (beginning-of-line)
286   (if arg (forward-line 1))
287   (if (eq (char-before (point)) ?\n)
288       (progn
289         (delete-region (point) (1- (point)))
290         ;; If the second line started with the fill prefix,
291         ;; delete the prefix.
292         (if (and fill-prefix
293                  (<= (+ (point) (length fill-prefix)) (point-max))
294                  (string= fill-prefix
295                           (buffer-substring (point)
296                                             (+ (point) (length fill-prefix)))))
297             (delete-region (point) (+ (point) (length fill-prefix))))
298         (fixup-whitespace))))
299
300 (defalias 'join-line 'delete-indentation)
301
302 (defun fixup-whitespace ()
303   "Fixup white space between objects around point.
304 Leave one space or none, according to the context."
305   (interactive "*")
306   (save-excursion
307     (delete-horizontal-space)
308     (if (or (looking-at #r"^\|\s)")
309             (save-excursion (backward-char 1)
310                             (looking-at #r"$\|\s(\|\s'")))
311         nil
312       (insert ?\ ))))
313
314 (defun delete-horizontal-space ()
315   "Delete all spaces and tabs around point."
316   (interactive "*")
317   (skip-chars-backward " \t")
318   (delete-region (point) (progn (skip-chars-forward " \t") (point))))
319
320 (defun just-one-space ()
321   "Delete all spaces and tabs around point, leaving one space."
322   (interactive "*")
323   (if abbrev-mode ; XEmacs
324       (expand-abbrev))
325   (skip-chars-backward " \t")
326   (if (eq (char-after (point)) ? ) ; XEmacs
327       (forward-char 1)
328     (insert ? ))
329   (delete-region (point) (progn (skip-chars-forward " \t") (point))))
330
331 (defun delete-blank-lines ()
332   "On blank line, delete all surrounding blank lines, leaving just one.
333 On isolated blank line, delete that one.
334 On nonblank line, delete any immediately following blank lines."
335   (interactive "*")
336   (let (thisblank singleblank)
337     (save-excursion
338       (beginning-of-line)
339       (setq thisblank (looking-at "[ \t]*$"))
340       ;; Set singleblank if there is just one blank line here.
341       (setq singleblank
342             (and thisblank
343                  (not (looking-at "[ \t]*\n[ \t]*$"))
344                  (or (bobp)
345                      (progn (forward-line -1)
346                             (not (looking-at "[ \t]*$")))))))
347     ;; Delete preceding blank lines, and this one too if it's the only one.
348     (if thisblank
349         (progn
350           (beginning-of-line)
351           (if singleblank (forward-line 1))
352           (delete-region (point)
353                          (if (re-search-backward "[^ \t\n]" nil t)
354                              (progn (forward-line 1) (point))
355                            (point-min)))))
356     ;; Delete following blank lines, unless the current line is blank
357     ;; and there are no following blank lines.
358     (if (not (and thisblank singleblank))
359         (save-excursion
360           (end-of-line)
361           (forward-line 1)
362           (delete-region (point)
363                          (if (re-search-forward "[^ \t\n]" nil t)
364                              (progn (beginning-of-line) (point))
365                            (point-max)))))
366     ;; Handle the special case where point is followed by newline and eob.
367     ;; Delete the line, leaving point at eob.
368     (if (looking-at "^[ \t]*\n\\'")
369         (delete-region (point) (point-max)))))
370
371 (defun back-to-indentation ()
372   "Move point to the first non-whitespace character on this line."
373   ;; XEmacs change
374   (interactive "_")
375   (beginning-of-line 1)
376   (skip-chars-forward " \t"))
377
378 (defun newline-and-indent ()
379   "Insert a newline, then indent according to major mode.
380 Indentation is done using the value of `indent-line-function'.
381 In programming language modes, this is the same as TAB.
382 In some text modes, where TAB inserts a tab, this command indents to the
383 column specified by the function `current-left-margin'."
384   (interactive "*")
385   (delete-region (point) (progn (skip-chars-backward " \t") (point)))
386   (newline)
387   (indent-according-to-mode))
388
389 (defun reindent-then-newline-and-indent ()
390   "Reindent current line, insert newline, then indent the new line.
391 Indentation of both lines is done according to the current major mode,
392 which means calling the current value of `indent-line-function'.
393 In programming language modes, this is the same as TAB.
394 In some text modes, where TAB inserts a tab, this indents to the
395 column specified by the function `current-left-margin'."
396   (interactive "*")
397   (save-excursion
398     (delete-region (point) (progn (skip-chars-backward " \t") (point)))
399     (indent-according-to-mode))
400   (newline)
401   (indent-according-to-mode))
402
403 ;; Internal subroutine of delete-char
404 (defun kill-forward-chars (arg)
405   (if (listp arg) (setq arg (car arg)))
406   (if (eq arg '-) (setq arg -1))
407   (kill-region (point) (+ (point) arg)))
408
409 ;; Internal subroutine of backward-delete-char
410 (defun kill-backward-chars (arg)
411   (if (listp arg) (setq arg (car arg)))
412   (if (eq arg '-) (setq arg -1))
413   (kill-region (point) (- (point) arg)))
414
415 (defun backward-delete-char-untabify (arg &optional killp)
416   "Delete characters backward, changing tabs into spaces.
417 Delete ARG chars, and kill (save in kill ring) if KILLP is non-nil.
418 Interactively, ARG is the prefix arg (default 1)
419 and KILLP is t if a prefix arg was specified."
420   (interactive "*p\nP")
421   (let ((count arg))
422     (save-excursion
423       (while (and (> count 0) (not (bobp)))
424         (if (eq (char-before (point)) ?\t) ; XEmacs
425             (let ((col (current-column)))
426               (backward-char 1)
427               (setq col (- col (current-column)))
428               (insert-char ?\ col)
429               (delete-char 1)))
430         (backward-char 1)
431         (setq count (1- count)))))