diff --git a/modules/feature/snippets/autoload.el b/modules/feature/snippets/autoload.el new file mode 100644 index 000000000..6cdd8f747 --- /dev/null +++ b/modules/feature/snippets/autoload.el @@ -0,0 +1,86 @@ +;;; autoload.el + +;;;###autoload +(defun +snippets/expand-on-region () + "Switch to insert mode when expanding a template via region selection, or go +back to normal mode if there are no fields." + (interactive) + (when (evil-visual-state-p) + (let ((end (region-end))) + (evil-visual-select + (region-beginning) + (if (eq evil-this-type 'line) end (1+ end)) + 'inclusive))) + (yas-insert-snippet) + (let* ((snippet (first (yas--snippets-at-point))) + (fields (yas--snippet-fields snippet))) + (evil-insert-state +1) + (unless fields (evil-change-state 'normal)))) + +;;;###autoload +(defun +snippets/goto-start-of-field () + "Go to the beginning of the current field." + (interactive) + (let* ((snippet (car (yas--snippets-at-point))) + (position (yas--field-start (yas--snippet-active-field snippet)))) + (if (= (point) position) + (move-beginning-of-line 1) + (goto-char position)))) + +;;;###autoload +(defun +snippets/goto-end-of-field () + "Go to the end of the current field." + (interactive) + (let* ((snippet (car (yas--snippets-at-point))) + (position (yas--field-end (yas--snippet-active-field snippet)))) + (if (= (point) position) + (move-end-of-line 1) + (goto-char position)))) + +;;;###autoload +(defun +snippets/delete-backward-char (&optional field) + "Prevents Yas from interfering with backspace deletion." + (interactive) + (let ((field (or field (and yas--active-field-overlay + (overlay-buffer yas--active-field-overlay) + (overlay-get yas--active-field-overlay 'yas--field))))) + (cond ((eq (point) (marker-position (yas--field-start field))) nil) + (t (delete-char -1))))) + +;;;###autoload +(defun +snippets/delete-forward-char-or-field (&optional field) + "Delete forward, or skip the current field if it's empty. This is to prevent +buggy behavior when is pressed in an empty field." + (interactive) + (let ((field (or field (and yas--active-field-overlay + (overlay-buffer yas--active-field-overlay) + (overlay-get yas--active-field-overlay 'yas--field))))) + (cond ((and field + (not (yas--field-modified-p field)) + (eq (point) (marker-position (yas--field-start field)))) + (yas--skip-and-clear field) + (yas-next-field 1)) + ((eq (point) (marker-position (yas--field-end field))) nil) + (t (delete-char 1))))) + +;;;###autoload +(defun +snippets/delete-to-start-of-field (&optional field) + "Delete to start-of-field." + (interactive) + (let* ((field (or field (and yas--active-field-overlay + (overlay-buffer yas--active-field-overlay) + (overlay-get yas--active-field-overlay 'yas--field)))) + (sof (marker-position (yas--field-start field)))) + (when (and field (> (point) sof)) + (delete-region sof (point))))) + +;;;###autoload +(defun +snippets/find-file () + "Browse through snippets folder" + (interactive) + (projectile-find-file-in-directory (car yas-snippet-dirs))) + +;; TODO move this to ivy +;;;###autoload +(defun +snippets/ivy-prompt (prompt choices &optional display-fn) + (yas-completing-prompt prompt choices display-fn #'ivy-completing-read)) diff --git a/modules/feature/snippets/config.el b/modules/feature/snippets/config.el new file mode 100644 index 000000000..b486b473f --- /dev/null +++ b/modules/feature/snippets/config.el @@ -0,0 +1,71 @@ +;;; feature/snippets/config.el + +(@def-package yasnippet + :commands (yas-minor-mode + yas-minor-mode-on + yas-expand + yas-insert-snippet + yas-new-snippet + yas-visit-snippet-file) + :preface + (defvar yas-minor-mode-map (make-sparse-keymap)) + :init + (setq yas-verbosity 0 + yas-indent-line 'auto + yas-also-auto-indent-first-line t + yas-prompt-functions '(yas-completing-prompt yas-ido-prompt yas-no-prompt) + yas-snippet-dirs '(yas-installed-snippets-dir)) + + (@add-hook (text-mode prog-mode snippet-mode markdown-mode org-mode) + 'yas-minor-mode-on) + + :config + (yas-reload-all) + (@map (:map yas-keymap + "C-e" '+snippets/goto-end-of-field + "C-a" '+snippets/goto-start-of-field + "" '+snippets/goto-end-of-field + "" '+snippets/goto-start-of-field + "" 'yas-prev-field + "" '+snippets/delete-to-start-of-field + "" 'evil-normal-state + [backspace] '+snippets/delete-backward-char + "" '+snippets/delete-forward-char-or-field) + + (:map yas-minor-mode-map + :i [tab] 'yas-expand + :v [tab] '+snippets/expand-on-region)) + + ;; Fix an error caused by smartparens interfering with yasnippet bindings + (advice-add 'yas-expand :before 'sp-remove-active-pair-overlay) + + ;; Exit snippets on ESC in normal mode + (advice-add 'evil-force-normal-state :before 'yas-exit-all-snippets) + + ;; Once you're in normal mode, you're out + (add-hook 'evil-normal-state-entry-hook 'yas-abort-snippet) + + ;; Strip out whitespace before a line selection + (defun +snippets|yas-before-expand () + "Strip out the shitespace before a line selection." + (when (and (evil-visual-state-p) + (eq (evil-visual-type) 'line)) + (setq-local + yas-selected-text + (replace-regexp-in-string + "\\(^ *\\|\n? $\\)" "" + (buffer-substring-no-properties (region-beginning) + (1- (region-end))))))) + (add-hook 'yas-before-expand-snippet-hook '+snippets|yas-before-expand) + + (defun +snippets|yas-after-expand () + "Fix previous hook persisting yas-selected-text between expansions." + (setq yas-selected-text nil)) + (add-hook 'yas-after-exit-snippet-hook '+snippets|yas-after-expand)) + + +(@def-package auto-yasnippet + :commands (aya-create aya-expand aya-open-line aya-persist-snippet) + :config + (setq aya-persist-snippets-dir (concat doom-local-dir "auto-snippets/"))) + diff --git a/modules/feature/snippets/packages.el b/modules/feature/snippets/packages.el new file mode 100644 index 000000000..fe4cbe54c --- /dev/null +++ b/modules/feature/snippets/packages.el @@ -0,0 +1,6 @@ +;; -*- no-byte-compile: t; -*- +;;; feature/snippets/packages.el + +(@package yasnippet) +(@package auto-yasnippet) +