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)")))
- Here we are making the indent-line-function as just insert-tab, By default, this will have programming language/file specific indentation function such as c-indent-line or python-indent-line.
- Then we are turning off tab-always-indent to nil. When it is true, it indents the current line when the tab is pressed; when it is nil, it does not. If the cursor is at the start of a line, it indents; otherwise, it inserts <tab> key.
- Turning off the electric indent local mode, a feature that automatically indents the line the moment you press RET (Enter) or type certain characters (like a { or }).
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.