| 1 | ;;; anything-c-yasnippet.el --- anything config for yasnippet.el |
|---|
| 2 | |
|---|
| 3 | ;; Author: Kenji.I (Kenji Imakado) <ken.imakaado@gmail.com> |
|---|
| 4 | ;; Version: 0.3 |
|---|
| 5 | ;; Keywords: anything yasnippet |
|---|
| 6 | |
|---|
| 7 | ;; This file 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 2, or (at your option) |
|---|
| 10 | ;; any later version. |
|---|
| 11 | |
|---|
| 12 | ;; This file is distributed in the hope that it will be useful, |
|---|
| 13 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 14 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 15 | ;; GNU General Public License for more details. |
|---|
| 16 | |
|---|
| 17 | ;; You should have received a copy of the GNU General Public License |
|---|
| 18 | ;; along with GNU Emacs; see the file COPYING. If not, write to the |
|---|
| 19 | ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
|---|
| 20 | ;; Boston, MA 02110-1301, USA. |
|---|
| 21 | |
|---|
| 22 | ;;; Commentary: |
|---|
| 23 | |
|---|
| 24 | ;; anything-source name => anything-c-source-yasnippet |
|---|
| 25 | ;; |
|---|
| 26 | ;; Actions: Insert snippet, Open snippet file, Open snippet file other window |
|---|
| 27 | ;; C-z: execute-persistent-action |
|---|
| 28 | |
|---|
| 29 | ;; here's my yasnippet's configuration |
|---|
| 30 | ;; (require 'yasnippet) |
|---|
| 31 | ;; (require 'anything-c-yasnippet) |
|---|
| 32 | ;; (setq anything-c-yas-space-match-any-greedy t) ;[default: nil] |
|---|
| 33 | ;; (global-set-key (kbd "C-c y") 'anything-c-yas-complete) |
|---|
| 34 | ;; (yas/initialize) |
|---|
| 35 | ;; (yas/load-directory "<path>/<to>/snippets/") |
|---|
| 36 | ;; (add-to-list 'yas/extra-mode-hooks 'ruby-mode-hook) |
|---|
| 37 | ;; (add-to-list 'yas/extra-mode-hooks 'cperl-mode-hook) |
|---|
| 38 | |
|---|
| 39 | (require 'cl) |
|---|
| 40 | (require 'anything) |
|---|
| 41 | (require 'yasnippet) |
|---|
| 42 | |
|---|
| 43 | ;;; Compatibility code |
|---|
| 44 | ;; written by rubikitch see http://d.hatena.ne.jp/rubikitch/20071228/anythingpersistent (japanese) |
|---|
| 45 | (unless (fboundp 'anything-execute-persistent-action) |
|---|
| 46 | (defun anything-execute-persistent-action () |
|---|
| 47 | "If a candidate was selected then perform the associated action without quitting anything." |
|---|
| 48 | (interactive) |
|---|
| 49 | (save-selected-window |
|---|
| 50 | (select-window (get-buffer-window anything-buffer)) |
|---|
| 51 | (select-window (setq minibuffer-scroll-window |
|---|
| 52 | (if (one-window-p t) (split-window) (next-window (selected-window) 1)))) |
|---|
| 53 | (let* ((anything-window (get-buffer-window anything-buffer)) |
|---|
| 54 | (selection (if anything-saved-sources |
|---|
| 55 | ;; the action list is shown |
|---|
| 56 | anything-saved-selection |
|---|
| 57 | (anything-get-selection))) |
|---|
| 58 | (default-action (anything-get-action)) |
|---|
| 59 | (action (assoc-default 'persistent-action (anything-get-current-source)))) |
|---|
| 60 | (setq action (or action default-action)) |
|---|
| 61 | (if (and (listp action) |
|---|
| 62 | (not (functionp action))) ; lambda |
|---|
| 63 | ;; select the default action |
|---|
| 64 | (setq action (cdar action))) |
|---|
| 65 | (set-window-dedicated-p anything-window t) |
|---|
| 66 | (unwind-protect |
|---|
| 67 | (and action selection (funcall action selection)) |
|---|
| 68 | (set-window-dedicated-p anything-window nil)))))) |
|---|
| 69 | |
|---|
| 70 | (define-key anything-map "\C-z" 'anything-execute-persistent-action) |
|---|
| 71 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
|---|
| 72 | |
|---|
| 73 | ;;; Code |
|---|
| 74 | (defvar anything-c-yas-version "0.3" "Version of anything-c-yasnippet") |
|---|
| 75 | |
|---|
| 76 | (defgroup anything-c-yasnippet nil |
|---|
| 77 | "anything config yasnippet") |
|---|
| 78 | |
|---|
| 79 | (defcustom anything-c-yas-not-display-dups t |
|---|
| 80 | "if non-nil not display duplicate snippet otherwise display all snippet" |
|---|
| 81 | :type 'boolean |
|---|
| 82 | :group 'anything-c-yasnippet) |
|---|
| 83 | |
|---|
| 84 | (defcustom anything-c-yas-display-msg-after-complete t |
|---|
| 85 | "if non-nil display snippet key message in minibuffer after Complete" |
|---|
| 86 | :type 'boolean |
|---|
| 87 | :group 'anything-c-yasnippet) |
|---|
| 88 | |
|---|
| 89 | ;(defvar anything-c-yas-snippets-dir-list nil) ;;customizable list of path(string) for finding snippet file in Action |
|---|
| 90 | (defcustom anything-c-yas-snippets-dir-list nil |
|---|
| 91 | "list of directory used to find snippet file" |
|---|
| 92 | :type '(repeat (directory |
|---|
| 93 | :tag "snippet-directory")) |
|---|
| 94 | :group 'anything-c-yasnippet) |
|---|
| 95 | |
|---|
| 96 | (defcustom anything-c-yas-space-match-any-greedy nil |
|---|
| 97 | "if non-nil anything pattern space match anyword greedy. |
|---|
| 98 | pattern regexp: \"if else\" replace to \"if.*else\" |
|---|
| 99 | match \"if (...) { ... } else { ... }\" and \"if, elsif, else ...\" |
|---|
| 100 | quite convenience |
|---|
| 101 | Default: nil" |
|---|
| 102 | :type 'boolean |
|---|
| 103 | :group 'anything-c-yasnippet) |
|---|
| 104 | |
|---|
| 105 | |
|---|
| 106 | (defvar anything-c-yas-snippets-dir-list nil) |
|---|
| 107 | (defadvice yas/load-directory-1 (around anything-yas-build-alist activate) |
|---|
| 108 | (let ((directory (ad-get-arg 0))) |
|---|
| 109 | (when (stringp directory) |
|---|
| 110 | (add-to-list 'anything-c-yas-snippets-dir-list directory))) |
|---|
| 111 | ad-do-it) |
|---|
| 112 | |
|---|
| 113 | |
|---|
| 114 | (defun anything-c-yas-create-new-snippet (selected-text) |
|---|
| 115 | (let* ((mode-name (symbol-name anything-c-yas-cur-major-mode)) |
|---|
| 116 | (root-dir (expand-file-name yas/root-directory)) |
|---|
| 117 | (default-snippet-dir (anything-c-yas-find-recursively mode-name root-dir 'dir)) |
|---|
| 118 | (dir (read-file-name "create snippet : " default-snippet-dir default-snippet-dir))) |
|---|
| 119 | (condition-case e |
|---|
| 120 | (progn (when (file-exists-p dir) |
|---|
| 121 | (error "can't create file [%s] already exists" (file-name-nondirectory dir))) |
|---|
| 122 | ;; create buffer, insert template |
|---|
| 123 | (find-file dir) |
|---|
| 124 | (insert "#name : \n# --\n " selected-text)) |
|---|
| 125 | (message "%s" (error-message-string e))))) |
|---|
| 126 | |
|---|
| 127 | (defun anything-c-yas-find-recursively (regexp &optional directory predicate) |
|---|
| 128 | (let ((directory (or directory default-directory)) |
|---|
| 129 | (predfunc (case predicate |
|---|
| 130 | (dir 'file-directory-p) |
|---|
| 131 | (file 'file-regular-p) |
|---|
| 132 | (otherwise 'identity))) |
|---|
| 133 | (files (remove-if (lambda (s) (string-match "^\\." (file-name-nondirectory s))) (directory-files directory t))) |
|---|
| 134 | (found nil) |
|---|
| 135 | (result nil)) |
|---|
| 136 | (loop for file in files |
|---|
| 137 | unless found |
|---|
| 138 | do (if (and (funcall predfunc file) |
|---|
| 139 | (string-match regexp file)) |
|---|
| 140 | (progn (setq found t) |
|---|
| 141 | (return (file-name-as-directory file))) |
|---|
| 142 | (when (file-directory-p file) |
|---|
| 143 | (setq result (anything-c-yas-find-recursively regexp file predicate)))) |
|---|
| 144 | finally (return result)))) |
|---|
| 145 | |
|---|
| 146 | (defun anything-c-yas-build-cur-snippets-alist (&optional table) |
|---|
| 147 | (let* ((result-alist '((candidates) (transformed) (template-key-alist))) |
|---|
| 148 | (hash-value-alist nil) |
|---|
| 149 | (cur-table (or table (yas/snippet-table anything-c-yas-cur-major-mode))) |
|---|
| 150 | (parent-table (yas/snippet-table-parent cur-table)) ;`yas/snippet-table-parent' |
|---|
| 151 | (hash-table (yas/snippet-table-hash cur-table))) ;`yas/snippet-table-hash' |
|---|
| 152 | (maphash (lambda (k v) (setq hash-value-alist (append v hash-value-alist))) hash-table) |
|---|
| 153 | (loop with transformed |
|---|
| 154 | with templates |
|---|
| 155 | with template-key-alist |
|---|
| 156 | for lst in hash-value-alist |
|---|
| 157 | for key = (car lst) |
|---|
| 158 | for template-struct = (cdr lst) |
|---|
| 159 | for name = (yas/template-name template-struct) ;`yas/template-name' |
|---|
| 160 | for template = (yas/template-content template-struct) ;`yas/template-content' |
|---|
| 161 | do (progn (push template templates) |
|---|
| 162 | (push `(,name . ,template) transformed) |
|---|
| 163 | (push `(,template . ,key) template-key-alist)) |
|---|
| 164 | finally (progn (push `(candidates . ,templates) result-alist) |
|---|
| 165 | (push `(transformed . ,transformed) result-alist) |
|---|
| 166 | (push `(template-key-alist . ,template-key-alist) result-alist))) |
|---|
| 167 | ;; if cur-table has parent build recursively |
|---|
| 168 | (when parent-table |
|---|
| 169 | (let ((rec-ret (anything-c-yas-build-cur-snippets-alist parent-table)) |
|---|
| 170 | (alist-keys '(candidates transformed template-key-alist))) |
|---|
| 171 | (mapc (lambda (key) |
|---|
| 172 | (let ((res-list (assq key result-alist)) |
|---|
| 173 | (rec-val (assoc-default key rec-ret))) |
|---|
| 174 | (setcdr res-list (nconc rec-val (cdr res-list))))) |
|---|
| 175 | alist-keys))) |
|---|
| 176 | result-alist)) |
|---|
| 177 | |
|---|
| 178 | (defun anything-c-yas-get-modes () |
|---|
| 179 | (let ((cur-major-mode anything-c-yas-cur-major-mode)) |
|---|
| 180 | (list cur-major-mode))) |
|---|
| 181 | |
|---|
| 182 | (defun anything-c-yas-get-cmp-context () |
|---|
| 183 | "Return list (initial-input point-start point-end) |
|---|
| 184 | like `yas/current-key'" |
|---|
| 185 | (let ((start (point)) |
|---|
| 186 | (end (point)) |
|---|
| 187 | (syntax "w_")) |
|---|
| 188 | (condition-case nil |
|---|
| 189 | (save-excursion |
|---|
| 190 | (when mark-active |
|---|
| 191 | (error "")) |
|---|
| 192 | (skip-syntax-backward syntax) |
|---|
| 193 | (setq start (point)) |
|---|
| 194 | (values (buffer-substring-no-properties start end) start end)) |
|---|
| 195 | (error (values "" (point) (point)))))) |
|---|
| 196 | |
|---|
| 197 | (defun anything-c-yas-get-key-by-template (template alist) ;str template |
|---|
| 198 | "Return key" |
|---|
| 199 | (assoc-default template (assoc-default 'template-key-alist alist))) |
|---|
| 200 | |
|---|
| 201 | (defun anything-c-yas-get-candidates (alist) |
|---|
| 202 | "Return list of template" |
|---|
| 203 | (assoc-default 'candidates alist 'eq)) |
|---|
| 204 | |
|---|
| 205 | (defun anything-c-yas-get-transformed-list (alist initial-input) |
|---|
| 206 | "Return list of dotlist, (DISPLAY . REAL) DISPLAY is name of snippet, REAL is template of snippet" |
|---|
| 207 | (let ((transformed-list (assoc-default 'transformed alist 'eq))) |
|---|
| 208 | (setq transformed-list (remove-if-not (lambda (lst) |
|---|
| 209 | (string-match (concat "^" (regexp-quote initial-input)) (car lst))) |
|---|
| 210 | transformed-list)) |
|---|
| 211 | (when anything-c-yas-not-display-dups |
|---|
| 212 | (setq transformed-list (delete-dups transformed-list))) |
|---|
| 213 | ;; sort |
|---|
| 214 | (setq transformed-list (sort* transformed-list 'string< :key 'car)) |
|---|
| 215 | transformed-list)) |
|---|
| 216 | |
|---|
| 217 | (defun anything-c-yas-find-snippet-file-by-key (key) |
|---|
| 218 | (let ((modes (anything-c-yas-get-modes)) |
|---|
| 219 | (snippet-dirs (add-to-list 'anything-c-yas-snippets-dir-list yas/root-directory))) |
|---|
| 220 | (let ((found-path (loop for mode in modes |
|---|
| 221 | for test-re = (concat (symbol-name mode) "/" key "$") |
|---|
| 222 | for path = (anything-c-yas-find-snippet-file-aux test-re snippet-dirs) |
|---|
| 223 | when path return path))) |
|---|
| 224 | ;; if not found in major-mode try to find in all dirs |
|---|
| 225 | (unless found-path |
|---|
| 226 | (setq found-path (anything-c-yas-find-snippet-file-aux (concat "/" key "$") snippet-dirs))) |
|---|
| 227 | found-path))) |
|---|
| 228 | |
|---|
| 229 | (defun anything-c-yas-find-snippet-file-aux (test-re dirs) |
|---|
| 230 | (loop with done |
|---|
| 231 | with path |
|---|
| 232 | for directory in dirs |
|---|
| 233 | for files = (directory-files directory t) |
|---|
| 234 | unless done |
|---|
| 235 | do (loop for file in files |
|---|
| 236 | when (string-match test-re file) |
|---|
| 237 | return (setq done t |
|---|
| 238 | path file)) |
|---|
| 239 | finally return path)) |
|---|
| 240 | |
|---|
| 241 | (defun anything-c-yas-find-file-snippet-by-template (template &optional other-window) |
|---|
| 242 | (let* ((path (anything-c-yas-get-path-by-template template)) |
|---|
| 243 | (ff-func (if other-window 'find-file-other-window 'find-file))) |
|---|
| 244 | (if path |
|---|
| 245 | (funcall ff-func path) |
|---|
| 246 | (message "not found snippet file")))) |
|---|
| 247 | |
|---|
| 248 | (defun anything-c-yas-get-path-by-template (template) |
|---|
| 249 | (let* ((key (anything-c-yas-get-key-by-template template anything-c-yas-cur-snippets-alist)) |
|---|
| 250 | (path (anything-c-yas-find-snippet-file-by-key key))) |
|---|
| 251 | path)) |
|---|
| 252 | |
|---|
| 253 | (defun anything-c-yas-match (candidate) |
|---|
| 254 | "if customize variable `anything-c-yas-space-match-any-greedy' is non-nil |
|---|
| 255 | space match anyword greedy" |
|---|
| 256 | (cond |
|---|
| 257 | (anything-c-yas-space-match-any-greedy |
|---|
| 258 | (let ((re (replace-regexp-in-string "[ \t]+" ".*" anything-pattern))) |
|---|
| 259 | (string-match re candidate))) |
|---|
| 260 | (t |
|---|
| 261 | (string-match anything-pattern candidate)))) |
|---|
| 262 | |
|---|
| 263 | (defvar anything-c-yas-cur-snippets-alist nil) |
|---|
| 264 | (defvar anything-c-yas-initial-input "") |
|---|
| 265 | (defvar anything-c-yas-point-start nil) |
|---|
| 266 | (defvar anything-c-yas-point-end nil) |
|---|
| 267 | (defvar anything-c-yas-cur-major-mode nil) |
|---|
| 268 | (defvar anything-c-yas-selected-text "" "region text if mark-active otherwise \"\"") |
|---|
| 269 | (defvar anything-c-source-yasnippet |
|---|
| 270 | `((name . "Yasnippet") |
|---|
| 271 | (init . (lambda () |
|---|
| 272 | (setq anything-c-yas-cur-major-mode major-mode) |
|---|
| 273 | (setq anything-c-yas-selected-text (if mark-active (buffer-substring-no-properties (region-beginning) (region-end)) "")) |
|---|
| 274 | (multiple-value-setq |
|---|
| 275 | (anything-c-yas-initial-input anything-c-yas-point-start anything-c-yas-point-end) (anything-c-yas-get-cmp-context)) ;return values(str point point) |
|---|
| 276 | (setq anything-c-yas-cur-snippets-alist (anything-c-yas-build-cur-snippets-alist)))) |
|---|
| 277 | ;; (candidates . (lambda () |
|---|
| 278 | ;; (anything-c-yas-get-candidates anything-c-yas-cur-snippets-alist))) |
|---|
| 279 | (candidates . (anything-c-yas-get-candidates anything-c-yas-cur-snippets-alist)) |
|---|
| 280 | (candidate-transformer . (lambda (candidates) |
|---|
| 281 | (anything-c-yas-get-transformed-list anything-c-yas-cur-snippets-alist anything-c-yas-initial-input))) |
|---|
| 282 | (action . (("Insert snippet" . (lambda (template) |
|---|
| 283 | (yas/expand-snippet anything-c-yas-point-start anything-c-yas-point-end template) |
|---|
| 284 | (when anything-c-yas-display-msg-after-complete |
|---|
| 285 | (message "this snippet is bound to [ %s ]" |
|---|
| 286 | (anything-c-yas-get-key-by-template template anything-c-yas-cur-snippets-alist))))) |
|---|
| 287 | ("Open snippet file" . (lambda (template) |
|---|
| 288 | (anything-c-yas-find-file-snippet-by-template template))) |
|---|
| 289 | ("Open snippet file other window" . (lambda (template) |
|---|
| 290 | (anything-c-yas-find-file-snippet-by-template template t))) |
|---|
| 291 | ("Create new snippet on region" . (lambda (template) |
|---|
| 292 | (anything-c-yas-create-new-snippet anything-c-yas-selected-text))) |
|---|
| 293 | ("Reload All Snippts" . (lambda (template) |
|---|
| 294 | (yas/reload-all) |
|---|
| 295 | (message "Reload All Snippts done"))) |
|---|
| 296 | ("Rename snippet file" . (lambda (template) |
|---|
| 297 | (let* ((path (or (anything-c-yas-get-path-by-template template) "")) |
|---|
| 298 | (dir (file-name-directory path)) |
|---|
| 299 | (filename (file-name-nondirectory path)) |
|---|
| 300 | (rename-to (read-string (concat "rename [" filename "] to: ")))) |
|---|
| 301 | (rename-file path (concat dir rename-to)) |
|---|
| 302 | (yas/reload-all)))) |
|---|
| 303 | ("Delete snippet file" . (lambda (template) |
|---|
| 304 | (let ((path (or (anything-c-yas-get-path-by-template template) ""))) |
|---|
| 305 | (when (y-or-n-p "really delete?") |
|---|
| 306 | (delete-file path) |
|---|
| 307 | (yas/reload-all))))))) |
|---|
| 308 | (persistent-action . (lambda (template) |
|---|
| 309 | (anything-c-yas-find-file-snippet-by-template template))) |
|---|
| 310 | (match . (anything-c-yas-match)))) |
|---|
| 311 | |
|---|
| 312 | |
|---|
| 313 | ;;; Commands |
|---|
| 314 | (defun anything-c-yas-complete () |
|---|
| 315 | (interactive) |
|---|
| 316 | (let ((anything-sources (list anything-c-source-yasnippet))) |
|---|
| 317 | (anything))) |
|---|
| 318 | |
|---|
| 319 | (defun anything-c-yas-create-snippet-on-regin (&optional start end) |
|---|
| 320 | (interactive "r") |
|---|
| 321 | (let ((str (buffer-substring-no-properties start end))) |
|---|
| 322 | (anything-c-yas-create-new-snippet str))) |
|---|
| 323 | |
|---|
| 324 | |
|---|
| 325 | (provide 'anything-c-yasnippet) |
|---|
| 326 | ;; anything-c-yasnippet.el ends here |
|---|