blob: fe14ae51fc6179780c4dfcde608cd86444c6d479 [file] [log] [blame]
external-reality8f34d232014-02-22 21:35:47 -05001;;; package --- Summary
2
3;;; Commentary:
4
5;;; Code:
6(require 'shm-node)
7(require 'shm-refactor)
8(require 'shm-lambda)
9(require 'popup)
10
11(defconst qualify-import "qualify import")
12(defconst raise-child "raise child")
13(defconst visit-module-definition "visit module definition")
14(defconst hlint-suggestion "hlint suggestion")
15(defconst create-top-level-function-from-lambda "create top level function from lambda")
16(defconst add-type-constraint "add type constraint")
17
18(if (eq window-system 'x)
19 (define-key shm-map (kbd "M-<return>") 'shm/present-actions-for-node)
20 (define-key shm-map (kbd "M-]") 'shm/present-actions-for-node))
21
22(defun shm/collapse-nested-lambda ()
23 (let* ((current-node (shm-current-node))
24 (refactors (shm-get-refactors current-node))
25 (refactor (shm-find-refactor-by-name refactors "collapse nested lambdas")))
26 (when refactor (shm-invoke-hlint-suggestion refactor))))
27
28(defun shm/present-actions-for-node ()
29 "Display menu of possible actions for node."
30 (interactive)
31 (let* ((pair (shm-current-node-pair))
32 (current (cdr pair))
33 (cons (shm-node-cons current))
34 (refactors (shm-get-refactors current))
35 (menu nil))
36 (when (shm-node-lambda-p current)
37 (add-to-list 'menu (shm-item-for-lambda)))
38 (when (shm-refactors-available-p refactors)
39 (setq menu (append menu (shm-items-for-refactors refactors))))
40 (when (shm-top-level-type-decl-p pair)
41 (add-to-list 'menu (shm-item-for-top-level-type-decl)))
42 (when (shm-import-decl-p cons)
43 (add-to-list 'menu (shm-item-for-import-decl)))
44 (when (shm-has-parent-with-matching-type-p pair)
45 (add-to-list 'menu (shm-item-for-child-nodes-with-matching-parent)))
46 (when (and (shm-module-name-p cons)
47 (fboundp (quote haskell-mode-tag-find)))
48 (add-to-list 'menu (shm-item-for-module-name)))
49 (if menu
50 (progn
51 (cancel-timer shm-parsing-timer)
52 (unwind-protect
53 (shm-invoke-action-for-menu-item (popup-menu* menu))
54 (setq shm-parsing-timer
55 (run-with-idle-timer shm-idle-timeout t 'shm-reparsing-timer)))))))
56
57;; collapse any nested lambdas
58;; compare arg list to variables in defintion
59;; turn free variables into application
60;; insert application surrounded by parens
61;; check parent node for redundant parens
62
63
64(defun shm/move-lambda-to-top-level ()
65 (let* ((function-name (read-from-minibuffer "function name: "))
66 (current-node-pair (shm-current-node-pair))
67 (current-node (cdr current-node-pair))
68 (free-variables (shm-lambda-free-vars current-node))
69 (lambda-args (shm-lambda-args current-node))
70 (lambda-body (shm-query-lambda-body current-node))
71 (current-top-level-node (cdr (shm-get-parent-top-level-decl current-node-pair)))
72 (replacement (concat function-name " " free-variables)))
73 (shm-replace-node-syntax current-node replacement)
74 (goto-char (shm-node-end current-top-level-node))
75 (insert ?\n?\n)
76 (insert function-name " " free-variables
77 " " lambda-args
78 " = " lambda-body )
79 (insert ?\n)))
80
81(defun shm-replace-node-syntax (node replacement-syntax)
82 (let ((start (shm-node-start node))
83 (end (shm-node-end node)))
84 (save-excursion
85 (delete-region start end)
86 (goto-char start)
87 (insert replacement-syntax))))
88
89(defun shm-get-lambda-args (node)
90 (car (shm-split-lambda node)))
91
92(defun shm-get-lambda-body (node)
93 (cdr (shm-split-lambda node)))
94
95(defun shm-split-lambda (node)
96 (let ((syntax (shm-concrete-syntax-for-node node)))
97 (string-match "\\\\\\(.*?\\)->\\(.*\\)" syntax)
98 (let ((lambda-args (match-string 1 syntax))
99 (lambda-body (match-string 2 syntax)))
100 (cons (chomp lambda-args) (chomp lambda-body)))))
101
102(defun shm-items-for-refactors (refactors)
103 "Create a popup menu items from (REFACTORS)."
104 (mapcar 'shm-item-for-refactor refactors))
105
106(defun shm-invoke-hlint-suggestion (refactor)
107 "Replace the current node with the suggestion from the (REFACTOR)."
108 (let* ((current-node (shm-current-node))
109 (start (shm-refactor-start current-node refactor))
110 (end (shm-refactor-end current-node refactor)))
111 (save-excursion
112 (delete-region start end)
113 (goto-char start)
114 (insert (elt refactor 3)))))
115
116(defun shm-start-refactor-line (refactor)
117 "Get the starting line of (REFACTOR) relative to the context in which it was found."
118 (elt refactor 4))
119
120(defun shm-item-for-refactor (refactor)
121 "Create the menu item for a particular (REFACTOR)."
122 (popup-make-item (concat "⚒ " (refactor-name refactor)) :value (cons hlint-suggestion refactor)))
123
124(defun shm-item-for-import-decl ()
125 (popup-make-item "✎ qualify import" :value qualify-import))
126
127(defun shm-item-for-child-nodes-with-matching-parent ()
128 (popup-make-item "⚒ raise" :value raise-child))
129
130(defun shm-item-for-module-name ()
131 (popup-make-item "✈ visit module" :value visit-module-definition))
132
133(defun shm-item-for-top-level-type-decl ()
134 (popup-make-item "✎ add type constraint" :value add-type-constraint))
135
136(defun shm-item-for-lambda ()
137 (popup-make-item (concat "⚒ " "create top-level function from lambda") :value create-top-level-function-from-lambda))
138
139(defun shm-invoke-action-for-menu-item (item-value)
140 "Invoke function on (ITEM-VALUE) chosen from the context menu."
141 (cond ((selected-item-value-p item-value qualify-import) (invoke-with-suggestion 'shm/qualify-import))
142 ((selected-item-value-p item-value raise-child) (invoke-with-suggestion 'shm/raise))
143 ((selected-item-value-p item-value visit-module-definition) (invoke-with-suggestion 'haskell-mode-tag-find))
144 ((selected-item-value-p item-value hlint-suggestion) (invoke-with-suggestion 'shm-invoke-hlint-suggestion (cdr item-value)))
145 ((selected-item-value-p item-value create-top-level-function-from-lambda) (invoke-with-suggestion 'shm/move-lambda-to-top-level))
146 ((selected-item-value-p item-value add-type-constraint) (invoke-with-suggestion 'shm/modify-type-constraint))))
147
148(defun selected-item-value-p (value match)
149 ;;Basically check to see if the value selected by the menu matches a given string
150 "Extract String from (VALUE) and check for string equality against (MATCH)."
151 (or (and (stringp value) (string= value match))
152 (and (listp value) (string= (car value) match))))
153
154(defun invoke-with-suggestion (function &optional arg)
155 "Invoke (FUNCTION) with on (ARG) and show its key binding in mini buffer if it has one."
156 (if arg (funcall function arg) (funcall function))
157 (let ((binding (where-is-internal function shm-map t)))
158 (when binding
159 (with-temp-message
160 (format "You can run the command `%s' with %s"
161 function (key-description binding))
162 (sit-for (if (numberp suggest-key-bindings)
163 suggest-key-bindings
164 2))))))
165
166(defun chomp (str)
167 "Chomp leading and tailing whitespace from STR."
168 (while (string-match "\\`\n+\\|^\\s-+\\|\\s-+$\\|\n+\\'"
169 str)
170 (setq str (replace-match "" t t str)))
171 str)
172
173(provide 'shm-context-menu)
174
175;;; shm-context-menu.el ends here