|
|
1.1 root 1: ;; Commands to sort text in an Emacs buffer.
2: ;; Copyright (C) 1986, 1987 Free Software Foundation, Inc.
3:
4: ;; This file is part of GNU Emacs.
5:
6: ;; GNU Emacs is distributed in the hope that it will be useful,
7: ;; but WITHOUT ANY WARRANTY. No author or distributor
8: ;; accepts responsibility to anyone for the consequences of using it
9: ;; or for whether it serves any particular purpose or works at all,
10: ;; unless he says so in writing. Refer to the GNU Emacs General Public
11: ;; License for full details.
12:
13: ;; Everyone is granted permission to copy, modify and redistribute
14: ;; GNU Emacs, but only under the conditions described in the
15: ;; GNU Emacs General Public License. A copy of this license is
16: ;; supposed to have been given to you along with GNU Emacs so you
17: ;; can know your rights and responsibilities. It should be in a
18: ;; file named COPYING. Among other things, the copyright notice
19: ;; and this notice must be preserved on all copies.
20:
21: (provide 'sort)
22:
23: ;; Original version of most of this contributed by Howie Kaye
24:
25: (defun sort-subr (reverse nextrecfun endrecfun &optional startkeyfun endkeyfun)
26: "General text sorting routine to divide buffer into records and sort them.
27: Arguments are REVERSE NEXTRECFUN ENDRECFUN &optional STARTKEYFUN ENDKEYFUN.
28:
29: We consider this portion of the buffer to be divided into disjoint pieces
30: called sort records. A portion of each sort record (perhaps all of it)
31: is designated as the sort key. The records are rearranged in the buffer
32: in order by their sort keys. The records may or may not be contiguous.
33:
34: Usually the records are rearranged in order of ascending sort key.
35: If REVERSE is non-nil, they are rearranged in order of descending sort key.
36:
37: The next four arguments are functions to be called to move point
38: across a sort record. They will be called many times from within sort-subr.
39:
40: NEXTRECFUN is called with point at the end of the previous record.
41: It moves point to the start of the next record.
42: The first record is assumed to start at the position of point when sort-subr
43: is called.
44:
45: ENDRECFUN is is called with point within the record.
46: It should move point to the end of the record.
47:
48: STARTKEYFUN may moves from the start of the record to the start of the key.
49: It may return either return a non-nil value to be used as the key, or
50: else the key will be the substring between the values of point after
51: STARTKEYFUNC and ENDKEYFUN are called.
52:
53: ENDKEYFUN moves from the start of the sort key to the end of the sort key.
54: ENDRECFUN may be nil if STARTKEYFUN returns a value or if it would be the
55: same as ENDRECFUN."
56: (save-excursion
57: (message "Finding sort keys...")
58: (let* ((sort-lists (sort-build-lists nextrecfun endrecfun
59: startkeyfun endkeyfun))
60: (old (reverse sort-lists)))
61: (if (null sort-lists)
62: ()
63: (or reverse (setq sort-lists (nreverse sort-lists)))
64: (message "Sorting records...")
65: (setq sort-lists
66: (if (fboundp 'sortcar)
67: (sortcar sort-lists
68: (cond ((numberp (car (car sort-lists)))
69: '<)
70: ((consp (car (car sort-lists)))
71: 'buffer-substring-lessp)
72: (t
73: 'string<)))
74: (sort sort-lists
75: (cond ((numberp (car (car sort-lists)))
76: (function
77: (lambda (a b)
78: (< (car a) (car b)))))
79: ((consp (car (car sort-lists)))
80: (function
81: (lambda (a b)
82: (buffer-substring-lessp (car a) (car b)))))
83: (t
84: (function
85: (lambda (a b)
86: (string< (car a) (car b)))))))))
87: (if reverse (setq sort-lists (nreverse sort-lists)))
88: (message "Reordering buffer...")
89: (sort-reorder-buffer sort-lists old)))
90: (message "Reordering buffer... Done")))
91:
92: ;; Parse buffer into records using the arguments as Lisp expressions;
93: ;; return a list of records. Each record looks like (KEY STARTPOS ENDPOS)
94: ;; where KEY is the sort key (a number or string),
95: ;; and STARTPOS and ENDPOS are the bounds of this record in the buffer.
96:
97: ;; The records appear in the list lastmost first!
98:
99: (defun sort-build-lists (nextrecfun endrecfun startkeyfun endkeyfun)
100: (let ((sort-lists ())
101: (start-rec nil)
102: done key)
103: ;; Loop over sort records.
104: ;(goto-char (point-min)) -- it is the caller's responsibility to
105: ;arrange this if necessary
106: (while (not (eobp))
107: (setq start-rec (point)) ;save record start
108: (setq done nil)
109: ;; Get key value, or move to start of key.
110: (setq key (catch 'key
111: (or (and startkeyfun (funcall startkeyfun))
112: ;; If key was not returned as value,
113: ;; move to end of key and get key from the buffer.
114: (let ((start (point)))
115: (funcall (or endkeyfun
116: (prog1 endrecfun (setq done t))))
117: (if (fboundp 'buffer-substring-lessp)
118: (cons start (point))
119: (buffer-substring start (point)))))))
120: ;; Move to end of this record (start of next one, or end of buffer).
121: (cond ((prog1 done (setq done nil)))
122: (endrecfun (funcall endrecfun))
123: (nextrecfun (funcall nextrecfun) (setq done t)))
124: (if key (setq sort-lists (cons
125: ;; consing optimization in case in which key
126: ;; is same as record.
127: (if (and (consp key)
128: (equal (car key) start-rec)
129: (equal (cdr key) (point)))
130: (cons key key)
131: (list key start-rec (point)))
132: sort-lists)))
133: (and (not done) nextrecfun (funcall nextrecfun)))
134: sort-lists))
135:
136: (defun sort-reorder-buffer (sort-lists old)
137: (let ((inhibit-quit t)
138: (last (point-min))
139: (min (point-min)) (max (point-max)))
140: (while sort-lists
141: (goto-char (point-max))
142: (insert-buffer-substring (current-buffer)
143: last
144: (nth 1 (car old)))
145: (goto-char (point-max))
146: (insert-buffer-substring (current-buffer)
147: (nth 1 (car sort-lists))
148: (nth 2 (car sort-lists)))
149: (setq last (nth 2 (car old))
150: sort-lists (cdr sort-lists)
151: old (cdr old)))
152: (goto-char (point-max))
153: (insert-buffer-substring (current-buffer)
154: last
155: max)
156: (delete-region min max))) ;get rid of old version
157:
158: (defun sort-lines (reverse beg end)
159: "Sort lines in region alphabetically; argument means descending order.
160: Called from a program, there are three arguments:
161: REVERSE (non-nil means reverse order), BEG and END (region to sort)."
162: (interactive "P\nr")
163: (save-restriction
164: (narrow-to-region beg end)
165: (goto-char (point-min))
166: (sort-subr reverse 'forward-line 'end-of-line)))
167:
168: (defun sort-paragraphs (reverse beg end)
169: "Sort paragraphs in region alphabetically; argument means descending order.
170: Called from a program, there are three arguments:
171: REVERSE (non-nil means reverse order), BEG and END (region to sort)."
172: (interactive "P\nr")
173: (save-restriction
174: (narrow-to-region beg end)
175: (goto-char (point-min))
176: (sort-subr reverse
177: (function (lambda () (skip-chars-forward "\n \t\f")))
178: 'forward-paragraph)))
179:
180: (defun sort-pages (reverse beg end)
181: "Sort pages in region alphabetically; argument means descending order.
182: Called from a program, there are three arguments:
183: REVERSE (non-nil means reverse order), BEG and END (region to sort)."
184: (interactive "P\nr")
185: (save-restriction
186: (narrow-to-region beg end)
187: (goto-char (point-min))
188: (sort-subr reverse
189: (function (lambda () (skip-chars-forward "\n")))
190: 'forward-page)))
191:
192: (defvar sort-fields-syntax-table nil)
193: (if sort-fields-syntax-table nil
194: (let ((table (make-syntax-table))
195: (i 0))
196: (while (< i 256)
197: (modify-syntax-entry i "w" table)
198: (setq i (1+ i)))
199: (modify-syntax-entry ?\ " " table)
200: (modify-syntax-entry ?\t " " table)
201: (modify-syntax-entry ?\n " " table)
202: (setq sort-fields-syntax-table table)))
203:
204: (defun sort-numeric-fields (field beg end)
205: "Sort lines in region numerically by the ARGth field of each line.
206: Fields are separated by whitespace and numbered from 1 up.
207: Specified field must contain a number in each line of the region.
208: With a negative arg, sorts by the -ARG'th field, in reverse order.
209: Called from a program, there are three arguments:
210: FIELD, BEG and END. BEG and END specify region to sort."
211: (interactive "p\nr")
212: (sort-fields-1 field beg end
213: (function (lambda ()
214: (sort-skip-fields (1- field))
215: (string-to-int
216: (buffer-substring
217: (point)
218: (save-excursion
219: (skip-chars-forward "[0-9]")
220: (point))))))
221: nil))
222:
223: (defun sort-fields (field beg end)
224: "Sort lines in region lexicographically by the ARGth field of each line.
225: Fields are separated by whitespace and numbered from 1 up.
226: With a negative arg, sorts by the -ARG'th field, in reverse order.
227: Called from a program, there are three arguments:
228: FIELD, BEG and END. BEG and END specify region to sort."
229: (interactive "p\nr")
230: (sort-fields-1 field beg end
231: (function (lambda ()
232: (sort-skip-fields (1- field))
233: nil))
234: (function (lambda () (skip-chars-forward "^ \t\n")))))
235:
236: (defun sort-fields-1 (field beg end startkeyfun endkeyfun)
237: (let ((reverse (< field 0))
238: (tbl (syntax-table)))
239: (setq field (max 1 field (- field)))
240: (unwind-protect
241: (save-restriction
242: (narrow-to-region beg end)
243: (goto-char (point-min))
244: (set-syntax-table sort-fields-syntax-table)
245: (sort-subr reverse
246: 'forward-line 'end-of-line
247: startkeyfun endkeyfun))
248: (set-syntax-table tbl))))
249:
250: (defun sort-skip-fields (n)
251: (let ((eol (save-excursion (end-of-line 1) (point))))
252: (forward-word n)
253: (if (> (point) eol)
254: (error "Line has too few fields: %s"
255: (buffer-substring (save-excursion
256: (beginning-of-line) (point))
257: eol)))
258: (skip-chars-forward " \t")))
259:
260:
261: (defun sort-regexp-fields (reverse record-regexp key-regexp beg end)
262: "Sort the region lexicographically as specifed by RECORD-REGEXP and KEY.
263: RECORD-REGEXP specifies the textual units which should be sorted.
264: For example, to sort lines RECORD-REGEXP would be \"^.*$\"
265: KEY specifies the part of each record (ie each match for RECORD-REGEXP)
266: is to be used for sorting.
267: If it is \"\\digit\" then the digit'th \"\\(...\\)\" match field from
268: RECORD-REGEXP is used.
269: If it is \"\\&\" then the whole record is used.
270: Otherwise, it is a regular-expression for which to search within the record.
271: If a match for KEY is not found within a record then that record is ignored.
272:
273: With a negative prefix arg sorts in reverse order.
274:
275: For example: to sort lines in the region by the first word on each line
276: starting with the letter \"f\",
277: RECORD-REGEXP would be \"^.*$\" and KEY \"\\<f\\w*\\>\""
278: (interactive "P\nsRegexp specifying records to sort:
279: sRegexp specifying key within record: \nr")
280: (cond ((or (equal key-regexp "") (equal key-regexp "\\&"))
281: (setq key-regexp 0))
282: ((string-match "\\`\\\\[1-9]\\'" key-regexp)
283: (setq key-regexp (- (aref key-regexp 1) ?0))))
284: (save-restriction
285: (narrow-to-region beg end)
286: (goto-char (point-min))
287: (let (sort-regexp-record-end) ;isn't dynamic scoping wonderful?
288: (re-search-forward record-regexp)
289: (setq sort-regexp-record-end (point))
290: (goto-char (match-beginning 0))
291: (sort-subr reverse
292: (function (lambda ()
293: (and (re-search-forward record-regexp nil 'move)
294: (setq sort-regexp-record-end (match-end 0))
295: (goto-char (match-beginning 0)))))
296: (function (lambda ()
297: (goto-char sort-regexp-record-end)))
298: (function (lambda ()
299: (let ((n 0))
300: (cond ((numberp key-regexp)
301: (setq n key-regexp))
302: ((re-search-forward
303: key-regexp sort-regexp-record-end t)
304: (setq n 0))
305: (t (throw 'key nil)))
306: (condition-case ()
307: (if (fboundp 'buffer-substring-lessp)
308: (cons (match-beginning n)
309: (match-end n))
310: (buffer-substring (match-beginning n)
311: (match-end n)))
312: ;; if there was no such register
313: (error (throw 'key nil))))))))))
314:
315:
316: (defun sort-columns (reverse &optional beg end)
317: "Sort lines in region alphabetically by a certain range of columns.
318: For the purpose of this command, the region includes
319: the entire line that point is in and the entire line the mark is in.
320: The column positions of point and mark bound the range of columns to sort on.
321: A prefix argument means sort into reverse order.
322:
323: Note that sort-columns uses the sort utility program and therefore
324: cannot work on text containing TAB characters. Use M-x untabify
325: to convert tabs to spaces before sorting."
326: (interactive "P\nr")
327: (save-excursion
328: (let (beg1 end1 col-beg1 col-end1 col-start col-end)
329: (goto-char (min beg end))
330: (setq col-beg1 (current-column))
331: (beginning-of-line)
332: (setq beg1 (point))
333: (goto-char (max beg end))
334: (setq col-end1 (current-column))
335: (forward-line)
336: (setq end1 (point))
337: (setq col-start (min col-beg1 col-end1))
338: (setq col-end (max col-beg1 col-end1))
339: (if (search-backward "\t" beg1 t)
340: (error "sort-columns does not work with tabs. Use M-x untabify."))
341: (call-process-region beg1 end1 "sort" t t nil
342: (if reverse "-rt\n" "-t\n")
343: (concat "+0." col-start)
344: (concat "-0." col-end)))))
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.