1 ;;; delim-col.el --- prettify all columns in a region or rectangle
3 ;; Copyright (C) 1999-2018 Free Software Foundation, Inc.
5 ;; Author: Vinicius Jose Latorre <viniciusjl@ig.com.br>
6 ;; Maintainer: Vinicius Jose Latorre <viniciusjl@ig.com.br>
9 ;; X-URL: http://www.emacswiki.org/cgi-bin/wiki/ViniciusJoseLatorre
11 ;; This file is part of GNU Emacs.
13 ;; GNU Emacs 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.
18 ;; GNU Emacs 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.
23 ;; You should have received a copy of the GNU General Public License
24 ;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
28 ;; delim-col helps to prettify columns in a text region or rectangle.
30 ;; To use it, make sure that this file is in load-path and insert in your
33 ;; (require 'delim-col)
35 ;; If you have, for example, the following columns:
42 ;; And the following settings:
44 ;; (setq delimit-columns-str-before "[ ")
45 ;; (setq delimit-columns-str-after " ]")
46 ;; (setq delimit-columns-str-separator ", ")
47 ;; (setq delimit-columns-before "")
48 ;; (setq delimit-columns-after "")
49 ;; (setq delimit-columns-separator "\t")
50 ;; (setq delimit-columns-format 'separator)
51 ;; (setq delimit-columns-extra t)
53 ;; If you select the lines above and type:
55 ;; M-x delimit-columns-region RET
57 ;; You obtain the following result:
60 ;; [ aaaa, bb , ccc , ddddd ]
61 ;; [ aaa , bbb, cccc , dddd ]
62 ;; [ aa , bb , ccccccc, ddd ]
64 ;; But if you select start from the very first b to the very last c and type:
66 ;; M-x delimit-columns-rectangle RET
68 ;; You obtain the following result:
71 ;; aaaa [ bb , ccc ] ddddd
72 ;; aaa [ bbb, cccc ] dddd
73 ;; aa [ bb , ccccccc ] ddd
75 ;; Now, if we change settings to:
77 ;; (setq delimit-columns-before "<")
78 ;; (setq delimit-columns-after ">")
80 ;; For the `delimit-columns-region' example above, the result is:
82 ;; [ <a> , <b> , <c> , <d> ]
83 ;; [ <aaaa>, <bb> , <ccc> , <ddddd> ]
84 ;; [ <aaa> , <bbb>, <cccc> , <dddd> ]
85 ;; [ <aa> , <bb> , <ccccccc>, <ddd> ]
87 ;; And for the `delimit-columns-rectangle' example above, the result is:
90 ;; aaaa [ <bb> , <ccc> ] ddddd
91 ;; aaa [ <bbb>, <cccc> ] dddd
92 ;; aa [ <bb> , <ccccccc> ] ddd
94 ;; Note that `delimit-columns-region' operates over all text region
95 ;; selected, extending the region start to the beginning of line and the
96 ;; region end to the end of line. While `delimit-columns-rectangle'
97 ;; operates over the text rectangle selected which rectangle diagonal is
98 ;; given by the region start and end.
100 ;; See `delimit-columns-format' variable documentation for column formatting.
102 ;; `delimit-columns-region' is useful when you have columns of text that
103 ;; are not well aligned, like:
107 ;; porcupine strawberry airplane
109 ;; `delimit-columns-region' and `delimit-columns-rectangle' handle lines
110 ;; with different number of columns, like:
113 ;; dog pineapple car EXTRA
114 ;; porcupine strawberry airplane
116 ;; Use `delimit-columns-customize' to customize delim-col package variables.
121 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
124 (defgroup columns nil
126 :link
'(emacs-library-link :tag
"Source Lisp File" "delim-col.el")
127 :prefix
"delimit-columns-"
130 (defcustom delimit-columns-str-before
""
131 "Specify a string to be inserted before all columns."
132 :type
'(string :tag
"Before All Columns")
135 (defcustom delimit-columns-str-separator
", "
136 "Specify a string to be inserted between each column."
137 :type
'(string :tag
"Between Each Column")
140 (defcustom delimit-columns-str-after
""
141 "Specify a string to be inserted after all columns."
142 :type
'(string :tag
"After All Columns")
145 (defcustom delimit-columns-before
""
146 "Specify a string to be inserted before each column."
147 :type
'(string :tag
"Before Each Column")
150 (defcustom delimit-columns-after
""
151 "Specify a string to be inserted after each column."
152 :type
'(string :tag
"After Each Column")
155 (defcustom delimit-columns-separator
"\t"
156 "Specify a regexp which separates each column."
157 :type
'(regexp :tag
"Column Separator")
160 (defcustom delimit-columns-format t
161 "Specify how to format columns.
163 For examples below, consider:
165 + columns `ccc' and `dddd',
166 + the maximum column length for each column is 6,
167 + and the following settings:
168 (setq delimit-columns-before \"<\")
169 (setq delimit-columns-after \">\")
170 (setq delimit-columns-separator \":\")
174 nil no formatting. That is, `delimit-columns-after' is followed by
175 `delimit-columns-separator'.
176 For example, the result is: \"<ccc>:<dddd>:\"
178 t align columns. That is, `delimit-columns-after' is followed by
179 `delimit-columns-separator' and then followed by spaces.
180 For example, the result is: \"<ccc>: <dddd>: \"
182 `separator' align separators. That is, `delimit-columns-after' is followed
183 by spaces and then followed by `delimit-columns-separator'.
184 For example, the result is: \"<ccc> :<dddd> :\"
186 `padding' format column by filling with spaces before
187 `delimit-columns-after'. That is, spaces are followed by
188 `delimit-columns-after' and then followed by
189 `delimit-columns-separator'.
190 For example, the result is: \"<ccc >:<dddd >:\"
192 Any other value is treated as t."
193 :type
'(choice :menu-tag
"Column Formatting"
194 :tag
"Column Formatting"
195 (const :tag
"No Formatting" nil
)
196 (const :tag
"Column Alignment" t
)
197 (const :tag
"Separator Alignment" separator
)
198 (const :tag
"Column Padding" padding
))
201 (defcustom delimit-columns-extra t
202 "Non-nil means that lines will have the same number of columns.
204 This has effect only when there are lines with different number of columns."
205 :type
'(boolean :tag
"Lines With Same Number Of Column")
208 (defcustom delimit-columns-start
0
209 "Specify column number to start prettifying.
211 See also `delimit-columns-end' for documentation.
213 The following relation must hold:
214 0 <= delimit-columns-start <= delimit-columns-end
216 The column number start from 0 and it's relative to the beginning of selected
217 region. So if you selected a text region, the first column (column 0) is
218 located at beginning of line. If you selected a text rectangle, the first
219 column (column 0) is located at left corner."
220 :type
'(integer :tag
"Column Start")
223 (defcustom delimit-columns-end
1000000
224 "Specify column number to end prettifying.
226 See also `delimit-columns-start' for documentation.
228 The following relation must hold:
229 0 <= delimit-columns-start <= delimit-columns-end
231 The column number start from 0 and it's relative to the beginning of selected
232 region. So if you selected a text region, the first column (column 0) is
233 located at beginning of line. If you selected a text rectangle, the first
234 column (column 0) is located at left corner."
235 :type
'(integer :tag
"Column End")
239 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
243 ;; to avoid compilation gripes
244 (defvar delimit-columns-max nil
)
245 (defvar delimit-columns-limit nil
)
249 (defun delimit-columns-customize ()
250 "Customization of `columns' group."
252 (customize-group 'columns
))
255 (defmacro delimit-columns-str
(str)
256 `(if (stringp ,str
) ,str
""))
260 (defun delimit-columns-region (start end
)
261 "Prettify all columns in a text region.
263 START and END delimits the text region."
265 (let ((delimit-columns-str-before
266 (delimit-columns-str delimit-columns-str-before
))
267 (delimit-columns-str-separator
268 (delimit-columns-str delimit-columns-str-separator
))
269 (delimit-columns-str-after
270 (delimit-columns-str delimit-columns-str-after
))
271 (delimit-columns-before
272 (delimit-columns-str delimit-columns-before
))
273 (delimit-columns-after
274 (delimit-columns-str delimit-columns-after
))
275 (delimit-columns-start
276 (if (and (integerp delimit-columns-start
)
277 (>= delimit-columns-start
0))
278 delimit-columns-start
281 (if (integerp delimit-columns-end
)
284 (delimit-columns-limit (make-marker))
285 (the-end (copy-marker end
))
287 (when (<= delimit-columns-start delimit-columns-end
)
291 ;; get maximum length for each column
292 (and delimit-columns-format
294 (while (< (point) the-end
)
295 (delimit-columns-rectangle-max
301 (while (< (point) the-end
)
302 (delimit-columns-rectangle-line
308 (set-marker delimit-columns-limit nil
)
309 (set-marker the-end nil
)))))
316 (defun delimit-columns-rectangle (start end
)
317 "Prettify all columns in a text rectangle.
319 START and END delimits the corners of text rectangle."
321 (let ((delimit-columns-str-before
322 (delimit-columns-str delimit-columns-str-before
))
323 (delimit-columns-str-separator
324 (delimit-columns-str delimit-columns-str-separator
))
325 (delimit-columns-str-after
326 (delimit-columns-str delimit-columns-str-after
))
327 (delimit-columns-before
328 (delimit-columns-str delimit-columns-before
))
329 (delimit-columns-after
330 (delimit-columns-str delimit-columns-after
))
331 (delimit-columns-start
332 (if (and (integerp delimit-columns-start
)
333 (>= delimit-columns-start
0))
334 delimit-columns-start
337 (if (integerp delimit-columns-end
)
340 (delimit-columns-limit (make-marker))
341 (the-end (copy-marker end
))
343 (when (<= delimit-columns-start delimit-columns-end
)
344 ;; get maximum length for each column
345 (and delimit-columns-format
347 (operate-on-rectangle 'delimit-columns-rectangle-max
351 (operate-on-rectangle 'delimit-columns-rectangle-line
354 (set-marker delimit-columns-limit nil
)
355 (set-marker the-end nil
))))
358 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
359 ;; Internal Variables and Functions:
362 (defun delimit-columns-rectangle-max (startpos &optional _ignore1 _ignore2
)
363 (set-marker delimit-columns-limit
(point))
367 ;; get current column length
369 (setq origin
(current-column))
370 (re-search-forward delimit-columns-separator
371 delimit-columns-limit
'move
))
373 (goto-char (match-beginning 0))
374 (setq values
(cons (- (current-column) origin
)
376 (setq ncol
(1+ ncol
)))
377 (setq values
(cons (- (current-column) origin
)
379 ;; extend delimit-columns-max, if needed
380 (let ((index (length delimit-columns-max
)))
382 (let ((extend (make-vector ncol
0)))
384 (setq index
(1- index
))
385 (aset extend index
(aref delimit-columns-max index
)))
386 (setq delimit-columns-max extend
))))
387 ;; get maximum column length
389 (setq ncol
(1- ncol
))
390 (aset delimit-columns-max ncol
(max (aref delimit-columns-max ncol
)
392 (setq values
(cdr values
)))))
395 (defun delimit-columns-rectangle-line (startpos &optional _ignore1 _ignore2
)
396 (let ((len (length delimit-columns-max
))
399 (set-marker delimit-columns-limit
(point))
401 ;; skip initial columns
402 (while (and (< ncol delimit-columns-start
)
403 (< (point) delimit-columns-limit
)
404 (re-search-forward delimit-columns-separator
405 delimit-columns-limit
'move
))
406 (setq ncol
(1+ ncol
)))
407 ;; insert first formatting
408 (insert delimit-columns-str-before delimit-columns-before
)
409 ;; Adjust all columns but last one
411 (setq origin
(current-column))
412 (and (< (point) delimit-columns-limit
)
413 (re-search-forward delimit-columns-separator
414 delimit-columns-limit
'move
)
415 (or (< ncol delimit-columns-end
)
417 (goto-char (match-beginning 0))
419 (delete-region (match-beginning 0) (point))
420 (delimit-columns-format
421 (and delimit-columns-format
422 (make-string (- (aref delimit-columns-max ncol
)
423 (- (current-column) origin
))
425 (setq ncol
(1+ ncol
)))
426 ;; Prepare last column spaces
427 (let ((spaces (and delimit-columns-format
428 (make-string (- (aref delimit-columns-max ncol
)
429 (- (current-column) origin
))
431 ;; Adjust extra columns, if needed
432 (and delimit-columns-extra
433 (while (and (< (setq ncol
(1+ ncol
)) len
)
434 (<= ncol delimit-columns-end
))
435 (delimit-columns-format spaces
)
436 (setq spaces
(and delimit-columns-format
437 (make-string (aref delimit-columns-max ncol
)
439 ;; insert last formatting
440 (cond ((null delimit-columns-format
)
441 (insert delimit-columns-after delimit-columns-str-after
))
442 ((eq delimit-columns-format
'padding
)
443 (insert spaces delimit-columns-after delimit-columns-str-after
))
445 (insert delimit-columns-after spaces delimit-columns-str-after
))
447 (goto-char (max (point) delimit-columns-limit
))))
450 (defun delimit-columns-format (spaces)
451 (cond ((null delimit-columns-format
)
452 (insert delimit-columns-after
453 delimit-columns-str-separator
454 delimit-columns-before
))
455 ((eq delimit-columns-format
'separator
)
456 (insert delimit-columns-after
458 delimit-columns-str-separator
459 delimit-columns-before
))
460 ((eq delimit-columns-format
'padding
)
462 delimit-columns-after
463 delimit-columns-str-separator
464 delimit-columns-before
))
466 (insert delimit-columns-after
467 delimit-columns-str-separator
469 delimit-columns-before
))
473 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
479 ;;; delim-col.el ends here