Skip to content

[General]: global-treesit-auto-mode extremely slow in emacs-plus 30.2 #828

@cmore-zz

Description

@cmore-zz

Issue description

Took me a bit to track this one down, ended up using "kill -USR2 {emacs-pid}" to get the lisp stacktrace.

The issue is that treesit-language-available-p seems to have gotten much slower in emacs-plus 30.2 (although it's possible it's a macOS change). Granted, one can turn global-treesit-auto-mode off as a work-around. Because treesit-available-p gets called on every file open when global-treesit-auto-mode is enabled, it makes emacs feel very slow, and affects various other commands that generate buffers that set a mode that treesit might support.

I (with LLM) hacked around it by adding a two level cache (one for per-session mtimes of library files, another for enabled and source mtimes.

   (defvar my/treesit-available-cache (make-hash-table :test #'equal)
     "Persistent cache mapping language symbols to t/nil.")

   (defvar my/treesit-libfile-mtime-cache (make-hash-table :test #'equal)
     "Ephemeral cache mapping language symbols to last-known grammar mtime.")

   (defvar my/treesit-available-persist-file
     (expand-file-name "treesit-available-cache.el" user-emacs-directory)
     "File to persist Tree-sitter availability cache.")

   (defun my/load-treesit-cache ()
     "Load persistent Tree-sitter availability cache from disk."
     (when (file-readable-p my/treesit-available-persist-file)
       (with-temp-buffer
         (insert-file-contents my/treesit-available-persist-file)
         (let ((data (read (current-buffer))))
           (when (hash-table-p data)
             (setq my/treesit-available-cache data))))))

   (defun my/save-treesit-cache ()
     "Save the Tree-sitter availability cache to disk."
     (with-temp-file my/treesit-available-persist-file
       (let ((print-length nil)
             (print-level nil))
         (prin1 my/treesit-available-cache (current-buffer)))))

   (defun my/treesit-get-libfile-mtime (lang)
     "Return the mtime of the Tree-sitter grammar file for LANG, or nil if not found."
     (let ((libfile (ignore-errors (treesit--lib-file lang))))
       (when (and libfile (file-exists-p libfile))
         (nth 5 (file-attributes libfile))))) ;; mtime

   (advice-add 'treesit-language-available-p :around
               (lambda (orig-fn &rest args)
                 (let* ((lang (car args))
                        (current-mtime (my/treesit-get-libfile-mtime lang))
                        (cached-mtime (gethash lang my/treesit-libfile-mtime-cache))
                        (cached-result (gethash lang my/treesit-available-cache)))
                   ;; Check if mtime changed during this session
                   (if (and cached-result
                            (equal cached-mtime current-mtime))
                       cached-result
                     (let ((result (apply orig-fn args)))
                       (when current-mtime
                         (puthash lang current-mtime my/treesit-libfile-mtime-cache))
                       (puthash lang result my/treesit-available-cache)
                       result)))))

   (add-hook 'emacs-startup-hook #'my/load-treesit-cache)
   (add-hook 'kill-emacs-hook #'my/save-treesit-cache)   

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions