Fuco1 / smartparens

Minor mode for Emacs that deals with parens pairs and tries to be smart about it.
GNU General Public License v3.0
1.83k stars 195 forks source link

Character insertion is slow with smartparens in Latex buffers #1013

Open ThibautVerron opened 4 years ago

ThibautVerron commented 4 years ago

Expected behavior

Normal, fluid text insertion.

Actual behavior

Typing text in LaTeX buffers feels slow and jumpy.

Steps to reproduce the problem

Open a LaTeX buffer, preferably on a slow-ish computer.

Copy/paste the following:

\documentclass{article}

\begin{document}

\end{document}

Place the point between \begin{document} and \end{document}, and insert some text, for example (including automatic insertions):

This is some text and some more. Now let's type a lot of maths: $\{[()]\}$ $() () []
\left\{  \right\}$

Backtraces if necessary (M-x toggle-debug-on-error)

Backtraces not necessary, but here are relevant sections of profiler report for the above text:

- command-execute                                                3678  47%
 - call-interactively                                            3671  47%
  - funcall-interactively                                        3671  47%
   - self-insert-command                                         2363  30%
    - sp--post-self-insert-hook-handler                          2350  30%
     - sp--all-pairs-to-insert                                   1158  14%
      - sp--do-action-p                                           839  10%
       - sp-in-math-p                                             517   6%
        - texmathp                                                494   6%
         + texmathp-match-macro                                   106   1%
(...)
- timer-event-handler                                            2521  32%
 - apply                                                         2521  32%
  - sp-show--pair-function                                       2513  32%
   - #<compiled 0x11395ad>                                       1469  19%
    - sp-get-thing                                               1204  15%
     + sp-skip-backward-to-symbol                                 251   3%

Environment & version information

ThibautVerron commented 4 years ago

Further comments which I didn't know where to put in the formatted report. They might be helpful, but they aren't really more than uneducated guesses.

From what I can see in the report, there are a few sources of slowness.

The easiest to fix (and the least impactful) is the repeated calls to texmathp. From what I can tell, most of those are not necessary, most pairs are either meaningful in both math and text, or only meaningful in math anyway.

The second is the listing of all possible pairs, both in all-pairs-to-insert and in -show-pair-function. I suspect that this is slow because there are a lot of pairs in LaTeX, compared to other modes.

To mitigate this (and yes, it goes opposite to what I wrote above), we could have two sets of pairs: those which are valid in text (very few) and those which are valid in maths. Then at least when typing text, there would be no looping over all maths pairs. But insertion of mathematics would still be slow.

Going further would require optimizing the lookup function, I guess.

And the third is in post-self-insert-hook-handler, I don't know why. Is the output of all-pairs-to-insert large enough that the subsequent loop is expensive?

ThibautVerron commented 4 years ago

Here is another profiler report, obtained after typing "This is some text and some more." a few times. There shouldn't be anything looking like the beginning of a pair in there, and definitely no pair. Yet smartparens does a non-trivial amount of work, including testing a few times whether we are in math mode.

- command-execute                                                5428  69%
 - call-interactively                                            5418  69%
  - funcall-interactively                                        5418  69%
   - self-insert-command                                         4039  51%
    - sp--post-self-insert-hook-handler                          3969  50%
     - sp--all-pairs-to-insert                                   1830  23%
      - sp--do-action-p                                           979  12%
       + sp-get-pair                                              291   3%
       + sp-in-math-p                                             273   3%
       + sp-point-in-string                                       162   2%
       + sp-point-in-comment                                       94   1%
       + -flatten                                                  46   0%
      + sp--looking-back-p                                        703   9%
        sp--strict-regexp-quote                                    55   0%
     - sp-insert-pair                                            1730  22%
      - sp--pair-to-insert                                       1446  18%
       - sp--all-pairs-to-insert                                 1438  18%
        - sp--do-action-p                                         720   9%
         + sp-get-pair                                            247   3%
         + sp-in-math-p                                           195   2%
         + sp-in-string-quotes-p                                   52   0%
         + sp-point-in-string                                      51   0%
         + sp-latex-point-after-backslash                          48   0%
         + sp-point-in-comment                                     32   0%
         + -flatten                                                20   0%
         + sp-text-mode-emoticon-p                                 15   0%
        - sp--looking-back-p                                      614   7%
         + sp--looking-back                                       295   3%
           #<compiled 0x1b9bee9>                                    8   0%
          sp--strict-regexp-quote                                  48   0%
      + sp--get-closing-regexp                                    272   3%

I have run some tests without loading all the fancy pairs, and text insertion does feel faster, but still not to the point of, say, elisp. Again the profiler points to smartparens, with more of the work in the post-self-insert-hook-handler.

So the length and complexity of the pairs list seems to play a role, but not exclusively.

ThibautVerron commented 4 years ago

For the record, doom disables a bunch of pairs for performance purposes:

https://github.com/hlissner/doom-emacs/blob/7cf2109b1a0ca43f2a8567db1da0c426477c6cab/modules/lang/latex/config.el#L62

mjkramer commented 4 years ago

It looks like the problem comes mainly from repeated calls to the slowpoke function texmathp (via sp-in-math-p). Based on my cursory glance at the code, for every inserted character, smartparens loops over all defined pairs and asks "is this pair allowed here?". For eight pairs, this check involves calling texmathp: single/double quotes, \left (w/ (, [, {, |), lvert, and lVert.

So, as a workaround, you could either disable these pairs (aside from single/double quotes) and let YASnippet handle them (like Doom does), or else get rid of the :when/:unless '(sp-in-math-p) on all of these pairs:

(let ((modes '(tex-mode plain-tex-mode latex-mode LaTeX-mode)))
  (dolist (pair '(("``" . "''")
                  ("`" . "'")))
    (sp-local-pair modes (car pair) (cdr pair)
                   :unless '(:rem sp-in-math-p)))
  (dolist (pair '(("\\left(" . "\\right)")
                  ("\\left[" . "\\right]")
                  ("\\left{" . "\\right}")
                  ("\\left|" . "\\right|")
                  ("\\lVert" . "\\rVert")
                  ("\\lvert" . "\\rvert")))
    (sp-local-pair modes (car pair) (cdr pair)
                   :when '(:rem sp-in-math-p))))

For me, this second technique significantly improves the performance. Of course, it gets rid of the math-mode sanity check, but personally I don't care. A better long-term fix might involve caching the result of texmathp?

ThibautVerron commented 4 years ago

Of course, it gets rid of the math-mode sanity check, but personally I don't care.

For left/right and so on, I cannot imagine a scenario where I would insert the text for one of those pairs in text mode and not want expansion. It can, on the other hand, happen that texmathp gets confused about the environment.

The test, though imperfect, might still be helpful for the quotes.

For me, this second technique significantly improves the performance.

If I remember correctly, I had tried a similar fix (but more rudimentary: redefining sp-in-math-p to the constant true function). I did observe a speed-up, but not sufficient to make editing completely fluid.

let YASnippet handle them (like Doom does)

It's slightly off-topic, but I have not been able to find out how and where this is configured exactly. I only found one snippet handling \left( / \right).

For now I have disabled those pairs altogether and replaced them with a custom function, but I would very much like to have smartparens or yasnippet handle the corner cases for me.

haji-ali commented 1 year ago

See #1141 for a possible solution