-
Notifications
You must be signed in to change notification settings - Fork 1
/
align.el
1564 lines (1381 loc) · 52.3 KB
/
align.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
;;; align --- align text to a specific column, by regexp
;; Copyright (C) 1999 John Wiegley
;; Author: John Wiegley <[email protected]>
;; Created: 21 Mar 1996
;; Version: 2.8
;; Keywords: convenience languages lisp
;; X-URL: http://www.emacs.org/~johnw/emacs.html
;; This program is free software; you can redistribute it and/or
;; modify it under the terms of the GNU General Public License as
;; published by the Free Software Foundation; either version 2, or (at
;; your option) any later version.
;; This program is distributed in the hope that it will be useful, but
;; WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
;; General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;; Commentary:
;; This mode allows you to align regions in a context-sensitive fashion.
;; The classic use is to align assignments:
;;
;; int a = 1;
;; short foo = 2;
;; double blah = 4;
;;
;; becomes
;;
;; int a = 1;
;; short foo = 2;
;; double blah = 4;
;;; Usage:
;; To use the aligner, simply call put the following in your .emacs
;; file; then mark a region and type `M-x align'.
;;
;; (autoload 'align "align" nil t)
;; (autoload 'align-regxp "align" nil t)
;; There are several variables which define how certain "categories"
;; of syntax are to be treated. These variables go by the name
;; `align-CATEGORY-modes'. For example, "c++" is such a category.
;; There are several rules which apply to c++, but since several other
;; languages have a syntax similar to c++ (e.g., c, java, etc), these
;; modes are treated as belonging to the same category.
;;
;; If you want to add a new mode under a certain category, just
;; customize that list, or add the new mode manually. For example, to
;; make jde-mode a c++ category mode, use this code in your .emacs
;; file:
;;
;; (setq align-c++-modes (cons 'jde-mode align-c++-modes))
;; In some programming modes, it's useful to have the aligner run only
;; after indentation is performed. To achieve this, customize or set
;; the variable `align-indent-before-aligning' to t.
;; Finally, this version of the aligner requires that both `subr.el'
;; and `custom.el' are loaded. Both are default packages in Emacs
;; 20.4, but those running earlier versions of Emacs may run into
;; problems with using the `defcustom' forms, and the `functionp'
;; routine used by this code. The solution is to use the subr.el file
;; from at least FSF Emacs 20.3, and the "custom" library in Emacs
;; 20.4.
;;; Module Authors:
;; In order to incorporate align's functionality into your own
;; modules, there are only a few steps you have to follow.
;; 1. Require or load in the align.el library.
;;
;; 2. Define your alignment and exclusion rules lists, either
;; customizable or not.
;;
;; 3. In your mode function, set the variables
;; `align-mode-rules-list' and `align-mode-exclude-rules-list'
;; to your own rules lists.
;; If there is any need to add your mode name to one of the
;; align-?-modes variables (for example, `align-dq-string-modes'), use
;; `add-to-list', or some similar function which checks first to see
;; if the value is already there. Since the user may customize that
;; mode list, and then write your mode name into their .emacs file,
;; causing the symbol already to be present the next time they load
;; your package.
;; Example:
;;
;; (require 'align)
;;
;; (defcustom my-align-rules-list
;; '((my-rule
;; (regexp . "Sample")))
;; :type align-rules-list-type
;; :group 'my-package)
;;
;; (put 'my-align-rules-list 'risky-local-variable t)
;;
;; (add-to-list 'align-dq-string-modes 'my-package-mode)
;; (add-to-list 'align-open-comment-modes 'my-package-mode)
;;
;; (defun my-mode ()
;; ...
;; (setq align-mode-rules-list my-align-rules-list))
;;
;; Note that if you need to install your own exclusion rules, then you
;; will also need to reproduce any double-quoted string, or open
;; comment exclusion rules that are defined in the standard
;; `align-exclude-rules-list'. At the moment there is no convenient
;; way to mix both mode-local and global rules lists.
;;; History:
;; Version 1.0 was created in the earlier part of 1996, using a very
;; simple algorithm that understand only basic regular expressions.
;; Parts of the code were broken up and included in vhdl-mode.el
;; around this time. After several comments from users, and a need to
;; find a more robust, performant algorithm, 2.0 was born in late
;; 1998. Many different approaches were taken (mostly due to the
;; complexity of TeX tables), but finally a scheme was discovered
;; which worked fairly well for most common usage cases. Development
;; beyond version 2.8 is not planned, except for problems that users
;; might encounter.
;;; Code:
(defconst align-version "2.8"
"This version of align.")
(defgroup align nil
"Align text to a specific column, by regexp."
:group 'fill)
;;; User Variables:
(defcustom align-load-hook nil
"*Hook that gets run after the aligner has been loaded."
:type 'hook
:group 'align)
(defcustom align-indent-before-aligning nil
"*If non-nil, indent the marked region before aligning it."
:type 'boolean
:group 'align)
(defcustom align-default-spacing 1
"*An integer that represents the default amount of padding to use.
If `align-to-tab-stop' is non-nil, this will represent the number of
tab stops to use for alignment, rather than the number of spaces.
Each alignment rule can optionally override both this variable. See
`align-mode-alist'."
:type 'integer
:group 'align)
(defcustom align-to-tab-stop 'indent-tabs-mode
"*If non-nil, alignments will always fall on a tab boundary.
It may also be a symbol, whose value will be taken."
:type '(choice (const nil) symbol)
:group 'align)
(defcustom align-region-heuristic 500
"*If non-nil, used as a heuristic by `align-current'.
Since each alignment rule can possibly have its own set of alignment
sections (whenever `align-region-separate' is non-nil, and not a
string), this heuristic is used to determine how far before and after
point we should search in looking for a region separator. Larger
values can mean slower perform in large files, although smaller values
may cause unexpected behavior at times."
:type 'integer
:group 'align)
(defcustom align-highlight-change-face 'highlight
"*The face to highlight with if changes are necessary."
:type 'face
:group 'align)
(defcustom align-highlight-nochange-face 'secondary-selection
"*The face to highlight with if no changes are necessary."
:type 'face
:group 'align)
(defcustom align-large-region 10000
"*If an integer, defines what constitutes a \"large\" region.
If nil,then no messages will ever be printed to the minibuffer."
:type 'integer
:group 'align)
(defcustom align-c++-modes '(c++-mode c-mode java-mode)
"*A list of modes whose syntax resembles C/C++."
:type '(repeat symbol)
:group 'align)
(defcustom align-perl-modes '(perl-mode cperl-mode)
"*A list of modes where perl syntax is to be seen."
:type '(repeat symbol)
:group 'align)
(defcustom align-lisp-modes
'(emacs-lisp-mode lisp-interaction-mode lisp-mode scheme-mode)
"*A list of modes whose syntax resembles Lisp."
:type '(repeat symbol)
:group 'align)
(defcustom align-tex-modes
'(tex-mode plain-tex-mode latex-mode slitex-mode)
"*A list of modes whose syntax resembles TeX (and family)."
:type '(repeat symbol)
:group 'align)
(defcustom align-text-modes '(text-mode outline-mode)
"*A list of modes whose content is plain text."
:type '(repeat symbol)
:group 'align)
(defcustom align-dq-string-modes (append align-lisp-modes
align-c++-modes
align-perl-modes)
"*A list of modes where double quoted strings should be excluded."
:type '(repeat symbol)
:group 'align)
(defcustom align-sq-string-modes align-perl-modes
"*A list of modes where single quoted strings should be excluded."
:type '(repeat symbol)
:group 'align)
(defcustom align-open-comment-modes (append align-lisp-modes
align-c++-modes
align-perl-modes
'(makefile-mode))
"*A list of modes with a single-line comment syntax.
These are comments as in Lisp, which have a beginning but, end with
the line (i.e., `comment-end' is an empty string)."
:type '(repeat symbol)
:group 'align)
(defcustom align-region-separate "^\\s-*[{}]?\\s-*$"
"*Select the method by which alignment sections will be separated.
If this is a symbol, that symbol's value will be used.
For the sake of clarification, consider the following example, which
will be referred to in the descriptions below.
int alpha = 1; /* one */
double beta = 2.0;
long gamma; /* ten */
unsigned int delta = 1; /* one */
long double epsilon = 3.0;
long long omega; /* ten */
The possible settings for `align-region-separate' are:
`entire' The entire region being aligned will be considered as a
single alignment section. Assuming that comments were not
being aligned to a particular column, the example would
become:
int alpha = 1; /* one */
double beta = 2.0;
long gamma; /* ten */
unsigned int delta = 1; /* one */
long double epsilon;
long long chi = 10; /* ten */
`group' Each contiguous set of lines where a specific alignment
occurs is considered a section for that alignment rule.
Note that each rule will may have any entirely different
set of section divisions than another.
int alpha = 1; /* one */
double beta = 2.0;
long gamma; /* ten */
unsigned int delta = 1; /* one */
long double epsilon;
long long chi = 10; /* ten */
`largest' When contiguous rule sets overlap, the largest section
described will be taken as the alignment section for each
rule touched by that section.
int alpha = 1; /* one */
double beta = 2.0;
long gamma; /* ten */
unsigned int delta = 1; /* one */
long double epsilon;
long long chi = 10; /* ten */
NOTE: This option is not supported yet, due to algorithmic
issues which haven't been satisfactorily resolved. There
are ways to do it, but they're both ugly and resource
consumptive.
regexp A regular expression string which defines the section
divider. If the mode you're in has a consistent divider
between sections, the behavior will be very similar to
`largest', and faster. But if the mode does not use clear
separators (for example, if you collapse your braces onto
the preceding statement in C or perl), `largest' is
probably the better alternative.
function A function that will be passed the beginning and ending
locations of the region in which to look for the section
separator. At the very beginning of the attempt to align,
both of these parameters will be nil, in which case the
function should return non-nil if it wants each rule to
define its own section, or nil if it wants the largest
section found to be used as the common section for all rules
that occur there.
list A list of markers within the buffer that represent where
the section dividers lie. Be certain to use markers! For
when the aligning begins, the ensuing contract/expanding of
whitespace will throw off any non-marker positions.
This method is intended for use in Lisp programs, and not
by the user."
:type '(choice
(const :tag "Entire region is one section" entire)
(const :tag "Align by contiguous groups" group)
; (const largest)
(regexp :tag "Regexp defines section boundaries")
(function :tag "Function defines section boundaries"))
:group 'align)
(put 'align-region-separate 'risky-local-variable t)
(defvar align-rules-list-type
'(repeat
(cons
:tag "Alignment rule"
(symbol :tag "Title")
(cons :tag "Required attributes"
(cons :tag "Regexp"
(const :tag "(Regular expression to match)" regexp)
(choice :value "\\(\\s-+\\)" regexp function))
(repeat
:tag "Optional attributes"
(choice
(cons :tag "Repeat"
(const :tag "(Repeat this rule throughout line)"
repeat)
(boolean :value t))
(cons :tag "Paren group"
(const :tag "(Parenthesis group to use)" group)
(choice :value 2
integer (repeat integer)))
(cons :tag "Modes"
(const :tag "(Modes where this rule applies)" modes)
(sexp :value (text-mode)))
(cons :tag "Case-fold"
(const :tag "(Should case be ignored for this rule)"
case-fold)
(boolean :value t))
(cons :tag "To Tab Stop"
(const :tag "(Should rule align to tab stops)"
tab-stop)
(boolean :value nil))
(cons :tag "Valid"
(const :tag "(Return non-nil if rule is valid)"
valid)
(function :value t))
(cons :tag "Run If"
(const :tag "(Return non-nil if rule should run)"
run-if)
(function :value t))
(cons :tag "Column"
(const :tag "(Column to fix alignment at)" column)
(choice :value comment-column
integer symbol))
(cons :tag "Spacing"
(const :tag "(Amount of spacing to use)" spacing)
(integer :value 1))
(cons :tag "Justify"
(const :tag "(Should text be right justified)"
justify)
(boolean :value t))
;; make sure this stays up-to-date with any changes
;; in `align-region-separate'
(cons :tag "Separate"
(const :tag "(Separation to use for this rule)"
separate)
(choice :value "^\\s-*$"
(const entire)
(const group)
; (const largest)
regexp function)))))))
"The `type' form for any `align-rules-list' variable.")
(unless (functionp 'c-guess-basic-syntax)
(autoload 'c-guess-basic-syntax "cc-engine"))
(defcustom align-rules-list
`((lisp-second-arg
(regexp . "\\(^\\s-+[^( \t\n]\\|(\\(\\S-+\\)\\s-+\\)\\S-+\\(\\s-+\\)")
(group . 3)
(modes . align-lisp-modes)
(run-if . ,(function (lambda () current-prefix-arg))))
(lisp-alist-dot
(regexp . "\\(\\s-*\\)\\.\\(\\s-*\\)")
(group . (1 2))
(modes . align-lisp-modes))
(open-comment
(regexp . ,(function
(lambda (end reverse)
(funcall (if reverse 're-search-backward
're-search-forward)
(concat "[^ \t\n\\\\]"
(regexp-quote comment-start)
"\\(.+\\)$") end t))))
(modes . align-open-comment-modes))
(c-macro-definition
(regexp . "^\\s-*#\\s-*define\\s-+\\S-+\\(\\s-+\\)")
(modes . align-c++-modes))
(c-variable-declaration
(regexp . ,(concat "[*&0-9A-Za-z_]>?[&*]*\\(\\s-+[*&]*\\)"
"[A-Za-z_][0-9A-Za-z:_]*\\s-*\\(\\()\\|"
"=[^=\n].*\\|(.*)\\|\\(\\[.*\\]\\)*\\)?"
"\\s-*[;,]\\|)\\s-*$\\)"))
(group . 1)
(modes . align-c++-modes)
(justify . t)
(valid
. ,(function
(lambda ()
(not (or (save-excursion
(goto-char (match-beginning 1))
(backward-word 1)
(looking-at
"\\(goto\\|return\\|new\\|delete\\|throw\\)"))
(if (and (boundp 'font-lock-mode) font-lock-mode)
(eq (cadr (memq 'face (text-properties-at (point))))
'font-lock-comment-face)
(eq (caar (c-guess-basic-syntax)) 'c))))))))
(c-assignment
(regexp . ,(concat "[^-=!^&*+<>/| \t\n]\\(\\s-*[-=!^&*+<>/|]*\\)"
"=\\(\\s-*\\)\\([^= \t\n]\\|$\\)"))
(group . (1 2))
(modes . align-c++-modes)
(justify . t)
(tab-stop . nil))
(perl-assignment
(regexp . ,(concat "[^=!^&*-+<>/| \t\n]\\(\\s-*\\)=[~>]?"
"\\(\\s-*\\)\\([^>= \t\n]\\|$\\)"))
(group . (1 2))
(modes . align-perl-modes)
(tab-stop . nil))
(make-assignment
(regexp . "^\\s-*\\w+\\(\\s-*\\):?=\\(\\s-*\\)\\([^\t\n \\\\]\\|$\\)")
(group . (1 2))
(modes . '(makefile-mode))
(tab-stop . nil))
(c-comma-delimiter
(regexp . ",\\(\\s-*\\)[^/ \t\n]")
(repeat . t)
(modes . align-c++-modes)
(run-if . ,(function (lambda () current-prefix-arg))))
; (valid
; . ,(function
; (lambda ()
; (memq (caar (c-guess-basic-syntax))
; '(brace-list-intro
; brace-list-entry
; brace-entry-open))))))
;; With a prefix argument, comma delimiter will be aligned. Since
;; perl-mode doesn't give us enough syntactic information (and we
;; don't do our own parsing yet), this rule is too destructive to
;; run normally.
(perl-comma-delimiter
(regexp . ",\\(\\s-*\\)[^# \t\n]")
(repeat . t)
(modes . align-perl-modes)
(run-if . ,(function (lambda () current-prefix-arg))))
(c++-comment
(regexp . "\\(\\s-*\\)\\(//.*\\|/\\*.*\\*/\\s-*\\)$")
(modes . align-c++-modes)
(column . comment-column)
(valid . ,(function
(lambda ()
(save-excursion
(goto-char (match-beginning 1))
(not (bolp)))))))
(c-macro-line-continuation
(regexp . "\\(\\s-*\\)\\\\$")
(modes . (append align-c++-modes '(makefile-mode)))
(column . c-backslash-column))
; (valid
; . ,(function
; (lambda ()
; (memq (caar (c-guess-basic-syntax))
; '(cpp-macro cpp-macro-cont))))))
(c-chain-logic
(regexp . "\\(\\s-*\\)\\(&&\\|||\\|\\<and\\>\\|\\<or\\>\\)")
(modes . align-c++-modes)
(valid . ,(function
(lambda ()
(save-excursion
(goto-char (match-end 2))
(looking-at "\\s-*\\(/[*/]\\|$\\)"))))))
(perl-chain-logic
(regexp . "\\(\\s-*\\)\\(&&\\|||\\|\\<and\\>\\|\\<or\\>\\)")
(modes . align-perl-modes)
(valid . ,(function
(lambda ()
(save-excursion
(goto-char (match-end 2))
(looking-at "\\s-*\\(#\\|$\\)"))))))
(tex-record-separator
(regexp . ,(function
(lambda (end reverse)
(align-match-tex-pattern "&" end reverse))))
(group . (1 2))
(modes . align-tex-modes)
(repeat . t))
(tex-tabbing-separator
(regexp . ,(function
(lambda (end reverse)
(align-match-tex-pattern "\\\\[=>]" end reverse))))
(group . (1 2))
(modes . align-tex-modes)
(repeat . t)
(run-if . ,(function
(lambda ()
(eq major-mode 'latex-mode)))))
(tex-record-break
(regexp . "\\(\\s-*\\)\\\\\\\\")
(modes . align-tex-modes))
;; With a numeric prefix argument, or C-u, space delimited text
;; tables will be aligned.
(text-column
(regexp . "\\(^\\|\\S-\\)\\(\\s-+\\)\\(\\S-\\|$\\)")
(group . 2)
(modes . align-text-modes)
(repeat . t)
(run-if . ,(function
(lambda ()
(and current-prefix-arg
(not (eq '- current-prefix-arg)))))))
;; With a negative prefix argument, lists of dollar figures will
;; be aligned.
(text-dollar-figure
(regexp . "\\$?\\(\\s-+[0-9]+\\)\\.")
(modes . align-text-modes)
(justify . t)
(run-if . ,(function
(lambda ()
(eq '- current-prefix-arg))))))
"*An list describing all of the available alignment rules.
The format is:
((TITLE
(ATTRIBUTE . VALUE) ...)
...)
The following attributes are meaningful:
`regexp' This required attribute must be either a string describing
a regular expression, or a function (described below).
For every line within the section that this regular
expression matches, the given rule will be applied to that
line. The exclusion rules denote which part(s) of the
line should not be modified; the alignment rules cause the
identified whitespace group to be contracted/expanded such
that the \"alignment character\" (the character
immediately following the identified parenthesis group),
occurs in the same column for every line within the
alignment section (see `align-region-separate' for a
description of how the region is broken up into alignment
sections).
The `regexp' attribute describes how the text should be
treated. Within this regexp, there must be at least one
group of characters (typically whitespace) identified by
the special opening and closing parens used in regexp
expressions (`\\\\(' and `\\\\)') (see the Emacs manual on
the syntax of regular expressions for more info).
If `regexp' is a function, it will be called as a
replacement for `re-search-forward'. This means that it
should return nil if nothing is found to match the rule,
or it should set the match data appropriately, move point
to the end of the match, and return the value of point.
`group' For exclusion rules, the group identifies the range of
characters that should be ignored. For alignment rules,
these are the characters that will be deleted/expanded for
the purposes of alignment. The \"alignment character\" is
always the first character immediately following this
parenthesis group. This attribute may also be a list of
integer, in which case multiple alignment characters will
be aligned, with the list of integer identifying the
whitespace groups which precede them. The default for
this attribute is 1.
`modes' The `modes' attribute, if set, should name a list of
major modes -- or evaluate to such a value -- in which the
rule is valid. If not set, the rule will apply to all
modes.
`case-fold' If `regexp' is an ordinary regular expression string
containing alphabetic character, sometimes you may want
the search to proceed case-insensitively (for languages
that ignore case, such as pascal for example). In that
case, set `case-fold' to nil, and the regular expression
search will ignore case. If `regexp' is set to a
function, that function must handle the job of ignoring
case by itself.
`tab-stop' If the `tab-stop' attribute is set, and non-nil, the
alignment character will always fall on a tab stop
(whether it uses tabs to get there or not depends on the
value of `indent-tabs-mode'). If the `tab-stop' attribute
is set to nil, tab stops will never be used. Otherwise,
the value of `align-to-tab-stop' determines whether or not
to align to a tab stop. The `tab-stop' attribute may also
be a list of t or nil values, corresponding to the number
of parenthesis groups specified by the `group' attribute.
`repeat' If the `repeat' attribute is present, and non-nil, the
rule will be applied to the line continuously until no
further matches are found.
`valid' If the `valid' attribute is set, it will be used to
determine whether the rule should be invoked. This form
is evaluated after the regular expression match has been
performed, so that it is possible to use the results of
that match to determine whether the alignment should be
performed. The buffer should not be modified during the
evaluation of this form.
`run-if' Like `valid', the `run-if' attribute tests whether the
rule should be run at all -- even before any searches are
done to determine if the rule applies to the alignment
region. This can save time, since `run-if' will only be
run once for each rule. If it returns nil, the rule will
not be attempted.
`column' For alignment rules, if the `column' attribute is set --
which must be an integer, or a symbol whose value is an
integer -- it will be used as the column in which to align
the alignment character. If the text on a particular line
happens to overrun that column, a single space character,
or tab stop (see `align-to-tab-stop') will be added
between the last text character and the alignment
character.
`spacing' Alignment rules may also override the amount of spacing
that would normally be used by providing a `spacing'
attribute. This must be an integer, or a list of integers
corresponding to the number of parenthesis groups matched
by the `group' attribute. If a list of value is used, and
any of those values is nil, `align-default-spacing' will
be used for that subgroup. See `align-default-spacing'
for more details on spacing, tab stops, and how to
indicate how much spacing should be used. If TAB-STOP is
present, it will override the value of `align-to-tab-stop'
for that rule.
`justify' It is possible with `regexp' and `group' to identify a
character group that contains more than just whitespace
characters. By default, any non-whitespace characters in
that group will also be deleted while aligning the
alignment character. However, if the `justify' attribute
is set to a non-nil value, only the initial whitespace
characters within that group will be deleted. This has
the effect of right-justifying the characters that remain,
and can be used for outdenting or just plain old right-
justification.
`separate' Each rule can define its own section separator, which
describes how to identify the separation of \"sections\"
within the region to be aligned. Setting the `separate'
attribute overrides the value of `align-region-separate'
(see the documentation of that variable for possible
values), and any separation argument passed to `align'."
:type align-rules-list-type
:group 'align)
(put 'align-rules-list 'risky-local-variable t)
(defvar align-exclude-rules-list-type
'(repeat
(cons
:tag "Exclusion rule"
(symbol :tag "Title")
(cons :tag "Required attributes"
(cons :tag "Regexp"
(const :tag "(Regular expression to match)" regexp)
(choice :value "\\(\\s-+\\)" regexp function))
(repeat
:tag "Optional attributes"
(choice
(cons :tag "Repeat"
(const :tag "(Repeat this rule throughout line)"
repeat)
(boolean :value t))
(cons :tag "Paren group"
(const :tag "(Parenthesis group to use)" group)
(choice :value 2
integer (repeat integer)))
(cons :tag "Modes"
(const :tag "(Modes where this rule applies)" modes)
(sexp :value (text-mode)))
(cons :tag "Case-fold"
(const :tag "(Should case be ignored for this rule)"
case-fold)
(boolean :value t)))))))
"The `type' form for any `align-exclude-rules-list' variable.")
(defcustom align-exclude-rules-list
`((exc-dq-string
(regexp . "\"\\([^\"\n]+\\)\"")
(repeat . t)
(modes . align-dq-string-modes))
(exc-sq-string
(regexp . "'\\([^'\n]+\\)'")
(repeat . t)
(modes . align-sq-string-modes))
(exc-open-comment
(regexp
. ,(function
(lambda (end reverse)
(funcall (if reverse 're-search-backward
're-search-forward)
(concat "[^ \t\n\\\\]"
(regexp-quote comment-start)
"\\(.+\\)$") end t))))
(modes . align-open-comment-modes))
(exc-c-comment
(regexp . "/\\*\\(.+\\)\\*/")
(repeat . t)
(modes . align-c++-modes))
(exc-c-func-params
(regexp . "(\\([^)\n]+\\))")
(repeat . t)
(modes . align-c++-modes))
(exc-c-macro
(regexp . "^\\s-*#\\s-*\\(if\\w*\\|endif\\)\\(.*\\)$")
(group . 2)
(modes . align-c++-modes)))
"*An list describing text that should be excluded from alignment.
See the documentation for `align-rules-list' for more info."
:type align-exclude-rules-list-type
:group 'align)
(put 'align-exclude-rules-list 'risky-local-variable t)
;;; Internal Variables:
(defvar align-mode-rules-list nil
"Alignment rules specific to the current major mode.
See the variable `align-rules-list' for more details.")
(make-variable-buffer-local 'align-mode-rules-list)
(defvar align-mode-exclude-rules-list nil
"Alignment exclusion rules specific to the current major mode.
See the variable `align-exclude-rules-list' for more details.")
(make-variable-buffer-local 'align-mode-exclude-rules-list)
(defvar align-highlight-overlays nil
"The current overlays highlighting the text matched by a rule.")
;; Sample extension rule set, for vhdl-mode. This should properly be
;; in vhdl-mode.el itself.
(defcustom align-vhdl-rules-list
`((vhdl-declaration
(regexp . "\\(signal\\|variable\\|constant\\)\\(\\s-+\\)\\S-")
(group . 2))
(vhdl-case
(regexp . "\\(others\\|[^ \t\n=<]\\)\\(\\s-*\\)=>\\(\\s-*\\)\\S-")
(group . (2 3))
(valid
. ,(function
(lambda ()
(not (string= (downcase (match-string 1))
"others"))))))
(vhdl-colon
(regexp . "[^ \t\n:]\\(\\s-*\\):\\(\\s-*\\)[^=\n]")
(group . (1 2)))
(direction
(regexp . ":\\s-*\\(in\\|out\\|inout\\|buffer\\)\\(\\s-*\\)")
(group . 2))
(sig-assign
(regexp . "[^ \t\n=<]\\(\\s-*\\)<=\\(\\s-*\\)\\S-")
(group . (1 2)))
(var-assign
(regexp . "[^ \t\n:]\\(\\s-*\\):="))
(use-entity
(regexp . "\\(\\s-+\\)use\\s-+entity")))
"*Alignment rules for `vhdl-mode'. See `align-rules-list' for more info."
:type align-rules-list-type
:group 'align)
(put 'align-vhdl-rules-list 'risky-local-variable t)
(defun align-set-vhdl-rules ()
"Setup the `align-mode-rules-list' variable for `vhdl-mode'."
(setq align-mode-rules-list align-vhdl-rules-list))
(add-hook 'vhdl-mode-hook 'align-set-vhdl-rules)
(add-to-list 'align-dq-string-modes 'vhdl-mode)
(add-to-list 'align-open-comment-modes 'vhdl-mode)
;;; User Functions:
;;;###autoload
(defun align (beg end &optional separate rules exclude-rules)
"Attempt to align a region based on a set of alignment rules.
BEG and END mark the region. If BEG and END are specifically set to
nil (this can only be done programmatically), the beginning and end of
the current alignment section will be calculated based on the location
of point, and the value of `align-region-separate' (or possibly each
rule's `separate' attribute).
If SEPARATE is non-nil, it overrides the value of
`align-region-separate' for all rules, except those that have their
`separate' attribute set.
RULES and EXCLUDE-RULES, if either is non-nil, will replace the
default rule lists defined in `align-rules-list' and
`align-exclude-rules-list'. See `align-rules-list' for more details
on the format of these lists."
(interactive "r")
(let ((separator
(or separate
(if (symbolp align-region-separate)
(symbol-value align-region-separate)
align-region-separate)
'entire)))
(if (not (or ;(eq separator 'largest)
(and (functionp separator)
(not (funcall separator nil nil)))))
(align-region beg end separator
(or rules align-mode-rules-list align-rules-list)
(or exclude-rules align-mode-exclude-rules-list
align-exclude-rules-list))
(let ((sec-first end)
(sec-last beg))
(align-region beg end
(or exclude-rules
align-mode-exclude-rules-list
align-exclude-rules-list) nil
separator
(function
(lambda (b e mode)
(when (and mode (listp mode))
(setq sec-first (min sec-first b)
sec-last (max sec-last e))))))
(if (< sec-first sec-last)
(align-region sec-first sec-last 'entire
(or rules align-mode-rules-list align-rules-list)
(or exclude-rules align-mode-exclude-rules-list
align-exclude-rules-list)))))))
;;;###autoload
(defun align-regexp (beg end regexp &optional group spacing repeat)
"Align the current region using an ad-hoc rule read from the minibuffer.
BEG and END mark the limits of the region. This function will prompt
for the REGEXP to align with. If no prefix arg was specified, you
only need to supply the characters to be lined up and any preceding
whitespace is replaced. If a prefix arg was specified, the full
regexp with parenthesized whitespace should be supplied; it will also
prompt for which parenthesis GROUP within REGEXP to modify, the amount
of SPACING to use, and whether or not to REPEAT the rule throughout
the line. See `align-rules-list' for more information about these
options.
For example, let's say you had a list of phone numbers, and wanted to
align them so that the opening parentheses would line up:
Fred (123) 456-7890
Alice (123) 456-7890
Mary-Anne (123) 456-7890
Joe (123) 456-7890
There is no predefined rule to handle this, but you could easily do it
using a REGEXP like \"(\". All you would have to do is to mark the
region, call `align-regexp' and type in that regular expression."
(interactive
(append
(list (min (point) (mark))
(max (point) (mark)))
(if current-prefix-arg
(list (read-string "Complex align using regexp: "
"\\(\\s-*\\)")
(string-to-int
(read-string
"Parenthesis group to modify (justify if negative): " "1"))
(string-to-int
(read-string "Amount of spacing (or column if negative): "
(number-to-string align-default-spacing)))
(y-or-n-p "Repeat throughout line? "))
(list (concat "\\(\\s-*\\)"
(read-string "Align regexp: "))
1 align-default-spacing nil))))
(let ((rule
(list (list nil (cons 'regexp regexp)
(cons 'group (abs group))
(if (< group 0)
(cons 'justify t)
(cons 'bogus nil))
(if (>= spacing 0)
(cons 'spacing spacing)
(cons 'column (abs spacing)))
(cons 'repeat repeat)))))
(align-region beg end 'entire rule nil nil)))
;;;###autoload
(defun align-entire (beg end &optional rules exclude-rules)
"Align the selected region as if it were one alignment section.
BEG and END mark the extent of the region. If RULES or EXCLUDE-RULES
is set to a list of rules (see `align-rules-list'), it can be used to
override the default alignment rules that would have been used to
align that section."
(interactive "r")
(align beg end 'entire rules exclude-rules))
;;;###autoload
(defun align-current (&optional rules exclude-rules)
"Call `align' on the current alignment section.
This function assumes you want to align only the current section, and
so saves you from having to specify the region. If RULES or
EXCLUDE-RULES is set to a list of rules (see `align-rules-list'), it
can be used to override the default alignment rules that would have
been used to align that section."
(interactive)
(align nil nil nil rules exclude-rules))
;;;###autoload
(defun align-highlight-rule (beg end title &optional rules exclude-rules)
"Highlight the whitespace which a given rule would have modified.
BEG and END mark the extent of the region. TITLE identifies the rule
that should be highlighted. If RULES or EXCLUDE-RULES is set to a
list of rules (see `align-rules-list'), it can be used to override the
default alignment rules that would have been used to identify the text
to be colored."
(interactive
(list (min (mark) (point))
(max (mark) (point))
(completing-read
"Title of rule to highlight: "
(mapcar
(function
(lambda (rule)
(list (symbol-name (car rule)))))
(append (or align-mode-rules-list align-rules-list)
(or align-mode-exclude-rules-list
align-exclude-rules-list))) nil t)))
(let ((ex-rule (assq (intern title)
(or align-mode-exclude-rules-list
align-exclude-rules-list)))
face)
(align-unhighlight-rule)
(align-region