;;; -*- lexical-binding: t; -*- (require 'dash) (load! "utils") ;;; ;;; Vars ;;; (defvar +splitjoin/split-callbacks '() "Alist mapping major mode symbol names to lists of split callbacks") (defvar +splitjoin/join-callbacks '() "Alist mapping major mode symbol names to lists of join callbacks") ;;; ;;; Definition macros ;;; (defmacro +splitjoin/defsplit (mode name &rest body) `(setf (alist-get ',name (alist-get ,mode +splitjoin/split-callbacks)) (λ! () ,@body))) (defmacro +splitjoin/defjoin (mode name &rest body) `(setf (alist-get ',name (alist-get ,mode +splitjoin/join-callbacks)) (λ! () ,@body))) ;;; ;;; Commands ;;; (defun +splitjoin/split () (interactive) (when-let (callbacks (->> +splitjoin/split-callbacks (alist-get major-mode) (-map #'cdr))) (find-if #'funcall callbacks))) (defun +splitjoin/join () (interactive) (when-let (callbacks (->> +splitjoin/join-callbacks (alist-get major-mode) (-map #'cdr))) (find-if #'funcall callbacks))) ;;; ;;; Splits and joins ;;; TODO: this should probably go in a file-per-language ;;; (+splitjoin/defjoin 'elixir-mode join-do (let* ((function-pattern (rx (and (zero-or-more whitespace) "do" (zero-or-more whitespace) (optional (and "#" (zero-or-more anything))) eol))) (end-pattern (rx bol (zero-or-more whitespace) "end" (zero-or-more whitespace) eol)) (else-pattern (rx bol (zero-or-more whitespace) "else" (zero-or-more whitespace) eol)) (lineno (line-number-at-pos)) (line (thing-at-point 'line t))) (when-let ((do-start-pos (string-match function-pattern line))) (cond ((string-match-p end-pattern (get-line (inc lineno))) (modify-then-indent (goto-line-char do-start-pos) (insert ",") (goto-char (line-end-position)) (insert ": nil") (line-move 1) (delete-line)) t) ((string-match-p end-pattern (get-line (+ 2 lineno))) (modify-then-indent (goto-line-char do-start-pos) (insert ",") (goto-char (line-end-position)) (insert ":") (join-line t) (line-move 1) (delete-line)) t) ((and (string-match-p else-pattern (get-line (+ 2 lineno))) (string-match-p end-pattern (get-line (+ 4 lineno)))) (modify-then-indent (goto-line-char do-start-pos) (insert ",") (goto-char (line-end-position)) (insert ":") (join-line t) (goto-eol) (insert ",") (join-line t) (goto-eol) (insert ":") (join-line t) (line-move 1) (delete-line)) t))))) (comment (string-match (rx (and bol "if " (one-or-more anything) "," (zero-or-more whitespace) "do:" (one-or-more anything) "," (zero-or-more whitespace) "else:" (one-or-more anything))) "if 1, do: nil, else: nil") ) (+splitjoin/defsplit 'elixir-mode split-do-with-optional-else (let* ((if-with-else-pattern (rx (and bol (one-or-more anything) "," (zero-or-more whitespace) "do:" (one-or-more anything) (optional "," (zero-or-more whitespace) "else:" (one-or-more anything))))) (current-line (get-line))) (when (string-match if-with-else-pattern current-line) (modify-then-indent (assert (goto-regex-on-line ",[[:space:]]*do:")) (delete-char 1) (assert (goto-regex-on-line ":")) (delete-char 1) (insert "\n") (when (goto-regex-on-line-r ",[[:space:]]*else:") (delete-char 1) (insert "\n") (assert (goto-regex-on-line ":")) (delete-char 1) (insert "\n")) (goto-eol) (insert "\nend")) t))) (comment (+splitjoin/defsplit 'elixir-mode split-def (let ((function-pattern (rx (and "," (zero-or-more whitespace) "do:"))) (line (thing-at-point 'line t))) (when-let (idx (string-match function-pattern line)) (let ((beg (line-beginning-position)) (orig-line-char (- (point) (line-beginning-position)))) (save-mark-and-excursion (goto-line-char idx) (delete-char 1) (goto-line-char (string-match ":" (thing-at-point 'line t))) (delete-char 1) (insert "\n") (goto-eol) (insert "\n") (insert "end") (evil-indent beg (+ (line-end-position) 1)))) (goto-line-char orig-line-char) t)))) (+splitjoin/defjoin 'elixir-mode join-if-with-else (let* ((current-line (thing-at-point 'line))))) (provide 'splitjoin)