ghc-flymake.el 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  2. ;;;
  3. ;;; ghc-flymake.el
  4. ;;;
  5. ;; Author: Kazu Yamamoto <Kazu@Mew.org>
  6. ;; Created: Mar 12, 2010
  7. ;;; Code:
  8. (require 'flymake)
  9. (require 'ghc-func)
  10. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  11. (defvar ghc-hlint-options nil "*Hlint options")
  12. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  13. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  14. (defconst ghc-flymake-allowed-file-name-masks
  15. '("\\.l?hs$" ghc-flymake-init))
  16. (defconst ghc-flymake-err-line-patterns
  17. '("^\\(.*\\):\\([0-9]+\\):\\([0-9]+\\):[ ]*\\(.+\\)" 1 2 3 4))
  18. (add-to-list 'flymake-allowed-file-name-masks
  19. ghc-flymake-allowed-file-name-masks)
  20. (add-to-list 'flymake-err-line-patterns
  21. ghc-flymake-err-line-patterns)
  22. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  23. (defun ghc-flymake-init ()
  24. (list ghc-module-command (ghc-flymake-command (flymake-init-create-temp-buffer-copy 'flymake-create-temp-inplace))))
  25. (defvar ghc-flymake-command nil) ;; nil: check, t: lint
  26. (defun ghc-flymake-command (file)
  27. (if ghc-flymake-command
  28. (let ((hopts (ghc-mapconcat (lambda (x) (list "-h" x)) ghc-hlint-options)))
  29. `(,@hopts "lint" ,file))
  30. `(,@(ghc-make-ghc-options) "check" ,file)))
  31. (defun ghc-flymake-toggle-command ()
  32. (interactive)
  33. (setq ghc-flymake-command (not ghc-flymake-command))
  34. (if ghc-flymake-command
  35. (message "Syntax check with hlint")
  36. (message "Syntax check with GHC")))
  37. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  38. (defun ghc-flymake-display-errors ()
  39. (interactive)
  40. (if (not (ghc-flymake-have-errs-p))
  41. (message "No errors or warnings")
  42. (let ((title (ghc-flymake-err-title))
  43. (errs (ghc-flymake-err-list)))
  44. (ghc-display
  45. nil
  46. (lambda (&rest ignore)
  47. (insert title "\n\n")
  48. (mapc (lambda (x) (insert x "\n")) errs))))))
  49. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  50. (defun ghc-flymake-jump ()
  51. (interactive)
  52. (if (not (ghc-flymake-have-errs-p))
  53. (message "No errors or warnings")
  54. (let* ((acts (ghc-flymake-act-list))
  55. (act (car acts)))
  56. (if (not act)
  57. (message "No destination")
  58. (eval act)))))
  59. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  60. (defun ghc-extract-type (str)
  61. (with-temp-buffer
  62. (insert str)
  63. (goto-char (point-min))
  64. (when (re-search-forward "Inferred type: \\|no type signature:\\( \\|\0 +\\)?" nil t)
  65. (delete-region (point-min) (point)))
  66. (when (re-search-forward " forall [^.]+\\." nil t)
  67. (replace-match ""))
  68. (while (re-search-forward "\0 +" nil t)
  69. (replace-match " "))
  70. (goto-char (point-min))
  71. (while (re-search-forward "\\[Char\\]" nil t)
  72. (replace-match "String"))
  73. (re-search-forward "\0" nil t)
  74. (buffer-substring-no-properties (point-min) (1- (point)))))
  75. (defun ghc-flymake-insert-from-warning ()
  76. (interactive)
  77. (dolist (data (ghc-flymake-err-list))
  78. (save-excursion
  79. (cond
  80. ((string-match "Inferred type: \\|no type signature:" data)
  81. (beginning-of-line)
  82. (insert (ghc-extract-type data) "\n"))
  83. ((string-match "lacks an accompanying binding" data)
  84. (beginning-of-line)
  85. (when (looking-at "^\\([^ ]+\\) *::")
  86. (save-match-data
  87. (forward-line)
  88. (if (not (bolp)) (insert "\n")))
  89. (insert (match-string 1) " = undefined\n")))
  90. ((string-match "Not in scope: `\\([^']+\\)'" data)
  91. (save-match-data
  92. (unless (re-search-forward "^$" nil t)
  93. (goto-char (point-max))
  94. (insert "\n")))
  95. (insert "\n" (match-string 1 data) " = undefined\n"))
  96. ((string-match "Pattern match(es) are non-exhaustive" data)
  97. (let* ((fn (ghc-get-function-name))
  98. (arity (ghc-get-function-arity fn)))
  99. (ghc-insert-underscore fn arity)))
  100. ((string-match "Found:\0[ ]*\\([^\0]+\\)\0Why not:\0[ ]*\\([^\0]+\\)" data)
  101. (let ((old (match-string 1 data))
  102. (new (match-string 2 data)))
  103. (beginning-of-line)
  104. (when (search-forward old nil t)
  105. (let ((end (point)))
  106. (search-backward old nil t)
  107. (delete-region (point) end))
  108. (insert new))))))))
  109. (defun ghc-get-function-name ()
  110. (save-excursion
  111. (beginning-of-line)
  112. (when (looking-at "\\([^ ]+\\) ")
  113. (match-string 1))))
  114. (defun ghc-get-function-arity (fn)
  115. (when fn
  116. (save-excursion
  117. (let ((regex (format "^%s *::" (regexp-quote fn))))
  118. (when (re-search-backward regex nil t)
  119. (ghc-get-function-arity0))))))
  120. (defun ghc-get-function-arity0 ()
  121. (let ((end (save-excursion (end-of-line) (point)))
  122. (arity 0))
  123. (while (search-forward "->" end t)
  124. (setq arity (1+ arity)))
  125. arity))
  126. (defun ghc-insert-underscore (fn ar)
  127. (when fn
  128. (let ((arity (or ar 1)))
  129. (save-excursion
  130. (goto-char (point-max))
  131. (re-search-backward (format "^%s *::" (regexp-quote fn)))
  132. (forward-line)
  133. (re-search-forward "^$" nil t)
  134. (insert fn)
  135. (dotimes (i arity)
  136. (insert " _"))
  137. (insert " = error \"" fn "\"")))))
  138. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  139. (defun ghc-flymake-err-get-title (x) (nth 0 x))
  140. (defun ghc-flymake-err-get-errs (x) (nth 1 x))
  141. (defun ghc-flymake-err-get-err-msg (x) (nth 0 x))
  142. (defun ghc-flymake-err-get-err-act (x) (nth 1 x))
  143. (defalias 'ghc-flymake-have-errs-p 'ghc-flymake-data)
  144. (defun ghc-flymake-data ()
  145. (let* ((line-no (flymake-current-line-no))
  146. (info (nth 0 (flymake-find-err-info flymake-err-info line-no))))
  147. (flymake-make-err-menu-data line-no info)))
  148. (defun ghc-flymake-err-title ()
  149. (ghc-flymake-err-get-title (ghc-flymake-data)))
  150. (defun ghc-flymake-err-list ()
  151. (mapcar 'ghc-flymake-err-get-err-msg (ghc-flymake-err-get-errs (ghc-flymake-data))))
  152. (defun ghc-flymake-act-list ()
  153. (mapcar 'ghc-flymake-err-get-err-act (ghc-flymake-err-get-errs (ghc-flymake-data))))
  154. (provide 'ghc-flymake)