Skip to content

Instantly share code, notes, and snippets.

@syohex
Created Sep 3, 2012
Embed
What would you like to do?
auto-complete for JSX
;;; ac-jsx.el --- auto-complete for JSX
;; Copyright (C) 2012 by Syohei YOSHIDA
;; Author: Syohei YOSHIDA <syohex@gmail.com>
;; URL:
;; 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/>.
;;; Commentary:
;;; Code:
(eval-when-compile
(require 'cl))
(require 'json)
(defgroup ac-jsx nil
"auto-complete for jsx"
:group 'jsx
:prefix "ac-jsx:")
(defcustom ac-jsx:function-format "%t\n(%f:%l)\n\n%d"
"format of function"
:type 'string
:group 'jsx)
(defcustom ac-jsx:variable-format "Class: %c\n(%f:%l)"
"format of variable"
:type 'string
:group 'jsx)
(defvar ac-jsx:command (or (and (boundp 'jsx-cmd) jsx-cmd) "jsx"))
(defun ac-jsx:complete-command (tempfile)
(let ((file (buffer-file-name)))
(format "%s --input-filename %s --complete %d:%d '%s'"
ac-jsx:command file
(line-number-at-pos) (ac-jsx:current-char-number) tempfile)))
(defun ac-jsx:current-char-number ()
(length (buffer-substring-no-properties
(line-beginning-position) (1+ (point)))))
(defun ac-jsx:write-temp-file ()
(let ((temp (concat (make-temp-name "jsx") ".jsx")))
(let ((str (buffer-substring-no-properties (point-min) (point-max))))
(with-temp-file temp
(insert str))
(expand-file-name temp))))
(defun ac-jsx:decode-json-buffer ()
(let ((str (buffer-substring-no-properties (point-min) (point-max))))
(json-read-from-string str)))
(defun ac-jsx:args-to-string (args)
(mapconcat 'identity
(loop for arg in args
collect (format "%s:%s" (car arg) (cdr arg)))
" "))
(defun ac-jsx:format-table (compinfo)
(mapcar (lambda (e)
(let* ((v (assoc-default (cdr e) compinfo)))
`(,(car e) . ,(typecase v
(number (number-to-string v))
(vector (if (zerop (length v))
"void"
(ac-jsx:args-to-string (aref v 0))))
(t v)))))
'(("%a" . args)
("%c" . definedClass)
("%d" . doc)
("%f" . definedFilename)
("%l" . definedLineNumber)
("%r" . returnType)
("%t" . type)
("%w" . word))))
(defun ac-jsx:fill-template (tmpl tbl)
(let ((replaced tmpl)
(value nil))
(dolist (pair tbl replaced)
(setq value (or (cdr pair) ""))
(setq replaced (replace-regexp-in-string (car pair) value replaced)))))
(defun ac-jsx:format-document (compinfo)
(let ((tbl (ac-jsx:format-table compinfo))
(tmpl (if (assoc-default 'args compinfo)
ac-jsx:function-format
ac-jsx:variable-format)))
(ac-jsx:fill-template tmpl tbl)))
(defun ac-jsx:completion-at-point ()
(let* ((tempfile (ac-jsx:write-temp-file))
(cmd (ac-jsx:complete-command tempfile)))
(with-temp-buffer
(let (retval)
(unwind-protect
(setq retval (call-process-shell-command cmd nil t nil))
(delete-file tempfile))
(unless (zerop retval)
(error (format "Failed: '%s'(ret=%d)" cmd retval))))
(loop for compinfo across (ac-jsx:decode-json-buffer)
for word = (assoc-default 'word compinfo)
for doc = (ac-jsx:format-document compinfo)
for menu = (assoc-default 'menu compinfo)
collect (propertize word 'doc doc)))))
(defun ac-jsx:document (item)
(get-text-property 0 'doc item))
(defvar ac-source-jsx
'((candidates . ac-jsx:completion-at-point)
(document . ac-jsx:document)
(prefix . "\\(.*\\)")
(requires . 0))
"Source for JSX completion")
(add-hook 'jsx-mode-hook (lambda () (add-to-list 'ac-sources 'ac-source-jsx)))
(provide 'ac-jsx)
;;; ac-jsx.el ends here
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment