bug#81150: 31.0.60; eglot: performance issue with markdown-ts-mode

Romain Ouabdelkader <[email protected]> Fri, 7 Aug 2026 10:57:47 +0200
Newsgroups gmane.emacs.bugs
Message-ID <CAJ8YToYCkwaEhLZEBffrid_2yedSxucN+RBsj1+S26f4eFteCg@mail.gmail.com>
Hello Rahul,

I tested with emacs -Q on Emacs 32 and I still see a freeze of
approximately 0.5s.
Attached is a cpu profile.

To reproduce the freeze, I used the previously shared test_standalone.py:
- place cursor on "test"
- wait ~0.5s (maybe try different timings)
- move cursor up, this should freeze
- wait for the eldoc text to disappear from the minibuffer
- move cursor down (repeat)


On Wed, 5 Aug 2026 at 04:57, Rahul Martim Juliato <[email protected]>
wrote:

> Eli Zaretskii <[email protected]> writes:
>
> >> [...]
> >
> > I think any such improvements are better developed on the master
> > branch.
>
>
> Hi everyone,
>
> I'd like to follow up on this issue from the perspective of current
> Emacs master (Emacs 32 to be).
>
> We recently fixed bug#81334 on master. That fix made rendering markdown
> buffers with many local parsers significantly faster, which is exactly
> the path Eglot takes when rendering documentation.
>
> I took the test_standalone.py file from this report and opened it with:
>
>
>   emacs -Q \
>     test_standalone.py \
>     --eval "(setq eglot-documentation-renderer 'markdown-ts-view-mode)"
>
>
> With the buffer open, `M-x eglot RET` started the LSP server, as the
> recipe in this report describes, and then moved the cursor through the
> call sites where the documentation is rendered with
> `markdown-ts-view-mode`.
>
> On a couple of runs on two different machines I could not notice any lag
> anymore.
>
> To be clear, this is about master only. Nothing here asks to revisit
> what was decided for emacs-31, where the modes stay experimental and
> opt-in.
>
> Romain, could you please give this recipe another try on master and tell
> us what you see?
>
> --
> Rahul Martim Juliato
>
profile32.txt (text/plain, 15.7 KB)
       23867  92% - timer-event-handler
       23865  92%  - apply
       23735  91%   - #<byte-code-function 34C>
       23735  91%    - jsonrpc-connection-receive
       23712  91%     - jsonrpc--continue
       23712  91%      - #<byte-code-function 485>
       23712  91%       - apply
       23712  91%        - #<byte-code-function B12>
       23491  90%         - mapconcat
       23477  90%          - #<byte-code-function 42F>
       23477  90%           - eglot--sig-info
       23428  90%            - eglot--format-markup
       23210  89%             - font-lock-ensure
       23208  89%              - #<byte-code-function 3D6>
       23208  89%               - font-lock-fontify-region
       23208  89%                - font-lock-default-fontify-region
       23205  89%                 - font-lock-fontify-syntactically-region
       23205  89%                  - treesit-font-lock-fontify-region
       23043  89%                   - treesit-update-ranges
       23041  89%                    - treesit--update-range-1
       23041  89%                     - treesit--update-ranges-local
       21979  85%                      - treesit-query-range
          10   0%                       - treesit-pattern-expand
           9   0%                          treesit-pattern-expand
        1058   4%                      - treesit-query-range-by-language
           2   0%                       - treesit-pattern-expand
           2   0%                          treesit-pattern-expand
           2   0%                        treesit-parser-create
           2   0%                        treesit--set-embed-ranges
         135   0%                   - treesit--font-lock-fontify-region-1
           5   0%                    - markdown-ts--fontify-hard-line-break
           3   0%                     - treesit-fontify-with-override
           3   0%                      - font-lock-append-text-property
           3   0%                         font-lock--add-text-property
           1   0%                     - treesit-node-text
           1   0%                        treesit-node-buffer
           4   0%                    - markdown-ts--fontify-entity
           2   0%                     - treesit-fontify-with-override
           1   0%                      - font-lock-append-text-property
           1   0%                         font-lock--add-text-property
           2   0%                      facep
           1   0%                    - treesit-fontify-with-override
           1   0%                     - font-lock-prepend-text-property
           1   0%                        font-lock--add-text-property
          11   0%                   - cl-remove-if-not
          11   0%                    - cl-remove
           6   0%                     - funcall
           5   0%                        #<byte-code-function EAA>
           7   0%                     treesit-parser-root-node
           2   0%                   - font-lock-unfontify-region
           1   0%                    - #<byte-code-function CC3>
           1   0%                       restore-buffer-modified-p
           1   0%                   font-lock-extend-region-wholelines
           1   0%                   font-lock-unfontify-region
           1   0%                   font-lock-extend-region-multiline
           2   0%                font-lock-set-defaults
         203   0%             - markdown-ts-mode
         198   0%              - markdown-ts-mode--initialize
         198   0%               - markdown-ts--set-up
          86   0%                - markdown-ts-code-block-context-mode
          85   0%                 - run-with-timer
          85   0%                  - run-at-time
          80   0%                   - timer-activate
          80   0%                    - timer--activate
          79   0%                     - timer--time-less-p
          15   0%                        timer--time
           5   0%                     timer-relative-time
           1   0%                   add-hook
          77   0%                - markdown-ts-table-mode
          77   0%                 - run-with-timer
          77   0%                  - run-at-time
          77   0%                   - timer-activate
          77   0%                    - timer--activate
          73   0%                     - timer--time-less-p
          16   0%                        timer--time
          14   0%                - treesit-major-mode-setup
           6   0%                 - copy-tree
           5   0%                    copy-tree
           5   0%                 - treesit-font-lock-recompute-features
           5   0%                  - treesit-validate-and-compile-font-lock-rules
           1   0%                   - treesit--compile-query-with-cache
           1   0%                      treesit-query-p
           2   0%                   treesit-thing-defined-p
           4   0%                - jit-lock-register
           4   0%                 - jit-lock-mode
           2   0%                    add-hook
           3   0%                - outline-minor-mode
           1   0%                   add-to-list
           1   0%                   add-hook
           2   0%                - treesit-merge-font-lock-feature-list
           1   0%                   cl-union
           1   0%                  treesit-ready-p
           1   0%                  treesit-range-rules
           4   0%              - text-mode
           3   0%               - add-hook
           2   0%                  #<byte-code-function 2B4>
           1   0%                abbrev-table-get
           9   0%               buffer-string
           2   0%               generate-new-buffer
           2   0%               string-trim
           1   0%             - #<byte-code-function 752>
           1   0%                kill-buffer
          26   0%            - replace-regexp-in-string
          16   0%               concat
           1   0%              ns-unselect-line
           1   0%            - eglot--check-object
           1   0%               eglot--interface
         221   0%         - #<byte-code-function 28D>
         221   0%          - #<byte-code-function 3F8>
         221   0%           - run-hook-with-args
         206   0%            - eldoc-display-in-echo-area
         205   0%             - eldoc--message
         205   0%              - eldoc-minibuffer-message
         205   0%               - message
         186   0%                - redisplay_internal (C function)
           5   0%                 - eval
           4   0%                  - cl-loop
           4   0%                   - cl-block
           4   0%                    - cl--block-wrapper
           4   0%                     - let
           4   0%                      - catch
           4   0%                       - let*
           3   0%                        - while
           3   0%                         - setq
           3   0%                          - format-mode-line
           3   0%                           - eval
           3   0%                            - when-let*
           1   0%                             - if-let*
           1   0%                              - #<byte-code-function BEC>
           1   0%                               - internal--build-bindings
           1   0%                                - #<byte-code-function 2C3>
           1   0%                                   internal--build-binding
           1   0%                        - cl-loop
           1   0%                         - #<byte-code-function 9F7>
           1   0%                          - cl--parse-loop-clause
           1   0%                             cl--loop-let
           1   0%                    flymake--mode-line-title
           1   0%                   redisplay--pre-redisplay-functions
           1   0%                 - mode-line-default-help-echo
           1   0%                  - window-at-side-p
           1   0%                   - window-pixel-edges
           1   0%                      window-edges
           1   0%                 - funcall
           1   0%                  - #<byte-code-function FE6>
           1   0%                   - gui-backend-selection-exists-p
           1   0%                    - apply
           1   0%                     - #<byte-code-function C0A>
           1   0%                        ns-selection-exists-p
           1   0%             - eldoc--echo-area-substring
           1   0%                end-of-visible-line
          15   0%            - eldoc-display-in-buffer
          15   0%               eldoc--format-doc-buffer
          22   0%     - apply
          22   0%      - jsonrpc--event
          22   0%       - #<byte-code-function 762>
          22   0%        - apply
          22   0%         - jsonrpc--log-event
           1   0%            jsonrpc--limit-buffer-size
           1   0%     - jsonrpc--remove
           1   0%      - slot-value
           1   0%         eieio--slot-name-index
         109   0%   - eldoc--update
         109   0%    - eldoc-print-current-symbol-info
         109   0%     - eldoc--invoke-strategy
         109   0%      - eldoc-documentation-compose
          83   0%       - eglot-code-action-suggestion
          73   0%        - eglot--async-request
          73   0%         - jsonrpc-async-request
          73   0%          - jsonrpc--async-request-1
          72   0%           - apply
          72   0%            - jsonrpc-connection-send
          72   0%             - apply
          72   0%              - #<byte-code-function 20D>
          71   0%                 process-send-string
           6   0%        - eglot--code-action-bounds
           6   0%         - bounds-of-thing-at-point
           2   0%          - thing-at-point--beginning-of-sexp
           2   0%           - forward-sexp
           2   0%            - python-nav-forward-sexp
           2   0%             - python-nav--forward-sexp
           1   0%              - python-syntax-context-type
           1   0%               - syntax-ppss
           1   0%                  parse-partial-sexp
           1   0%              - python-info-end-of-statement-p
           1   0%               - python-nav-end-of-statement
           1   0%                - syntax-ppss
           1   0%                   parse-partial-sexp
           2   0%          - thing-at-point--end-of-sexp
           1   0%           - forward-sexp
           1   0%            - python-nav-forward-sexp
           1   0%             - python-nav--forward-sexp
           1   0%              - python-syntax-context-type
           1   0%               - syntax-ppss
           1   0%                  parse-partial-sexp
           1   0%             syntax-after
           1   0%            re-search-forward
           4   0%        - eglot-server-capable
           2   0%         - eglot--capabilities
           1   0%          - apply
           1   0%           - #<byte-code-function 7CD>
           1   0%            - slot-value
           1   0%               eieio-object-p
           1   0%           throw
          13   0%       - eglot-hover-eldoc-function
          13   0%        - eglot--async-request
          13   0%         - jsonrpc-async-request
          13   0%          - jsonrpc--async-request-1
          13   0%           - apply
          13   0%            - jsonrpc-connection-send
          13   0%             - apply
          13   0%              - #<byte-code-function 20D>
          13   0%                 process-send-string
           8   0%       - eglot-highlight-eldoc-function
           8   0%        - eglot--async-request
           8   0%         - jsonrpc-async-request
           8   0%          - jsonrpc--async-request-1
           8   0%           - apply
           8   0%            - jsonrpc-connection-send
           8   0%             - apply
           8   0%              - #<byte-code-function 20D>
           8   0%                 process-send-string
           5   0%       - eglot-signature-eldoc-function
           5   0%        - eglot--async-request
           5   0%         - jsonrpc-async-request
           5   0%          - jsonrpc--async-request-1
           5   0%           - apply
           5   0%            - jsonrpc-connection-send
           5   0%             - apply
           5   0%              - #<byte-code-function 20D>
           5   0%                 process-send-string
           8   0%   - markdown-ts--enable-code-block-in-context-mode
           8   0%      markdown-ts-code-block-in-context-mode
           7   0%   - markdown-ts--enable-in-table-mode
           6   0%      markdown-ts-in-table-mode
           1   0%    - markdown-ts-at-table-p
           1   0%       markdown-ts--table-abutting-pos
           5   0%   - show-paren-function
           5   0%    - show-paren--default
           3   0%     - show-paren--locate-near-paren
           2   0%        back-to-indentation
           1   0%      - show-paren--categorize-paren
           1   0%         syntax-after
           2   0%     - syntax-ppss
           1   0%        syntax-ppss--data
           1   0%        parse-partial-sexp
           1   0%   - jit-lock-context--update
           1   0%      jit-lock-context-fontify
           2   0%  - timer-activate
           2   0%   - timer--activate
           2   0%    - timer--time-less-p
           1   0%       timer--time
        1149   4%   Automatic GC
         647   2% - redisplay_internal (C function)
           3   0%  - redisplay--pre-redisplay-functions
           1   0%     selected-window
           1   0%   - run-hook-with-args
           1   0%    - redisplay--update-region-highlight
           1   0%       redisplay--unhighlight-overlay-function
           1   0%  - eval
           1   0%   - flymake--mode-line-exception
           1   0%    - flymake-running-backends
           1   0%     - flymake--collect
           1   0%        #<byte-code-function 138>
         104   0% - command-execute
         102   0%  - byte-code
         102   0%   - read-extended-command
         102   0%    - read-extended-command-1
         102   0%     - completing-read-default
          30   0%        redisplay_internal (C function)
           7   0%      - timer-event-handler
           7   0%       - apply
           7   0%        - completions--background-update
           6   0%           input-pending-p
           1   0%         - redisplay
           1   0%            redisplay_internal (C function)
           2   0%  - funcall-interactively
           2   0%   - execute-extended-command
           1   0%    - command-execute
           1   0%     - funcall-interactively
           1   0%        profiler-start
          63   0% - eldoc-pre-command-refresh-echo-area
          63   0%  - eldoc--message
          63   0%   - eldoc-minibuffer-message
          63   0%      message
           3   0% - ...
           3   0%  - completion--do-completion
           3   0%   - completion-try-completion
           3   0%    - completion--nth-completion
           3   0%     - seq-some
           3   0%      - seq-do
           3   0%       - mapc
           3   0%        - #<byte-code-function 48C>
           3   0%         - #<byte-code-function 4E2>
           3   0%          - eval
           3   0%           - let
           3   0%            - funcall
           3   0%             - #<byte-code-function 4A4>
           3   0%              - completion-basic-try-completion
           3   0%               - #<native-comp-function F616e6f6e796d6f75732d6c616d626461_anonymous_lambda_56>
           3   0%                - complete-with-action
           3   0%                   try-completion
           3   0% - jsonrpc--process-filter
           2   0%  - jsonrpc--json-read
           2   0%     json-parse-buffer
           1   0%    search-forward-regexp
           1   0%   clear-minibuffer-message