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.

251 lines
8.9 KiB

  1. ;;; js2r-formatting.el --- Private helper functions for formatting -*- 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. (defun js2r--ensure-newline ()
  19. (if (and (not (looking-at "\s*\n"))
  20. (not (looking-back "\n\s*")))
  21. (newline-and-indent)))
  22. (defun js2r--ensure-just-one-space ()
  23. (interactive)
  24. (while (or (looking-at "\s*\n")
  25. (looking-back "\n\s*"))
  26. (when (looking-at "\n")
  27. (delete-char 1))
  28. (when (looking-back "\n\s")
  29. (backward-char)
  30. (delete-char -1))
  31. (just-one-space))
  32. (just-one-space))
  33. (defmacro js2r--create-bracketed-whitespace-traverser
  34. (name ws-fix-func looking-at-start-func
  35. goto-closest-start-func subexpr-str)
  36. "Build a function to expand or contract a given type of
  37. bracketed expression, i.e., function body, object literal, or
  38. array (any of which may be nested).
  39. Parameters:
  40. name: name of the function to be built
  41. ws-fix-func: function to adjust whitespace at point
  42. looking-at-start-func: returns t if point is at
  43. the start of the bracketed
  44. thing we want to act on
  45. goto-closest-start-func: moves point if necessary
  46. until looking-at-start-func
  47. is true
  48. subexpr-str: literal delimiter of parts of the
  49. thing to be expanded or contracted"
  50. `(defun ,name ()
  51. (interactive)
  52. (save-excursion
  53. (if (not ,looking-at-start-func)
  54. ,goto-closest-start-func)
  55. (let ((end (make-marker)))
  56. (set-marker end (save-excursion
  57. (forward-list)
  58. (point)))
  59. (forward-char)
  60. ,ws-fix-func
  61. (while (< (point) end)
  62. (while (js2r--point-inside-string-p)
  63. (forward-char))
  64. (when (looking-at ,subexpr-str)
  65. (forward-char)
  66. (unless (js2-comment-node-p (js2-node-at-point))
  67. ,ws-fix-func))
  68. (if (looking-at "\\s(")
  69. (forward-list)
  70. (forward-char)))
  71. (backward-char)
  72. ,ws-fix-func))))
  73. (defun js2r--looking-at-object-start ()
  74. (and (looking-at "{")
  75. (not (looking-back ")[\s\n]*"))))
  76. (defun js2r--goto-closest-object-start ()
  77. (while (not (js2r--looking-at-object-start))
  78. (if (eq (car (syntax-ppss)) 0)
  79. (error "Cursor is not on an object")
  80. (goto-char (nth 1 (syntax-ppss))))))
  81. (js2r--create-bracketed-whitespace-traverser js2r-expand-object
  82. (js2r--ensure-newline)
  83. (js2r--looking-at-object-start)
  84. (js2r--goto-closest-object-start)
  85. ",")
  86. (js2r--create-bracketed-whitespace-traverser js2r-contract-object
  87. (js2r--ensure-just-one-space)
  88. (js2r--looking-at-object-start)
  89. (js2r--goto-closest-object-start)
  90. ",")
  91. (defun js2r--looking-at-array-start ()
  92. (looking-at "\\["))
  93. (defun js2r--goto-closest-array-start ()
  94. (while (not (js2r--looking-at-array-start))
  95. (if (eq (car (syntax-ppss)) 0)
  96. (error "Cursor is not on an array")
  97. (goto-char (nth 1 (syntax-ppss))))))
  98. (js2r--create-bracketed-whitespace-traverser js2r-expand-array
  99. (js2r--ensure-newline)
  100. (js2r--looking-at-array-start)
  101. (js2r--goto-closest-array-start)
  102. ",")
  103. (js2r--create-bracketed-whitespace-traverser js2r-contract-array
  104. (js2r--ensure-just-one-space)
  105. (js2r--looking-at-array-start)
  106. (js2r--goto-closest-array-start)
  107. ",")
  108. ;; (defun js2r--looking-at-function-start ()
  109. ;; (or
  110. ;; (and (looking-at "{")
  111. ;; (looking-back
  112. ;; ;; This horrible-looking regexp is actually pretty simple. It
  113. ;; ;; matches "function <optional_name> (<optional_parameters,...>)"
  114. ;; ;; allowing for whitespace. TODO: support Unicode in function and
  115. ;; ;; parameter names.
  116. ;; (concat "function[\s\n]*"
  117. ;; "\\\([a-zA-Z_$][a-zA-Z_$0-9]*[\s\n]*\\\)?"
  118. ;; "\(\\\([a-zA-Z_$][a-zA-Z_$0-9]*"
  119. ;; "[\s\n]*,[\s\n]*\\\)*[\s\n]*"
  120. ;; "\\\([a-zA-Z_$][a-zA-Z_$0-9]*[\s\n]*\\\)*"
  121. ;; "[\s\n]*\)[\s\n]*")))
  122. ;; ;; arrow functions
  123. ;; (and (looking-at "{")
  124. ;; (looking-back "=>[\s\n]*")
  125. ;; (not (js2r--point-inside-string-p)))))
  126. (defun js2r--looking-at-function-start ()
  127. "Return non-nil if the point is at the start of a function body."
  128. (let* ((node (js2-node-at-point))
  129. (parent (js2-node-parent node)))
  130. (and (looking-at "{")
  131. (js2-block-node-p node)
  132. (js2-function-node-p parent))))
  133. (defun js2r--goto-closest-function-start ()
  134. (while (not (js2r--looking-at-function-start))
  135. (if (eq (car (syntax-ppss)) 0)
  136. (error "Cursor is not on a function body")
  137. (goto-char (nth 1 (syntax-ppss))))))
  138. (js2r--create-bracketed-whitespace-traverser js2r-expand-function
  139. (js2r--ensure-newline)
  140. (js2r--looking-at-function-start)
  141. (js2r--goto-closest-function-start)
  142. ";")
  143. ;; TODO: It'd be great if js2r-contract-function could recognize
  144. ;; newlines that are implied statement terminators and insert
  145. ;; semicolons correctly, but that would probably mean not using the
  146. ;; same macro as the other "contract" function definitions.
  147. (js2r--create-bracketed-whitespace-traverser js2r-contract-function
  148. (js2r--ensure-just-one-space)
  149. (js2r--looking-at-function-start)
  150. (js2r--goto-closest-function-start)
  151. ";")
  152. (defun js2r--looking-at-call-start ()
  153. (looking-at "("))
  154. (defun js2r--goto-closest-call-start ()
  155. (while (not (js2r--looking-at-call-start))
  156. (if (eq (car (syntax-ppss)) 0)
  157. (error "Cursor is not on a call")
  158. (goto-char (nth 1 (syntax-ppss))))))
  159. (js2r--create-bracketed-whitespace-traverser js2r-expand-call-args
  160. (js2r--ensure-newline)
  161. (js2r--looking-at-call-start)
  162. (js2r--goto-closest-call-start)
  163. ",")
  164. (js2r--create-bracketed-whitespace-traverser js2r-contract-call-args
  165. (js2r--ensure-just-one-space)
  166. (js2r--looking-at-call-start)
  167. (js2r--goto-closest-call-start)
  168. ",")
  169. (defun js2r--expand-contract-node-at-point (&optional is-contract)
  170. "Expand or contract bracketed list according to node type in point.
  171. Currently working on array, object, function and call args node types.
  172. With argument, contract closest expression, otherwise expand."
  173. (let ((pos (point))
  174. (array-start (point-max))
  175. (object-start (point-max))
  176. (function-start (point-max))
  177. (call-start (point-max)))
  178. (save-excursion
  179. (ignore-errors
  180. (js2r--goto-closest-array-start)
  181. (setq array-start (- pos (point)))))
  182. (save-excursion
  183. (ignore-errors
  184. (js2r--goto-closest-object-start)
  185. (setq object-start (- pos (point)))))
  186. (save-excursion
  187. (ignore-errors
  188. (js2r--goto-closest-function-start)
  189. (setq function-start (- pos (point)))))
  190. (save-excursion
  191. (ignore-errors
  192. (js2r--goto-closest-call-start)
  193. (setq call-start (- pos (point)))))
  194. (setq pos (-min (list array-start object-start function-start call-start)))
  195. (when (= pos array-start)
  196. (if is-contract
  197. (js2r-contract-array)
  198. (js2r-expand-array)))
  199. (when (= pos object-start)
  200. (if is-contract
  201. (js2r-contract-object)
  202. (js2r-expand-object)))
  203. (when (= pos function-start)
  204. (if is-contract
  205. (js2r-contract-function)
  206. (js2r-expand-function)))
  207. (when (= pos call-start)
  208. (if is-contract
  209. (js2r-contract-call-args)
  210. (js2r-expand-call-args)))))
  211. (defun js2r-expand-node-at-point ()
  212. "Expand bracketed list according to node type at point."
  213. (interactive)
  214. (js2r--expand-contract-node-at-point))
  215. (defun js2r-contract-node-at-point ()
  216. "Contract bracketed list according to node type at point."
  217. (interactive)
  218. (js2r--expand-contract-node-at-point t))
  219. (provide 'js2r-formatting)
  220. ;;; js2-formatting.el ends here