10

Yet another question about indentation in AUCTeX...

I would like to be able to indent content inside square brackets [...]. It would be useful for tikz option lists, for example, or trees drawn using forest or qtree: both these packages use square brackets for their syntax, which makes it easy to scan in source code in an editor supporting paren-indentation.

Unfortunately, AUCTeX only indents the content of environments and content between braces {...}.

The forest example is also a situation where cheating by adding braces around the content we want indented won't work, because braces will escape all content from being evaluated as a tree.

Example:

\documentclass{article}
\usepackage{forest}

\begin{document}

\begin{forest}
  [A
  [B]
  [C
  [D]
  [E]
  ]
  ]
\end{forest}

\end{document}

Which I would like indented as:

\documentclass{article}
\usepackage{forest}

\begin{document}

\begin{forest}
  [A
    [B]
    [C
      [D]
      [E]
    ]
  ]
\end{forest}

\end{document}

Is there a way?

T. Verron
  • 4,233
  • 1
  • 22
  • 55
  • Just inside `forest` environment? – giordano Oct 15 '15 at 12:39
  • @giordano Ideally, everywhere. But if you have a solution working specifically for forest, that will still be a good start. – T. Verron Oct 15 '15 at 12:52
  • Well, I had an idea but wasn't really working, so I have to think more – giordano Oct 15 '15 at 14:15
  • @giordano @T.Verron As some time has past since the last post: Have you found a solution to this? I've tried to adapt the solution for Lisp mentioned [here](http://stackoverflow.com/questions/23689045/how-to-indent-square-brackets-identically-as-parentheses-in-emacs) but without results. More precisely, I've tried `(modify-syntax-entry ?\[ "(]" LaTeX-mode-syntax-table) (modify-syntax-entry ?\] ")[" LaTeX-mode-syntax-table)`. – Timm Oct 25 '16 at 14:18
  • @Timm Sadly, I still don't have any solution. The syntax table is correct: `character: [ (...) syntax: (] which means: open, matches ]` but it seems that AUCTeX's indentation mechanism disregards this information. – T. Verron Oct 25 '16 at 14:23

3 Answers3

8

Setting LaTeX-begin-regexp and LaTeX-end-regexp does not help here because inside LaTeX-indent-level-count it searches a backslash before checking the regexp.

I got it working by modifying TeX-brace-count-line. This solution counts [] as brace {}. Compare it will original function if you want to see the difference.

(defun TeX-brace-count-line ()
  "Count number of open/closed braces."
  (save-excursion
    (let ((count 0) (limit (line-end-position)) char)
      (while (progn
               (skip-chars-forward "^{}[]\\\\" limit)
               (when (and (< (point) limit) (not (TeX-in-comment)))
                 (setq char (char-after))
                 (forward-char)
                 (cond ((eq char ?\{)
                        (setq count (+ count TeX-brace-indent-level)))
                       ((eq char ?\})
                        (setq count (- count TeX-brace-indent-level)))
                       ((eq char ?\[)
                        (setq count (+ count TeX-brace-indent-level)))
                       ((eq char ?\])
                        (setq count (- count TeX-brace-indent-level)))
                       ((eq char ?\\)
                        (when (< (point) limit)
                          (forward-char)
                          t))))))
      count)))
Hebi Li
  • 81
  • 1
  • 2
  • Thanks for this great answer. However, if we try it on the example in the question, we wee that the ending bracket is aligned with the previous line. Would it be possible to make it aligned with the starting bracket? – tobiasBora Nov 25 '21 at 17:58
3

This feature was added to AUCTeX with this change. Note that this is an opt-in feature: if you want to activate it for square brackets, you have to customize the variables TeX-indent-open-delimiters and TeX-indent-close-delimiters or set them in your init file like this:

(setq TeX-indent-open-delimiters "[")
(setq TeX-indent-close-delimiters "]")

In case AUCTeX is misparsing an opening bracket, you can always add a closing one as a comment in the same line which is taken into account.

Arash Esbati
  • 1,795
  • 1
  • 8
  • 13
  • Ah, very nice! Thank you! – gusbrs Mar 17 '22 at 15:30
  • @gusbrs - I'm just the messenger here, the coding was done by others. But you're welcome, of course `:-)` – Arash Esbati Mar 17 '22 at 15:42
  • Arash, I saw the patch and was just taking a look at the recent messages at auctex-devel. But "you" meant "you maintainers", and also "you you" because if it was not for the "message" I wouldn't have known about it. ;) – gusbrs Mar 17 '22 at 15:48
  • I was following that discussion in the mailing list with great interest, it's nice to see it pushed. Thanks for posting it here! – T. Verron Mar 18 '22 at 13:06
1

Based on Hebi Li's answer. In addition of changing the function TeX-brace-count-line, you actually also have to modify two additional functions, namely LaTeX-indent-calculate and LaTeX-indent-calculate-last.

Just change the last (looking-at "}") into (looking-at "}\\|]") in both functions.

Here is my complete solution (also with a modification to TeX-brace-count-line):

    (defun TeX-brace-count-line ()
      "Count number of open/closed braces."
      (save-excursion
        (let ((count 0) (limit (line-end-position)) char)
          (while (progn
                   (skip-chars-forward "^{}[]\\\\" limit)
                   (when (and (< (point) limit) (not (TeX-in-comment)))
                     (setq char (char-after))
                     (forward-char)
                     (cond ((or (eq char ?\{) (eq char ?\[))
                            (setq count (+ count TeX-brace-indent-level)))
                           ((or (eq char ?\}) (eq char ?\]))
                            (setq count (- count TeX-brace-indent-level)))
                           ((eq char ?\\)
                            (when (< (point) limit)
                              (forward-char)
                              t))))))
          count)))

    (defun LaTeX-indent-calculate (&optional force-type)
      "Return the indentation of a line of LaTeX source.
     FORCE-TYPE can be used to force the calculation of an inner or
     outer indentation in case of a commented line.  The symbols
     'inner and 'outer are recognized."
      (save-excursion
        (LaTeX-back-to-indentation force-type)
        (let ((i 0)
              (list-length (safe-length docTeX-indent-inner-fixed))
              (case-fold-search nil)
              entry
              found)
          (cond ((save-excursion (beginning-of-line) (bobp)) 0)
                ((and (eq major-mode 'doctex-mode)
                      fill-prefix
                      (TeX-in-line-comment)
                      (progn
                        (while (and (< i list-length)
                                    (not found))
                          (setq entry (nth i docTeX-indent-inner-fixed))
                          (when (looking-at (nth 0 entry))
                            (setq found t))
                          (setq i (1+ i)))
                        found))
                 (if (nth 2 entry)
                     (- (nth 1 entry) (if (integerp comment-padding)
                                          comment-padding
                                        (length comment-padding)))
                   (nth 1 entry)))
                ((looking-at (concat (regexp-quote TeX-esc)
                                     "\\(begin\\|end\\){\\("
                                     (LaTeX-verbatim-regexp)
                                     "\\)}"))
                 ;; \end{verbatim} must be flush left, otherwise an unwanted
                 ;; empty line appears in LaTeX's output.
                 0)
                ((and LaTeX-indent-environment-check
                      ;; Special environments.
                      (let ((entry (assoc (or LaTeX-current-environment
                                              (LaTeX-current-environment))
                                          LaTeX-indent-environment-list)))
                        (and entry
                             (nth 1 entry)
                             (funcall (nth 1 entry))))))
                ((looking-at (concat (regexp-quote TeX-esc)
                                     "\\("
                                     LaTeX-end-regexp
                                     "\\)"))
                 ;; Backindent at \end.
                 (- (LaTeX-indent-calculate-last force-type) LaTeX-indent-level))
                ((looking-at (concat (regexp-quote TeX-esc) "right\\b"))
                 ;; Backindent at \right.
                 (- (LaTeX-indent-calculate-last force-type)
                    LaTeX-left-right-indent-level))
                ((looking-at (concat (regexp-quote TeX-esc)
                                     "\\("
                                     LaTeX-item-regexp
                                     "\\)"))
                 ;; Items.
                 (+ (LaTeX-indent-calculate-last force-type) LaTeX-item-indent))
                ((looking-at "]\\|}")
                 ;; End brace in the start of the line.
                 (- (LaTeX-indent-calculate-last force-type)
                    TeX-brace-indent-level))
                (t (LaTeX-indent-calculate-last force-type))))))

    (defun LaTeX-indent-calculate-last (&optional force-type)
      "Return the correct indentation of a normal line of text.
    The point is supposed to be at the beginning of the current line.
    FORCE-TYPE can be used to force the calculation of an inner or
    outer indentation in case of a commented line.  The symbols
    'inner and 'outer are recognized."
      (let (line-comment-current-flag
            line-comment-last-flag
            comment-current-flag
            comment-last-flag)
        (beginning-of-line)
        (setq line-comment-current-flag (TeX-in-line-comment)
              comment-current-flag (TeX-in-commented-line))
        (if comment-current-flag
            (skip-chars-backward "%\n\t ")
          (skip-chars-backward "\n\t "))
        (beginning-of-line)
        ;; If we are called in a non-comment line, skip over comment
        ;; lines.  The computation of indentation should in this case
        ;; rather take the last non-comment line into account.
        ;; Otherwise there might arise problems with e.g. multi-line
        ;; code comments.  This behavior is not enabled in docTeX mode
        ;; where large amounts of line comments may have to be skipped
        ;; and indentation should not be influenced by unrelated code in
        ;; other macrocode environments.
        (while (and (not (eq major-mode 'doctex-mode))
                    (not comment-current-flag)
                    (TeX-in-commented-line)
                    (not (bobp)))
          (skip-chars-backward "\n\t ")
          (beginning-of-line))
        (setq line-comment-last-flag (TeX-in-line-comment)
              comment-last-flag (TeX-in-commented-line))
        (LaTeX-back-to-indentation force-type)
        ;; Separate line comments and other stuff (normal text/code and
        ;; code comments).  Additionally we don't want to compute inner
        ;; indentation when a commented and a non-commented line are
        ;; compared.
        (cond ((or (and (eq major-mode 'doctex-mode)
                        (or (and line-comment-current-flag
                                 (not line-comment-last-flag))
                            (and (not line-comment-current-flag)
                                 line-comment-last-flag)))
                   (and force-type
                        (eq force-type 'inner)
                        (or (and comment-current-flag
                                 (not comment-last-flag))
                            (and (not comment-current-flag)
                                 comment-last-flag))))
               0)
              ((looking-at (concat (regexp-quote TeX-esc)
                                   "begin *{\\("
                                   LaTeX-document-regexp
                                   "\\)}"))
               ;; I dislike having all of the document indented...
               (+ (LaTeX-current-indentation force-type)
                  ;; Some people have opening braces at the end of the
                  ;; line, e.g. in case of `\begin{letter}{%'.
                  (TeX-brace-count-line)))
              ((and (eq major-mode 'doctex-mode)
                    (looking-at (concat (regexp-quote TeX-esc)
                                        "end[ \t]*{macrocode\\*?}"))
                    fill-prefix
                    (TeX-in-line-comment))
               ;; Reset indentation to zero after a macrocode
               ;; environment.
               0)
              ((looking-at (concat (regexp-quote TeX-esc)
                                   "begin *{\\("
                                   (LaTeX-verbatim-regexp)
                                   "\\)}"))
               0)
              ((looking-at (concat (regexp-quote TeX-esc)
                                   "end *{\\("
                                   (LaTeX-verbatim-regexp)
                                   "\\)}"))
               ;; If I see an \end{verbatim} in the previous line I skip
               ;; back to the preceding \begin{verbatim}.
               (save-excursion
                 (if (re-search-backward (concat (regexp-quote TeX-esc)
                                                 "begin *{\\("
                                                 (LaTeX-verbatim-regexp)
                                                 "\\)}") 0 t)
                     (LaTeX-indent-calculate-last force-type)
                   0)))
              (t (+ (LaTeX-current-indentation force-type)
                    (if (not (and force-type
                                  (eq force-type 'outer)
                                  (TeX-in-commented-line)))
                        (+ (LaTeX-indent-level-count)
                           (TeX-brace-count-line))
                      0)
                    (cond ((looking-at (concat (regexp-quote TeX-esc)
                                               "\\("
                                               LaTeX-end-regexp
                                               "\\)"))
                           LaTeX-indent-level)
                          ((looking-at
                            (concat (regexp-quote TeX-esc) "right\\b"))
                           LaTeX-left-right-indent-level)
                          ((looking-at (concat (regexp-quote TeX-esc)
                                               "\\("
                                               LaTeX-item-regexp
                                               "\\)"))
                           (- LaTeX-item-indent))
                          ((looking-at "}\\|]")
                           TeX-brace-indent-level)
                          (t 0)))))))
Uriziel47
  • 11
  • 1