* align.el:
[bpt/emacs.git] / lisp / delim-col.el
CommitLineData
e8af40ee 1;;; delim-col.el --- prettify all columns in a region or rectangle
854f487a 2
e91081eb 3;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004,
409cc4a3 4;; 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
854f487a 5
739ce395
VJL
6;; Author: Vinicius Jose Latorre <viniciusjl@ig.com.br>
7;; Maintainer: Vinicius Jose Latorre <viniciusjl@ig.com.br>
e8af40ee
PJ
8;; Version: 2.1
9;; Keywords: internal
63381c91 10;; X-URL: http://www.emacswiki.org/cgi-bin/wiki/ViniciusJoseLatorre
854f487a
DL
11
12;; This file is part of GNU Emacs.
13
eb3fa2cf 14;; GNU Emacs is free software: you can redistribute it and/or modify
1416c7ff 15;; it under the terms of the GNU General Public License as published by
eb3fa2cf
GM
16;; the Free Software Foundation, either version 3 of the License, or
17;; (at your option) any later version.
854f487a 18
1416c7ff
DL
19;; GNU Emacs is distributed in the hope that it will be useful,
20;; but WITHOUT ANY WARRANTY; without even the implied warranty of
21;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22;; GNU General Public License for more details.
854f487a 23
1416c7ff 24;; You should have received a copy of the GNU General Public License
eb3fa2cf 25;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
854f487a
DL
26
27;;; Commentary:
28
29;; delim-col helps to prettify columns in a text region or rectangle.
30;;
31;; To use it, make sure that this file is in load-path and insert in your
32;; .emacs:
33;;
34;; (require 'delim-col)
35;;
36;; If you have, for example, the following columns:
37;;
38;; a b c d
39;; aaaa bb ccc ddddd
395be66e 40;; aaa bbb cccc dddd
854f487a
DL
41;; aa bb ccccccc ddd
42;;
43;; And the following settings:
44;;
45;; (setq delimit-columns-str-before "[ ")
46;; (setq delimit-columns-str-after " ]")
47;; (setq delimit-columns-str-separator ", ")
395be66e
GM
48;; (setq delimit-columns-before "")
49;; (setq delimit-columns-after "")
854f487a 50;; (setq delimit-columns-separator "\t")
395be66e
GM
51;; (setq delimit-columns-format 'separator)
52;; (setq delimit-columns-extra t)
854f487a
DL
53;;
54;; If you select the lines above and type:
55;;
56;; M-x delimit-columns-region RET
57;;
58;; You obtain the following result:
59;;
60;; [ a , b , c , d ]
61;; [ aaaa, bb , ccc , ddddd ]
62;; [ aaa , bbb, cccc , dddd ]
63;; [ aa , bb , ccccccc, ddd ]
64;;
395be66e 65;; But if you select start from the very first b to the very last c and type:
854f487a
DL
66;;
67;; M-x delimit-columns-rectangle RET
68;;
69;; You obtain the following result:
70;;
71;; a [ b , c ] d
72;; aaaa [ bb , ccc ] ddddd
73;; aaa [ bbb, cccc ] dddd
74;; aa [ bb , ccccccc ] ddd
75;;
395be66e
GM
76;; Now, if we change settings to:
77;;
78;; (setq delimit-columns-before "<")
79;; (setq delimit-columns-after ">")
80;;
81;; For the `delimit-columns-region' example above, the result is:
82;;
83;; [ <a> , <b> , <c> , <d> ]
84;; [ <aaaa>, <bb> , <ccc> , <ddddd> ]
85;; [ <aaa> , <bbb>, <cccc> , <dddd> ]
86;; [ <aa> , <bb> , <ccccccc>, <ddd> ]
87;;
88;; And for the `delimit-columns-rectangle' example above, the result is:
89;;
90;; a [ <b> , <c> ] d
91;; aaaa [ <bb> , <ccc> ] ddddd
92;; aaa [ <bbb>, <cccc> ] dddd
93;; aa [ <bb> , <ccccccc> ] ddd
94;;
854f487a
DL
95;; Note that `delimit-columns-region' operates over all text region
96;; selected, extending the region start to the beginning of line and the
97;; region end to the end of line. While `delimit-columns-rectangle'
98;; operates over the text rectangle selected which rectangle diagonal is
99;; given by the region start and end.
100;;
395be66e
GM
101;; See `delimit-columns-format' variable documentation for column formating.
102;;
854f487a
DL
103;; `delimit-columns-region' is useful when you have columns of text that
104;; are not well aligned, like:
105;;
106;; horse apple bus
107;; dog pineapple car
108;; porcupine strawberry airplane
109;;
110;; `delimit-columns-region' and `delimit-columns-rectangle' handle lines
111;; with different number of columns, like:
112;;
113;; horse apple bus
114;; dog pineapple car EXTRA
115;; porcupine strawberry airplane
395be66e
GM
116;;
117;; Use `delimit-columns-customize' to customize delim-col package variables.
854f487a
DL
118
119;;; Code:
120
121
122;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
123;; User Options:
124
395be66e 125(defgroup columns nil
0c5a1b51 126 "Prettify columns."
395be66e
GM
127 :link '(emacs-library-link :tag "Source Lisp File" "delim-col.el")
128 :prefix "delimit-columns-"
129 :group 'internal)
130
131(defcustom delimit-columns-str-before ""
9201cc28 132 "Specify a string to be inserted before all columns."
395be66e
GM
133 :type '(string :tag "Before All Columns")
134 :group 'columns)
135
136(defcustom delimit-columns-str-separator ", "
9201cc28 137 "Specify a string to be inserted between each column."
395be66e
GM
138 :type '(string :tag "Between Each Column")
139 :group 'columns)
140
141(defcustom delimit-columns-str-after ""
9201cc28 142 "Specify a string to be inserted after all columns."
395be66e
GM
143 :type '(string :tag "After All Columns")
144 :group 'columns)
145
146(defcustom delimit-columns-before ""
9201cc28 147 "Specify a string to be inserted before each column."
395be66e
GM
148 :type '(string :tag "Before Each Column")
149 :group 'columns)
150
151(defcustom delimit-columns-after ""
9201cc28 152 "Specify a string to be inserted after each column."
395be66e
GM
153 :type '(string :tag "After Each Column")
154 :group 'columns)
155
156(defcustom delimit-columns-separator "\t"
9201cc28 157 "Specify a regexp which separates each column."
395be66e
GM
158 :type '(regexp :tag "Column Separator")
159 :group 'columns)
160
161(defcustom delimit-columns-format t
9201cc28 162 "Specify how to format columns.
395be66e
GM
163
164For examples below, consider:
165
166 + columns `ccc' and `dddd',
167 + the maximum column length for each column is 6,
168 + and the following settings:
169 (setq delimit-columns-before \"<\")
170 (setq delimit-columns-after \">\")
171 (setq delimit-columns-separator \":\")
172
173Valid values are:
174
175 nil no formating. That is, `delimit-columns-after' is followed by
176 `delimit-columns-separator'.
177 For example, the result is: \"<ccc>:<dddd>:\"
178
179 t align columns. That is, `delimit-columns-after' is followed by
180 `delimit-columns-separator' and then followed by spaces.
181 For example, the result is: \"<ccc>: <dddd>: \"
182
183 'separator align separators. That is, `delimit-columns-after' is followed
184 by spaces and then followed by `delimit-columns-separator'.
185 For example, the result is: \"<ccc> :<dddd> :\"
186
187 'padding format column by filling with spaces before
188 `delimit-columns-after'. That is, spaces are followed by
189 `delimit-columns-after' and then followed by
190 `delimit-columns-separator'.
191 For example, the result is: \"<ccc >:<dddd >:\"
192
193Any other value is treated as t."
194 :type '(choice :menu-tag "Column Formating"
195 :tag "Column Formating"
196 (const :tag "No Formating" nil)
197 (const :tag "Column Alignment" t)
198 (const :tag "Separator Aligment" separator)
199 (const :tag "Column Padding" padding))
200 :group 'columns)
201
202(defcustom delimit-columns-extra t
9201cc28 203 "Non-nil means that lines will have the same number of columns.
395be66e
GM
204
205This has effect only when there are lines with different number of columns."
206 :type '(boolean :tag "Lines With Same Number Of Column")
207 :group 'columns)
208
209(defcustom delimit-columns-start 0
9201cc28 210 "Specify column number to start prettifing.
395be66e
GM
211
212See also `delimit-columns-end' for documentation.
213
214The following relation must hold:
215 0 <= delimit-columns-start <= delimit-columns-end
216
217The column number start from 0 and it's relative to the beginning of selected
218region. So if you selected a text region, the first column (column 0) is
219located at beginning of line. If you selected a text rectangle, the first
220column (column 0) is located at left corner."
221 :type '(integer :tag "Column Start")
222 :group 'columns)
223
224(defcustom delimit-columns-end 1000000
9201cc28 225 "Specify column number to end prettifing.
395be66e
GM
226
227See also `delimit-columns-start' for documentation.
228
229The following relation must hold:
230 0 <= delimit-columns-start <= delimit-columns-end
231
232The column number start from 0 and it's relative to the beginning of selected
233region. So if you selected a text region, the first column (column 0) is
234located at beginning of line. If you selected a text rectangle, the first
235column (column 0) is located at left corner."
236 :type '(integer :tag "Column End")
237 :group 'columns)
854f487a
DL
238
239\f
240;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
241;; User Commands:
242
243
395be66e
GM
244;;;###autoload
245(defun delimit-columns-customize ()
246 "Customization of `columns' group."
247 (interactive)
248 (customize-group 'columns))
249
250
9e20722f
GM
251(defmacro delimit-columns-str (str)
252 `(if (stringp ,str) ,str ""))
253
254
854f487a
DL
255;;;###autoload
256(defun delimit-columns-region (start end)
257 "Prettify all columns in a text region.
258
259START and END delimits the text region."
260 (interactive "*r")
261 (let ((delimit-columns-str-before
9e20722f 262 (delimit-columns-str delimit-columns-str-before))
854f487a 263 (delimit-columns-str-separator
9e20722f 264 (delimit-columns-str delimit-columns-str-separator))
854f487a 265 (delimit-columns-str-after
9e20722f 266 (delimit-columns-str delimit-columns-str-after))
395be66e 267 (delimit-columns-before
9e20722f 268 (delimit-columns-str delimit-columns-before))
395be66e 269 (delimit-columns-after
9e20722f 270 (delimit-columns-str delimit-columns-after))
395be66e
GM
271 (delimit-columns-start
272 (if (and (integerp delimit-columns-start)
273 (>= delimit-columns-start 0))
274 delimit-columns-start
275 0))
276 (delimit-columns-end
277 (if (integerp delimit-columns-end)
278 delimit-columns-end
279 1000000))
854f487a
DL
280 (delimit-columns-limit (make-marker))
281 (the-end (copy-marker end))
282 delimit-columns-max)
395be66e 283 (when (<= delimit-columns-start delimit-columns-end)
854f487a 284 (save-excursion
395be66e
GM
285 (goto-char start)
286 (beginning-of-line)
287 ;; get maximum length for each column
35923e46 288 (and delimit-columns-format
395be66e
GM
289 (save-excursion
290 (while (< (point) the-end)
291 (delimit-columns-rectangle-max
292 (prog1
293 (point)
294 (end-of-line)))
295 (forward-char 1))))
296 ;; prettify columns
854f487a 297 (while (< (point) the-end)
395be66e 298 (delimit-columns-rectangle-line
854f487a
DL
299 (prog1
300 (point)
301 (end-of-line)))
395be66e
GM
302 (forward-char 1))
303 ;; nullify markers
304 (set-marker delimit-columns-limit nil)
305 (set-marker the-end nil)))))
854f487a
DL
306
307
308(require 'rect)
309
310
311;;;###autoload
312(defun delimit-columns-rectangle (start end)
313 "Prettify all columns in a text rectangle.
314
315START and END delimits the corners of text rectangle."
316 (interactive "*r")
317 (let ((delimit-columns-str-before
9e20722f 318 (delimit-columns-str delimit-columns-str-before))
854f487a 319 (delimit-columns-str-separator
9e20722f 320 (delimit-columns-str delimit-columns-str-separator))
854f487a 321 (delimit-columns-str-after
9e20722f 322 (delimit-columns-str delimit-columns-str-after))
395be66e 323 (delimit-columns-before
9e20722f 324 (delimit-columns-str delimit-columns-before))
395be66e 325 (delimit-columns-after
9e20722f 326 (delimit-columns-str delimit-columns-after))
395be66e
GM
327 (delimit-columns-start
328 (if (and (integerp delimit-columns-start)
329 (>= delimit-columns-start 0))
330 delimit-columns-start
331 0))
332 (delimit-columns-end
333 (if (integerp delimit-columns-end)
334 delimit-columns-end
335 1000000))
854f487a
DL
336 (delimit-columns-limit (make-marker))
337 (the-end (copy-marker end))
338 delimit-columns-max)
395be66e
GM
339 (when (<= delimit-columns-start delimit-columns-end)
340 ;; get maximum length for each column
35923e46 341 (and delimit-columns-format
395be66e
GM
342 (save-excursion
343 (operate-on-rectangle 'delimit-columns-rectangle-max
344 start the-end nil)))
345 ;; prettify columns
346 (save-excursion
347 (operate-on-rectangle 'delimit-columns-rectangle-line
348 start the-end nil))
349 ;; nullify markers
350 (set-marker delimit-columns-limit nil)
351 (set-marker the-end nil))))
854f487a
DL
352
353\f
354;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
355;; Internal Variables and Functions:
356
357
358;; to avoid compilation gripes
359(defvar delimit-columns-max nil)
360(defvar delimit-columns-limit nil)
361
362
a2bdcec4 363(defun delimit-columns-rectangle-max (startpos &optional ignore1 ignore2)
854f487a
DL
364 (set-marker delimit-columns-limit (point))
365 (goto-char startpos)
366 (let ((ncol 1)
367 origin values)
368 ;; get current column length
369 (while (progn
370 (setq origin (current-column))
371 (re-search-forward delimit-columns-separator
372 delimit-columns-limit 'move))
373 (save-excursion
374 (goto-char (match-beginning 0))
375 (setq values (cons (- (current-column) origin)
376 values)))
377 (setq ncol (1+ ncol)))
378 (setq values (cons (- (current-column) origin)
379 values))
380 ;; extend delimit-columns-max, if needed
381 (let ((index (length delimit-columns-max)))
382 (and (> ncol index)
383 (let ((extend (make-vector ncol 0)))
384 (while (> index 0)
385 (setq index (1- index))
386 (aset extend index (aref delimit-columns-max index)))
387 (setq delimit-columns-max extend))))
388 ;; get maximum column length
389 (while values
390 (setq ncol (1- ncol))
391 (aset delimit-columns-max ncol (max (aref delimit-columns-max ncol)
392 (car values)))
393 (setq values (cdr values)))))
394
395
a2bdcec4 396(defun delimit-columns-rectangle-line (startpos &optional ignore1 ignore2)
395be66e
GM
397 (let ((len (length delimit-columns-max))
398 (ncol 0)
854f487a
DL
399 origin)
400 (set-marker delimit-columns-limit (point))
401 (goto-char startpos)
395be66e
GM
402 ;; skip initial columns
403 (while (and (< ncol delimit-columns-start)
404 (< (point) delimit-columns-limit)
405 (re-search-forward delimit-columns-separator
406 delimit-columns-limit 'move))
407 (setq ncol (1+ ncol)))
408 ;; insert first formating
409 (insert delimit-columns-str-before delimit-columns-before)
854f487a
DL
410 ;; Adjust all columns but last one
411 (while (progn
412 (setq origin (current-column))
413 (and (< (point) delimit-columns-limit)
414 (re-search-forward delimit-columns-separator
395be66e
GM
415 delimit-columns-limit 'move)
416 (or (< ncol delimit-columns-end)
417 (progn
418 (goto-char (match-beginning 0))
419 nil))))
854f487a 420 (delete-region (match-beginning 0) (point))
395be66e
GM
421 (delimit-columns-format
422 (and delimit-columns-format
423 (make-string (- (aref delimit-columns-max ncol)
854f487a 424 (- (current-column) origin))
0c5a1b51 425 ?\s)))
395be66e
GM
426 (setq ncol (1+ ncol)))
427 ;; Prepare last column spaces
428 (let ((spaces (and delimit-columns-format
429 (make-string (- (aref delimit-columns-max ncol)
430 (- (current-column) origin))
0c5a1b51 431 ?\s))))
395be66e
GM
432 ;; Adjust extra columns, if needed
433 (and delimit-columns-extra
434 (while (and (< (setq ncol (1+ ncol)) len)
435 (<= ncol delimit-columns-end))
436 (delimit-columns-format spaces)
437 (setq spaces (and delimit-columns-format
438 (make-string (aref delimit-columns-max ncol)
0c5a1b51 439 ?\s)))))
395be66e
GM
440 ;; insert last formating
441 (cond ((null delimit-columns-format)
442 (insert delimit-columns-after delimit-columns-str-after))
443 ((eq delimit-columns-format 'padding)
444 (insert spaces delimit-columns-after delimit-columns-str-after))
445 (t
446 (insert delimit-columns-after spaces delimit-columns-str-after))
447 ))
448 (goto-char (max (point) delimit-columns-limit))))
449
450
451(defun delimit-columns-format (spaces)
452 (cond ((null delimit-columns-format)
453 (insert delimit-columns-after
454 delimit-columns-str-separator
455 delimit-columns-before))
456 ((eq delimit-columns-format 'separator)
457 (insert delimit-columns-after
458 spaces
459 delimit-columns-str-separator
460 delimit-columns-before))
461 ((eq delimit-columns-format 'padding)
462 (insert spaces
463 delimit-columns-after
464 delimit-columns-str-separator
465 delimit-columns-before))
466 (t
467 (insert delimit-columns-after
468 delimit-columns-str-separator
469 spaces
470 delimit-columns-before))
471 ))
854f487a
DL
472
473\f
474;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
475
476
477(provide 'delim-col)
478
479
cbee283d 480;; arch-tag: 1cc0c5c5-1b2a-43e4-9ba5-bf9441cfd1a9
854f487a 481;;; delim-col.el ends here