Gold Ayan's Tinker Garage

I often work on legacy codebases. Using Emacs at work is not a lot of fun due to the mix of indentation. As some lines in the project files have used TAB, some lines have 2, 4, or sometimes 8 spaces.

I thought, how about turning the indentation in a particular buffer off. That should solve the problem.

I asked Gemini to create an interactive function to do that. which toggle the new and old indentation when it is called.

  (defvar-local my-free-flow-state nil
  "Tracks whether free-flow mode is active in the current buffer.")

(defvar-local my-original-indent-func nil
  "Stores the original indentation function to restore it later.")

(defun toggle-free-flow ()
  "Toggle between standard Emacs indentation and 'Free Flow' (no auto-indent)."
  (interactive)
  (if (not my-free-flow-state)
      ;; ENABLE FREE FLOW
      (progn
        (setq my-original-indent-func indent-line-function)
        (setq-local indent-line-function 'insert-tab)
        (setq-local tab-always-indent nil)
        (electric-indent-local-mode -1)
        (setq my-free-flow-state t)
        (message "Free Flow: ON (Emacs will not touch your indentation)"))

    ;; DISABLE FREE FLOW (Restore Defaults)
    (progn
      (setq-local indent-line-function (or my-original-indent-func 'indent-relative))
      (setq-local tab-always-indent t)
      (electric-indent-local-mode 1)
      (setq my-free-flow-state nil)
      (message "Free Flow: OFF (Standard indentation restored)")))

After navigating with the legacy code with new indentation, I was not satisfied with the result. There has to be a better way.

Suddenly, an idea pops out: how about using the indentation of the previous line when a new line is inserted? This should do it…

  (defun my/indent-like-previous-line ()
  "Insert a newline and indent it exactly like the previous line."
  (interactive)
  (let ((indent (save-excursion
                  (forward-line 0)
                  (let ((start (point)))
                    (back-to-indentation)
                    (buffer-substring-no-properties start (point))))))
    (newline)
    (insert indent)))

(defvar-local my-free-flow-state nil)

(defun toggle-free-flow ()
  "Toggle between standard Emacs indentation and 'Previous Line' flow."
  (interactive)
  (if (not my-free-flow-state)
      (progn
        ;; Map RET to our new 'copy previous indent' function
        (local-set-key (kbd "RET") 'my/indent-like-previous-line)
        ;; Disable Emacs's automatic 'electric' indentation
        (electric-indent-local-mode -1)
        (setq my-free-flow-state t)
        (message "Free Flow: ON (Indenting based on previous line)"))
    (progn
      ;; Restore default Return behavior
      (local-unset-key (kbd "RET"))
      (electric-indent-local-mode 1)
      (setq my-free-flow-state nil)
      (message "Free Flow: OFF (Default Emacs indentation restored)")))

Here I removed tab-based behavior for now and applied new behavior to enter key followed by disabling electric-indent-local-mode. I worked with legacy source code, much, much better now.

I have called this function for each file in the project I am working on.Planning to add this function to .dir-locals.el after some time, I will update the article once I work on it.