Merge pull request #144 from tsukimizake/master

Update emacs integration
This commit is contained in:
Brian Schott 2014-07-04 22:13:31 +00:00
commit 71b3b5f890
2 changed files with 100 additions and 55 deletions

View File

@ -2,12 +2,12 @@
##Requirements ##Requirements
* You must have the [auto-complete](https://github.com/auto-complete/auto-complete) package. * You must have the [auto-complete](https://github.com/auto-complete/auto-complete) package.
And [yasnippet](https://github.com/capitaomorte/yasnippet) package is recommended. [yasnippet](https://github.com/capitaomorte/yasnippet) and [popwin](https://github.com/m2ym/popwin-el) is recommended.
* Make sure dcd-client and dcd-server is in your exec-path. Otherwise, please set the variable ```dcd-exectutable``` and ```dcd-server-executable``` using ```M-x customize```. * Make sure dcd-client and dcd-server is in your exec-path. Otherwise, please set the variable ```dcd-exectutable``` and ```dcd-server-executable``` using ```M-x customize```.
## Setup ## Setup
* First, follow the Setup section in the root README. * First, follow the Setup section in the root README.
* Second, add the following to your .emacs. With this setting, dcd-server starts automatically when you open file in d-mode. * Second, add the following to your .emacs. With this setting, dcd-server starts automatically when you open file in d-mode. (Of course, you should edit ```path_to_ac-dcd.el``` to suit your enviroment.)
``` ```
;;; ac-dcd ;;; ac-dcd
@ -20,9 +20,13 @@ And [yasnippet](https://github.com/capitaomorte/yasnippet) package is recommende
(ac-dcd-maybe-start-server) (ac-dcd-maybe-start-server)
(add-to-list 'ac-sources 'ac-source-dcd))) (add-to-list 'ac-sources 'ac-source-dcd)))
(define-key d-mode-map (kbd "C-c ?") 'ac-dcd-popup-ddoc-at-point) (define-key d-mode-map (kbd "C-c ?") 'ac-dcd-show-ddoc-with-buffer)
(define-key d-mode-map (kbd "C-c .") 'ac-dcd-goto-definition) (define-key d-mode-map (kbd "C-c .") 'ac-dcd-goto-definition)
(define-key d-mode-map (kbd "C-c ,") 'ac-dcd-goto-def-pop-marker) (define-key d-mode-map (kbd "C-c ,") 'ac-dcd-goto-def-pop-marker)
(when (featurep 'popwin)
(add-to-list 'popwin:special-display-config
`(,ac-dcd-document-buffer-name :position right :width 80)))
``` ```
* Third, set import path using ```M-x customize-variable RET ac-dcd-flags```. * Third, set import path using ```M-x customize-variable RET ac-dcd-flags```.

View File

@ -24,7 +24,7 @@
(require 'auto-complete) (require 'auto-complete)
(require 'rx) (require 'rx)
(require 'yasnippet) (require 'yasnippet)
(require 'eshell)
(defcustom ac-dcd-executable (defcustom ac-dcd-executable
"dcd-client" "dcd-client"
"Location of dcd-client executable." "Location of dcd-client executable."
@ -44,8 +44,9 @@ You can't put port number flag here. Set `ac-dcd-server-port' instead."
"Regex to parse dcd output. "Regex to parse dcd output.
\\1 is candidate itself, \\2 is kind of candidate.") \\1 is candidate itself, \\2 is kind of candidate.")
(defconst ac-dcd-error-buffer-name "*dcd error*") (defconst ac-dcd-error-buffer-name "*dcd-error*")
(defconst ac-dcd-output-buffer-name "*dcd-output*")
(defconst ac-dcd-document-buffer-name "*dcd-document*")
(defcustom ac-dcd-server-executable (defcustom ac-dcd-server-executable
"dcd-server" "dcd-server"
"Location of dcd-server executable." "Location of dcd-server executable."
@ -57,7 +58,8 @@ You can't put port number flag here. Set `ac-dcd-server-port' instead."
:group 'auto-complete) :group 'auto-complete)
(defvar ac-dcd-delay-after-kill-process 200 (defvar ac-dcd-delay-after-kill-process 200
"Duration after killing server process in milli second.") "Duration after killing server process in milli second.
If `ac-dcd-init-server' doesn't work correctly, please set bigger number for this variable.")
;;server handle functions ;;server handle functions
@ -147,7 +149,7 @@ If you want to restart server, use `ac-dcd-init-server' instead."
;; utility functions to call process ;; utility functions to call process
(defun ac-dcd-call-process (prefix args) (defun ac-dcd-call-process (prefix args)
(let ((buf (get-buffer-create "*dcd-output*")) (let ((buf (get-buffer-create ac-dcd-output-buffer-name))
res) res)
(with-current-buffer buf (erase-buffer)) (with-current-buffer buf (erase-buffer))
(setq res (apply 'call-process-region (point-min) (point-max) (setq res (apply 'call-process-region (point-min) (point-max)
@ -232,9 +234,8 @@ TODO: multi byte character support"
(let ((lastcompl (cdr ac-last-completion))) (let ((lastcompl (cdr ac-last-completion)))
(cond (cond
((equal (get-text-property 0 'ac-dcd-help lastcompl) "f") ; when it was a function ((equal "f" (get-text-property 0 'ac-dcd-help lastcompl)) ; when it was a function
(progn (progn
(insert "(") ;dcd-client requires open parenthesis to complete calltip.
(ac-complete-dcd-calltips))) (ac-complete-dcd-calltips)))
(t nil) (t nil)
)))) ))))
@ -256,16 +257,24 @@ TODO: multi byte character support"
"Do calltip completion of the D symbol at point. "Do calltip completion of the D symbol at point.
The cursor must be at the end of a D symbol. The cursor must be at the end of a D symbol.
When the symbol is not a function, returns nothing" When the symbol is not a function, returns nothing"
(let ((buf (get-buffer-create "*dcd-output*"))) (let ((buf (get-buffer-create ac-dcd-output-buffer-name)))
(ac-dcd-call-process-for-calltips) (ac-dcd-call-process-for-calltips)
(with-current-buffer buf (ac-dcd-parse-calltips)) (with-current-buffer buf (ac-dcd-parse-calltips))
)) ))
(defun ac-dcd-call-process-for-calltips () (defun ac-dcd-call-process-for-calltips ()
"Call process to get calltips of the function at point." "Call process to get calltips of the function at point."
(insert "( ;")
(backward-char 2)
(ac-dcd-call-process (ac-dcd-call-process
(concat (cdr ac-last-completion) "(") (concat (cdr ac-last-completion) "(")
(ac-dcd-build-complete-args (ac-dcd-cursor-position)))) (ac-dcd-build-complete-args (ac-dcd-cursor-position)))
(forward-char 2)
(delete-char -3)
)
(defconst ac-dcd-calltip-pattern (defconst ac-dcd-calltip-pattern
@ -273,17 +282,31 @@ When the symbol is not a function, returns nothing"
"Regexp to parse calltip completion output. "Regexp to parse calltip completion output.
\\1 is function return type (if exists) and name, and \\2 is args.") \\1 is function return type (if exists) and name, and \\2 is args.")
(defsubst ac-dcd-remove-function-return-type (s) (defsubst ac-dcd-cleanup-function-candidate (s)
"Remove return type of the function." "Remove return type of the head of the function.
(let ((sl (split-string s))) `S' is candidate string."
(if (string-match "(" (car sl)) ; (let (res)
s (with-temp-buffer
(mapconcat 'identity (cdr sl) " ") (insert s)
;;goto beggining of function name
(progn
(end-of-line)
(backward-sexp)
(re-search-backward (rx (or bol " "))))
(setq res (buffer-substring
(point)
(progn
(end-of-line)
(point))))
(when (equal " " (substring res 0 1))
(setq res (substring res 1)))
res
))) )))
(defun ac-dcd-parse-calltips () (defun ac-dcd-parse-calltips ()
"Parse dcd output for calltips completion. "Parse dcd output for calltip completion.
It returns a list of calltip candidates." It returns a list of calltip candidates."
(goto-char (point-min)) (goto-char (point-min))
(let ((pattern ac-dcd-calltip-pattern) (let ((pattern ac-dcd-calltip-pattern)
@ -291,7 +314,10 @@ It returns a list of calltip candidates."
match match
(prev-match "")) (prev-match ""))
(while (re-search-forward pattern nil t) (while (re-search-forward pattern nil t)
(setq match (ac-dcd-remove-function-return-type (concat (match-string-no-properties 1) (match-string-no-properties 2)))) (setq match
(ac-dcd-cleanup-function-candidate
(concat (match-string-no-properties 1) (match-string-no-properties 2))
))
(push match lines)) (push match lines))
lines lines
)) ))
@ -303,10 +329,9 @@ This function should be called at *dcd-output* buf."
(save-excursion (save-excursion
(setq end (point)) (setq end (point))
(setq beg (progn (setq beg (progn
;; find the end of function name. some function arguments have parenthesis of its own, (backward-sexp)
;; so I had to do it like this. (point)
(search-backward (cdr ac-last-completion)) ))
(- (search-forward "(" ) 1)))
(kill-region beg end)) (kill-region beg end))
(let ((str (car kill-ring)) (let ((str (car kill-ring))
yasstr) yasstr)
@ -324,7 +349,8 @@ This function should be called at *dcd-output* buf."
;; ;;debug ;; ;;debug
;; (message (format "str: %s" str)) ;; (message (format "str: %s" str))
;; (message (format "yasstr: %s" yasstr)) ;; (message (format "yasstr: %s" yasstr))
(yas-expand-snippet yasstr)))) (yas-expand-snippet yasstr)
)))
(defun ac-dcd-calltip-prefix () (defun ac-dcd-calltip-prefix ()
(car ac-last-completion)) (car ac-last-completion))
@ -339,40 +365,55 @@ This function should be called at *dcd-output* buf."
;;show document ;;show document
(defun ac-dcd-reformat-document ()
"Currently, it just decodes \n and \\n."
(with-current-buffer (get-buffer ac-dcd-document-buffer-name)
;;doit twice to catch '\n\n'
(goto-char (point-min))
(while (re-search-forward (rx (and (not (any "\\")) (submatch "\\n"))) nil t)
(replace-match "\n" nil nil nil 1))
(goto-char (point-min))
(while (re-search-forward (rx (and (not (any "\\")) (submatch "\\n"))) nil t)
(replace-match "\n" nil nil nil 1))
;; replace '\\n' in D src to '\n'
(while (re-search-forward (rx "\\\\n") nil t)
(replace-match "\\\\n"))
))
(defun ac-dcd-get-ddoc (pos) (defun ac-dcd-get-ddoc (pos)
"Get document with `dcd-client --doc'. `POS' is cursor position. "Get document with `dcd-client --doc'. `POS' is cursor position."
TODO:reformat it."
(save-buffer) (save-buffer)
(let ((args (let ((args
(append (append
(ac-dcd-build-complete-args (ac-dcd-cursor-position)) (ac-dcd-build-complete-args (ac-dcd-cursor-position))
'("--doc") '("-d")
(list (buffer-file-name)))) (list (buffer-file-name))))
(buf (get-buffer-create "*dcd-output*"))) (buf (get-buffer-create ac-dcd-document-buffer-name)))
(with-current-buffer
buf (erase-buffer)
(apply 'call-process ac-dcd-executable nil buf nil args))
(let ((raw-doc (with-current-buffer buf (buffer-string))))
;; ;; TODO: format document string.
;; (setq raw-doc (replace-regexp-in-string
;; (rx (and (not (any "\\\\")) (submatch "\\n")))
;; " " raw-doc nil nil 1 nil)); replace \n with space
;; ;; (setq raw-doc (replace-regexp-in-string
;; ;; (rx "\\n") "\n" raw-doc));replace \\n(RET in D src) with \n
;; (setq raw-doc (replace-regexp-in-string
;; (rx (and "$(D" (submatch (* anything)) ")"))
;; " `\\1' " raw-doc)) ;format D src
raw-doc)))
(defun ac-dcd-popup-ddoc-at-point () ;; If I use `call-process', dcd-client errors out when to get long doc(e.g. doc of writef).
"Popup Ddoc at point using popup.el." ;; I have no idea why.
(interactive) (with-current-buffer buf
(let ((doc (ac-dcd-get-ddoc (ac-dcd-cursor-position)))) (erase-buffer)
(when (or(string= doc "") (eshell-command
(string= doc "\n\n\n") ;when symbol has no doc (mapconcat 'identity `(,(executable-find ac-dcd-executable) ,@args) " ")
t)
(when (or
(string= (buffer-string) "")
(string= (buffer-string) "\n\n\n") ;when symbol has no doc
) )
(message "No document for the symbol at point!")) (error "No document for the symbol at point!"))
(popup-tip doc))) (buffer-string)
)))
(defun ac-dcd-show-ddoc-with-buffer ()
"Display Ddoc at point using `display-buffer'."
(interactive)
(ac-dcd-get-ddoc (ac-dcd-cursor-position))
(ac-dcd-reformat-document)
(display-buffer (get-buffer-create ac-dcd-document-buffer-name)))
;; goto definition ;; goto definition
@ -431,9 +472,9 @@ TODO:reformat it."
(let ((args (let ((args
(append (append
(ac-dcd-build-complete-args (ac-dcd-cursor-position)) (ac-dcd-build-complete-args (ac-dcd-cursor-position))
'("--symbolLocation") '("-l")
(list (buffer-file-name)))) (list (buffer-file-name))))
(buf (get-buffer-create "*dcd-output*"))) (buf (get-buffer-create ac-dcd-output-buffer-name)))
(with-current-buffer (with-current-buffer
buf (erase-buffer) buf (erase-buffer)
(apply 'call-process ac-dcd-executable nil buf nil args)) (apply 'call-process ac-dcd-executable nil buf nil args))
@ -444,7 +485,7 @@ TODO:reformat it."
"Parse output of `ac-dcd-get-symbol-declaration'. "Parse output of `ac-dcd-get-symbol-declaration'.
output is just like following.\n output is just like following.\n
`(cons \"PATH_TO_IMPORT/import/std/stdio.d\" \"63946\")'" `(cons \"PATH_TO_IMPORT/import/std/stdio.d\" \"63946\")'"
(let ((buf (get-buffer-create "*dcd-output*"))) (let ((buf (get-buffer-create ac-dcd-output-buffer-name)))
(with-current-buffer buf (with-current-buffer buf
(goto-char (point-min)) (goto-char (point-min))
(if (not (string= "Not found\n" (buffer-string))) (if (not (string= "Not found\n" (buffer-string)))