;;; go-eldoc.el --- eldoc for go-mode -*- lexical-binding: t; -*- ;; Copyright (C) 2017 by Syohei YOSHIDA ;; Author: Syohei YOSHIDA ;; URL: https://github.com/syohex/emacs-go-eldoc ;; Package-Version: 20170305.627 ;; Version: 0.30 ;; Package-Requires: ((emacs "24.3") (go-mode "1.0.0")) ;; 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 . ;;; Commentary: ;; `go-eldoc.el' provides eldoc for Go language. `go-eldoc.el' shows type information ;; for variable, functions and current argument position of function. ;; To use this package, add these lines to your init.el file: ;; ;; (require 'go-eldoc) ;; (add-hook 'go-mode-hook 'go-eldoc-setup) ;; ;;; Code: (require 'cl-lib) (require 'eldoc) (require 'go-mode) (require 'thingatpt) (defgroup go-eldoc nil "Eldoc for golang" :group 'go :prefix "go-eldoc-") (defcustom go-eldoc-gocode "gocode" "gocode path" :type 'string) (defcustom go-eldoc-gocode-args nil "Additional arguments to pass to `gocode'" :type '(repeat string)) (defvar go-eldoc--builtins '(("append" . "append,,func(slice []Type, elems ...Type) []Type") ("close" . "close,,func(c chan<- Type)") ("delete" . "delete,,func(m map[Type]Type1, key Type)") ("panic" . "panic,,func(v interface{})") ("recover" . "recover,,func() interface{}") ("complex" . "complex,,func(r, i FloatType) ComplexType") ("imag" . "imag,,func(c ComplexType) FloatType") ("real" . "real,,func(c ComplexType) FloatType") ("new" . "new,,func(Type) *Type") ("cap" . "cap,,func(v Type) int") ("copy" . "copy,,func(dst, src []Type) int") ("len" . "len,,func(v Type) int"))) (defun go-eldoc--current-arg-index (curpoint) (save-excursion (let ((count 1) (start-level (go-paren-level))) (while (search-forward "," curpoint t) (when (and (not (go-in-string-or-comment-p)) (= start-level (1- (go-paren-level)))) (cl-incf count))) count))) (defun go-eldoc--count-string (str from to) (goto-char from) (cl-loop while (search-forward str to t) unless (go-in-string-or-comment-p) counting 1)) (defun go-eldoc--inside-funcall-p (from to) (save-excursion (let ((left-paren (go-eldoc--count-string "(" from to)) (right-paren (go-eldoc--count-string ")" from to))) (> left-paren right-paren)))) (defsubst go-eldoc--goto-opening-parenthesis () (and (ignore-errors (backward-up-list) t) (eql (char-after) ?\())) (defun go-eldoc--inside-anon-function-p (from to) (save-excursion (goto-char to) (when (go-eldoc--goto-opening-parenthesis) (when (char-equal (char-after) ?\{) (let ((func-start (point)) (case-fold-search nil)) (goto-char from) (re-search-forward "\\ index 0) (let ((highlighed-args (go-eldoc--highlight-index-position arg-type index))) (concat highlighed-args " " ret-type)) (let ((highlighed-rets (go-eldoc--highlight-index-position ret-type (- index) t))) (concat "(" arg-type ") " highlighed-rets)))))) (defun go-eldoc--analyze-func-signature () (let (arg-start arg-end) (when (search-forward "func(" nil t) (setq arg-start (point)) (backward-char 1) (when (ignore-errors (forward-list) t) (setq arg-end (1- (point))) (skip-chars-forward " \t") (list :type 'function :arg-type (buffer-substring-no-properties arg-start arg-end) :ret-type (buffer-substring-no-properties (point) (point-max))))))) (defun go-eldoc--analyze-type-signature () (when (search-forward "type " nil t) (list :type 'type :real-type (buffer-substring-no-properties (point) (point-max))))) (defun go-eldoc--analyze-signature (signature) (with-temp-buffer (set-syntax-table go-mode-syntax-table) (insert signature) (goto-char (point-min)) (let ((word (thing-at-point 'word))) (cond ((string= "func" word) (go-eldoc--analyze-func-signature)) ((string= "type" word) (go-eldoc--analyze-type-signature)))))) (defun go-eldoc--format-signature (funcinfo) (let ((name (plist-get funcinfo :name)) (signature (go-eldoc--analyze-signature (plist-get funcinfo :signature))) (index (plist-get funcinfo :index))) (when signature (cl-case (plist-get signature :type) (function (format "%s: %s" (propertize name 'face 'font-lock-function-name-face) (go-eldoc--highlight-argument signature index))) (type (format "%s: %s" (propertize name 'face 'font-lock-type-face) (plist-get signature :real-type))))))) (defun go-eldoc--retrieve-type (typeinfo symbol) (let ((case-fold-search nil)) (cond ((string-match (format "^%s,,var \\(.+\\)$" symbol) typeinfo) (match-string-no-properties 1 typeinfo)) ((string-match-p (format "\\`%s,,package\\s-*$" symbol) typeinfo) "package") ((string-match (format "^%s,,\\(func.+\\)$" symbol) typeinfo) (match-string-no-properties 1 typeinfo)) ((string-match (format "^%s,,\\(.+\\)$" symbol) typeinfo) (match-string-no-properties 1 typeinfo))))) (defun go-eldoc--get-cursor-info (bounds) (save-excursion (goto-char (cdr bounds)) (go-eldoc--retrieve-type (go-eldoc--invoke-autocomplete) (buffer-substring-no-properties (car bounds) (cdr bounds))))) (defun go-eldoc--retrieve-concrete-name (bounds) (save-excursion (goto-char (car bounds)) (while (looking-back "\\." (1- (point))) (backward-char 1) (skip-chars-backward "[:word:][:multibyte:]\\[\\]")) (buffer-substring-no-properties (point) (cdr bounds)))) (defun go-eldoc--bounds-of-go-symbol () (save-excursion (let (start) (skip-chars-backward "[:word:][:multibyte:]") (setq start (point)) (skip-chars-forward "[:word:][:multibyte:]") (unless (= start (point)) (cons start (point)))))) (defsubst go-eldoc--propertize-cursor-thing (bounds) (propertize (go-eldoc--retrieve-concrete-name bounds) 'face 'font-lock-variable-name-face)) (defun go-eldoc--documentation-function () (let ((funcinfo (go-eldoc--get-funcinfo))) (if funcinfo (go-eldoc--format-signature funcinfo) (let ((bounds (go-eldoc--bounds-of-go-symbol))) (when bounds (let ((curinfo (go-eldoc--get-cursor-info bounds))) (when curinfo (format "%s: %s" (go-eldoc--propertize-cursor-thing bounds) curinfo)))))))) ;;;###autoload (defun go-eldoc-setup () "Set up eldoc function and enable eldoc-mode." (interactive) (setq-local eldoc-documentation-function #'go-eldoc--documentation-function) (eldoc-mode +1)) (provide 'go-eldoc) ;;; go-eldoc.el ends here