192 lines
		
	
	
	
		
			5.6 KiB
		
	
	
	
		
			EmacsLisp
		
	
	
	
	
	
			
		
		
	
	
			192 lines
		
	
	
	
		
			5.6 KiB
		
	
	
	
		
			EmacsLisp
		
	
	
	
	
	
| ;;; private/grfn/splitjoin.el -*- 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)
 |