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