Merge pull request #1 from jmaschme/emacs

First cut at emacs auto-complete support
This commit is contained in:
Hackerpilot 2013-08-09 18:44:38 -07:00
commit e858ae2268
2 changed files with 244 additions and 0 deletions

View File

@ -1 +1,16 @@
#EMACS Integration
This is a first cut at emacs support. It is far from complete.
##Requirements
* You must have the [auto-complete](https://github.com/auto-complete/auto-complete) package
* This integration will not automatically start dcd-server, so you'll have to do hat yourself.
* Make sure dcd-client is in your path
* Add the following to your .emacs
(require 'ac-dcd)
(add-to-list 'ac-modes 'd-mode)
(defun ac-d-mode-setup ()
(setq ac-sources (append '(ac-source-dcd) ac-sources))
(global-auto-complete-mode t))
(add-hook 'd-mode-hook 'ac-d-mode-setup)

229
editors/emacs/ac-dcd.el Normal file
View File

@ -0,0 +1,229 @@
;;; ac-dcd.el --- Auto Completion source for dcd for GNU Emacs
;; 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:
;;
;; Auto Completion source for dcd. This code was modified from ac-dscanner.el,
;; which originally came from auto-complete-clang-async.el
;;; Code:
(provide 'ac-dcd)
(require 'auto-complete)
(defcustom ac-dcd-executable
(executable-find "dcd-client")
"*Location of dcd-client executable"
:group 'auto-complete
:type 'file)
;;; Extra compilation flags to pass to dcd.
(defcustom ac-dcd-flags nil
"Extra flags to pass to the Dcd executable.
This variable will typically contain include paths, e.g., ( \"-I~/MyProject\", \"-I.\" )."
:group 'auto-complete
:type '(repeat (string :tag "Argument" "")))
(defconst ac-dcd-completion-pattern
"^\\(%s[^\s\n]*\\)[ \t]+[cisuvmkfgepM]")
(defconst ac-dcd-error-buffer-name "*dcd error*")
(defun ac-dcd-parse-output (prefix)
(goto-char (point-min))
(let ((pattern (format ac-dcd-completion-pattern
(regexp-quote prefix)))
lines match detailed_info
(prev-match ""))
(while (re-search-forward pattern nil t)
(setq match (match-string-no-properties 1))
(unless (string= "Pattern" match)
(setq detailed_info (match-string-no-properties 2))
(if (string= match prev-match)
(progn
(when detailed_info
(setq match (propertize match
'ac-dcd-help
(concat
(get-text-property 0 'ac-dcd-help (car lines))
"\n"
detailed_info)))
(setf (car lines) match)))
(setq prev-match match)
(when detailed_info
(setq match (propertize match 'ac-dcd-help detailed_info)))
(push match lines))))
lines))
(defun ac-dcd-handle-error (res args)
(goto-char (point-min))
(let* ((buf (get-buffer-create ac-dcd-error-buffer-name))
(cmd (concat ac-dcd-executable " " (mapconcat 'identity args " ")))
(pattern (format ac-dcd-completion-pattern ""))
(err (if (re-search-forward pattern nil t)
(buffer-substring-no-properties (point-min)
(1- (match-beginning 0)))
;; Warn the user more agressively if no match was found.
(message "dcd-client failed with error %d:\n%s" res cmd)
(buffer-string))))
(with-current-buffer buf
(let ((inhibit-read-only t))
(erase-buffer)
(insert (current-time-string)
(format "\ndcd-client failed with error %d:\n" res)
cmd "\n\n")
(insert err)
(setq buffer-read-only t)
(goto-char (point-min))))))
(defun ac-dcd-call-process (prefix &rest args)
(let ((buf (get-buffer-create "*dcd-output*"))
res)
(with-current-buffer buf (erase-buffer))
(setq res (apply 'call-process-region (point-min) (point-max)
ac-dcd-executable nil buf nil args))
(with-current-buffer buf
(unless (eq 0 res)
(ac-dcd-handle-error res args))
;; Still try to get any useful input.
(ac-dcd-parse-output prefix))))
(defsubst ac-dcd-build-complete-args (pos)
(append '()
'("-c")
(list (format "%s" pos))
ac-dcd-flags))
(defsubst ac-dcd-clean-document (s)
(when s
(setq s (replace-regexp-in-string "<#\\|#>\\|\\[#" "" s))
(setq s (replace-regexp-in-string "#\\]" " " s)))
s)
(defun ac-dcd-document (item)
(if (stringp item)
(let (s)
(setq s (get-text-property 0 'ac-dcd-help item))
(ac-dcd-clean-document s))))
(defsubst ac-in-string/comment ()
"Return non-nil if point is in a literal (a comment or string)."
(nth 8 (syntax-ppss)))
(defun ac-dcd-candidate ()
(unless (ac-in-string/comment)
(save-restriction
(widen)
(apply 'ac-dcd-call-process
ac-prefix
(ac-dcd-build-complete-args (point))))))
(defvar ac-template-start-point nil)
(defvar ac-template-candidates (list "ok" "no" "yes:)"))
(defun ac-dcd-action ()
(interactive)
(let ((help (ac-dcd-clean-document (get-text-property 0 'ac-dcd-help (cdr ac-last-completion))))
(raw-help (get-text-property 0 'ac-dcd-help (cdr ac-last-completion)))
(candidates (list)) ss fn args (ret-t "") ret-f)
(setq ss (split-string raw-help "\n"))
(dolist (s ss)
(when (string-match "\\[#\\(.*\\)#\\]" s)
(setq ret-t (match-string 1 s)))
(setq s (replace-regexp-in-string "\\[#.*?#\\]" "" s)))
(cond (candidates
(setq candidates (delete-dups candidates))
(setq candidates (nreverse candidates))
(setq ac-template-candidates candidates)
(setq ac-template-start-point (point))
(ac-complete-template)
(unless (cdr candidates) ;; unless length > 1
(message (replace-regexp-in-string "\n" " ; " help))))
(t
(message (replace-regexp-in-string "\n" " ; " help))))))
(defun ac-dcd-prefix ()
(or (ac-prefix-symbol)
(let ((c (char-before)))
(when (or (eq ?\. c)
(and (eq ?> c)
(eq ?- (char-before (1- (point)))))
(and (eq ?: c)
(eq ?: (char-before (1- (point))))))
(point)))))
(ac-define-source dcd
'((candidates . ac-dcd-candidate)
(prefix . ac-dcd-prefix)
(requires . 0)
(document . ac-dcd-document)
(action . ac-dcd-action)
(cache)))
(defun ac-dcd-same-count-in-string (c1 c2 s)
(let ((count 0) (cur 0) (end (length s)) c)
(while (< cur end)
(setq c (aref s cur))
(cond ((eq c1 c)
(setq count (1+ count)))
((eq c2 c)
(setq count (1- count))))
(setq cur (1+ cur)))
(= count 0)))
(defun ac-dcd-split-args (s)
(let ((sl (split-string s ", *")))
(cond ((string-match "<\\|(" s)
(let ((res (list)) (pre "") subs)
(while sl
(setq subs (pop sl))
(unless (string= pre "")
(setq subs (concat pre ", " subs))
(setq pre ""))
(cond ((and (ac-dcd-same-count-in-string ?\< ?\> subs)
(ac-dcd-same-count-in-string ?\( ?\) subs))
(push subs res))
(t
(setq pre subs))))
(nreverse res)))
(t
sl))))
(defun ac-template-candidate ()
ac-template-candidates)
(defun ac-template-action ()
(interactive)
(unless (null ac-template-start-point)
(let ((pos (point)) sl (snp "")
(s (get-text-property 0 'raw-args (cdr ac-last-completion)))))))
(defun ac-template-prefix ()
ac-template-start-point)
;; this source shall only be used internally.
(ac-define-source template
'((candidates . ac-template-candidate)
(prefix . ac-template-prefix)
(requires . 0)
(action . ac-template-action)
(document . ac-dcd-document)
(cache)
(symbol . "t")))
;;; auto-complete-dcd.el ends here