Mercurial > hg > Members > kokubo > emacs
diff .emacs.d/haskell-mode/haskell-show.el @ 0:2764b4f45f9f
1st commit
author | Shohei KOKUBO <e105744@ie.u-ryukyu.ac.jp> |
---|---|
date | Mon, 21 Apr 2014 04:30:59 +0900 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.emacs.d/haskell-mode/haskell-show.el Mon Apr 21 04:30:59 2014 +0900 @@ -0,0 +1,263 @@ +;;; haskell-show.el --- A pretty printer for Haskell Show values + +;; Copyright (C) 2011 Chris Done + +;; Author: Chris Done <chrisdone@gmail.com> + +;; This file is not part of GNU Emacs. + +;; This program 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 of the +;; License, or (at your option) any later version. + +;; This program 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 +;; <http://www.gnu.org/licenses/>. + +;;; Commentary: + +;; It doesn't support some number literals (probably). I'm not +;; precisely sure what values Show will always produce. There is +;; however a test suite available, so patches for extra Show support +;; is welcome and should be easy to test. + +;;; Code: + +(defvar sexp-show "sexp-show") +(require 'haskell-string) +(with-no-warnings (require 'cl)) + +(defun haskell-show-replace-region () + "Replace the given region with a pretty printed version." + (interactive) + (haskell-show-replace (region-beginning) (region-end))) + +;;;###autoload +(defun haskell-show-replace (start end) + "Replace the given region containing a Show value with a pretty + printed collapsible version." + (let ((text (buffer-substring-no-properties start end))) + (goto-char start) + (delete-region start end) + (haskell-show-parse-and-insert text))) + +;;;###autoload +(defun haskell-show-parse-and-insert (given) + "Parse a `string' containing a Show instance value and insert + it pretty printed into the current buffer." + (when (not (string= "" (haskell-trim given))) + (let ((current-column (- (point) + (line-beginning-position))) + (result (haskell-show-parse given))) + (if (string-match "^[\\(]" result) + (let ((v (read result))) + (if (equal (car v) 'arbitrary) + (insert given) + (haskell-show-insert-pretty current-column v))) + (insert given))))) + +;;;###autoload +(defun haskell-show-parse (given) + "Parse the given input into a tree." + (with-temp-buffer + (insert given) + (shell-command-on-region + (point-min) + (point-max) + sexp-show + t) + (buffer-substring-no-properties (point-min) (point-max)))) + +(defun haskell-show-insert-pretty (column tree &optional parens) + "Insert a Show `tree' into the current buffer with collapsible nodes." + (case (car tree) + ('list (let ((start (point))) + (insert "[") + (haskell-show-mapcar/i (lambda (x i len) + (haskell-show-insert-pretty (+ column 1) x) + (unless (> i (- len 2)) + (if (< (+ column (length (haskell-show-pretty tree parens))) + 80) + (insert ",") + (insert (concat ",\n" (haskell-show-indent (+ 1 column) "")))))) + (cdr tree)) + (insert "]"))) + ('tuple (let ((start (point))) + (insert "(") + (haskell-show-mapcar/i (lambda (x i len) + (haskell-show-insert-pretty (+ column 1) x) + (unless (> i (- len 2)) + (if (< (+ column (length (haskell-show-pretty tree parens))) + 80) + (insert ",") + (insert (concat ",\n" (haskell-show-indent (+ 1 column) "")))))) + (cdr tree)) + (insert ")"))) + ('record + (let ((record (cdr tree)) (overlay (list 'nil))) + (insert (if parens "(" "")) + (let ((link-start (point))) + (insert (car record)) + (let ((button (make-text-button link-start (point) :type 'haskell-show-toggle-button))) + (put-text-property link-start (point) 'face 'font-lock-type-face) + (button-put button 'overlay overlay))) + (insert " {\n") + (let ((curly-start (1- (point))) + (show-len (+ column (length (haskell-show-pretty tree parens))))) + (haskell-show-mapcar/i (lambda (field i len) + (insert + (haskell-show-indent + (if (and (> i 0) (< show-len 80)) 0 column) + (car field))) + (insert " = ") + (put-text-property (- (point) 3) (point) 'face + 'font-lock-constant-face) + (haskell-show-insert-pretty + (if (< show-len 80) + 0 + (+ (length (car field)) column 3)) + (cdr field)) + (unless (> i (- len 2)) + (if (< show-len 80) + (insert ", ") + (insert ",\n")))) + (cdr record)) + (insert (concat "\n" (haskell-show-indent column "}"))) + (progn + (setf (car overlay) (make-overlay curly-start (- (point) 1) nil t)) + (overlay-put (car overlay) 'invisible t)) + (insert (if parens ")" ""))))) + ('num (let ((num-start (point))) + (insert (format "%d" (cdr tree))) + (put-text-property num-start (point) 'face 'font-lock-constant-face))) + ('string (let ((str-start (point))) + (insert "\"") + (if (< (+ column (length (cdr tree))) 60) + (progn + (insert (format "%s" (cdr tree))) + (put-text-property (+ 1 str-start) (point) 'face 'font-lock-string-face)) + (progn + (insert "…") + (insert (format "%s" (cdr tree))) + (let ((overlay (make-overlay (+ 2 str-start) (point) nil t))) + (overlay-put overlay 'invisible t) + (put-text-property (+ 2 str-start) (point) 'face 'font-lock-string-face) + (let ((button (make-text-button (+ 1 str-start) (+ 2 str-start) + :type 'haskell-show-toggle-button))) + (put-text-property (+ 1 str-start) (+ 2 str-start) + 'face 'font-lock-keyword-face) + (button-put button 'overlay (list overlay)) + (button-put button 'hide-on-click t))))) + (insert "\""))) + ('data (let ((data (cdr tree))) + (insert (if parens "(" "")) + (let ((cons-start (point))) + (insert (car data)) + (put-text-property cons-start (point) 'face 'font-lock-type-face)) + (unless (null (cdr data)) + (progn (insert " ") + (haskell-show-mapcar/i + (lambda (x i len) + (haskell-show-insert-pretty column x t) + (unless (> i (- len 2)) + (insert " "))) + (cdr data)))) + (insert (if parens ")" "")))) + ('char (progn (insert "'") + (insert (char-to-string (cdr tree))) + (put-text-property (- (point) 1) (point) 'face 'font-lock-string-face) + (insert "'"))) + ('arbitrary (let ((start (point))) + (insert (cdr tree)) + (put-text-property start (point) 'face 'font-lock-comment-face))) + (otherwise (error "Unsupported node type: %S" tree)))) + +(define-button-type 'haskell-show-toggle-button + 'action 'haskell-show-toggle-button-callback + 'follow-link t + 'help-echo "Click to expand…") + +(defun haskell-show-toggle-button-callback (btn) + "The callback to toggle the overlay visibility." + (let ((overlay (button-get btn 'overlay))) + (when overlay + (overlay-put (car overlay) + 'invisible (not (overlay-get (car overlay) + 'invisible))))) + (let ((hide (button-get btn 'remove-on-click))) + (when hide + (button-put btn 'invisible t)))) + +(defun haskell-show-pretty (tree &optional parens) + "Show a Show `tree'." + (case (car tree) + ('list (format "[%s]" + (mapconcat + (lambda (x) + (haskell-show-pretty x)) + (cdr tree) + ","))) + ('record (let ((record (cdr tree))) + (format "%s%s {%s}%s" + (if parens "(" "") + (car record) + (mapconcat (lambda (field) + (format "%s = %s" + (car field) + (haskell-show-pretty (cdr field)))) + (cdr record) + ", ") + (if parens ")" "")))) + ('num (format "%s" (cdr tree))) + ('string (format "%S" (cdr tree))) + ('data (let ((data (cdr tree))) + (format "%s%s%s%s" + (if parens "(" "") + (car data) + (if (null (cdr data)) + "" + (concat " " + (mapconcat + (lambda (x) (haskell-show-pretty x t)) + (cdr data) + " "))) + (if parens ")" "")))) + ('tuple (format "(%s)" + (mapconcat + (lambda (x) + (haskell-show-pretty x)) + (cdr tree) + ","))) + ('char (format "'%s'" (if (= (cdr tree) ?') + "\\'" + (char-to-string (cdr tree))))) + ('arbitrary (cdr tree)) + (otherwise (error "Unsupported node type: %S" tree)))) + +(defun haskell-show-mapcar/i (f xs) + "Map `f' across `xs' giving the index and length to `f' as extra parameters." + (let ((len (length xs)) + (i 0)) + (mapcar (lambda (x) + (funcall f x i len) + (setq i (1+ i))) + xs))) + +(defun haskell-show-indent (n s) + "Indent a string `s' at colum `n'." + (concat (make-string n ? ) + s)) + +(provide 'haskell-show) + +;; Local Variables: +;; byte-compile-warnings: (not cl-functions) +;; End: + +;;; haskell-show.el ends here