diff options
author | mattkae <mattkae@protonmail.com> | 2022-05-11 09:23:58 -0400 |
---|---|---|
committer | mattkae <mattkae@protonmail.com> | 2022-05-11 09:23:58 -0400 |
commit | 3f4a0d5370ae6c34afe180df96add3b8522f4af1 (patch) | |
tree | ae901409e02bde8ee278475f8cf6818f8f680a60 /elpa/js2-refactor-20210306.2003/js2r-functions.el |
initial commit
Diffstat (limited to 'elpa/js2-refactor-20210306.2003/js2r-functions.el')
-rw-r--r-- | elpa/js2-refactor-20210306.2003/js2r-functions.el | 537 |
1 files changed, 537 insertions, 0 deletions
diff --git a/elpa/js2-refactor-20210306.2003/js2r-functions.el b/elpa/js2-refactor-20210306.2003/js2r-functions.el new file mode 100644 index 0000000..31e331a --- /dev/null +++ b/elpa/js2-refactor-20210306.2003/js2r-functions.el @@ -0,0 +1,537 @@ +;;; js2r-functions.el --- Function manipulation functions for js2-refactor -*- lexical-binding: t; -*- + +;; Copyright (C) 2012-2014 Magnar Sveen +;; Copyright (C) 2015-2016 Magnar Sveen and Nicolas Petton + +;; Author: Magnar Sveen <magnars@gmail.com>, +;; Nicolas Petton <nicolas@petton.fr> +;; Keywords: conveniences + +;; 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/>. + +;;; Code: + +(require 'dash) +(require 'yasnippet) + +(require 'js2r-helpers) + +(defun js2r-localize-parameter () + "Turn parameter into local var in local function." + (interactive) + (js2r--guard) + (js2r--wait-for-parse + (if (js2-name-node-p (js2-node-at-point)) + (js2r--localize-parameter-pull) + (js2r--localize-parameter-push)))) + +(defun js2r--localize-parameter-push () + (let* ((node (js2-node-at-point)) + (arg-node (or (js2r--closest-node-where 'js2r--parent-is-call-node node) + (error "Place cursor on argument to localize"))) + (call-node (js2-node-parent arg-node)) + (value (js2-node-string arg-node)) + (target (js2-call-node-target call-node)) + (fn (if (js2-name-node-p target) + (js2r--local-fn-from-name-node target) + (error "Can only localize parameter for local functions"))) + (usages (js2r--function-usages fn)) + (index (car (--keep (when (eq arg-node it) it-index) + (js2-call-node-args call-node)))) + (name (js2-name-node-name (nth index (js2-function-node-params fn))))) + (js2r--localize-parameter fn usages index name value))) + +(defun js2r--localize-parameter-pull () + (let* ((name-node (js2-node-at-point)) + (name (if (js2-name-node-p name-node) + (js2-name-node-name name-node) + (error "Place cursor on parameter to localize"))) + (fn (or (js2r--closest-node-where #'js2r--is-local-function name-node) + (error "Can only localize parameter in local functions"))) + (index (or (js2r--param-index-for name fn) + (error "%S isn't a parameter to this function" name))) + (usages (js2r--function-usages fn)) + (examples (-distinct (--map (js2r--argument index it) usages))) + (value (js2r--choose-one "Value: " examples))) + (js2r--localize-parameter fn usages index name value))) + +(defun js2r--localize-parameter (fn usages index name value) + (save-excursion + (js2r--goto-fn-body-beg fn) + (save-excursion + (--each usages (js2r--remove-argument-at-index index it))) + (newline-and-indent) + (insert "var " name " = " value ";") + (js2r--remove-parameter-at-index index fn))) + +(defun js2r--parent-is-call-node (node) + (js2-call-node-p (js2-node-parent node))) + +(defun js2r--local-fn-from-name-node (name-node) + (->> name-node + (js2r--local-usages-of-name-node) + (-map #'js2-node-parent) + (-first #'js2-function-node-p))) + +(defun js2r--param-index-for (name fn) + (car (--keep (when (equal name (js2-name-node-name it)) it-index) + (js2-function-node-params fn)))) + +(defun js2r--argument (index call-node) + (js2-node-string (nth index (js2-call-node-args call-node)))) + +(defun js2r--remove-parameter-at-index (index fn) + (js2r--delete-node-in-params (nth index (js2-function-node-params fn)))) + +(defun js2r--remove-argument-at-index (index call-node) + (js2r--delete-node-in-params (nth index (js2-call-node-args call-node)))) + +(defun js2r--delete-node-in-params (node) + (goto-char (js2-node-abs-pos node)) + (delete-char (js2-node-len node)) + (if (and (looking-back "(") + (looking-at ", ")) + (delete-char 2) + (when (looking-back ", ") + (delete-char -2)))) + +(defun js2r--choose-one (prompt options) + (when options + (if (cdr options) + (completing-read prompt options) + (car options)))) + +(defun js2r-introduce-parameter () + "Introduce a parameter in a local function." + (interactive) + (js2r--guard) + (js2r--wait-for-parse + (if (use-region-p) + (js2r--introduce-parameter-between (region-beginning) (region-end)) + (let ((node (js2r--closest-extractable-node))) + (js2r--introduce-parameter-between (js2-node-abs-pos node) + (js2-node-abs-end node)))))) + +(defun js2r--introduce-parameter-between (beg end) + (unless (js2r--single-complete-expression-between-p beg end) + (error "Can only introduce single, complete expressions as parameter")) + + (let ((fn (js2r--closest-node-where #'js2r--is-local-function (js2-node-at-point)))) + (unless fn + (error "Can only introduce parameter in local functions")) + (save-excursion + (let ((name (read-string "Parameter name: ")) + (val (buffer-substring beg end)) + (usages (js2r--function-usages fn))) + (goto-char beg) + (save-excursion + (-each usages (-partial #'js2r--add-parameter val))) + (delete-char (- end beg)) + (insert name) + (js2r--add-parameter name fn) + (query-replace val name nil (js2-node-abs-pos fn) (js2r--fn-body-end fn)))))) + +(defun js2r--function-usages (fn) + (-map #'js2-node-parent (js2r--function-usages-name-nodes fn))) + +(defun js2r--function-usages-name-nodes (fn) + (let ((name-node (or (js2-function-node-name fn) + (js2-var-init-node-target (js2-node-parent fn))))) + (remove name-node (js2r--local-usages-of-name-node name-node)))) + +(defun js2r--add-parameter (name node) + (save-excursion + (js2r--goto-closing-paren node) + (unless (looking-back "(") + (insert ", ")) + (insert name))) + +(defun js2r--goto-closing-paren (node) + (goto-char (js2-node-abs-pos node)) + (search-forward "(") + (forward-char -1) + (forward-list) + (forward-char -1)) + +(defun js2r--goto-fn-body-beg (fn) + (goto-char (js2-node-abs-pos fn)) + (search-forward "{")) + +(defun js2r--fn-body-end (fn) + (save-excursion + (js2r--goto-fn-body-beg fn) + (forward-char -1) + (forward-list) + (point))) + +(defun js2r--is-local-function (node) + (or (js2r--is-var-function-expression node) + (js2r--is-function-declaration node))) + +(defun js2r--is-method (node) + (and (js2-function-node-p node) + (js2-object-prop-node-p (js2-node-parent node)))) + +(defun js2r--is-var-function-expression (node) + (and (js2-function-node-p node) + (js2-var-init-node-p (js2-node-parent node)))) + +(defun js2r--is-assigned-function-expression (node) + (and (js2-function-node-p node) + (js2-assign-node-p (js2-node-parent node)))) + +(defun js2r--is-function-declaration (node) + (let ((parent (js2-node-parent node))) + (and (js2-function-node-p node) + (not (js2-assign-node-p parent)) + (not (js2-var-init-node-p parent)) + (not (js2-object-prop-node-p parent))))) + +(defun js2r-arguments-to-object () + "Change from a list of arguments to a parameter object." + (interactive) + (js2r--guard) + (js2r--wait-for-parse + (let ((node (js2-node-at-point))) + (unless (and (looking-at "(") + (or (js2-function-node-p node) + (js2-call-node-p node) + (js2-new-node-p node))) + (error "Place point right before the opening paren in the call or function")) + + (-when-let* ((target (js2r--node-target node)) + (fn (and (js2-name-node-p target) + (js2r--local-fn-from-name-node target)))) + (setq node fn)) + (if (js2-function-node-p node) + (js2r--arguments-to-object-for-function node) + (js2r--arguments-to-object-for-args-with-unknown-function (js2r--node-args node)))))) + +(defun js2r--arguments-to-object-for-function (function-node) + (let ((params (js2-function-node-params function-node))) + (when (null params) + (error "No params to convert")) + (save-excursion + (js2r--execute-changes + (-concat + ;; change parameter list to just (params) + (list + (list :beg (+ (js2-node-abs-pos function-node) (js2-function-node-lp function-node)) + :end (+ (js2-node-abs-pos function-node) (js2-function-node-rp function-node) 1) + :contents "(params)")) + + ;; add params. in front of function local param usages + (let* ((local-param-name-nodes (--mapcat (-> it + (js2-node-abs-pos) + (js2r--local-name-node-at-point) + (js2r--local-usages-of-name-node)) + params)) + (local-param-name-usages (--remove (js2-function-node-p (js2-node-parent it)) + local-param-name-nodes)) + (local-param-name-positions (-map #'js2-node-abs-pos local-param-name-usages))) + (--map + (list :beg it :end it :contents "params.") + local-param-name-positions)) + + ;; update usages of function + (let ((names (-map #'js2-name-node-name params)) + (usages (js2r--function-usages function-node))) + (--map + (js2r--changes/arguments-to-object it names) + usages))))))) + +(defun js2r--changes/arguments-to-object (node names) + (let ((args (js2r--node-args node))) + (list :beg (+ (js2-node-abs-pos node) (js2r--node-lp node)) + :end (+ (js2-node-abs-pos node) (js2r--node-rp node) 1) + :contents (js2r--create-object-with-arguments names args)))) + +(defun js2r--arguments-to-object-for-args-with-unknown-function (args) + (when (null args) + (error "No arguments to convert")) + (let ((names (--map-indexed + (format "${%d:%s}" + (1+ it-index) + (if (js2-name-node-p it) + (js2-name-node-name it) + "key")) + args))) + (yas-expand-snippet (js2r--create-object-with-arguments names args) + (point) + (save-excursion (forward-list) (point))))) + +(defun js2r--create-object-with-arguments (names args) + (let (arg key result) + (--dotimes (length args) + (setq arg (nth it args)) + (setq key (nth it names)) + (setq result + (concat result + (format " %s: %s,\n" + key + (buffer-substring (js2-node-abs-pos arg) + (js2-node-abs-end arg)))))) + (concat "({\n" (substring result 0 -2) "\n})"))) + +(defun js2r-extract-function (name) + "Extract a function from the closest statement expression from the point." + (interactive "sName of new function: ") + (js2r--extract-fn + name + (lambda () + (unless (js2r--looking-at-function-declaration) + (goto-char (js2-node-abs-pos (js2r--closest #'js2-expr-stmt-node-p))))) + "%s(%s);" + "function %s(%s) {\n%s\n}\n\n")) + +(defun js2r-extract-method (name) + "Extract a method from the closest statement expression from the point." + (interactive "sName of new method: ") + (let ((class-node (js2r--closest #'js2-class-node-p))) + (js2r--extract-fn + name + (unless class-node + (lambda () + (goto-char (js2-node-abs-pos (js2r--closest #'js2-object-prop-node-p))))) + "this.%s(%s);" + (if class-node + "%s(%s) {\n%s\n}\n\n" + "%s: function (%s) {\n%s\n},\n\n")))) + +(defun js2r--extract-fn (name goto-position call-template function-template) + (js2r--guard) + (js2r--wait-for-parse + (unless (use-region-p) + (error "Mark the expressions to extract first")) + (save-excursion + (let* ((parent (js2r--first-common-ancestor-in-region (region-beginning) (region-end))) + (block (js2r--closest-node-where #'js2-block-node-p parent)) + (fn (js2r--closest-node-where #'js2-function-node-p block)) + (exprs (js2r--marked-expressions-in-block block)) + (vars (-mapcat #'js2r--name-node-decendants exprs)) + (local (--filter (js2r--local-to-fn-p fn it) vars)) + (names (-distinct (-map 'js2-name-node-name local))) + (declared-in-exprs (-map #'js2r--var-init-node-target-name (-mapcat #'js2r--var-init-node-decendants exprs))) + (outside-exprs (-difference (js2-block-node-kids block) exprs)) + (outside-var-uses (-map #'js2-name-node-name (-mapcat #'js2r--name-node-decendants outside-exprs))) + (declared-in-but-used-outside (-intersection declared-in-exprs outside-var-uses)) + (export-var (car declared-in-but-used-outside)) + (params (-difference names declared-in-exprs)) + (params-string (mapconcat #'identity (reverse params) ", ")) + (first (car exprs)) + (last (car (last exprs))) + (beg (js2-node-abs-pos (car exprs))) + (end (js2-node-abs-end last)) + (contents (buffer-substring beg end))) + (goto-char beg) + (delete-region beg end) + (when (js2-return-node-p last) + (insert "return ")) + (when export-var + (setq contents (concat contents "\nreturn " export-var ";")) + (insert "var " export-var " = ")) + (insert (format call-template name params-string)) + (goto-char (js2-node-abs-pos fn)) + (when goto-position (funcall goto-position)) + (let ((start (point))) + (insert (format function-template name params-string contents)) + (indent-region start (1+ (point)))))))) + +(defun js2r--var-init-node-target-name (node) + (js2-name-node-name + (js2-var-init-node-target node))) + +(defun js2r--function-around-region () + (or + (js2r--closest-node-where #'js2-function-node-p + (js2r--first-common-ancestor-in-region + (region-beginning) + (region-end))) + (error "This only works when you mark stuff inside a function"))) + +(defun js2r--marked-expressions-in-block (fn) + (-select #'js2r--node-is-marked (js2-block-node-kids fn))) + +(defun js2r--node-is-marked (node) + (and + (<= (region-beginning) (js2-node-abs-end node)) + (>= (region-end) (js2-node-abs-pos node)))) + +(defun js2r--name-node-decendants (node) + (-select #'js2-name-node-p (js2r--decendants node))) + +(defun js2r--var-init-node-decendants (node) + (-select #'js2-var-init-node-p (js2r--decendants node))) + +(defun js2r--decendants (node) + (let (vars) + (js2-visit-ast node + (lambda (node end-p) + (unless end-p + (setq vars (cons node vars))))) + vars)) + +(defun js2r--local-to-fn-p (fn name-node) + (let* ((name (js2-name-node-name name-node)) + (scope (js2-node-get-enclosing-scope name-node)) + (scope (js2-get-defining-scope scope name))) + (eq fn scope))) + + +(defun js2r-toggle-arrow-function-and-expression () + "Toggle between function expression to arrow function." + (interactive) + (save-excursion + (js2r--find-closest-function) + (cond ((js2r--arrow-function-p) + (js2r--transform-arrow-function-to-expression)) + ((and (js2r--function-start-p) (not (js2r--looking-at-function-declaration))) + (js2r--transform-function-expression-to-arrow)) + (t (error "Can only toggle between function expressions and arrow function"))))) + + +;; Toggle between function name() {} and var name = function (); + +(defun js2r-toggle-function-expression-and-declaration () + (interactive) + (save-excursion + (js2r--find-closest-function) + (cond + ((js2r--looking-at-var-function-expression) + (when (js2r--arrow-function-p) (js2r--transform-arrow-function-to-expression)) + (js2r--transform-function-expression-to-declaration)) + ((js2r--looking-at-function-declaration) + (js2r--transform-function-declaration-to-expression)) + (t (error "Can only toggle between function declarations and free standing function expressions"))))) + + +(defun js2r--arrow-function-p () + (interactive) + (save-excursion + (ignore-errors + (js2r--find-closest-function) + (and (looking-at "(?[,[:space:][:word:]]*)?[[:space:]]*=>") + (not (js2r--point-inside-string-p)))))) + +(defun js2r--transform-arrow-function-to-expression () + (when (js2r--arrow-function-p) + (let (has-parenthesis) + (save-excursion + (js2r--find-closest-function) + (let ((end (make-marker))) + (save-excursion + (search-forward "=>") + (set-marker end (js2-node-abs-end (js2-node-at-point)))) + (setq has-parenthesis (looking-at "\\s-*(")) + (insert "function ") + (if has-parenthesis + (forward-list) + (insert "(")) + (search-forward "=>") + (delete-char -2) + (js2r--ensure-just-one-space) + (unless has-parenthesis + (backward-char 1) + (insert ")")) + (unless (looking-at "\\s-*{") + (js2r--ensure-just-one-space) + (insert "{ return ") + (js2r--ensure-just-one-space) + (goto-char (marker-position end)) + (insert "; }"))))))) + +(defun js2r--transform-function-expression-to-arrow () + (when (not (js2r--arrow-function-p)) + (save-excursion + (js2r--find-closest-function) + (let ((pos (point)) + (params + (js2-function-node-params (js2-node-at-point))) + parenthesis-start + parenthesis-end) + (when (js2r--looking-at-function-declaration) + (error "Can not convert function declarations to arrow function")) + (search-forward "(") + (backward-char 1) + (delete-region pos (point)) + (setq parenthesis-start (point)) + (forward-list) + (setq parenthesis-end (point)) + (insert " => ") + (js2r--ensure-just-one-space) + (when (and (= 1 (length params)) + (not js2r-always-insert-parens-around-arrow-function-params)) + (goto-char parenthesis-end) + (backward-delete-char 1) + (goto-char parenthesis-start) + (delete-char 1)))))) + + +(defun js2r--function-start-p() + (let* ((fn (js2r--closest #'js2-function-node-p))) + (and fn + (= (js2-node-abs-pos fn) (point))))) + +(defun js2r--find-closest-function () + (when (not (js2r--function-start-p)) + (let* ((fn (js2r--closest #'js2-function-node-p))) + (goto-char (js2-node-abs-pos fn))))) + +(defun js2r--looking-at-method () + (and (js2r--function-start-p) + (looking-back ": ?"))) + +(defun js2r--looking-at-function-declaration () + (and (js2r--function-start-p) + (looking-back "^ *"))) + +(defun js2r--looking-at-var-function-expression () + (and (js2r--function-start-p) + (looking-back "^ *var[\s\n]*[a-z_$]+[\s\n]*=[\s\n]*"))) + +(defun js2r--transform-function-expression-to-declaration () + (when (js2r--looking-at-var-function-expression) + (delete-char 9) + (forward-list) + (forward-list) + (delete-char 1) + (backward-list) + (backward-list) + (delete-backward-char 3) + (back-to-indentation) + (delete-char 4) + (insert "function "))) + +(defun js2r--transform-function-declaration-to-expression () + (when (js2r--looking-at-function-declaration) + (delete-char 9) + (insert "var ") + (search-forward "(") + (backward-char 1) + (insert " = function ") + (forward-list) + (forward-list) + (insert ";"))) + +(defun js2r-toggle-function-async () + "Toggle the innermost function from sync to async." + (interactive) + (save-excursion + (js2r--find-closest-function) + (if (looking-back "async[[:space:]\n]+") + (delete-region (match-beginning 0) (match-end 0)) + (insert "async ")))) + +(provide 'js2r-functions) +;;; js2-functions.el ends here |