Malabarba / aggressive-indent-mode

Emacs minor mode that keeps your code always indented. More reliable than electric-indent-mode.
853 stars 35 forks source link

Why even moving point such as `C-f`, `C-b` so slow? #108

Open cmal opened 7 years ago

cmal commented 7 years ago

I found in clojure-mode aggressive-indent-mode is very slow, when I used cider-connect to connect to a remote nrepl.

Enabled minor modes: Abbrev Aggressive-Indent Async-Bytecomp-Package
Auto-Composition Auto-Compression Auto-Encryption Auto-Revert Cider Clj-Refactor
Column-Number Company Diff-Auto-Refine Editorconfig Electric-Indent Encourage
Eval-Sexp-Fu-Flash Fic File-Name-Shadow Flycheck Font-Lock Global-Auto-Revert
Global-Eldoc Global-Font-Lock Global-Git-Commit Global-Hl-Line Global-Smartscan
Global-Subword Global-Undo-Tree Global-Visual-Line Global-Whitespace-Cleanup
Guide-Key Helm Helm-Descbinds Hes Hs Keyfreq Keyfreq-Autosave Line-Number Linum
Magit-Auto-Revert Menu-Bar Mouse-Wheel Paredit Rainbow-Delimiters Recentf
Save-Place Savehist Shell-Dirtrack Show-Paren Smartscan Subword Transient-Mark
Undo-Tree Visual-Line Whitespace-Cleanup Winner Yas Yas-Global
Malabarba commented 7 years ago

Are you sure it's aggressive-indent-mode? Have you tried disabling it to verify?

cmal commented 7 years ago

This has been a problem to me for long. Whenever I disable aggressive-indent-mode, Emacs will never slow again. But I found it hard to detect why it slows down when enable aggressive-indent-mode. And it can also be another mode which get slows down when using together with aggressive-indent-mode. Can you help me with that? I am using Mac OSX and my Emacs config files are at https://github.com/cmal/.emacs.d

cmal commented 7 years ago

The value of my before-save-hook is (aggressive-indent--proccess-changed-list-and-indent t) and the function aggressive-indent--proccess-changed-list-and-indent is the only one before the * changed to - when I call save-buffer. It is common when I call save-buffer and my Emacs becomes non-responsible for a long while. So there must be some function in aggressive-indent--proccess-changed-list-and-indent that takes a lot of time to run.

Am I right about this deduction?

Malabarba commented 7 years ago

aggressive-indent--proccess-changed-list-and-indent can be a little slow on very large buffers, but it only runs on save, so it shouldn't affect movement like C-b and C-f.

I suspect that aggressive-indent might be doing something that's normally very fast but turns out to be slow on your system because you're on a remote repl. Try the following to debug what's causing the hang.

  1. Do M-x toggle-debug-on-quit.
  2. Reproduce the hang (or wait for it to happen)
  3. Hit C-g while it's hanged.

You'll get a new window with a backtrace you can paste here. It will show what was running when you hit C-g

cmal commented 7 years ago

I found it just hang and cannot apply C-g after hit C-f or C-b. C-g will start to work after C-f returns, so that toggle-debug-on-quit will not print a debug message in this kind of hang.

There is a circumstances that will trigger the hang. For example, I insert a sexp into a case statement, which makes the case statement unbalanced, so the aggressive-indent-mode will become slow. Then another sexp is inserted to make the case statement balanced, but now the aggressive-indent-mode is slower, which makes the hang to happen for a while.

I am not sure whether this is the only case which triggers the hang but it really happens again and again.

cmal commented 7 years ago

Sometimes when trying to save file it hangs for a while. Here is the debug info:

Debugger entered--Lisp error: (quit)
  re-search-backward("^(\\(clojure.core/\\)?\\(in-\\)?ns\\+?[\n[:space:]]+\\(?:\\(?:\\(#?\\^{[^}]*}\\)\\|\\(?:\\^:[^[:space:]]+\\)*\\)[\n[:space:]]+\\)*[':]?\\([^\"()[:space:]]+\\_>\\)" nil t)
  clojure-find-ns()
  cider-current-ns()
  cider--get-symbol-indent(#("comment" 0 7 (help-echo cider--help-echo cider-locals ("secucode" "data" "uid" "res-body" "res" "post-data" "redirect-url" "template-id" "openid" "weixin-api" "access-token" "item" "secucode" "hotrank-change" "fever-items" "uid" "fever-records" "lastweek-records" "lastday-records" "keyword1" "notice" "uid" "build-fever-item" "fevers" "uids" "favorstocks" "param2" "param1" "timestamp" "secucode" "t" "interval" "msg" "notice" "param1" "timestamp" "uid" "param2" "param1" "timestamp" "secucode" "uid" "t" "_" "day-change-rank-pc" "day-change-rank" "day-change" "uid" "data" "uid" ...) face font-lock-builtin-face)))
  clojure--get-indent-method(#("comment" 0 7 (help-echo cider--help-echo cider-locals ("secucode" "data" "uid" "res-body" "res" "post-data" "redirect-url" "template-id" "openid" "weixin-api" "access-token" "item" "secucode" "hotrank-change" "fever-items" "uid" "fever-records" "lastweek-records" "lastday-records" "keyword1" "notice" "uid" "build-fever-item" "fevers" "uids" "favorstocks" "param2" "param1" "timestamp" "secucode" "t" "interval" "msg" "notice" "param1" "timestamp" "uid" "param2" "param1" "timestamp" "secucode" "uid" "t" "_" "day-change-rank-pc" "day-change-rank" "day-change" "uid" "data" "uid" ...) face font-lock-builtin-face)))
  clojure--find-indent-spec-backtracking()
  clojure--find-indent-spec()
  clojure-indent-function(55050 (1 55029 55030 nil nil nil 0 nil nil (55029)))
  calculate-lisp-indent()
  lisp-indent-line()
  clojure-indent-line()
  indent-according-to-mode()
  indent-region(51906 56609)
  clojure-indent-region(51906 56609)
  indent-region(51906 56609)
  aggressive-indent-indent-defun(51967 56127)
  aggressive-indent--softly-indent-defun(51967 56127)
  apply(aggressive-indent--softly-indent-defun (51967 56127))
  aggressive-indent--proccess-changed-list-and-indent()
  run-hooks(before-save-hook)
  basic-save-buffer(t)
  save-buffer(1)
  funcall-interactively(save-buffer 1)
  call-interactively(save-buffer nil nil)
  command-execute(save-buffer)
cmal commented 6 years ago

Hi, @Malabarba I did a cpu profiling in Emacs when using aggressive indent mode with clojure-mode. The result follows:

- timer-event-handler                                           16538  81%
 - apply                                                        16495  81%
  - aggressive-indent--indent-if-changed                        15742  77%
   - aggressive-indent--proccess-changed-list-and-indent              15666  77%
    - apply                                                     15665  77%
     - aggressive-indent--softly-indent-defun                   15665  77%
      - aggressive-indent-indent-defun                          15635  76%
       - indent-region                                          15630  76%
        - clojure-indent-region                                 15630  76%
         - indent-region                                         8240  40%
          - indent-according-to-mode                             8214  40%
           - clojure-indent-line                                 8177  40%
            - lisp-indent-line                                   8145  40%
             - calculate-lisp-indent                             8000  39%
              - clojure-indent-function                          6754  33%
               - clojure--find-indent-spec                       6450  31%
                - clojure--find-indent-spec-backtracking               6441  31%
                 - clojure--find-indent-spec-backtracking               5006  24%
                  - clojure-backward-logical-sexp                2168  10%
                   - backward-sexp                               2061  10%
                      forward-sexp                               1388   6%
                   - clojure--looking-at-non-logical-sexp                 83   0%
                      comment-normalize-vars                       42   0%
                      comment-forward                               7   0%
                  - clojure--find-indent-spec-backtracking               2020   9%
                   - clojure--find-indent-spec-backtracking               1501   7%
                    - clojure-backward-logical-sexp               1203   5%
                     - backward-sexp                             1144   5%
                        forward-sexp                              890   4%
                     + clojure--looking-at-non-logical-sexp                 44   0%
                    + clojure--get-indent-method                  211   1%
                      syntax-ppss                                  32   0%
                    + thing-at-point                               13   0%
                   + clojure-backward-logical-sexp                114   0%
                     syntax-ppss                                   99   0%
                   + clojure--get-indent-method                    87   0%
                   + thing-at-point                                13   0%
                  + clojure--get-indent-method                    430   2%
                    syntax-ppss                                   190   0%
                  + thing-at-point                                 18   0%
                 + clojure--get-indent-method                     603   2%
                   syntax-ppss                                    315   1%
                 + thing-at-point                                  38   0%
                 + clojure-backward-logical-sexp                    2   0%
               + #<compiled 0x43818003>                           197   0%
               + clojure--normal-indent                            36   0%
               + thing-at-point                                    24   0%
                 clojure--not-function-form-p                       6   0%
              + beginning-of-defun                                557   2%
            progress-reporter-do-update                            21   0%
         - clojure-align                                         7358  36%
          - indent-region                                        6037  29%
           - clojure-indent-region                               6037  29%
            - indent-region                                      5999  29%
             - indent-according-to-mode                          5986  29%
              - clojure-indent-line                              5953  29%
               - lisp-indent-line                                5934  29%
                - calculate-lisp-indent                          5883  28%
                 - clojure-indent-function                       4972  24%
                  - clojure--find-indent-spec                    4894  24%
                   - clojure--find-indent-spec-backtracking               4887  24%
                    - clojure--find-indent-spec-backtracking               3747  18%
                     - clojure-backward-logical-sexp               1552   7%
                      - backward-sexp                            1463   7%
                         forward-sexp                             925   4%
                      + clojure--looking-at-non-logical-sexp                 61   0%
                     - clojure--find-indent-spec-backtracking               1380   6%
                      - clojure--get-indent-method                689   3%
                       - cider--get-symbol-indent                 680   3%
                        - cider-current-ns                        508   2%
                           clojure-find-ns                        507   2%
                        + cider-resolve-var                       156   0%
                        + cider-resolve-alias                       8   0%
                      + clojure--find-indent-spec-backtracking                282   1%
                        syntax-ppss                                87   0%
                      + clojure-backward-logical-sexp                 65   0%
                      + thing-at-point                             46   0%
                     + clojure--get-indent-method                 444   2%
                       syntax-ppss                                171   0%
                     + thing-at-point                              36   0%
                    + clojure--get-indent-method                  441   2%
                      syntax-ppss                                 243   1%
                    + thing-at-point                               28   0%
                    + clojure-backward-logical-sexp                  1   0%
                  + clojure--normal-indent                         22   0%
                  + thing-at-point                                 20   0%
                    clojure--not-function-form-p                    7   0%
                 + beginning-of-defun                             443   2%
               progress-reporter-do-update                         10   0%
          + align-region                                         1141   5%
          + clojure--find-sexp-to-align                            26   0%
            forward-sexp                                            2   0%
       + end-of-defun                                               4   0%
         beginning-of-defun                                         1   0%
    + cl-member-if                                                  1   0%
   + run-hook-wrapped                                              53   0%
   + internal--before-save-selected-window                          5   0%
   + #<compiled 0x40420e61>                                         3   0%
     cancel-timer                                                   1   0%
  + auto-revert-buffers                                           640   3%
  + savehist-autosave                                              26   0%
  + #<compiled 0x400f80e9>                                          9   0%
  + guide-key/polling-function                                      7   0%
  + flycheck-handle-idle-change                                     3   0%
  + helm-match-line-cleanup                                         2   0%
    jit-lock-context-fontify                                        1   0%
 + timer-activate-when-idle                                        16   0%
 + timer-inc-time                                                  14   0%
   cancel-timer-internal                                            3   0%
 + timer-activate                                                   1   0%
   timer-until                                                      1   0%
+ ...                                                            2318  11%
+ linum-update-current                                            868   4%
+ redisplay_internal (C function)                                 293   1%
+ command-execute                                                 250   1%
+ cider--help-echo                                                 22   0%
  tooltip-show-help-non-mode                                       17   0%
+ sp--post-command-hook-handler                                     5   0%
+ guide-key/close-guide-buffer                                      3   0%
+ eldoc-schedule-timer                                              2   0%
+ winner-save-old-configurations                                    2   0%
  mouse-fixup-help-message                                          1   0%
+ sp--save-pre-command-state                                        1   0%
+ global-hl-line-highlight                                          1   0%
+ isearch-pre-command-hook                                          1   0%
+ deactivate-mark                                                   1   0%

It seems that backward-sexp and forward-sexp runs take most of cpu, and clojure-find-ns is slow, too. But I am not sure whether this is the problem of the slow, because my cpu inspector does not show apparent fluctuates when the slow happens. What I am sure about is that when I disable aggressive-indent-mode, Emacs will not slow again. Are there any other methods that I can use to detect the reason of the problem? Thanks!