DCD/editors/emacs/ac-dcd.el

286 lines
9.0 KiB
EmacsLisp
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

;;; 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:
(require 'auto-complete)
(defcustom ac-dcd-executable
"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-server.
This variable will typically contain include paths, e.g., (\"-I~/MyProject\", \"-I.\").
You can't put port number flag here. Set `ac-dcd-server-port' instead."
: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*")
(defcustom ac-dcd-server-executable
"dcd-server"
"Location of dcd-server executable."
:group 'auto-complete
:type 'file)
(defcustom ac-dcd-server-port 9166
"Port number of dcd-server. default is 9166."
:group 'auto-complete)
(defvar ac-dcd-delay-after-kill-process 200
"Duration after killing server process in milli second.")
(defun ac-dcd-stop-server ()
"Stop dcd-server manually. Ordinary, you don't have to call it.
If you want to restart server, use `ac-dcd-init-server' instead."
(interactive)
(interrupt-process "dcd-server"))
(defsubst ac-dcd-start-server ()
"Start dcd-server."
(let ((buf (get-buffer-create "*dcd-server*")))
(with-current-buffer buf (start-process "dcd-server" (current-buffer)
ac-dcd-server-executable
(mapconcat 'identity ac-dcd-flags " ")
"-p"
(format "%s" ac-dcd-server-port)
))))
(defun ac-dcd-maybe-start-server ()
"Start dcd-server. When the server process is already running, do nothing."
(unless (get-process "dcd-server")
(ac-dcd-start-server)))
(defun ac-dcd-init-server ()
"Start dcd-server. When the server process is already running, restart it."
(interactive)
(when (get-process "dcd-server")
(ac-dcd-stop-server)
(sleep-for 0 ac-dcd-delay-after-kill-process))
(ac-dcd-start-server))
(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 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)
(list
"-c"
(format "%s" pos)
"-p"
(format "%s" ac-dcd-server-port)
))
(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)
(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)
(symbol . "D")))
(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")))
(provide 'ac-dcd)
;;; ac-dcd.el ends here