Personal emacs config
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.

215 lines
7.2 KiB

  1. ;;; js2r-helpers.el --- Private helper functions for js2-refactor -*- lexical-binding: t; -*-
  2. ;; Copyright (C) 2012-2014 Magnar Sveen
  3. ;; Copyright (C) 2015-2016 Magnar Sveen and Nicolas Petton
  4. ;; Author: Magnar Sveen <magnars@gmail.com>,
  5. ;; Nicolas Petton <nicolas@petton.fr>
  6. ;; Keywords: conveniences
  7. ;; This program is free software; you can redistribute it and/or modify
  8. ;; it under the terms of the GNU General Public License as published by
  9. ;; the Free Software Foundation, either version 3 of the License, or
  10. ;; (at your option) any later version.
  11. ;; This program is distributed in the hope that it will be useful,
  12. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. ;; GNU General Public License for more details.
  15. ;; You should have received a copy of the GNU General Public License
  16. ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. ;;; Code:
  18. (require 'dash)
  19. (require 's)
  20. (defun js2r--wrap-text (&rest text)
  21. "Wrap TEXT with the prefered quotes. The prefered quotes is set with `js2r-prefered-quote-type'."
  22. (let ((prefered-quotes "\""))
  23. (when (= 2 js2r-prefered-quote-type)
  24. (setq prefered-quotes "'"))
  25. (concat prefered-quotes (apply 'concat text) prefered-quotes)))
  26. (defun js2r--fix-special-modifier-combinations (key)
  27. (case key
  28. ("C-s-i" "s-TAB")
  29. ("C-s-m" "s-RET")
  30. (otherwise key)))
  31. (defun js2r--key-pairs-with-modifier (modifier keys)
  32. (->> (string-to-list keys)
  33. (--map (js2r--fix-special-modifier-combinations
  34. (concat modifier (char-to-string it))))
  35. (s-join " ")
  36. (read-kbd-macro)))
  37. (defun js2r--key-pairs-with-prefix (prefix keys)
  38. (read-kbd-macro (concat prefix " " keys)))
  39. (defun js2r--guard ()
  40. (when js2-parsed-errors
  41. (error "Can't refactor while buffer has parse errors")))
  42. (defun js2r--current-quotes-char ()
  43. "The char that is the current quote delimiter"
  44. (nth 3 (syntax-ppss)))
  45. (defalias 'js2r--point-inside-string-p 'js2r--current-quotes-char)
  46. (defun js2r--closest-node-where (p node)
  47. (if (or (null node)
  48. (apply p node nil))
  49. node
  50. (js2r--closest-node-where p (js2-node-parent node))))
  51. (defun js2r--closest (p)
  52. (save-excursion
  53. (cond
  54. ((bolp) (back-to-indentation))
  55. ((looking-at ";") (forward-char -1))
  56. ((looking-back ";") (forward-char -2))
  57. ((looking-back "}") (forward-char -1)))
  58. (js2r--closest-node-where p (js2-node-at-point))))
  59. (defun js2r--goto-and-delete-node (node)
  60. (goto-char (js2-node-abs-pos node))
  61. (delete-char (js2-node-len node)))
  62. (defun js2r--path-to-root (node)
  63. (when node
  64. (cons node (js2r--path-to-root (js2-node-parent node)))))
  65. (defun js2r--first-common-ancestor (node1 node2)
  66. (if (eq node1 node2)
  67. node1
  68. (let ((path1 (reverse (js2r--path-to-root node1)))
  69. (path2 (reverse (js2r--path-to-root node2)))
  70. (last-common nil))
  71. (while (eq (car path1) (car path2))
  72. (setq last-common (car path1))
  73. (setq path1 (cdr path1))
  74. (setq path2 (cdr path2)))
  75. last-common)))
  76. (defun js2r--first-common-ancestor-in-region (beg end)
  77. (js2r--first-common-ancestor (js2-node-at-point beg)
  78. (js2-node-at-point end)))
  79. ;; abstract away node type on some common property getters
  80. (defun js2r--node-target (node)
  81. (cond
  82. ((js2-call-node-p node) (js2-call-node-target node))
  83. ((js2-new-node-p node) (js2-new-node-target node))
  84. (:else nil)))
  85. (defun js2r--node-args (node)
  86. (cond
  87. ((js2-call-node-p node) (js2-call-node-args node))
  88. ((js2-new-node-p node) (js2-new-node-args node))
  89. (:else nil)))
  90. (defun js2r--node-lp (node)
  91. (cond
  92. ((js2-call-node-p node) (js2-call-node-lp node))
  93. ((js2-new-node-p node) (js2-new-node-lp node))
  94. (:else nil)))
  95. (defun js2r--node-rp (node)
  96. (cond
  97. ((js2-call-node-p node) (js2-call-node-rp node))
  98. ((js2-new-node-p node) (js2-new-node-rp node))
  99. (:else nil)))
  100. (defun js2r--node-kids (node)
  101. (cond
  102. ((js2-function-node-p node) (js2-block-node-kids (js2-function-node-body node)))
  103. ((js2-if-node-p node) (js2-scope-kids (js2-if-node-then-part node)))
  104. ((js2-for-node-p node) (js2-block-node-kids (js2-for-node-body node)))
  105. ((js2-while-node-p node) (js2-block-node-kids (js2-while-node-body node)))))
  106. ;; finding expressions and arguments
  107. (defun js2r--closest-extractable-node ()
  108. "Return the most appropriate node the be extracted into a variable or paramter.
  109. Lookup the closest expression node from the point, or the closest literal node instead.
  110. If no node is found, signal an error."
  111. (or (or (js2r--closest #'js2r--expression-p)
  112. (js2r--closest #'js2r--literal-node-p))
  113. (error "Cannot perform refactoring: Nothing to extract at point")))
  114. (defun js2r--closest-stmt-node ()
  115. "Return the closest standalone statement node.
  116. Special care is taken for if branch nodes: if a statement node is
  117. part of an if branch node (like 'else if' nodes), return the
  118. parent node."
  119. (let* ((node (js2-node-parent-stmt (js2-node-at-point)))
  120. (parent (js2-node-parent node)))
  121. (if (and (js2-if-node-p node)
  122. (js2-if-node-p parent))
  123. parent
  124. node)))
  125. (defun js2r--argument-p (node)
  126. (let ((parent (js2-node-parent node)))
  127. (and (js2-call-node-p parent)
  128. (member node (js2-call-node-args parent)))))
  129. (defun js2r--expression-p (node)
  130. (or (js2-call-node-p node)
  131. (js2r--argument-p node)
  132. (and (js2-prop-get-node-p node)
  133. (not (js2-call-node-p (js2-node-parent node))))))
  134. (defun js2r--literal-node-p (node)
  135. (or (js2-object-node-p node)
  136. (js2-string-node-p node)
  137. (js2-number-node-p node)
  138. (js2r--boolean-node-p node)))
  139. (defun js2r--boolean-node-p (node)
  140. (let* ((beg (js2-node-abs-pos node))
  141. (end (js2-node-abs-end node))
  142. (content (buffer-substring beg end)))
  143. (and (js2-keyword-node-p node)
  144. (member content '("true" "false")))))
  145. (defun js2r--single-complete-expression-between-p (beg end)
  146. (let ((ancestor (js2r--first-common-ancestor-in-region beg (- end 1))))
  147. (and (= beg (js2-node-abs-pos ancestor))
  148. (= end (js2-node-abs-end ancestor)))))
  149. ;; executing a list of changes
  150. ;; ensures changes are executed from last to first
  151. (defun js2r--by-end-descending (change1 change2)
  152. (> (plist-get change1 :end)
  153. (plist-get change2 :end)))
  154. (defun js2r--any-overlapping-changes (sorted-changes)
  155. (--any?
  156. (let ((one (car it))
  157. (two (cadr it)))
  158. (< (plist-get one :beg)
  159. (plist-get two :end)))
  160. (-partition-in-steps 2 1 sorted-changes)))
  161. (defun js2r--execute-changes (changes)
  162. (when changes
  163. (let ((sorted-changes (sort changes 'js2r--by-end-descending)))
  164. (when (js2r--any-overlapping-changes sorted-changes)
  165. (error "These changes overlap, cannot execute properly."))
  166. (let ((abs-end (set-marker (make-marker) (1+ (plist-get (car sorted-changes) :end))))
  167. (abs-beg (plist-get (car (last sorted-changes)) :beg)))
  168. (--each sorted-changes
  169. (goto-char (plist-get it :beg))
  170. (delete-char (- (plist-get it :end) (plist-get it :beg)))
  171. (insert (plist-get it :contents)))
  172. (indent-region abs-beg abs-end)
  173. (set-marker abs-end nil)))))
  174. (provide 'js2r-helpers)
  175. ;;; js2-helpers.el ends here