Preventing Emacs from Freezing on Files with Very Long Lines (e.g, minified JavaScript or CSS, SQL dumps, large JSON files...)

Emacs can slow down when opening files with exceptionally long lines, such as minified JavaScript or CSS, SQL dumps, or large JSON files. Emacs struggles with these files because it parses bi-directional text properties and applies complex regular expressions for syntax highlighting across thousands of characters on a single line.

One solution to prevent these files from slowing down Emacs is to use the built-in global-so-long-mode (or so-long-mode). When such files are visited, this mode detects abnormally long lines and automatically disables expensive features.

Below is a configuration to improve how global-so-long-mode handles these files, balancing performance and usability.

Managing syntax highlighting

By default, so-long disables font-lock-mode entirely to maximize performance. The configuration below retains syntax highlighting but limits the decoration level:

(with-eval-after-load 'so-long
  ;; Keep syntax highlighting
  (setq so-long-minor-modes (delq 'font-lock-mode so-long-minor-modes))

  ;; Limit font-lock to the minimum decoration level to save CPU cycles
  (add-to-list 'so-long-variable-overrides '(font-lock-maximum-decoration . 1)))

The benefit is that you maintain basic syntax highlighting, which makes code easier to read than plain text. The tradeoff is that even minimal font-lock parsing consumes CPU cycles, which can still cause minor performance degradation in massive files.

Changing the default action

By default, when so-long detects a file with abnormally long lines, its standard behavior is to replace the buffer's major mode with so-long-mode. This is undesirable if you want to edit files using their original major mode, which allows you to retain useful minor-mode behavior and tooling.

Setting so-long-action to so-long-minor-mode retains the original major mode while applying the configured so-long performance mitigations:

(setq so-long-action 'so-long-minor-mode)

Note: The trade-off is that so-long-minor-mode leaves some features active that so-long-mode would otherwise disable. If additional performance-heavy features cause latency, you must disable them explicitly by appending them to so-long-minor-modes (see below).

Extending target modes

By default, so-long primarily targets prog-mode, css-mode, sgml-mode, nxml-mode, and fundamental-mode, leaving other buffers unprotected.

Adding conf-mode and text-mode to so-long-target-modes extends this protection to structured configurations, documentation, and patch files:

;; Apply so-long to configuration, plain text files, and diff files
(with-eval-after-load 'so-long
  (add-to-list 'so-long-target-modes 'text-mode)
  (add-to-list 'so-long-target-modes 'conf-mode))

In practice, generated configuration files (such as dense JSON or exported INI data), single-line log dumps treated as text, and massive diffs regularly contain long lines that can freeze the Emacs.

Including these modes ensures that visiting non-code or patch buffers will not introduce UI freezes or responsiveness issues.

Managing variable overrides and display behavior

When so-long triggers, it overrides buffer-local variables to improve performance. However, some default settings degrade usability by marking buffers as read-only and allowing save-place-mode to reposition the point within long lines.

The snippet below adjusts so-long-variable-overrides to keep the buffer writable and disables save-place-alist, preventing unnecessary cursor calculations on long lines:

(with-eval-after-load 'so-long
  ;; Prevent so-long from attempting to restore the cursor position
  (add-to-list 'so-long-variable-overrides '(save-place-alist . nil))

  ;; Ensure the buffer remains writable when so-long triggers, overriding the
  ;; default behavior that locks the buffer as read-only.
  (setf (alist-get 'buffer-read-only so-long-variable-overrides nil t) nil))

Keeping basic UI elements

The default so-long-minor-modes includes display-line-numbers-mode. The following configuration removes it so it remains active when so-long triggers:

(with-eval-after-load 'so-long
  ;; Retain line numbers
  (setq so-long-minor-modes (delq 'display-line-numbers-mode so-long-minor-modes)))

Disabling resource-intensive minor modes

Many third-party packages can become expensive on buffers containing very long lines. This block adds commonly used minor modes to so-long-minor-modes. When so-long triggers, active modes in this list are disabled for the current buffer.

(with-eval-after-load 'so-long
  (dolist (mode '(;; Structural Editing and Parenthesis Matching
                  paredit-mode
                  enhanced-evil-paredit-mode
                  rainbow-delimiters-mode
                  smartparens-mode
                  smartparens-strict-mode

                  ;; Regex and Custom Highlighting
                  easy-escape-minor-mode
                  highlight-defined-mode
                  highlight-indent-guides-mode
                  auto-composition-mode

                  ;; Outline Scanning / Text folding
                  outline-minor-mode
                  treesit-fold-mode
                  ts-fold-mode
                  ts-fold-indicators-mode

                  ;; State & History Persistence / I/O
                  undo-fu-session-mode
                  undo-tree-mode
                  better-jumper-local-mode
                  auto-revert-mode

                  ;; Formatters & Whitespace Managers
                  aggressive-indent-mode
                  stripspace-local-mode
                  ws-butler-mode

                  ;; Linters & Language Servers
                  eglot--managed-mode
                  eldoc-mode
                  flycheck-mode
                  flymake-mode

                  ;; Spell Checkers
                  jinx-mode
                  spell-fu-mode

                  ;; UI Overlays & Margins
                  indent-bars-mode
                  highlight-numbers-mode
                  diff-hl-mode
                  git-gutter-mode
                  line-reminder-mode
                  page-break-lines-mode
                  hl-fill-column-mode))
    (add-to-list 'so-long-minor-modes mode)))

Adjusting the threshold

You can control when so-long activates by adjusting the following threshold:

;; so-long activates when a file contains a line exceeding the character count below
(setq so-long-threshold 6000)

Enable the mode globally

Finally, enable global-so-long-mode by adding the following to your Emacs configuration:

(global-so-long-mode 1)

Leave a Reply

Your email address will not be published. Required fields are marked *