-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathlatex.el
9548 lines (8760 loc) · 407 KB
/
latex.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
;;; latex.el --- Support for LaTeX documents. -*- lexical-binding: t; -*-
;; Copyright (C) 1991, 1993-2025 Free Software Foundation, Inc.
;; Maintainer: auctex-devel@gnu.org
;; Keywords: tex
;; This file is part of AUCTeX.
;; AUCTeX 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 3, or (at your option) any
;; later version.
;; AUCTeX 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 this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; This file provides AUCTeX support for LaTeX.
;;; Code:
(require 'tex)
(require 'tex-style)
(require 'tex-ispell)
(require 'latex-flymake)
;; Silence the compiler for functions:
(declare-function multi-prompt "multi-prompt")
(declare-function multi-prompt-key-value "multi-prompt")
(declare-function LaTeX-install-toolbar "tex-bar" nil)
(declare-function outline-level "ext:outline" nil)
(declare-function outline-mark-subtree "ext:outline" nil)
(declare-function turn-off-filladapt-mode "ext:filladapt" nil)
;; Silence the compiler for variables:
(defvar outline-heading-alist)
(defvar LaTeX-section-list-changed)
;;; Syntax
(defvar LaTeX-optop "["
"The LaTeX optional argument opening character.")
(defvar LaTeX-optcl "]"
"The LaTeX optional argument closeing character.")
;;; Style
(defcustom LaTeX-default-style "article"
"Default when creating new documents."
:group 'LaTeX-environment
:type 'string)
(defcustom LaTeX-default-options nil
"Default options to documentclass.
A comma-seperated list of strings."
:group 'LaTeX-environment
:type '(repeat (string :format "%v"))
:local t)
(defcustom LaTeX-insert-into-comments nil
"Whether insertion commands stay in comments.
This allows using the insertion commands even when the lines are
outcommented, like in dtx files."
:group 'LaTeX-environment
:type 'boolean
:safe #'booleanp
:package-version '(auctex . "14.0.8"))
(defcustom docTeX-indent-across-comments nil
"If non-nil, indentation in docTeX is done across comments."
:group 'LaTeX-indentation
:type 'boolean)
(defun LaTeX-newline ()
"Start a new line potentially staying within comments.
This depends on `LaTeX-insert-into-comments'."
(interactive)
(if LaTeX-insert-into-comments
(cond ((and (save-excursion (skip-chars-backward " \t") (bolp))
(save-excursion
(skip-chars-forward " \t")
(looking-at (concat TeX-comment-start-regexp "+"))))
(beginning-of-line)
(insert (buffer-substring-no-properties
(line-beginning-position) (match-end 0)))
(newline))
((and (not (bolp))
(save-excursion
(skip-chars-forward " \t") (not (TeX-escaped-p)))
(looking-at
(concat "[ \t]*" TeX-comment-start-regexp "+[ \t]*")))
(delete-region (match-beginning 0) (match-end 0))
(indent-new-comment-line))
;; `indent-new-comment-line' does nothing when
;; `comment-auto-fill-only-comments' is non-nil, so we
;; must be sure to be in a comment before calling it. In
;; any other case `newline' is used.
((TeX-in-comment)
(indent-new-comment-line))
(t
(newline)))
(newline)))
;;; Syntax Table
(defvar LaTeX-mode-syntax-table (make-syntax-table TeX-mode-syntax-table)
"Syntax table used in LaTeX mode.")
(progn ; set [] to match for LaTeX.
(modify-syntax-entry (string-to-char LaTeX-optop)
(concat "(" LaTeX-optcl)
LaTeX-mode-syntax-table)
(modify-syntax-entry (string-to-char LaTeX-optcl)
(concat ")" LaTeX-optop)
LaTeX-mode-syntax-table))
;;; Sections
;; Declare dynamically scoped vars.
(defvar LaTeX-title nil "Dynamically bound by `LaTeX-section'.")
(defvar LaTeX-name nil "Dynamically bound by `LaTeX-section'.")
(defvar LaTeX-level nil "Dynamically bound by `LaTeX-section'.")
(defvar LaTeX-done-mark nil "Dynamically bound by `LaTeX-section'.")
(defvar LaTeX-toc nil "Dynamically bound by `LaTeX-section'.")
(defun LaTeX-section (arg)
"Insert a template for a LaTeX section.
Determine the type of section to be inserted, by the argument ARG.
If ARG is nil or missing, use the current level.
If ARG is a list (selected by \\[universal-argument]), go downward one level.
If ARG is negative, go up that many levels.
If ARG is positive or zero, use absolute level:
0 : part
1 : chapter
2 : section
3 : subsection
4 : subsubsection
5 : paragraph
6 : subparagraph
The following variables can be set to customize:
`LaTeX-section-hook' Hooks to run when inserting a section.
`LaTeX-section-label' Prefix to all section labels."
(interactive "*P")
(let* ((val (prefix-numeric-value arg))
(LaTeX-level (cond ((null arg)
(LaTeX-current-section))
((listp arg)
(LaTeX-down-section))
((< val 0)
(LaTeX-up-section (- val)))
(t val)))
(LaTeX-name (LaTeX-section-name LaTeX-level))
(LaTeX-toc nil)
(LaTeX-title (if (TeX-active-mark)
(buffer-substring (region-beginning)
(region-end))
""))
(LaTeX-done-mark (make-marker)))
(run-hooks 'LaTeX-section-hook)
(LaTeX-newline)
(if (marker-position LaTeX-done-mark)
(goto-char (marker-position LaTeX-done-mark)))
(set-marker LaTeX-done-mark nil)))
(defun LaTeX-current-section ()
"Return the level of the section that contain point.
See also `LaTeX-section' for description of levels."
(save-excursion
(max (LaTeX-largest-level)
(if (re-search-backward (LaTeX-outline-regexp) nil t)
(- (LaTeX-outline-level) (LaTeX-outline-offset))
(LaTeX-largest-level)))))
(defun LaTeX-down-section ()
"Return the value of a section one level under the current.
Tries to find what kind of section that have been used earlier in the
text, if this fail, it will just return one less than the current
section."
(save-excursion
(let ((current (LaTeX-current-section))
(next nil)
(regexp (LaTeX-outline-regexp)))
(if (not (re-search-backward regexp nil t))
(1+ current)
(while (not next)
(cond
((eq (LaTeX-current-section) current)
(if (re-search-forward regexp nil t)
(if (<= (setq next (LaTeX-current-section)) current) ;Wow!
(setq next (1+ current)))
(setq next (1+ current))))
((not (re-search-backward regexp nil t))
(setq next (1+ current)))))
next))))
(defun LaTeX-up-section (arg)
"Return the value of the section ARG levels above this one."
(save-excursion
(if (zerop arg)
(LaTeX-current-section)
(let ((current (LaTeX-current-section)))
(while (and (>= (LaTeX-current-section) current)
(re-search-backward (LaTeX-outline-regexp)
nil t)))
(LaTeX-up-section (1- arg))))))
(defvar LaTeX-section-list '(("part" 0)
("chapter" 1)
("section" 2)
("subsection" 3)
("subsubsection" 4)
("paragraph" 5)
("subparagraph" 6))
"List which elements is the names of the sections used by LaTeX.")
(defvar-local LaTeX-section-menu nil)
(defun LaTeX-section-list-add-locally (sections &optional clean)
"Add SECTIONS to `LaTeX-section-list'.
SECTIONS can be a single list containing the section macro name
as a string and the level as an integer or a list of such lists.
If optional argument CLEAN is non-nil, remove any existing
entries from `LaTeX-section-list' before adding the new ones.
The function will make `LaTeX-section-list' buffer-local and
invalidate the section submenu in order to let the menu filter
regenerate it. It is mainly a convenience function which can be
used in style files."
(when (stringp (car sections))
(setq sections (list sections)))
(make-local-variable 'LaTeX-section-list)
(when clean (setq LaTeX-section-list nil))
(dolist (elt sections) (add-to-list 'LaTeX-section-list elt t))
(setq LaTeX-section-list
(sort (copy-sequence LaTeX-section-list)
(lambda (a b) (< (nth 1 a) (nth 1 b)))))
(setq LaTeX-section-menu nil))
(defun LaTeX-section-name (level)
"Return the name of the section corresponding to LEVEL."
(car (rassoc (list level) LaTeX-section-list)))
(defun LaTeX-section-level (name)
"Return the level of the section NAME.
NAME can be starred variant."
(if (string-suffix-p "*" name)
(setq name (substring-no-properties name 0 -1)))
(cadr (assoc name LaTeX-section-list)))
(defcustom TeX-outline-extra nil
"List of extra TeX outline levels.
Each element is a list with two entries. The first entry is the
regular expression matching a header, and the second is the level of
the header. See `LaTeX-section-list' for existing header levels."
:group 'LaTeX
:type '(repeat (group (regexp :tag "Match")
(integer :tag "Level"))))
(defun LaTeX-outline-regexp (&optional anywhere)
"Return regexp for LaTeX sections.
If optional argument ANYWHERE is not nil, do not require that the
header is at the start of a line."
(concat (if anywhere "" "^")
"[ \t]*"
(regexp-quote TeX-esc)
"\\(appendix\\|documentstyle\\|documentclass\\|"
(mapconcat #'car LaTeX-section-list "\\|")
"\\)\\b"
(if TeX-outline-extra
"\\|"
"")
(mapconcat #'car TeX-outline-extra "\\|")
"\\|" TeX-header-end
"\\|" TeX-trailer-start))
(defvar-local LaTeX-largest-level nil
"Largest sectioning level with current document class.")
(defun LaTeX-largest-level ()
"Return largest sectioning level with current document class.
Run style hooks before it has not been done."
(TeX-update-style)
LaTeX-largest-level)
(defun LaTeX-largest-level-set (section)
"Set `LaTeX-largest-level' to the level of SECTION.
SECTION has to be a string contained in `LaTeX-section-list'.
Additionally the function will invalidate the section submenu in
order to let the menu filter regenerate it."
(setq LaTeX-largest-level (LaTeX-section-level section))
(let ((offset (LaTeX-outline-offset)))
(when (> offset 0)
(let (lst)
(dolist (tup outline-heading-alist)
(setq lst (cons (cons (car tup)
(+ offset (cdr tup)))
lst)))
(setq outline-heading-alist (nreverse lst)))))
(setq LaTeX-section-menu nil))
(defun LaTeX-outline-offset ()
"Offset to add to `LaTeX-section-list' levels to get outline level."
(- 2 (LaTeX-largest-level)))
(defun TeX-look-at (list)
"Check if we are looking at the first element of a member of LIST.
If so, return the second element, otherwise return nil."
(while (and list
(not (looking-at (nth 0 (car list)))))
(setq list (cdr list)))
(if list
(nth 1 (car list))
nil))
(defvar LaTeX-header-end
(concat "^[^%\n]*" (regexp-quote TeX-esc) "begin *"
TeX-grop "document" TeX-grcl)
"Default end of header marker for LaTeX documents.")
(defvar LaTeX-trailer-start
(concat "^[^%\n]*" (regexp-quote TeX-esc) "end *"
TeX-grop "document" TeX-grcl)
"Default start of trailer marker for LaTeX documents.")
(defun LaTeX-outline-level ()
"Find the level of current outline heading in an LaTeX document."
(cond ((looking-at LaTeX-header-end) 1)
((looking-at LaTeX-trailer-start) 1)
((TeX-look-at TeX-outline-extra)
(max 1 (+ (TeX-look-at TeX-outline-extra)
(LaTeX-outline-offset))))
(t
(save-excursion
(skip-chars-forward " \t")
(forward-char 1)
(cond ((looking-at "appendix") 1)
((looking-at "documentstyle") 1)
((looking-at "documentclass") 1)
((TeX-look-at LaTeX-section-list)
(max 1 (+ (TeX-look-at LaTeX-section-list)
(LaTeX-outline-offset))))
(t (outline-level)))))))
(defun LaTeX-outline-name ()
"Guess a name for the current header line."
(save-excursion
(search-forward "{" nil t)
(let ((beg (point)))
(backward-char)
(condition-case nil
(with-syntax-table (TeX-search-syntax-table ?\{ ?\})
(forward-sexp)
(backward-char))
(error (forward-sentence)))
(replace-regexp-in-string "[\n\r][ ]*" " "
(buffer-substring beg (point))))))
(add-hook 'TeX-remove-style-hook
(lambda () (setq LaTeX-largest-level nil)))
(defcustom LaTeX-section-hook
'(LaTeX-section-heading
LaTeX-section-title
;; LaTeX-section-toc ; Most people won't want this
LaTeX-section-section
LaTeX-section-label)
"List of hooks to run when a new section is inserted.
The following variables are set before the hooks are run
`LaTeX-level' - numeric section level, see the documentation of
`LaTeX-section'.
`LaTeX-name' - name of the sectioning command, derived from
`LaTeX-level'.
`LaTeX-title' - The title of the section, default to an empty
string.
`LaTeX-toc' - Entry for the table of contents list, default
nil.
`LaTeX-done-mark' - Position of point afterwards, default nil
(meaning end).
The following standard hooks exist -
`LaTeX-section-heading': Query the user about the name of the
sectioning command. Modifies `LaTeX-level' and `LaTeX-name'.
`LaTeX-section-title': Query the user about the title of the
section. Modifies `LaTeX-title'.
`LaTeX-section-toc': Query the user for the toc entry. Modifies
`LaTeX-toc'.
`LaTeX-section-section': Insert LaTeX section command according to
`LaTeX-name', `LaTeX-title', and `LaTeX-toc'. If `LaTeX-toc' is
nil, no toc entry is inserted. If `LaTeX-toc' or `LaTeX-title'
are empty strings, `LaTeX-done-mark' will be placed at the point
they should be inserted.
`LaTeX-section-label': Insert a label after the section command.
Controled by the variable `LaTeX-section-label'.
To get a full featured `LaTeX-section' command, insert
(setq LaTeX-section-hook
\\='(LaTeX-section-heading
LaTeX-section-title
LaTeX-section-toc
LaTeX-section-section
LaTeX-section-label))
in your init file such as .emacs.d/init.el or .emacs."
:group 'LaTeX-macro
:type 'hook
:options '(LaTeX-section-heading
LaTeX-section-title
LaTeX-section-toc
LaTeX-section-section
LaTeX-section-label))
(defcustom LaTeX-section-label
'(("part" . "part:")
("chapter" . "chap:")
("section" . "sec:")
("subsection" . "sec:")
("subsubsection" . "sec:"))
"Default prefix when asking for a label.
Some LaTeX packages \(such as `fancyref'\) look at the prefix to generate some
text around cross-references automatically. When using those packages, you
should not change this variable.
If it is a string, it it used unchanged for all kinds of sections.
If it is nil, no label is inserted.
If it is a list, the list is searched for a member whose car is equal
to the name of the sectioning command being inserted. The cdr is then
used as the prefix. If the name is not found, or if the cdr is nil,
no label is inserted."
:group 'LaTeX-label
:type '(choice (const :tag "none" nil)
(string :format "%v" :tag "Common")
(repeat :menu-tag "Level specific"
:format "\n%v%i"
(cons :format "%v"
(string :tag "Type")
(choice :tag "Prefix"
(const :tag "none" nil)
(string :format "%v"))))))
;;; Section Hooks.
(defun LaTeX-section-heading ()
"Hook to prompt for LaTeX section name.
Insert this hook into `LaTeX-section-hook' to allow the user to change
the name of the sectioning command inserted with \\[LaTeX-section]."
(let ((string (completing-read
(concat "Level (default " LaTeX-name "): ")
(append
;; Include starred variants in candidates.
(mapcar (lambda (sct)
(list (concat (car sct) "*")))
LaTeX-section-list)
LaTeX-section-list)
nil nil nil nil LaTeX-name)))
;; Update LaTeX-name
(if (not (zerop (length string)))
(setq LaTeX-name string))
;; Update level
(setq LaTeX-level (LaTeX-section-level LaTeX-name))))
(defun LaTeX-section-title ()
"Hook to prompt for LaTeX section title.
Insert this hook into `LaTeX-section-hook' to allow the user to change
the title of the section inserted with \\[LaTeX-section]."
(setq LaTeX-title (TeX-read-string "Title: " LaTeX-title))
(let ((region (and (TeX-active-mark)
(cons (region-beginning) (region-end)))))
(when region (delete-region (car region) (cdr region)))))
(defun LaTeX-section-toc ()
"Hook to prompt for the LaTeX section entry in the table of contents.
Insert this hook into `LaTeX-section-hook' to allow the user to insert
a different entry for the section in the table of contents."
(setq LaTeX-toc (TeX-read-string "Toc Entry: "))
(if (zerop (length LaTeX-toc))
(setq LaTeX-toc nil)))
(defun LaTeX-section-section ()
"Hook to insert LaTeX section command into the file.
Insert this hook into `LaTeX-section-hook' after those hooks that
set the `LaTeX-name', `LaTeX-title', and `LaTeX-toc' variables,
but before those hooks that assume that the section is already
inserted."
;; insert a new line if the current line and the previous line are
;; not empty (except for whitespace), with one exception: do not
;; insert a new line if the previous (or current, sigh) line starts
;; an environment (i.e., starts with `[optional whitespace]\begin')
(unless (save-excursion
(re-search-backward
(concat "^\\s-*\n\\s-*\\=\\|^\\s-*" (regexp-quote TeX-esc)
"begin")
(line-beginning-position 0) t))
(LaTeX-newline))
(insert TeX-esc LaTeX-name)
(cond ((null LaTeX-toc))
((zerop (length LaTeX-toc))
(insert LaTeX-optop)
(set-marker LaTeX-done-mark (point))
(insert LaTeX-optcl))
(t
(insert LaTeX-optop LaTeX-toc LaTeX-optcl)))
(insert TeX-grop)
(if (zerop (length LaTeX-title))
(set-marker LaTeX-done-mark (point)))
(insert LaTeX-title TeX-grcl)
(LaTeX-newline)
;; If RefTeX is available, tell it that we've just made a new section
(and (fboundp 'reftex-notice-new-section)
(reftex-notice-new-section)))
(defun LaTeX-section-label ()
"Hook to insert a label after the sectioning command.
Insert this hook into `LaTeX-section-hook' to prompt for a label to be
inserted after the sectioning command.
The behaviour of this hook is controlled by variable `LaTeX-section-label'."
(and (LaTeX-label LaTeX-name 'section)
(LaTeX-newline)))
;;; Environments
(defgroup LaTeX-environment nil
"Environments in AUCTeX."
:group 'LaTeX-macro)
(defcustom LaTeX-default-environment "itemize"
"The default environment when creating new ones with `LaTeX-environment'.
It is overridden by `LaTeX-default-document-environment' when it
is non-nil and the current environment is \"document\"."
:group 'LaTeX-environment
:type 'string
:local t)
(defvar-local LaTeX-default-document-environment nil
"The default environment when creating new ones with
`LaTeX-environment' and the current one is \"document\". This
variable overrides `LaTeX-default-environment'.")
(defvar-local LaTeX-default-tabular-environment "tabular"
"The default tabular-like environment used when inserting a table env.
Styles such as tabularx may set it according to their needs.")
(defvar LaTeX-environment-history nil)
;; Variable used to cache the current environment, e.g. for repeated
;; tasks in an environment, like indenting each line in a paragraph to
;; be filled. It must not have a non-nil value in general. That
;; means it is usually let-bound for such operations.
(defvar LaTeX-current-environment nil)
(defun LaTeX-environment (arg)
"Make LaTeX environment (\\begin{...}-\\end{...} pair).
With prefix ARG, modify current environment.
It may be customized with the following variables:
`LaTeX-default-environment' Your favorite environment.
`LaTeX-default-style' Your favorite document class.
`LaTeX-default-options' Your favorite document class options.
`LaTeX-float' Where you want figures and tables to float.
`LaTeX-table-label' Your prefix to labels in tables.
`LaTeX-figure-label' Your prefix to labels in figures.
`LaTeX-default-format' Format for array and tabular.
`LaTeX-default-width' Width for minipage and tabular*.
`LaTeX-default-position' Position for array and tabular."
(interactive "*P")
(let* ((default (cond
((TeX-near-bobp) "document")
((and LaTeX-default-document-environment
(string-equal (LaTeX-current-environment) "document"))
LaTeX-default-document-environment)
(t LaTeX-default-environment)))
(environment (completing-read (concat "Environment type (default "
default "): ")
(LaTeX-environment-list-filtered) nil nil
nil 'LaTeX-environment-history default)))
;; Use `environment' as default for the next time only if it is different
;; from the current default.
(unless (equal environment default)
(setq LaTeX-default-environment environment))
(let ((entry (assoc environment (LaTeX-environment-list))))
(if (null entry)
(LaTeX-add-environments (list environment)))
(if arg
(LaTeX-modify-environment environment)
(LaTeX-environment-menu environment)))))
(defun LaTeX-environment-menu (environment)
"Insert ENVIRONMENT around point or region."
(let ((entry (assoc environment (LaTeX-environment-list))))
(cond ((not (and entry (nth 1 entry)))
(LaTeX-insert-environment environment))
((numberp (nth 1 entry))
(let ((count (nth 1 entry))
(args ""))
(while (> count 0)
(setq args (concat args TeX-grop TeX-grcl))
(setq count (- count 1)))
(LaTeX-insert-environment environment args)))
((or (stringp (nth 1 entry)) (vectorp (nth 1 entry)))
(let ((prompts (cdr entry))
(args ""))
(dolist (elt prompts)
(let* ((optional (vectorp elt))
(elt (if optional (elt elt 0) elt))
(arg (TeX-read-string
(TeX-argument-prompt optional elt nil))))
(setq args (concat args
(cond ((and optional (> (length arg) 0))
(concat LaTeX-optop arg LaTeX-optcl))
((not optional)
(concat TeX-grop arg TeX-grcl)))))))
(LaTeX-insert-environment environment args)))
(t
(apply (nth 1 entry) environment (nthcdr 2 entry))))))
(defun LaTeX-close-environment (&optional reopen)
"Create an \\end{...} to match the current environment.
With prefix argument REOPEN, reopen environment afterwards."
(interactive "*P")
(if (> (point)
(save-excursion
(beginning-of-line)
(when LaTeX-insert-into-comments
(if (looking-at comment-start-skip)
(goto-char (match-end 0))))
(skip-chars-forward " \t")
(point)))
(LaTeX-newline))
(let ((environment (LaTeX-current-environment 1)) marker)
(insert "\\end{" environment "}")
(indent-according-to-mode)
(if (or (not (looking-at "[ \t]*$"))
(and (TeX-in-commented-line)
(save-excursion (beginning-of-line 2)
(not (TeX-in-commented-line)))))
(LaTeX-newline)
(unless (= (forward-line 1) 0)
(insert "\n")))
(indent-according-to-mode)
(when reopen
(save-excursion
(setq marker (point-marker))
(set-marker-insertion-type marker t)
(LaTeX-environment-menu environment)
(delete-region (point)
(if (save-excursion (goto-char marker)
(bolp))
(1- marker)
marker))
(move-marker marker nil)))))
(define-obsolete-variable-alias 'LaTeX-after-insert-env-hooks 'LaTeX-after-insert-env-hook "11.89")
(defvar LaTeX-indent-environment-list) ;; Defined further below.
(defvar LaTeX-after-insert-env-hook nil
"List of functions to be run at the end of `LaTeX-insert-environment'.
Each function is called with three arguments: the name of the
environment just inserted, the buffer position just before
\\begin and the position just before \\end.")
(defun LaTeX-insert-environment (environment &optional extra)
"Insert LaTeX ENVIRONMENT with optional argument EXTRA."
(let ((active-mark (and (TeX-active-mark) (not (eq (mark) (point)))))
prefix content-start env-start env-end additional-indent)
(when (and active-mark (< (mark) (point))) (exchange-point-and-mark))
;; Compute the prefix.
(when (and LaTeX-insert-into-comments (TeX-in-commented-line))
(save-excursion
(beginning-of-line)
(looking-at
(concat "^\\([ \t]*" TeX-comment-start-regexp "+\\)+[ \t]*"))
(setq prefix (match-string 0))))
;; What to do with the line containing point.
;; - Open a new empty line for later insertion of "\begin{foo}" and
;; put the point there.
;; - If there were at first any non-whitespace texts between the
;; point and EOL, send them into their new own line with possible
;; comment prefix.
(cond (;; When the entire line consists of whitespaces except
;; possible prefix...
(save-excursion (beginning-of-line)
(looking-at (concat prefix "[ \t]*$")))
;; ...make the line empty and put the point there.
(delete-region (match-beginning 0) (match-end 0)))
(;; When there are only whitespaces except possible prefix
;; between the point and BOL (including the case the point
;; is at BOL)...
(TeX-looking-at-backward (if prefix
(concat "^\\(" prefix "\\)?[ \t]*")
"^[ \t]*")
(line-beginning-position))
;; ...in this case, we have non-whitespace texts between
;; the point and EOL, so send the entire line into a new
;; next line and put the point on the empty line just
;; created.
(beginning-of-line)
(newline)
(beginning-of-line 0)
;; Take note that there are texts to be indented later
;; unless the region is activated.
(unless active-mark
(setq additional-indent t)))
(;; In all other cases...
t
;; ...insert a new empty line after deleting all
;; whitespaces around the point, put the point there...
(delete-horizontal-space)
(if (eolp)
(newline)
;; ...and if there were at first any non-whitespace texts
;; between (the original position of) the point and EOL,
;; send them into a new next line with possible comment
;; prefix.
(newline 2)
(when prefix (insert prefix))
(beginning-of-line 0)
;; Take note that there are texts to be indented later
;; unless the region is activated.
(unless active-mark
(setq additional-indent t)))))
;; What to do with the line containing mark.
;; If there is active region...
(when active-mark
;; - Open a new empty line for later insertion of "\end{foo}"
;; and put the mark there.
;; - If there were at first any non-whitespace texts between the
;; mark and EOL, pass them over the empty line and put them on
;; their own line with possible comment prefix.
(save-excursion
(goto-char (mark))
(cond (;; When the entire line consists of whitespaces except
;; possible prefix...
(save-excursion (beginning-of-line)
(looking-at
(if prefix
(concat "\\(" prefix "\\)?[ \t]*$")
"[ \t]*$")))
;; ...make the line empty and put the mark there.
(delete-region (match-beginning 0) (match-end 0)))
(;; When there are only whitespaces except possible prefix
;; between the mark and BOL (including the case the mark
;; is at BOL)...
(TeX-looking-at-backward (if prefix
(concat "^\\(" prefix "\\)?[ \t]*")
"^[ \t]*")
(line-beginning-position))
;; ...in this case, we have non-whitespace texts
;; between the mark and EOL, so send the entire line
;; into a new next line and put the mark on the empty
;; line just created.
(beginning-of-line)
(set-mark (point))
(newline)
;; Take note that there are texts to be indented later.
(setq additional-indent t))
(;; In all other cases...
t
;; ...make a new empty line after deleting all
;; whitespaces around the mark, put the mark there...
(delete-horizontal-space)
(insert-before-markers "\n")
;; ...and if there were at first any non-whitespace
;; texts between (the original position of) the mark
;; and EOL, send them into a new next line with
;; possible comment prefix.
(unless (eolp)
(newline)
(when prefix (insert prefix))
;; Take note that there are texts to be indented
;; later.
(setq additional-indent t))))))
;; Now insert the environment.
(when prefix (insert prefix))
(setq env-start (point))
(insert TeX-esc "begin" TeX-grop environment TeX-grcl)
(indent-according-to-mode)
(when extra (insert extra))
(setq content-start (line-beginning-position 2))
(unless active-mark
(newline)
(when prefix (insert prefix))
(newline))
(when active-mark (goto-char (mark)))
(when prefix (insert prefix))
(insert TeX-esc "end" TeX-grop environment TeX-grcl)
(end-of-line 0)
(if active-mark
(progn
(if (and auto-fill-function
(not (assoc environment LaTeX-indent-environment-list)))
;; Fill the region only when `auto-fill-mode' is active
;; and no special indent rule exists.
(LaTeX-fill-region content-start (line-beginning-position 2))
;; Else just indent the region. (bug#48518, bug#28382)
(indent-region content-start (line-beginning-position 2)))
(set-mark content-start))
(indent-according-to-mode))
;; Indent \end{foo}.
(save-excursion (beginning-of-line 2) (indent-according-to-mode)
(when additional-indent
;; Indent texts sent after the inserted
;; environment.
(forward-line 1) (indent-according-to-mode)))
(TeX-math-input-method-off)
(setq env-end (save-excursion
(search-forward
(concat TeX-esc "end" TeX-grop
environment TeX-grcl))
(match-beginning 0)))
(run-hook-with-args 'LaTeX-after-insert-env-hook
environment env-start env-end)))
(defun LaTeX-environment-name-regexp ()
"Return the regexp matching the name of a LaTeX environment.
This matches everything different from a TeX closing brace but
allowing one level of TeX group braces."
(concat "\\([^" TeX-grcl TeX-grop "]*\\(" (regexp-quote TeX-grop)
"[^" TeX-grcl TeX-grop "]*" (regexp-quote TeX-grcl) "\\)*[^"
TeX-grcl TeX-grop "]*\\)"))
(defvar LaTeX-after-modify-env-hook nil
"List of functions to be run at the end of `LaTeX-modify-environment'.
Each function is called with four arguments: the new name of the
environment, the former name of the environment, the buffer
position just before \\begin and the position just before
\\end.")
(defun LaTeX-modify-environment (environment)
"Modify current environment to new ENVIRONMENT."
(let ((goto-end (lambda ()
(LaTeX-find-matching-end)
(re-search-backward (concat (regexp-quote TeX-esc)
"end"
(regexp-quote TeX-grop)
(LaTeX-environment-name-regexp)
(regexp-quote TeX-grcl))
(line-beginning-position))))
(goto-begin (lambda ()
(LaTeX-find-matching-begin)
(prog1 (point)
(re-search-forward (concat (regexp-quote TeX-esc)
"begin"
(regexp-quote TeX-grop)
(LaTeX-environment-name-regexp)
(regexp-quote TeX-grcl))
(line-end-position))))))
(save-excursion
(funcall goto-end)
(let ((old-env (match-string-no-properties 1))
beg-pos)
(replace-match environment t t nil 1)
;; This failed when \begin and \end lie on the same line. (bug#58689)
;; (beginning-of-line 1)
(setq beg-pos (funcall goto-begin))
(replace-match environment t t nil 1)
;; (end-of-line 1)
(run-hook-with-args 'LaTeX-after-modify-env-hook
environment old-env
beg-pos
(funcall goto-end))))))
(defvar LaTeX-syntactic-comments) ;; Defined further below.
(defun LaTeX-current-environment (&optional arg)
"Return the name (a string) of the enclosing LaTeX environment.
With optional ARG>=1, find that outer level.
If function is called inside a comment and
`LaTeX-syntactic-comments' is enabled, try to find the
environment in commented regions with the same comment prefix.
The functions `LaTeX-find-matching-begin' and `LaTeX-find-matching-end'
work analogously."
(save-excursion
(if (LaTeX-backward-up-environment arg)
(progn
(re-search-forward (concat
TeX-grop (LaTeX-environment-name-regexp)
TeX-grcl))
(match-string-no-properties 1))
"document")))
(defun LaTeX-backward-up-environment (&optional arg)
"Move backward out of the enclosing environment.
Helper function of `LaTeX-current-environment' and
`LaTeX-find-matching-begin'.
With optional ARG>=1, find that outer level.
Return non-nil if the operation succeeded.
Assume the current point is on neither \"begin{foo}\" nor \"end{foo}\"."
(setq arg (if arg (if (< arg 1) 1 arg) 1))
(let* ((in-comment (TeX-in-commented-line))
(comment-prefix (and in-comment (TeX-comment-prefix)))
(case-fold-search nil))
(while (and (/= arg 0)
(re-search-backward
(concat (regexp-quote TeX-esc) "\\(begin\\|end\\)\\b") nil t))
(when (or (and LaTeX-syntactic-comments
(eq in-comment (TeX-in-commented-line))
(or (not in-comment)
;; Consider only matching prefixes in the
;; commented case.
(string= comment-prefix (TeX-comment-prefix))))
(and (not LaTeX-syntactic-comments)
(not (TeX-in-commented-line)))
;; macrocode*? in docTeX-mode is special since we have
;; also regular code lines not starting with a
;; comment-prefix. Hence, the next check just looks
;; if we're inside such a group and returns non-nil to
;; recognize such a situation.
(and (eq major-mode 'docTeX-mode)
(looking-at-p (concat (regexp-quote TeX-esc)
"\\(?:begin\\|end\\) *{macrocode\\*?}"))))
(setq arg (if (= (char-after (match-beginning 1)) ?e)
(1+ arg)
(1- arg)))))
(= arg 0)))
(defun docTeX-in-macrocode-p ()
"Determine if point is inside a macrocode environment."
(let ((case-fold-search nil))
(save-excursion
(re-search-backward
(concat "^% " (regexp-quote TeX-esc)
"\\(begin\\|end\\)[ \t]*{macrocode\\*?}") nil 'move)
(not (or (bobp)
(= (char-after (match-beginning 1)) ?e))))))
;;; Environment Hooks
(defvar LaTeX-document-style-hook nil
"List of hooks to run when inserting a document environment.
To insert a hook here, you must insert it in the appropriate style file.")
(defun LaTeX-env-document (&optional _ignore)
"Create new LaTeX document.
Also inserts a \\documentclass macro if there's none already and
prompts for the insertion of \\usepackage macros.
The compatibility argument IGNORE is ignored."
;; just assume a single valid \\documentclass, i.e., one not in a
;; commented line
(let ((found nil))
(save-excursion
(while (and (not found)
(re-search-backward
"\\\\documentclass\\(\\[[^]\n\r]*\\]\\)?\\({[^}]+}\\)"
nil t))
(and (not (TeX-in-commented-line))
(setq found t))))
(when (not found)
(TeX-insert-macro "documentclass")
(LaTeX-newline)
(LaTeX-newline)
;; Add a newline only if some `\usepackage' has been inserted.
(if (LaTeX-insert-usepackages)
(LaTeX-newline))
(LaTeX-newline)
(end-of-line 0)))
(LaTeX-insert-environment "document")
(run-hooks 'LaTeX-document-style-hook)
(setq LaTeX-document-style-hook nil))
(defcustom LaTeX-float ""
"Default float position for figures and tables.
If nil, act like the empty string is given, but do not prompt.