From 0c357f1b90cd383724593d05b39844e369b67795 Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Sat, 5 Dec 2015 11:24:30 -0700 Subject: initial commit to independent repo --- haskell-tab-indent.el | 140 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 haskell-tab-indent.el diff --git a/haskell-tab-indent.el b/haskell-tab-indent.el new file mode 100644 index 0000000..19cb44d --- /dev/null +++ b/haskell-tab-indent.el @@ -0,0 +1,140 @@ +;;; haskell-tab-indent.el --- tab-based indentation for haskell-mode + +;; Copyright (C) 2015 Sean Whitton + +;; Author: Sean Whitton +;; URL: https://spwhitton.name/tech/code/haskell-tab-indent/ +;; Version: 0.1.0 +;; Keywords: indentation, haskell + +;; This file is NOT part of GNU Emacs. + +;;; License: + +;; This file is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 3, or (at your option) +;; any later version. + +;; This file is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; This file provides `haskell-tab-indent-mode', a simple indentation +;; mode for Haskell projects which require tabs for indentation and do +;; not permit spaces (except for where clauses, as a special case). +;; +;; The user may use TAB to cycle between possible indentations. +;; +;; Installation: +;; +;; If you set `indent-tabs-mode' in the .dir-locals.el file for a +;; project requiring tabs, you can use something like this in your +;; init file to enable this mode for such projects: +;; +;; (add-hook 'haskell-mode-hook +;; (lambda () +;; (add-hook 'hack-local-variables-hook +;; (lambda () +;; (if indent-tabs-mode +;; (haskell-tab-indent-mode) +;; (haskell-indentation-mode))) +;; nil t))) ; local hook + +;;; Code: + +(defun haskell-tab-indent () + "Auto indentation on TAB for `haskell-tab-indent-mode'." + (interactive) + (save-excursion + (back-to-indentation) + ;; check for special case of where clause + (if (looking-at "where") + (haskell-tab-indent--where) + ;; check for special case of being called by + ;; `newline-and-indent': if the user has `electric-indent-mode' + ;; on and RET bound to `newline-and-indent', we'll end up + ;; indenting too far, or not enough if the previous line was a + ;; top level declaration + (unless (let ((previous-line-tabs (haskell-tab-indent--previous-line-tabs)) + (this-line-tabs (haskell-tab-indent--this-line-tabs))) + (or + ;; avoid indenting too far + (and (equal this-command 'newline-and-indent) + (= this-line-tabs previous-line-tabs) + (not (haskell-tab-indent--previous-line-topdecl-p))) + ;; avoid indenting too little + (and (haskell-tab-indent--previous-line-topdecl-p) + (= 1 this-line-tabs)))) + (haskell-tab-indent--cycle)))) + ;; On a line with only indentation, ensure point is at the end of + ;; it. + (when (save-excursion (beginning-of-line) (looking-at "[[:space:]]*$")) + (end-of-line))) + +(defun haskell-tab-indent--previous-line-topdecl-p () + "Determine whether previous line is a top-level declaration." + (save-excursion + (beginning-of-line 0) ; go up one line + (equal 'haskell-definition-face (car (cdr (text-properties-at (point))))))) + +(defun haskell-tab-indent--where () + ;; `haskell-tab-indent' leaves us just after the indentation + (delete-region (line-beginning-position) (point)) + (insert " ")) + +(defun haskell-tab-indent--cycle () + (let ((previous-line-tabs (haskell-tab-indent--previous-line-tabs)) + (this-line-tabs (haskell-tab-indent--this-line-tabs))) + (if (= (1+ previous-line-tabs) this-line-tabs) + (haskell-tab-indent--reset) + (haskell-tab-indent--indent)))) + +(defun haskell-tab-indent--reset () + (save-excursion + (back-to-indentation) + (delete-region (line-beginning-position) (point)))) + +(defun haskell-tab-indent--indent () + (save-excursion + (back-to-indentation) + (insert "\t"))) + +(defun haskell-tab-indent--previous-line-tabs () + (save-excursion + (beginning-of-line 0) ; go up one line + ;; keep going up past blank spacer lines + (while (looking-at "[[:space:]]*$") (beginning-of-line 0)) + (haskell-tab-indent--this-line-tabs))) + +(defun haskell-tab-indent--this-line-tabs () + (save-excursion + (save-restriction + (back-to-indentation) + (narrow-to-region (line-beginning-position) (point)) + (beginning-of-line) + (let ((count 0)) + (while (re-search-forward "\t" nil t) + (setq count (1+ count))) + count)))) + +;;;###autoload +(define-minor-mode haskell-tab-indent-mode + "Haskell indentation mode for projects requiring that only tabs +-- with no spaces -- be used for indentation. + +Binds the TAB key to cycle between possible indents." + :lighter " TabInd" + (kill-local-variable 'indent-line-function) + (when haskell-tab-indent-mode + (set (make-local-variable 'indent-line-function) 'haskell-tab-indent) + (set (make-local-variable 'indent-tabs-mode) t))) + +(provide 'haskell-tab-indent) +;;; haskell-tab-indent.el ends here -- cgit v1.2.3