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.

203 lines
7.6 KiB

  1. ;;; js2r-paredit.el --- Paredit-like extensions 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. (defun js2r--nesting-node-p (node)
  20. (or (js2-function-node-p node)
  21. (js2-if-node-p node)
  22. (js2-for-node-p node)
  23. (js2-while-node-p node)))
  24. (defun js2r--standalone-node-p (node)
  25. (or (js2-stmt-node-p node)
  26. (and (js2-function-node-p node)
  27. (eq 'FUNCTION_STATEMENT (js2-function-node-form node)))))
  28. (defun js2r-kill ()
  29. "Kill a line like `kill-line' but tries to respect node boundaries.
  30. Falls back to `kill-line' if the buffer has parse errors.
  31. if(|foo) {bar();} -> if() {bar();}
  32. function foo() {|2 + 3} -> function foo() {}
  33. // some |comment -> // some
  34. 'this is a| string' -> 'this is a'
  35. "
  36. (interactive)
  37. (if js2-parsed-errors
  38. (progn
  39. (message "Buffer has parse errors. Killing the line")
  40. (kill-line))
  41. (js2r--kill-line)))
  42. (defun js2r--kill-line ()
  43. "Kill a line, but respecting node boundaries."
  44. (let ((node (js2r--next-node)))
  45. (cond
  46. ((js2-comment-node-p node) (kill-line))
  47. ((js2-string-node-p node) (js2r--kill-line-in-string))
  48. (t (js2r--kill-line-in-sexp)))))
  49. (defun js2r--next-node ()
  50. "Return the node at point, or the node after the point if the
  51. point is at the exact end of a node."
  52. (save-excursion
  53. (when (= (js2-node-abs-end (js2-node-at-point))
  54. (point))
  55. (forward-char 1))
  56. (js2-node-at-point)))
  57. (defun js2r--kill-line-in-sexp ()
  58. "Kill a line, but only kills until the closest outer sexp on
  59. the current line, delimited with \")}]\". If no sexp is found
  60. on the current line, falls back to
  61. `js2r--kill-line-with-inner-sexp'."
  62. (condition-case error
  63. (let* ((beg (point))
  64. (end (save-excursion
  65. (up-list)
  66. (forward-char -1)
  67. (point))))
  68. (if (js2-same-line end)
  69. (kill-region beg end)
  70. (js2r--kill-line-with-inner-sexp)))
  71. (scan-error
  72. (js2r--kill-line-with-inner-sexp))))
  73. (defun js2r--kill-line-with-inner-sexp ()
  74. "Kill a line, but respecting inner killed sexps, ensuring that
  75. we kill up to the end to the next inner sexp if it starts in
  76. the current line.
  77. If the parentheses are unbalanced, fallback to `kill-line' and
  78. warn the user."
  79. (condition-case error
  80. (let* ((beg (point))
  81. (end (save-excursion
  82. (forward-visible-line 1)
  83. (point)))
  84. (beg-of-sexp (save-excursion
  85. (js2r--goto-last-sexp-on-line)
  86. (point)))
  87. (end-of-sexp (save-excursion
  88. (goto-char beg-of-sexp)
  89. (forward-list)
  90. ;; Kill all remaining semi-colons as well
  91. (while (looking-at ";")
  92. (forward-char))
  93. (point))))
  94. (if (js2-same-line beg-of-sexp)
  95. (kill-region beg (max end end-of-sexp))
  96. (kill-line)))
  97. (scan-error
  98. (message "Unbalanced parentheses. Killing the line.")
  99. (kill-line))))
  100. (defun js2r--goto-last-sexp-on-line ()
  101. "Move the cursor to the opening of the last sexp on the current
  102. line, or to the end of the line if no sexp is found."
  103. (let ((pos (point)))
  104. (down-list)
  105. (backward-char 1)
  106. (forward-list)
  107. (if (js2-same-line pos)
  108. (js2r--goto-last-sexp-on-line)
  109. (backward-list))))
  110. (defun js2r--kill-line-in-string ()
  111. "Kill a line in a string node, respecting the node boundaries.
  112. When at the beginning of the node, kill from outside of it."
  113. (let* ((node (js2-node-at-point))
  114. (beg (point))
  115. (node-start (js2-node-abs-pos node))
  116. (node-end (js2-node-abs-end node)))
  117. (if (= beg node-start)
  118. (js2r--kill-line-in-sexp)
  119. (kill-region beg (1- node-end)))))
  120. (defun js2r-forward-slurp (&optional arg)
  121. (interactive "p")
  122. (js2r--guard)
  123. (let* ((nesting (js2r--closest 'js2r--nesting-node-p))
  124. (standalone (if (js2r--standalone-node-p nesting)
  125. nesting
  126. (js2-node-parent-stmt nesting)))
  127. (next-sibling (js2-node-next-sibling standalone))
  128. (beg (js2-node-abs-pos next-sibling))
  129. (last-sibling (if (wholenump arg)
  130. (let ((num arg)
  131. (iter-sibling next-sibling))
  132. (while (> num 1) ;; Do next-sibling arg nbr of times
  133. (setq iter-sibling (js2-node-next-sibling iter-sibling))
  134. (setq num (1- num)))
  135. iter-sibling)
  136. next-sibling)) ;; No optional arg. Just use next-sibling
  137. (end (1+ (js2-node-abs-end last-sibling))) ;; include whitespace after statement
  138. (text (buffer-substring beg end)))
  139. (save-excursion
  140. (delete-region beg end)
  141. (goto-char (js2-node-abs-end nesting))
  142. (forward-char -1)
  143. (when (looking-back "{ *") (newline))
  144. (setq beg (point))
  145. (insert text)
  146. (indent-region beg end))))
  147. (defun js2r-forward-barf (&optional arg)
  148. (interactive "p")
  149. (js2r--guard)
  150. (let* ((nesting (js2r--closest 'js2r--nesting-node-p))
  151. (standalone (if (js2r--standalone-node-p nesting)
  152. nesting
  153. (js2-node-parent-stmt nesting)))
  154. (standalone-end (js2-node-abs-end standalone))
  155. (last-child (car (last (if (js2-if-node-p nesting)
  156. (js2-scope-kids (js2r--closest 'js2-scope-p))
  157. (js2r--node-kids nesting)))))
  158. (first-barf-child (if (wholenump arg)
  159. (let ((num arg)
  160. (iter-child last-child))
  161. (while (> num 1) ;; Do prev-sibling arg nbr of times
  162. (setq iter-child (js2-node-prev-sibling iter-child))
  163. (setq num (1- num)))
  164. iter-child)
  165. last-child)); No optional arg. Just use last-child
  166. (last-child-beg (save-excursion
  167. (goto-char (js2-node-abs-pos first-barf-child))
  168. (skip-syntax-backward " ")
  169. (while (looking-back "\n") (backward-char))
  170. (point)))
  171. (last-child-end (js2-node-abs-end last-child))
  172. (text (buffer-substring last-child-beg last-child-end)))
  173. (save-excursion
  174. (js2r--execute-changes
  175. (list
  176. (list :beg last-child-beg :end last-child-end :contents "")
  177. (list :beg standalone-end :end standalone-end :contents text))))))
  178. (provide 'js2r-paredit)
  179. ;;; js2r-paredit.el ends here