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