Klimi's new dotfiles with stow.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

231 lines
8.4 KiB

преди 4 години
  1. ;;; haskell-align-imports.el --- Align the import lines in a Haskell file -*- lexical-binding: t -*-
  2. ;; Copyright (C) 2010 Chris Done
  3. ;; Author: Chris Done <chrisdone@gmail.com>
  4. ;; This file is not part of GNU Emacs.
  5. ;; This program is free software: you can redistribute it and/or
  6. ;; modify it under the terms of the GNU General Public License as
  7. ;; published by the Free Software Foundation, either version 3 of
  8. ;; the License, or (at your option) any later version.
  9. ;; This program is distributed in the hope that it will be
  10. ;; useful, but WITHOUT ANY WARRANTY; without even the implied
  11. ;; warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  12. ;; PURPOSE. See the GNU General Public License for more details.
  13. ;; You should have received a copy of the GNU General Public
  14. ;; License along with this program. If not, see
  15. ;; <http://www.gnu.org/licenses/>.
  16. ;;; Commentary:
  17. ;; Consider the following imports list:
  18. ;;
  19. ;; import One
  20. ;; import Two as A
  21. ;; import qualified Three
  22. ;; import qualified Four as PRELUDE
  23. ;; import Five (A)
  24. ;; import Six (A,B)
  25. ;; import qualified Seven (A,B)
  26. ;; import "abc" Eight
  27. ;; import "abc" Nine as TWO
  28. ;; import qualified "abc" Ten
  29. ;; import qualified "defg" Eleven as PRELUDE
  30. ;; import "barmu" Twelve (A)
  31. ;; import "zotconpop" Thirteen (A,B)
  32. ;; import qualified "z" Fourteen (A,B)
  33. ;; import Fifteen hiding (A)
  34. ;; import Sixteen as TWO hiding (A)
  35. ;; import qualified Seventeen hiding (A)
  36. ;; import qualified Eighteen as PRELUDE hiding (A)
  37. ;; import "abc" Nineteen hiding (A)
  38. ;; import "abc" Twenty as TWO hiding (A)
  39. ;;
  40. ;; When haskell-align-imports is run within the same buffer, the
  41. ;; import list is transformed to:
  42. ;;
  43. ;; import "abc" Eight
  44. ;; import qualified Eighteen as PRELUDE hiding (A)
  45. ;; import qualified "defg" Eleven as PRELUDE
  46. ;; import Fifteen hiding (A)
  47. ;; import Five (A)
  48. ;; import qualified Four as PRELUDE
  49. ;; import qualified "z" Fourteen (A,B)
  50. ;; import "abc" Nine as TWO
  51. ;; import "abc" Nineteen hiding (A)
  52. ;; import One
  53. ;; import qualified Seven (A,B)
  54. ;; import qualified Seventeen hiding (A)
  55. ;; import Six (A,B)
  56. ;; import Sixteen as TWO hiding (A)
  57. ;; import qualified "abc" Ten
  58. ;; import "zotconpop" Thirteen (A,B)
  59. ;; import qualified Three
  60. ;; import "barmu" Twelve (A)
  61. ;; import "abc" Twenty as TWO hiding (A)
  62. ;; import Two as A
  63. ;;
  64. ;; If you want everything after module names to be padded out, too,
  65. ;; customize `haskell-align-imports-pad-after-name', and you'll get:
  66. ;;
  67. ;; import One
  68. ;; import Two as A
  69. ;; import qualified Three
  70. ;; import qualified Four as PRELUDE
  71. ;; import Five (A)
  72. ;; import Six (A,B)
  73. ;; import qualified Seven (A,B)
  74. ;; import "abc" Eight
  75. ;; import "abc" Nine as TWO
  76. ;; import qualified "abc" Ten
  77. ;; import qualified "defg" Eleven as PRELUDE
  78. ;; import "barmu" Twelve (A)
  79. ;; import "zotconpop" Thirteen (A,B)
  80. ;; import qualified "z" Fourteen (A,B)
  81. ;; import Fifteen hiding (A)
  82. ;; import Sixteen as TWO hiding (A)
  83. ;; import qualified Seventeen hiding (A)
  84. ;; import qualified Eighteen as PRELUDE hiding (A)
  85. ;; import "abc" Nineteen hiding (A)
  86. ;; import "abc" Twenty as TWO hiding (A)
  87. ;;; Code:
  88. (require 'cl-lib)
  89. (defvar haskell-align-imports-regexp
  90. (concat "^\\(import[ ]+\\)"
  91. "\\(qualified \\)?"
  92. "[ ]*\\(\"[^\"]*\" \\)?"
  93. "[ ]*\\([A-Za-z0-9_.']+\\)"
  94. "[ ]*\\([ ]*as [A-Z][^ ]*\\)?"
  95. "[ ]*\\((.*)\\)?"
  96. "\\([ ]*hiding (.*)\\)?"
  97. "\\( -- .*\\)?[ ]*$")
  98. "Regex used for matching components of an import.")
  99. (defcustom haskell-align-imports-pad-after-name
  100. nil
  101. "Pad layout after the module name also."
  102. :type 'boolean
  103. :group 'haskell-interactive)
  104. ;;;###autoload
  105. (defun haskell-align-imports ()
  106. "Align all the imports in the buffer."
  107. (interactive)
  108. (when (haskell-align-imports-line-match)
  109. (save-excursion
  110. (goto-char (point-min))
  111. (let* ((imports (haskell-align-imports-collect))
  112. (padding (haskell-align-imports-padding imports)))
  113. (mapc (lambda (x)
  114. (goto-char (cdr x))
  115. (delete-region (point) (line-end-position))
  116. (insert (haskell-align-imports-chomp
  117. (haskell-align-imports-fill padding (car x)))))
  118. imports))))
  119. nil)
  120. (defun haskell-align-imports-line-match ()
  121. "Try to match the current line as a regexp."
  122. (let ((line (buffer-substring-no-properties (line-beginning-position)
  123. (line-end-position))))
  124. (if (string-match "^import " line)
  125. line
  126. nil)))
  127. (defun haskell-align-imports-collect ()
  128. "Collect a list of mark / import statement pairs."
  129. (let ((imports '()))
  130. (while (not (or (equal (point) (point-max)) (haskell-align-imports-after-imports-p)))
  131. (let ((line (haskell-align-imports-line-match-it)))
  132. (when line
  133. (let ((match
  134. (haskell-align-imports-merge-parts
  135. (cl-loop for i from 1 to 8
  136. collect (haskell-align-imports-chomp (match-string i line))))))
  137. (setq imports (cons (cons match (line-beginning-position))
  138. imports)))))
  139. (forward-line))
  140. imports))
  141. (defun haskell-align-imports-merge-parts (l)
  142. "Merge together parts of an import statement that shouldn't be separated."
  143. (let ((parts (apply #'vector l))
  144. (join (lambda (ls)
  145. (cl-reduce (lambda (a b)
  146. (concat a
  147. (if (and (> (length a) 0)
  148. (> (length b) 0))
  149. " "
  150. "")
  151. b))
  152. ls))))
  153. (if haskell-align-imports-pad-after-name
  154. (list (funcall join (list (aref parts 0)
  155. (aref parts 1)
  156. (aref parts 2)))
  157. (aref parts 3)
  158. (funcall join (list (aref parts 4)
  159. (aref parts 5)
  160. (aref parts 6)))
  161. (aref parts 7))
  162. (list (funcall join (list (aref parts 0)
  163. (aref parts 1)
  164. (aref parts 2)))
  165. (funcall join (list (aref parts 3)
  166. (aref parts 4)
  167. (aref parts 5)
  168. (aref parts 6)
  169. (aref parts 7)))))))
  170. (defun haskell-align-imports-chomp (str)
  171. "Chomp leading and tailing whitespace from STR."
  172. (if str
  173. (replace-regexp-in-string "\\(^[[:space:]\n]*\\|[[:space:]\n]*$\\)" ""
  174. str)
  175. ""))
  176. (defun haskell-align-imports-padding (imports)
  177. "Find the padding for each part of the import statements."
  178. (if (null imports)
  179. imports
  180. (cl-reduce (lambda (a b) (cl-mapcar #'max a b))
  181. (mapcar (lambda (x) (mapcar #'length (car x)))
  182. imports))))
  183. (defun haskell-align-imports-fill (padding line)
  184. "Fill an import line using the padding worked out from all statements."
  185. (mapconcat #'identity
  186. (cl-mapcar (lambda (pad part)
  187. (if (> (length part) 0)
  188. (concat part (make-string (- pad (length part)) ? ))
  189. (make-string pad ? )))
  190. padding
  191. line)
  192. " "))
  193. (defun haskell-align-imports-line-match-it ()
  194. "Try to match the current line as a regexp."
  195. (let ((line (buffer-substring-no-properties (line-beginning-position)
  196. (line-end-position))))
  197. (if (string-match haskell-align-imports-regexp line)
  198. line
  199. nil)))
  200. (defun haskell-align-imports-after-imports-p ()
  201. "Are we after the imports list?"
  202. (save-excursion
  203. (goto-char (line-beginning-position))
  204. (let ((case-fold-search nil))
  205. (not (not (search-forward-regexp "\\( = \\|\\<instance\\>\\| :: \\| ∷ \\)"
  206. (line-end-position) t 1))))))
  207. (provide 'haskell-align-imports)
  208. ;;; haskell-align-imports.el ends here