dotemacs

My Emacs configuration
git clone git://git.entf.net/dotemacs
Log | Files | Refs | LICENSE

commit af85a1f93e4e98c715c2d374b6dd950a63056965
parent 1066c18e3a6503114f7dc89387d0f5b12b5f66e2
Author: Lukas Henkel <lh@entf.net>
Date:   Sat, 26 Mar 2022 20:58:32 +0100

Update corfu and go-mode

Diffstat:
Delpa/corfu-0.17.signed | 2--
Delpa/corfu-0.17/README.org | 227-------------------------------------------------------------------------------
Delpa/corfu-0.17/corfu-autoloads.el | 60------------------------------------------------------------
Delpa/corfu-0.17/corfu-pkg.el | 2--
Delpa/corfu-0.17/corfu.el | 1214-------------------------------------------------------------------------------
Delpa/corfu-0.17/corfu.elc | 0
Delpa/corfu-0.17/corfu.info | 327-------------------------------------------------------------------------------
Aelpa/corfu-0.20.signed | 2++
Relpa/corfu-0.17/LICENSE -> elpa/corfu-0.20/LICENSE | 0
Aelpa/corfu-0.20/README.org | 466+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/corfu-0.20/corfu-autoloads.el | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/corfu-0.20/corfu-pkg.el | 2++
Aelpa/corfu-0.20/corfu.el | 1278+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/corfu-0.20/corfu.elc | 0
Aelpa/corfu-0.20/corfu.info | 611+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Relpa/corfu-0.17/dir -> elpa/corfu-0.20/dir | 0
Delpa/go-mode-1.5.0/go-mode-autoloads.el | 107-------------------------------------------------------------------------------
Delpa/go-mode-1.5.0/go-mode-pkg.el | 2--
Delpa/go-mode-1.5.0/go-mode.el | 2011-------------------------------------------------------------------------------
Delpa/go-mode-1.5.0/go-mode.elc | 0
Aelpa/go-mode-1.6.0.signed | 2++
Aelpa/go-mode-1.6.0/.github/workflows/emacs.yml | 17+++++++++++++++++
Aelpa/go-mode-1.6.0/.mailmap | 1+
Aelpa/go-mode-1.6.0/AUTHORS | 50++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/go-mode-1.6.0/AUTHORS.old | 31+++++++++++++++++++++++++++++++
Aelpa/go-mode-1.6.0/LICENSE | 27+++++++++++++++++++++++++++
Aelpa/go-mode-1.6.0/NEWS | 134+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/go-mode-1.6.0/README.md | 178+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/go-mode-1.6.0/go-guru.el | 561+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/go-mode-1.6.0/go-guru.elc | 0
Aelpa/go-mode-1.6.0/go-mode-autoloads.el | 213+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/go-mode-1.6.0/go-mode-pkg.el | 2++
Aelpa/go-mode-1.6.0/go-mode.el | 3047+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/go-mode-1.6.0/go-mode.elc | 0
Aelpa/go-mode-1.6.0/go-rename.el | 110+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/go-mode-1.6.0/go-rename.elc | 0
Aelpa/go-mode-1.6.0/test/go-comment-test.el | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/go-mode-1.6.0/test/go-comment-test.elc | 0
Aelpa/go-mode-1.6.0/test/go-fill-paragraph-test.el | 224+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/go-mode-1.6.0/test/go-fill-paragraph-test.elc | 0
Aelpa/go-mode-1.6.0/test/go-font-lock-test.el | 290+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/go-mode-1.6.0/test/go-font-lock-test.elc | 0
Aelpa/go-mode-1.6.0/test/go-indentation-test.el | 124+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/go-mode-1.6.0/test/go-indentation-test.elc | 0
Aelpa/go-mode-1.6.0/test/testdata/behaviour_tests/gh-16.go | 6++++++
Aelpa/go-mode-1.6.0/test/testdata/indentation_tests/comments.go | 7+++++++
Aelpa/go-mode-1.6.0/test/testdata/indentation_tests/composite_literal.go | 34++++++++++++++++++++++++++++++++++
Aelpa/go-mode-1.6.0/test/testdata/indentation_tests/composite_literal_key.go | 5+++++
Aelpa/go-mode-1.6.0/test/testdata/indentation_tests/dangling_decls.go | 47+++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/go-mode-1.6.0/test/testdata/indentation_tests/dangling_operator.go | 294+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/go-mode-1.6.0/test/testdata/indentation_tests/function_call.go | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/go-mode-1.6.0/test/testdata/indentation_tests/gh-10.go | 6++++++
Aelpa/go-mode-1.6.0/test/testdata/indentation_tests/gh-11.go | 7+++++++
Aelpa/go-mode-1.6.0/test/testdata/indentation_tests/gh-13.go | 9+++++++++
Aelpa/go-mode-1.6.0/test/testdata/indentation_tests/gh-14.go | 7+++++++
Aelpa/go-mode-1.6.0/test/testdata/indentation_tests/gh-15.go | 12++++++++++++
Aelpa/go-mode-1.6.0/test/testdata/indentation_tests/gh-9.go | 7+++++++
Aelpa/go-mode-1.6.0/test/testdata/indentation_tests/go.mod | 19+++++++++++++++++++
Aelpa/go-mode-1.6.0/test/testdata/indentation_tests/labels.go | 20++++++++++++++++++++
Aelpa/go-mode-1.6.0/test/testdata/indentation_tests/multiline_comment.go | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/go-mode-1.6.0/test/testdata/indentation_tests/multiline_if.go | 152+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/go-mode-1.6.0/test/testdata/indentation_tests/multiline_string.go | 9+++++++++
Aelpa/go-mode-1.6.0/test/testdata/indentation_tests/multiline_struct.go | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/go-mode-1.6.0/test/testdata/indentation_tests/numeric_literals.go | 12++++++++++++
Aelpa/go-mode-1.6.0/test/testdata/indentation_tests/return_function_call_struct.go | 11+++++++++++
Aelpa/go-mode-1.6.0/test/testdata/indentation_tests/switch.go | 76++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/go-mode-1.6.0/test/testdata/indentation_tests/unnamed_method_receiver.go | 8++++++++
Aelpa/go-mode-1.6.0/test/testdata/movement_tests/functions.go | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
68 files changed, 8486 insertions(+), 3952 deletions(-)

diff --git a/elpa/corfu-0.17.signed b/elpa/corfu-0.17.signed @@ -1 +0,0 @@ -Good signature from 066DAFCB81E42C40 GNU ELPA Signing Agent (2019) <elpasign@elpa.gnu.org> (trust undefined) created at 2021-12-31T11:05:02+0100 using RSA -\ No newline at end of file diff --git a/elpa/corfu-0.17/README.org b/elpa/corfu-0.17/README.org @@ -1,227 +0,0 @@ -#+title: corfu.el - Completion Overlay Region FUnction -#+author: Daniel Mendler -#+language: en -#+export_file_name: corfu.texi -#+texinfo_dir_category: Emacs -#+texinfo_dir_title: Corfu: (corfu). -#+texinfo_dir_desc: Completion Overlay Region FUnction - -#+html: <a href="https://www.gnu.org/software/emacs/"><img alt="GNU Emacs" src="https://github.com/minad/corfu/blob/screenshots/emacs.svg?raw=true"/></a> -#+html: <a href="http://elpa.gnu.org/packages/corfu.html"><img alt="GNU ELPA" src="https://elpa.gnu.org/packages/corfu.svg"/></a> -#+html: <a href="http://elpa.gnu.org/devel/corfu.html"><img alt="GNU-devel ELPA" src="https://elpa.gnu.org/devel/corfu.svg"/></a> - -* Introduction - - Corfu enhances the default completion in region function with a completion - overlay. The current candidates are shown in a popup below or above the point. - Corfu is the minimalistic ~completion-in-region~ counterpart of the [[https://github.com/minad/vertico][Vertico]] - minibuffer UI. - - Corfu is a minimal package, which relies on the Emacs completion facilities and - concentrates on providing a polished completion UI. Completions are either - provided by commands like ~dabbrev-completion~ or by pluggable backends - (~completion-at-point-functions~, Capfs). Most programming language major modes - implement a Capf. Furthermore the language server packages, [[https://github.com/joaotavora/eglot][Eglot]] and [[https://github.com/emacs-lsp/lsp-mode][Lsp-mode]], - both use Capfs which talk to the LSP server to retrieve the completions. - - Corfu does not include custom completion backends. In contrast, the complex - Company package includes custom completion backends, which deviate from the - Emacs completion infrastructure. The Emacs built-in Capfs are mostly - sufficient, but a few additional Capfs and completion functions are provided - by the [[https://github.com/minad/cape][Cape]] package. - - *NOTE*: Corfu uses child frames to show the popup. For now Corfu falls back to - the default setting of the ~completion-in-region-function~ on non-graphical - displays. You may want to use ~consult-completion-in-region~. - - [[https://github.com/minad/corfu/blob/screenshots/light.png?raw=true]] - - [[https://github.com/minad/corfu/blob/screenshots/dark.png?raw=true]] - -* Features - - - Timer-based auto-completions (/off/ by default, set ~corfu-auto~). - - Popup display with scrollbar indicator and arrow key navigation. - - The popup can be summoned explicitly by pressing =TAB= at any time. - - The current candidate is inserted with =TAB= and selected with =RET=. - - Candidates sorting by prefix, string length and alphabetically. - - The selected candidate is previewed (configuable via ~corfu-preview-current~). - - The selected candidate automatically committed on further input by default - (configurable via ~corfu-commit-predicate~). - - The [[https://github.com/oantolin/orderless][Orderless]] completion style is supported. The filter string can contain - arbitrary characters, including spaces, if ~corfu-quit-at-boundary~ is nil. - - Deferred completion style highlighting for performance. - - Jumping to location/documentation of current candidate. - - Show candidate documentation/signature string in the echo area. - - Deprecated candidates are crossed out in the display. - - Support for annotations (~annotation-function~, ~affixation-function~). - - Icons can be provided by an external package via margin formatter functions. - -* Installation and Configuration - - Corfu is available from [[http://elpa.gnu.org/packages/corfu.html][GNU ELPA]], such that it can be installed directly via - ~package-install~. After installation, the global minor mode can be enabled with - =M-x corfu-global-mode=. In order to configure Corfu and other packages in your - init.el, you may want to use ~use-package~. - - Corfu is highly flexible and customizable via ~corfu-*~ customization variables. - For filtering I recommend to give Orderless completion a try, which is - different from the familiar prefix TAB completion. Corfu can be used with the - default completion styles, the use of Orderless is not a necessity. See also - the [[https://github.com/minad/corfu/wiki][Corfu Wiki]] for additional configuration tips. In particular the Lsp-mode - configuration is documented in the Wiki. - - Here is an example configuration: - - #+begin_src emacs-lisp - (use-package corfu - ;; Optional customizations - ;; :custom - ;; (corfu-cycle t) ;; Enable cycling for `corfu-next/previous' - ;; (corfu-auto t) ;; Enable auto completion - ;; (corfu-commit-predicate nil) ;; Do not commit selected candidates on next input - ;; (corfu-quit-at-boundary t) ;; Automatically quit at word boundary - ;; (corfu-quit-no-match t) ;; Automatically quit if there is no match - ;; (corfu-preview-current nil) ;; Disable current candidate preview - ;; (corfu-preselect-first nil) ;; Disable candidate preselection - ;; (corfu-echo-documentation nil) ;; Disable documentation in the echo area - ;; (corfu-scroll-margin 5) ;; Use scroll margin - - ;; You may want to enable Corfu only for certain modes. - ;; :hook ((prog-mode . corfu-mode) - ;; (shell-mode . corfu-mode) - ;; (eshell-mode . corfu-mode)) - - ;; Recommended: Enable Corfu globally. - ;; This is recommended since dabbrev can be used globally (M-/). - :init - (corfu-global-mode)) - - ;; Optionally use the `orderless' completion style. See `+orderless-dispatch' - ;; in the Consult wiki for an advanced Orderless style dispatcher. - ;; Enable `partial-completion' for files to allow path expansion. - ;; You may prefer to use `initials' instead of `partial-completion'. - (use-package orderless - :init - ;; Configure a custom style dispatcher (see the Consult wiki) - ;; (setq orderless-style-dispatchers '(+orderless-dispatch) - ;; orderless-component-separator #'orderless-escapable-split-on-space) - (setq completion-styles '(orderless) - completion-category-defaults nil - completion-category-overrides '((file (styles . (partial-completion)))))) - - ;; Use dabbrev with Corfu! - (use-package dabbrev - ;; Swap M-/ and C-M-/ - :bind (("M-/" . dabbrev-completion) - ("C-M-/" . dabbrev-expand))) - - ;; A few more useful configurations... - (use-package emacs - :init - ;; TAB cycle if there are only few candidates - (setq completion-cycle-threshold 3) - - ;; Emacs 28: Hide commands in M-x which do not apply to the current mode. - ;; Corfu commands are hidden, since they are not supposed to be used via M-x. - ;; (setq read-extended-command-predicate - ;; #'command-completion-default-include-p) - - ;; Enable indentation+completion using the TAB key. - ;; `completion-at-point' is often bound to M-TAB. - (setq tab-always-indent 'complete)) - #+end_src - - See also the [[https://github.com/minad/corfu/wiki][Corfu Wiki]] for additional configuration tips. For more general - documentation read the chapter about completion in the [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Completion.html][Emacs manual]]. If you - want to create your own Capfs, you can find documentation about completion in - the [[https://www.gnu.org/software/emacs/manual/html_node/elisp/Completion.html][Elisp manual]]. - -** TAB-and-Go completion - -You may be interested in configuring Corfu in TAB-and-Go style. Pressing TAB -moves to the next candidate and further input will then commit the selection. - -#+begin_src emacs-lisp - (use-package corfu - ;; TAB-and-Go customizations - :custom - (corfu-cycle t) ;; Enable cycling for `corfu-next/previous' - (corfu-preselect-first nil) ;; Disable candidate preselection - - ;; Use TAB for cycling, default is `corfu-complete'. - :bind - (:map corfu-map - ("TAB" . corfu-next) - ([tab] . corfu-next) - ("S-TAB" . corfu-previous) - ([backtab] . corfu-previous)) - - :init - (corfu-global-mode)) -#+end_src - -* Key bindings - - Corfu uses a transient keymap ~corfu-map~ which is active while the popup is shown. - The keymap defines the following remappings and bindings: - - - ~beginning-of-buffer~ -> ~corfu-first~ - - ~end-of-buffer~ -> ~corfu-last~ - - ~scroll-down-command~ -> ~corfu-scroll-down~ - - ~scroll-up-command~ -> ~corfu-scroll-up~ - - ~next-line~, =down=, =M-n= -> ~corfu-next~ - - ~previous-line~, =up=, =M-p= -> ~corfu-previous~ - - ~completion-at-point~, =TAB= -> ~corfu-complete~ - - =RET= -> ~corfu-insert~ - - =M-g= -> ~corfu-show-location~ - - =M-h= -> ~corfu-show-documentation~ - - =C-g= -> ~corfu-quit~ - - ~keyboard-escape-quit~ -> ~corfu-reset~ - -* Complementary packages - - Corfu works well together with all packages providing code completion via the - ~completion-at-point-functions~. Many modes and packages already provide a Capf - out of the box. Nevertheless you may want to look into complementary packages - to enhance your setup. - - - [[https://github.com/oantolin/orderless][Orderless]]: Cofu supports completion styles, - including the advanced [[https://github.com/oantolin/orderless][Orderless]] completion style, where the filtering - expressions are separated by spaces (see ~corfu-quit-at-boundary~). - - - [[https://github.com/minad/cape][Cape]]: I collect additional Capf backends and =completion-in-region= commands - in my [[https://github.com/minad/cape][Cape]] package. The package provides a file path, a dabbrev completion - backend and a backend which allows you to enter unicode characters in the - form of TeX commands. Cape provides an adapter to reuse Company backends in - Corfu. Furthermore the function ~cape-super-capf~ can merge/groups multiple - Capfs, such that the candidates of multiple Capfs are displayed together at - the same time. - - - [[https://github.com/jdtsmith/kind-icon][kind-icon]]: Icons are supported by Corfu via an external package. For example - the [[https://github.com/jdtsmith/kind-icon][kind-icon]] package provides beautifully styled SVG icons based on - monochromatic icon sets like material design. - - - [[https://github.com/galeo/corfu-doc][corfu-doc]]: The corfu-doc package by @galeo allows you to display the candidate - documentation in a popup next to the Corfu popup, similar to - =company-quickhelp=. /Note that the corfu-doc package is new and still work in - progress./ - - - [[https://github.com/minad/vertico][Vertico]]: You may also want to look into my [[https://github.com/minad/vertico][Vertico]] package. Vertico is the - minibuffer completion counterpart of Corfu. - -* Caveats - - Corfu is robust in most scenarios. There are a few known technical caveats. - - - Corfu uses child frames to show the popup. For now Corfu falls back to the - default setting of the ~completion-in-region-function~ on non-graphical - displays. You may want to use ~consult-completion-in-region~. - - - Corfu does not sort by history, since ~completion-at-point~ does not - maintain a history (See branch =history= for a possible solution). - -* Contributions - - Since this package is part of [[http://elpa.gnu.org/packages/corfu.html][GNU ELPA]] contributions require a copyright - assignment to the FSF. diff --git a/elpa/corfu-0.17/corfu-autoloads.el b/elpa/corfu-0.17/corfu-autoloads.el @@ -1,60 +0,0 @@ -;;; corfu-autoloads.el --- automatically extracted autoloads -;; -;;; Code: - -(add-to-list 'load-path (directory-file-name - (or (file-name-directory #$) (car load-path)))) - - -;;;### (autoloads nil "corfu" "corfu.el" (0 0 0 0)) -;;; Generated autoloads from corfu.el - -(autoload 'corfu-mode "corfu" "\ -Completion Overlay Region FUnction - -If called interactively, enable Corfu mode if ARG is positive, -and disable it if ARG is zero or negative. If called from Lisp, -also enable the mode if ARG is omitted or nil, and toggle it if -ARG is `toggle'; disable the mode otherwise. - -\(fn &optional ARG)" t nil) - -(put 'corfu-global-mode 'globalized-minor-mode t) - -(defvar corfu-global-mode nil "\ -Non-nil if Corfu-Global mode is enabled. -See the `corfu-global-mode' command -for a description of this minor mode. -Setting this variable directly does not take effect; -either customize it (see the info node `Easy Customization') -or call the function `corfu-global-mode'.") - -(custom-autoload 'corfu-global-mode "corfu" nil) - -(autoload 'corfu-global-mode "corfu" "\ -Toggle Corfu mode in all buffers. -With prefix ARG, enable Corfu-Global mode if ARG is positive; -otherwise, disable it. If called from Lisp, enable the mode if -ARG is omitted or nil. - -Corfu mode is enabled in all buffers where -`corfu--on' would do it. -See `corfu-mode' for more information on Corfu mode. - -\(fn &optional ARG)" t nil) - -(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "corfu" '("corfu-"))) - -;;;*** - -;;;### (autoloads nil nil ("corfu-pkg.el") (0 0 0 0)) - -;;;*** - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; coding: utf-8 -;; End: -;;; corfu-autoloads.el ends here diff --git a/elpa/corfu-0.17/corfu-pkg.el b/elpa/corfu-0.17/corfu-pkg.el @@ -1,2 +0,0 @@ -;; Generated package description from corfu.el -*- no-byte-compile: t -*- -(define-package "corfu" "0.17" "Completion Overlay Region FUnction" '((emacs "27.1")) :authors '(("Daniel Mendler" . "mail@daniel-mendler.de")) :maintainer '("Daniel Mendler" . "mail@daniel-mendler.de") :url "https://github.com/minad/corfu") diff --git a/elpa/corfu-0.17/corfu.el b/elpa/corfu-0.17/corfu.el @@ -1,1214 +0,0 @@ -;;; corfu.el --- Completion Overlay Region FUnction -*- lexical-binding: t -*- - -;; Copyright (C) 2021 Free Software Foundation, Inc. - -;; Author: Daniel Mendler <mail@daniel-mendler.de> -;; Maintainer: Daniel Mendler <mail@daniel-mendler.de> -;; Created: 2021 -;; Version: 0.17 -;; Package-Requires: ((emacs "27.1")) -;; Homepage: https://github.com/minad/corfu - -;; This file is part of 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: - -;; Corfu enhances the default completion in region function with a -;; completion overlay. The current candidates are shown in a popup -;; below or above the point. Corfu can be considered the minimalistic -;; completion-in-region counterpart of Vertico. - -;;; Code: - -(require 'seq) -(eval-when-compile - (require 'cl-lib) - (require 'subr-x)) - -(defgroup corfu nil - "Completion Overlay Region FUnction." - :group 'convenience - :prefix "corfu-") - -(defcustom corfu-count 10 - "Maximal number of candidates to show." - :type 'integer) - -(defcustom corfu-scroll-margin 2 - "Number of lines at the top and bottom when scrolling. -The value should lie between 0 and corfu-count/2." - :type 'integer) - -(defcustom corfu-min-width 15 - "Popup minimum width in characters." - :type 'integer) - -(defcustom corfu-max-width 100 - "Popup maximum width in characters." - :type 'integer) - -(defcustom corfu-cycle nil - "Enable cycling for `corfu-next' and `corfu-previous'." - :type 'boolean) - -(defcustom corfu-continue-commands - ;; nil is undefined command - '(nil ignore completion-at-point universal-argument universal-argument-more digit-argument - "\\`corfu-" "\\`scroll-other-window") - "Continue Corfu completion after executing these commands." - :type '(repeat (choice regexp symbol))) - -(defcustom corfu-commit-predicate #'corfu-candidate-previewed-p - "Automatically commit if the predicate returns t." - :type '(choice (const nil) function)) - -(defcustom corfu-preview-current t - "Preview currently selected candidate." - :type 'boolean) - -(defcustom corfu-preselect-first t - "Preselect first candidate." - :type 'boolean) - -(defcustom corfu-quit-at-boundary nil - "Automatically quit at completion field/word boundary. -If automatic quitting is disabled, Orderless filter strings with spaces -are allowed." - :type 'boolean) - -(defcustom corfu-quit-no-match 1.0 - "Automatically quit if no matching candidate is found. -If a floating point number, quit on no match only if the auto-started -completion began less than that number of seconds ago." - :type '(choice boolean float)) - -(defcustom corfu-excluded-modes nil - "List of modes excluded by `corfu-global-mode'." - :type '(repeat symbol)) - -(defcustom corfu-left-margin-width 0.5 - "Width of the left margin in units of the character width." - :type 'float) - -(defcustom corfu-right-margin-width 0.5 - "Width of the right margin in units of the character width." - :type 'float) - -(defcustom corfu-bar-width 0.2 - "Width of the bar in units of the character width." - :type 'float) - -(defcustom corfu-echo-documentation 0.5 - "Show documentation string in the echo area after that number of seconds." - :type '(choice boolean float)) - -(defcustom corfu-margin-formatters nil - "Registry for margin formatter functions. -Each function of the list is called with the completion metadata as -argument until an appropriate formatter is found. The function should -return a formatter function, which takes the candidate string and must -return a string, possibly an icon." - :type 'hook) - -(defcustom corfu-sort-function #'corfu-sort-length-alpha - "Default sorting function, used if no `display-sort-function' is specified." - :type `(choice - (const :tag "No sorting" nil) - (const :tag "By length and alpha" ,#'corfu-sort-length-alpha) - (function :tag "Custom function"))) - -(defcustom corfu-auto-prefix 3 - "Minimum length of prefix for auto completion. -The completion backend can override this with -:company-prefix-length." - :type 'integer) - -(defcustom corfu-auto-delay 0.2 - "Delay for auto completion." - :type 'float) - -(defcustom corfu-auto-commands - '("self-insert-command\\'" - c-electric-colon c-electric-lt-gt c-electric-slash c-scope-operator) - "Commands which initiate auto completion." - :type '(repeat (choice regexp symbol))) - -(defcustom corfu-auto nil - "Enable auto completion." - :type 'boolean) - -(defgroup corfu-faces nil - "Faces used by Corfu." - :group 'corfu - :group 'faces) - -(defface corfu-default - '((((class color) (min-colors 88) (background dark)) :background "#191a1b") - (((class color) (min-colors 88) (background light)) :background "#f0f0f0") - (t :background "gray")) - "Default face used for the popup, in particular the background and foreground color.") - -(defface corfu-current - '((((class color) (min-colors 88) (background dark)) - :background "#00415e" :foreground "white") - (((class color) (min-colors 88) (background light)) - :background "#c0efff" :foreground "black") - (t :background "blue" :foreground "white")) - "Face used to highlight the currently selected candidate.") - -(defface corfu-bar - '((((class color) (min-colors 88) (background dark)) :background "#a8a8a8") - (((class color) (min-colors 88) (background light)) :background "#505050") - (t :background "gray")) - "The background color is used for the scrollbar indicator.") - -(defface corfu-border - '((((class color) (min-colors 88) (background dark)) :background "#323232") - (((class color) (min-colors 88) (background light)) :background "#d7d7d7") - (t :background "gray")) - "The background color used for the thin border.") - -(defface corfu-echo - '((t :inherit completions-annotations)) - "Face used for echo area messages.") - -(defface corfu-annotations - '((t :inherit completions-annotations)) - "Face used for annotations.") - -(defface corfu-deprecated - '((t :inherit shadow :strike-through t)) - "Face used for deprecated candidates.") - -(defvar corfu-map - (let ((map (make-sparse-keymap))) - (define-key map [remap beginning-of-buffer] #'corfu-first) - (define-key map [remap end-of-buffer] #'corfu-last) - (define-key map [remap scroll-down-command] #'corfu-scroll-down) - (define-key map [remap scroll-up-command] #'corfu-scroll-up) - (define-key map [remap next-line] #'corfu-next) - (define-key map [remap previous-line] #'corfu-previous) - (define-key map [remap completion-at-point] #'corfu-complete) - (define-key map [down] #'corfu-next) - (define-key map [up] #'corfu-previous) - (define-key map [remap keyboard-escape-quit] #'corfu-reset) - ;; XXX [tab] is bound because of org-mode - ;; The binding should be removed from org-mode-map. - (define-key map [tab] #'corfu-complete) - (define-key map "\en" #'corfu-next) - (define-key map "\ep" #'corfu-previous) - (define-key map "\C-g" #'corfu-quit) - (define-key map "\r" #'corfu-insert) - (define-key map "\t" #'corfu-complete) - (define-key map "\eg" #'corfu-show-location) - (define-key map "\eh" #'corfu-show-documentation) - map) - "Corfu keymap used when popup is shown.") - -(defvar corfu--auto-timer nil - "Auto completion timer.") - -(defvar-local corfu--candidates nil - "List of candidates.") - -(defvar-local corfu--metadata nil - "Completion metadata.") - -(defvar-local corfu--base 0 - "Size of the base string, which is concatenated with the candidate.") - -(defvar-local corfu--total 0 - "Length of the candidate list `corfu--candidates'.") - -(defvar-local corfu--highlight #'identity - "Deferred candidate highlighting function.") - -(defvar-local corfu--index -1 - "Index of current candidate or negative for prompt selection.") - -(defvar-local corfu--preselect -1 - "Index of preselected candidate, negative for prompt selection.") - -(defvar-local corfu--scroll 0 - "Scroll position.") - -(defvar-local corfu--input nil - "Cons of last prompt contents and point or t.") - -(defvar-local corfu--preview-ov nil - "Current candidate overlay.") - -(defvar-local corfu--extra nil - "Extra completion properties.") - -(defvar-local corfu--change-group nil - "Undo change group.") - -(defvar-local corfu--auto-start nil - "Auto completion start time.") - -(defvar-local corfu--echo-timer nil - "Echo area message timer.") - -(defvar-local corfu--echo-message nil - "Last echo message.") - -(defvar corfu--frame nil - "Popup frame.") - -(defconst corfu--state-vars - '(corfu--base - corfu--candidates - corfu--highlight - corfu--index - corfu--preselect - corfu--scroll - corfu--input - corfu--total - corfu--preview-ov - corfu--extra - corfu--auto-start - corfu--echo-timer - corfu--echo-message - corfu--change-group - corfu--metadata) - "Buffer-local state variables used by Corfu.") - -(defvar corfu--frame-parameters - '((no-accept-focus . t) - (no-focus-on-map . t) - (min-width . t) - (min-height . t) - (width . 0) - (height . 0) - (border-width . 0) - (child-frame-border-width . 1) - (left-fringe . 0) - (right-fringe . 0) - (vertical-scroll-bars . nil) - (horizontal-scroll-bars . nil) - (menu-bar-lines . 0) - (tool-bar-lines . 0) - (tab-bar-lines . 0) - (no-other-frame . t) - (no-other-window . t) - (no-delete-other-windows . t) - (unsplittable . t) - (undecorated . t) - (cursor-type . nil) - (visibility . nil) - (no-special-glyphs . t) - (desktop-dont-save . t)) - "Default child frame parameters.") - -(defvar corfu--buffer-parameters - '((mode-line-format . nil) - (header-line-format . nil) - (tab-line-format . nil) - (tab-bar-format . nil) ;; Emacs 28 tab-bar-format - (frame-title-format . "") - (truncate-lines . t) - (cursor-in-non-selected-windows . nil) - (cursor-type . nil) - (show-trailing-whitespace . nil) - (display-line-numbers . nil) - (left-fringe-width . nil) - (right-fringe-width . nil) - (left-margin-width . 0) - (right-margin-width . 0) - (fringes-outside-margins . 0) - (buffer-read-only . t)) - "Default child frame buffer parameters.") - -(defvar corfu--mouse-ignore-map - (let ((map (make-sparse-keymap))) - (dotimes (i 7) - (dolist (k '(mouse down-mouse drag-mouse double-mouse triple-mouse)) - (define-key map (vector (intern (format "%s-%s" k (1+ i)))) #'ignore))) - map) - "Ignore all mouse clicks.") - -(defun corfu--popup-redirect-focus () - "Redirect focus from popup." - (redirect-frame-focus corfu--frame (frame-parent corfu--frame))) - -(defun corfu--make-buffer (content) - "Create corfu buffer with CONTENT." - (let ((fr face-remapping-alist) - (buffer (get-buffer-create " *corfu*"))) - (with-current-buffer buffer - ;;; XXX HACK install redirect focus hook - (add-hook 'pre-command-hook #'corfu--popup-redirect-focus nil 'local) - ;;; XXX HACK install mouse ignore map - (use-local-map corfu--mouse-ignore-map) - (dolist (var corfu--buffer-parameters) - (set (make-local-variable (car var)) (cdr var))) - (setq-local face-remapping-alist (copy-tree fr)) - (cl-pushnew 'corfu-default (alist-get 'default face-remapping-alist)) - (let ((inhibit-modification-hooks t) - (inhibit-read-only t)) - (erase-buffer) - (insert content) - (goto-char (point-min)))) - buffer)) - -;; Function adapted from posframe.el by tumashu -(defun corfu--make-frame (x y width height content) - "Show child frame at X/Y with WIDTH/HEIGHT and CONTENT." - (let* ((window-min-height 1) - (window-min-width 1) - (x-gtk-resize-child-frames - (let ((case-fold-search t)) - (and - ;; XXX HACK to fix resizing on gtk3/gnome taken from posframe.el - ;; More information: - ;; * https://github.com/minad/corfu/issues/17 - ;; * https://gitlab.gnome.org/GNOME/mutter/-/issues/840 - ;; * https://lists.gnu.org/archive/html/emacs-devel/2020-02/msg00001.html - (string-match-p "gtk3" system-configuration-features) - (string-match-p "gnome\\|cinnamon" (or (getenv "XDG_CURRENT_DESKTOP") - (getenv "DESKTOP_SESSION") "")) - 'resize-mode))) - (after-make-frame-functions) - (edge (window-inside-pixel-edges)) - (lh (default-line-height)) - (border (alist-get 'child-frame-border-width corfu--frame-parameters)) - (x (max border (min (+ (car edge) x (- border)) - (- (frame-pixel-width) width)))) - (yb (+ (cadr edge) (window-tab-line-height) y lh)) - (y (if (> (+ yb height lh lh) (frame-pixel-height)) - (- yb height lh 1) - yb)) - (buffer (corfu--make-buffer content))) - (unless (and (frame-live-p corfu--frame) - (eq (frame-parent corfu--frame) (window-frame))) - (when corfu--frame (delete-frame corfu--frame)) - (setq corfu--frame (make-frame - `((parent-frame . ,(window-frame)) - (minibuffer . ,(minibuffer-window (window-frame))) - (line-spacing . ,line-spacing) - ;; Set `internal-border-width' for Emacs 27 - (internal-border-width . ,border) - ,@corfu--frame-parameters)))) - ;; XXX HACK Setting the same frame-parameter/face-background is not a nop (BUG!). - ;; Check explicitly before applying the setting. - ;; Without the check, the frame flickers on Mac. - ;; XXX HACK We have to apply the face background before adjusting the frame parameter, - ;; otherwise the border is not updated (BUG!). - (let* ((face (if (facep 'child-frame-border) 'child-frame-border 'internal-border)) - (new (face-attribute 'corfu-border :background nil 'default))) - (unless (equal (face-attribute face :background corfu--frame 'default) new) - (set-face-background face new corfu--frame))) - (let ((new (face-attribute 'corfu-default :background nil 'default))) - (unless (equal (frame-parameter corfu--frame 'background-color) new) - (set-frame-parameter corfu--frame 'background-color new))) - (let ((win (frame-root-window corfu--frame))) - (set-window-buffer win buffer) - ;; Mark window as dedicated to prevent frame reuse (#60) - (set-window-dedicated-p win t)) - ;; XXX HACK Make the frame invisible before moving the popup in order to avoid flicker. - (unless (eq (cdr (frame-position corfu--frame)) y) - (make-frame-invisible corfu--frame)) - (set-frame-position corfu--frame x y) - (set-frame-size corfu--frame width height t) - (make-frame-visible corfu--frame))) - -(defun corfu--popup-show (pos off width lines &optional curr lo bar) - "Show LINES as popup at POS - OFF. -WIDTH is the width of the popup. -The current candidate CURR is highlighted. -A scroll bar is displayed from LO to LO+BAR." - (let* ((ch (default-line-height)) - (cw (default-font-width)) - (lm (ceiling (* cw corfu-left-margin-width))) - (rm (ceiling (* cw corfu-right-margin-width))) - (bw (ceiling (min rm (* cw corfu-bar-width)))) - (lmargin (and (> lm 0) (propertize " " 'display `(space :width (,lm))))) - (rmargin (and (> rm 0) (propertize " " 'display `(space :align-to right)))) - (sbar (when (> bw 0) - (concat (propertize " " 'display `(space :align-to (- right (,rm)))) - (propertize " " 'display `(space :width (,(- rm bw)))) - (propertize " " 'face 'corfu-bar 'display `(space :width (,bw)))))) - (row 0) - (pos (posn-x-y (posn-at-point pos))) - (x (or (car pos) 0)) - (y (or (cdr pos) 0))) - (corfu--make-frame - (- x lm (* cw off)) y - (+ (* width cw) lm rm) (* (length lines) ch) - (mapconcat (lambda (line) - (let ((str (concat lmargin line - (if (and lo (<= lo row (+ lo bar))) sbar rmargin)))) - (when (eq row curr) - (add-face-text-property - 0 (length str) 'corfu-current 'append str)) - (setq row (1+ row)) - str)) - lines "\n")))) - -(defun corfu--popup-hide () - "Hide Corfu popup." - (when (frame-live-p corfu--frame) - (make-frame-invisible corfu--frame) - (with-current-buffer (window-buffer (frame-root-window corfu--frame)) - (let ((inhibit-read-only t)) - (erase-buffer))))) - -(defun corfu--move-to-front (elem list) - "Move ELEM to front of LIST." - (if-let (found (member elem list)) - (let ((head (list (car found)))) - (nconc head (delq (setcar found nil) list))) - list)) - -;; bug#47711: Deferred highlighting for `completion-all-completions' -;; XXX There is one complication: `completion--twq-all' already adds `completions-common-part'. -(defun corfu--all-completions (&rest args) - "Compute all completions for ARGS with deferred highlighting." - (cl-letf* ((orig-pcm (symbol-function #'completion-pcm--hilit-commonality)) - (orig-flex (symbol-function #'completion-flex-all-completions)) - ((symbol-function #'completion-flex-all-completions) - (lambda (&rest args) - ;; Unfortunately for flex we have to undo the deferred highlighting, since flex uses - ;; the completion-score for sorting, which is applied during highlighting. - (cl-letf (((symbol-function #'completion-pcm--hilit-commonality) orig-pcm)) - (apply orig-flex args)))) - ;; Defer the following highlighting functions - (hl #'identity) - ((symbol-function #'completion-hilit-commonality) - (lambda (cands prefix &optional base) - (setq hl (lambda (x) (nconc (completion-hilit-commonality x prefix base) nil))) - (and cands (nconc cands base)))) - ((symbol-function #'completion-pcm--hilit-commonality) - (lambda (pattern cands) - (setq hl (lambda (x) - ;; `completion-pcm--hilit-commonality' sometimes throws an internal error - ;; for example when entering "/sudo:://u". - (condition-case nil - (completion-pcm--hilit-commonality pattern x) - (t x)))) - cands))) - ;; Only advise orderless after it has been loaded to avoid load order issues - (if (and (fboundp 'orderless-highlight-matches) (fboundp 'orderless-pattern-compiler)) - (cl-letf (((symbol-function 'orderless-highlight-matches) - (lambda (pattern cands) - (let ((regexps (orderless-pattern-compiler pattern))) - (setq hl (lambda (x) (orderless-highlight-matches regexps x)))) - cands))) - (cons (apply #'completion-all-completions args) hl)) - (cons (apply #'completion-all-completions args) hl)))) - -(defun corfu--sort-predicate (x y) - "Sorting predicate which compares X and Y." - (or (< (length x) (length y)) (and (= (length x) (length y)) (string< x y)))) - -(defun corfu-sort-length-alpha (list) - "Sort LIST by length and alphabetically." - (sort list #'corfu--sort-predicate)) - -(defmacro corfu--partition! (list form) - "Evaluate FORM for every element and partition LIST." - (let ((head1 (make-symbol "head1")) - (head2 (make-symbol "head2")) - (tail1 (make-symbol "tail1")) - (tail2 (make-symbol "tail2"))) - `(let* ((,head1 (cons nil nil)) - (,head2 (cons nil nil)) - (,tail1 ,head1) - (,tail2 ,head2)) - (while ,list - (if (let ((it (car ,list))) ,form) - (progn - (setcdr ,tail1 ,list) - (pop ,tail1)) - (setcdr ,tail2 ,list) - (pop ,tail2)) - (pop ,list)) - (setcdr ,tail1 (cdr ,head2)) - (setcdr ,tail2 nil) - (setq ,list (cdr ,head1))))) - -(defun corfu--move-prefix-candidates-to-front (field candidates) - "Move CANDIDATES which match prefix of FIELD to the beginning." - (let* ((word (replace-regexp-in-string " .*" "" field)) - (len (length word))) - (corfu--partition! candidates - (and (>= (length it) len) - (eq t (compare-strings word 0 len it 0 len)))))) - -(defun corfu--filter-files (files) - "Filter FILES by `completion-ignored-extensions'." - (let ((re (concat "\\(?:\\(?:\\`\\|/\\)\\.\\.?/\\|" - (regexp-opt completion-ignored-extensions) - "\\)\\'"))) - (or (seq-remove (lambda (x) (string-match-p re x)) files) files))) - -(defun corfu--sort-function () - "Return the sorting function." - (or (corfu--metadata-get 'display-sort-function) corfu-sort-function)) - -(defun corfu--recompute-candidates (str pt table pred) - "Recompute candidates from STR, PT, TABLE and PRED." - (pcase-let* ((before (substring str 0 pt)) - (after (substring str pt)) - (corfu--metadata (completion-metadata before table pred)) - ;; bug#47678: `completion-boundaries` fails for `partial-completion` - ;; if the cursor is moved between the slashes of "~//". - ;; See also vertico.el which has the same issue. - (bounds (or (condition-case nil - (completion-boundaries before table pred after) - (t (cons 0 (length after)))))) - (field (substring str (car bounds) (+ pt (cdr bounds)))) - (completing-file (eq (corfu--metadata-get 'category) 'file)) - (`(,all . ,hl) (corfu--all-completions str table pred pt corfu--metadata)) - (base (or (when-let (z (last all)) (prog1 (cdr z) (setcdr z nil))) 0))) - ;; Filter the ignored file extensions. We cannot use modified predicate for this filtering, - ;; since this breaks the special casing in the `completion-file-name-table' for `file-exists-p' - ;; and `file-directory-p'. - (when completing-file (setq all (corfu--filter-files all))) - (setq all (delete-consecutive-dups (funcall (or (corfu--sort-function) #'identity) all))) - (setq all (corfu--move-prefix-candidates-to-front field all)) - (when (and completing-file (not (string-suffix-p "/" field))) - (setq all (corfu--move-to-front (concat field "/") all))) - (setq all (corfu--move-to-front field all)) - (list base all (length all) hl corfu--metadata - ;; Select the prompt when the input is a valid completion - ;; and if it is not equal to the first candidate. - (if (or (not corfu-preselect-first) (not all) - (and (not (equal field (car all))) - (not (and completing-file (equal (concat field "/") (car all)))) - (test-completion str table pred))) - -1 0)))) - -(defun corfu--update-candidates (str pt table pred) - "Update candidates from STR, PT, TABLE and PRED." - ;; Redisplay such that the input becomes immediately visible before the - ;; expensive candidate recomputation is performed (Issue #48). See also - ;; corresponding vertico#89. - (redisplay) - (pcase (while-no-input (corfu--recompute-candidates str pt table pred)) - ('nil (keyboard-quit)) - (`(,base ,candidates ,total ,hl ,metadata ,preselect) - (setq corfu--input (cons str pt) - corfu--candidates candidates - corfu--base base - corfu--total total - corfu--preselect preselect - corfu--index preselect - corfu--highlight hl - corfu--metadata metadata)))) - -(defun corfu--match-symbol-p (pattern sym) - "Return non-nil if SYM is matching an element of the PATTERN list." - (and (symbolp sym) - (cl-loop for x in pattern - thereis (if (symbolp x) - (eq sym x) - (string-match-p x (symbol-name sym)))))) - -(defun corfu-quit () - "Quit Corfu completion." - (interactive) - (completion-in-region-mode -1)) - -(defun corfu-reset () - "Reset Corfu completion. -This command can be executed multiple times by hammering the ESC key. If a -candidate is selected, unselect the candidate. Otherwise reset the input. If -there hasn't been any input, then quit." - (interactive) - (if (/= corfu--index corfu--preselect) - (progn - (corfu--goto -1) - (setq this-command #'corfu-first)) - ;; Cancel all changes and start new change group. - (cancel-change-group corfu--change-group) - (activate-change-group (setq corfu--change-group (prepare-change-group))) - (when (eq last-command #'corfu-reset) (corfu-quit)))) - -(defun corfu--affixate (cands) - "Annotate CANDS with annotation function." - (setq cands - (if-let (aff (or (corfu--metadata-get 'affixation-function) - (plist-get corfu--extra :affixation-function))) - (funcall aff cands) - (if-let (ann (or (corfu--metadata-get 'annotation-function) - (plist-get corfu--extra :annotation-function))) - (cl-loop for cand in cands collect - (let ((suffix (or (funcall ann cand) ""))) - (list cand "" - ;; The default completion UI adds the `completions-annotations' face - ;; if no other faces are present. We use a custom `corfu-annotations' - ;; face to allow further styling which fits better for popups. - (if (text-property-not-all 0 (length suffix) 'face nil suffix) - suffix - (propertize suffix 'face 'corfu-annotations))))) - (cl-loop for cand in cands collect (list cand "" ""))))) - (let* ((dep (plist-get corfu--extra :company-deprecated)) - (completion-extra-properties corfu--extra) - (mf (run-hook-with-args-until-success 'corfu-margin-formatters corfu--metadata))) - (cl-loop for x in cands for (c . _) = x do - (when mf - (setf (cadr x) (funcall mf c))) - (when (and dep (funcall dep c)) - (setcar x (setq c (substring c))) - (add-face-text-property 0 (length c) 'corfu-deprecated 'append c))) - (cons mf cands))) - -(defun corfu--metadata-get (prop) - "Return PROP from completion metadata." - ;; Note: Do not use `completion-metadata-get' in order to avoid Marginalia. - ;; The Marginalia annotators are too heavy for the Corfu popup! - (cdr (assq prop corfu--metadata))) - -(defun corfu--format-candidates (cands) - "Format annotated CANDS." - (setq cands - (cl-loop for c in cands collect - (cl-loop for s in c collect - (replace-regexp-in-string "[ \t]*\n[ \t]*" " " s)))) - (let* ((cw (cl-loop for x in cands maximize (string-width (car x)))) - (pw (cl-loop for x in cands maximize (string-width (cadr x)))) - (sw (cl-loop for x in cands maximize (string-width (caddr x)))) - (width (+ pw cw sw))) - (when (< width corfu-min-width) - (setq cw (+ cw (- corfu-min-width width)) - width corfu-min-width)) - ;; -4 because of margins and some additional safety - (setq width (min width corfu-max-width (- (frame-width) 4))) - (list pw width - (cl-loop for (cand prefix suffix) in cands collect - (truncate-string-to-width - (concat prefix - (make-string (- pw (string-width prefix)) ?\s) - cand - (when (/= sw 0) - (make-string (+ (- cw (string-width cand)) - (- sw (string-width suffix))) - ?\s)) - suffix) - width))))) - -(defun corfu--update-scroll () - "Update scroll position." - (let ((off (max (min corfu-scroll-margin (/ corfu-count 2)) 0)) - (corr (if (= corfu-scroll-margin (/ corfu-count 2)) (1- (mod corfu-count 2)) 0))) - (setq corfu--scroll (min (max 0 (- corfu--total corfu-count)) - (max 0 (+ corfu--index off 1 (- corfu-count)) - (min (- corfu--index off corr) corfu--scroll)))))) - -(defun corfu--candidates-popup (pos) - "Show candidates popup at POS." - (corfu--update-scroll) - (pcase-let* ((last (min (+ corfu--scroll corfu-count) corfu--total)) - (bar (ceiling (* corfu-count corfu-count) corfu--total)) - (lo (min (- corfu-count bar 1) (floor (* corfu-count corfu--scroll) corfu--total))) - (`(,mf . ,acands) (corfu--affixate (funcall corfu--highlight - (seq-subseq corfu--candidates corfu--scroll last)))) - (`(,pw ,width ,fcands) (corfu--format-candidates acands)) - ;; Disable the left margin if a margin formatter is active. - (corfu-left-margin-width (if mf 0 corfu-left-margin-width))) - ;; Nonlinearity at the end and the beginning - (when (/= corfu--scroll 0) - (setq lo (max 1 lo))) - (when (/= last corfu--total) - (setq lo (min (- corfu-count bar 2) lo))) - (corfu--popup-show (+ pos corfu--base) pw width fcands (- corfu--index corfu--scroll) - (and (> corfu--total corfu-count) lo) bar))) - -(defun corfu--preview-current (beg end str) - "Show current candidate as overlay given BEG, END and STR." - (when-let (cand (and corfu-preview-current (>= corfu--index 0) - (/= corfu--index corfu--preselect) - (nth corfu--index corfu--candidates))) - (setq corfu--preview-ov (make-overlay beg end nil t t)) - (overlay-put corfu--preview-ov 'priority 1000) - (overlay-put corfu--preview-ov 'window (selected-window)) - (overlay-put corfu--preview-ov - (if (= beg end) 'after-string 'display) - (concat (substring str 0 corfu--base) cand)))) - -(defun corfu--echo-refresh () - "Refresh echo message to prevent flicker during redisplay." - (when corfu--echo-timer - (cancel-timer corfu--echo-timer) - (setq corfu--echo-timer nil)) - (when corfu--echo-message - (corfu--echo-show corfu--echo-message))) - -(defun corfu--echo-show (msg) - "Show MSG in echo area." - (let ((message-log-max nil)) - (setq corfu--echo-message msg) - (message "%s" (if (text-property-not-all 0 (length msg) 'face nil msg) - msg - (propertize msg 'face 'corfu-echo))))) - -(defun corfu--echo-documentation () - "Show documentation string of current candidate in echo area." - (when corfu-echo-documentation - (if-let* ((fun (plist-get corfu--extra :company-docsig)) - (cand (and (>= corfu--index 0) (nth corfu--index corfu--candidates))) - (doc (funcall fun cand))) - (if (or (eq corfu-echo-documentation t) corfu--echo-message) - (corfu--echo-show doc) - (setq corfu--echo-timer (run-at-time corfu-echo-documentation - nil #'corfu--echo-show doc))) - (when corfu--echo-message - (corfu--echo-show ""))))) - -(defun corfu--update () - "Refresh Corfu UI." - (pcase-let* ((`(,beg ,end ,table ,pred) completion-in-region--data) - (pt (- (point) beg)) - (str (buffer-substring-no-properties beg end)) - (initializing (not corfu--input)) - (continue (or (/= beg end) - (corfu--match-symbol-p corfu-continue-commands - this-command)))) - (corfu--echo-refresh) - (cond - ;; XXX Guard against errors during candidate generation. - ;; Turn off completion immediately if there are errors - ;; For example dabbrev throws error "No dynamic expansion ... found". - ;; TODO Report this as a bug? Are completion tables supposed to throw errors? - ((condition-case err - ;; Only recompute when input changed and when input is non-empty - (when (and continue (not (equal corfu--input (cons str pt)))) - (corfu--update-candidates str pt table pred) - nil) - (error (corfu-quit) - (message "Corfu completion error: %s" (error-message-string err))))) - ;; 1) Initializing, no candidates => Quit - ((and initializing (not corfu--candidates)) - (corfu-quit)) - ;; 2) Single matching candidate and no further completion is possible - ((and (not (equal str "")) - (equal corfu--candidates (list str)) - (not (consp (completion-try-completion str table pred pt corfu--metadata)))) - (corfu--done str (if initializing 'exact 'finished))) - ;; 3) There exist candidates - ;; & Input is non-empty or continue command - ;; => Show candidates popup - ((and corfu--candidates continue) - (corfu--candidates-popup beg) - (corfu--echo-documentation) - (corfu--preview-current beg end str)) - ;; 4) There are no candidates & corfu-quit-no-match => Confirmation popup - ((not (or corfu--candidates - ;; When `corfu-quit-no-match' is a number of seconds and the auto completion wasn't - ;; initiated too long ago, quit directly without showing the "No match" popup. - (if (and corfu--auto-start (numberp corfu-quit-no-match)) - (< (- (float-time) corfu--auto-start) corfu-quit-no-match) - (eq t corfu-quit-no-match)))) - (corfu--popup-show beg 0 8 '(#("No match" 0 8 (face italic))))) - (t (corfu-quit))))) - -(defun corfu--pre-command () - "Insert selected candidate unless command is marked to continue completion." - (add-hook 'window-configuration-change-hook #'corfu-quit) - (when corfu--preview-ov - (delete-overlay corfu--preview-ov) - (setq corfu--preview-ov nil)) - (when (and corfu-commit-predicate - (not (corfu--match-symbol-p corfu-continue-commands this-command)) - (funcall corfu-commit-predicate)) - (corfu--insert 'exact))) - -(defun corfu-candidate-previewed-p () - "Return t if a candidate is selected and previewed." - (and corfu-preview-current (/= corfu--index corfu--preselect))) - -(defun corfu--post-command () - "Refresh Corfu after last command." - (remove-hook 'window-configuration-change-hook #'corfu-quit) - (or (pcase completion-in-region--data - (`(,beg ,end . ,_) - (when (let ((pt (point))) - (and (eq (marker-buffer beg) (current-buffer)) - (<= beg pt end) - (save-excursion - (goto-char beg) - (<= (line-beginning-position) pt (line-end-position))) - (or (not corfu-quit-at-boundary) - (funcall completion-in-region-mode--predicate)))) - (corfu--update) - t))) - (corfu-quit))) - -(defun corfu--goto (index) - "Go to candidate with INDEX." - (setq corfu--index (max corfu--preselect (min index (1- corfu--total))) - ;; Reset auto start in order to disable the `corfu-quit-no-match' timer - corfu--auto-start nil)) - -(defun corfu-next (&optional n) - "Go forward N candidates." - (interactive "p") - (let ((index (+ corfu--index (or n 1)))) - (corfu--goto - (cond - ((not corfu-cycle) index) - ((= corfu--total 0) -1) - ((< corfu--preselect 0) (1- (mod (1+ index) (1+ corfu--total)))) - (t (mod index corfu--total)))))) - -(defun corfu-previous (&optional n) - "Go backward N candidates." - (interactive "p") - (corfu-next (- (or n 1)))) - -(defun corfu-scroll-down (&optional n) - "Go back by N pages." - (interactive "p") - (corfu--goto (max 0 (- corfu--index (* (or n 1) corfu-count))))) - -(defun corfu-scroll-up (&optional n) - "Go forward by N pages." - (interactive "p") - (corfu-scroll-down (- (or n 1)))) - -(defun corfu-first () - "Go to first candidate, or to the prompt when the first candidate is selected." - (interactive) - (corfu--goto (if (> corfu--index 0) 0 -1))) - -(defun corfu-last () - "Go to last candidate." - (interactive) - (corfu--goto (1- corfu--total))) - -(defun corfu--restore-on-next-command () - "Restore window configuration before next command." - (let ((config (current-window-configuration)) - (other other-window-scroll-buffer) - (restore (make-symbol "corfu--restore"))) - (fset restore - (lambda () - (setq other-window-scroll-buffer other) - (unless (memq this-command '(scroll-other-window scroll-other-window-down)) - (when (memq this-command '(corfu-quit corfu-reset)) - (setq this-command #'ignore)) - (remove-hook 'pre-command-hook restore) - (set-window-configuration config)))) - (add-hook 'pre-command-hook restore))) - -;; Company support, taken from `company.el', see `company-show-doc-buffer'. -(defun corfu-show-documentation () - "Show documentation of current candidate." - (interactive) - (when (< corfu--index 0) - (user-error "No candidate selected")) - (if-let* ((fun (plist-get corfu--extra :company-doc-buffer)) - (res (funcall fun (nth corfu--index corfu--candidates)))) - (let ((buf (or (car-safe res) res))) - (corfu--restore-on-next-command) - (setq other-window-scroll-buffer (get-buffer buf)) - (set-window-start (display-buffer buf t) (or (cdr-safe res) (point-min)))) - (user-error "No documentation available"))) - -;; Company support, taken from `company.el', see `company-show-location'. -(defun corfu-show-location () - "Show location of current candidate." - (interactive) - (when (< corfu--index 0) - (user-error "No candidate selected")) - (if-let* ((fun (plist-get corfu--extra :company-location)) - (loc (funcall fun (nth corfu--index corfu--candidates)))) - (let ((buf (or (and (bufferp (car loc)) (car loc)) (find-file-noselect (car loc) t)))) - (corfu--restore-on-next-command) - (setq other-window-scroll-buffer buf) - (with-selected-window (display-buffer buf t) - (save-restriction - (widen) - (if (bufferp (car loc)) - (goto-char (cdr loc)) - (goto-char (point-min)) - (forward-line (1- (cdr loc)))) - (set-window-start nil (point))))) - (user-error "No candidate location available"))) - -(defun corfu-complete () - "Try to complete current input." - (interactive) - (pcase-let ((`(,beg ,end ,table ,pred) completion-in-region--data)) - (if completion-cycling - ;; Proceed with cycling - (let ((completion-extra-properties corfu--extra)) - (corfu--completion-in-region beg end table pred)) - (if (>= corfu--index 0) - ;; Continue completion with selected candidate - (corfu--insert nil) - ;; Try to complete the current input string - (let* ((pt (max 0 (- (point) beg))) - (str (buffer-substring-no-properties beg end)) - (metadata (completion-metadata (substring str 0 pt) table pred))) - (pcase (completion-try-completion str table pred pt metadata) - (`(,newstr . ,newpt) - (completion--replace beg end newstr) - (goto-char (+ beg newpt)))))) - ;; No further completion is possible and the current string is a valid - ;; match, exit with status 'finished. - (let* ((pt (max 0 (- (point) beg))) - (str (buffer-substring-no-properties beg end)) - (metadata (completion-metadata (substring str 0 pt) table pred))) - (when (and (not (consp (completion-try-completion str table pred pt metadata))) - (test-completion str table pred)) - (corfu--done str 'finished)))))) - -(defun corfu--insert (status) - "Insert current candidate, exit with STATUS if non-nil." - (pcase-let* ((`(,beg ,end ,table ,pred) completion-in-region--data) - (str (buffer-substring-no-properties beg end))) - ;; Replace if candidate is selected or if current input is not valid completion. - ;; For example str can be a valid path, e.g., ~/dir/. - (when (or (>= corfu--index 0) (equal str "") - (not (test-completion str table pred))) - ;; XXX There is a small bug here, depending on interpretation. - ;; When completing "~/emacs/master/li|/calc" where "|" is the - ;; cursor, then the candidate only includes the prefix - ;; "~/emacs/master/lisp/", but not the suffix "/calc". Default - ;; completion has the same problem when selecting in the - ;; *Completions* buffer. See bug#48356. - (setq str (concat (substring str 0 corfu--base) - (substring-no-properties (nth (max 0 corfu--index) corfu--candidates)))) - (completion--replace beg end str) - (corfu--goto -1)) ;; Reset selection, but continue completion. - (when status (corfu--done str status)))) ;; Exit with status - -(defun corfu--done (str status) - "Call the `:exit-function' with STR and STATUS and exit completion." - (let ((exit (plist-get corfu--extra :exit-function))) - ;; For successfull completions, amalgamate undo operations, - ;; such that completion can be undone in a single step. - (undo-amalgamate-change-group corfu--change-group) - (corfu-quit) - ;; XXX Is the :exit-function handling sufficient? - (when exit (funcall exit str status)))) - -(defun corfu-insert () - "Insert current candidate." - (interactive) - (if (> corfu--total 0) - (corfu--insert 'finished) - (corfu-quit))) - -(defun corfu--setup () - "Setup Corfu completion state." - (setq corfu--extra completion-extra-properties) - (activate-change-group (setq corfu--change-group (prepare-change-group))) - (setcdr (assq #'completion-in-region-mode minor-mode-overriding-map-alist) corfu-map) - (add-hook 'pre-command-hook #'corfu--pre-command nil 'local) - (add-hook 'post-command-hook #'corfu--post-command nil 'local) - ;; Disable default post-command handling, since we have our own - ;; checks in `corfu--post-command'. - (remove-hook 'post-command-hook #'completion-in-region--postch) - (let ((sym (make-symbol "corfu--teardown")) - (buf (current-buffer))) - (fset sym (lambda () - ;; Ensure that the teardown runs in the correct buffer, if still alive. - (unless completion-in-region-mode - (remove-hook 'completion-in-region-mode-hook sym) - (with-current-buffer (if (buffer-live-p buf) buf (current-buffer)) - (corfu--teardown))))) - (add-hook 'completion-in-region-mode-hook sym))) - -(defun corfu--teardown () - "Teardown Corfu." - ;; Redisplay such that the input becomes immediately visible before the popup - ;; hiding, which is slow (Issue #48). See also corresponding vertico#89. - (redisplay) - (corfu--popup-hide) - (remove-hook 'window-configuration-change-hook #'corfu-quit) - (remove-hook 'pre-command-hook #'corfu--pre-command 'local) - (remove-hook 'post-command-hook #'corfu--post-command 'local) - (when corfu--preview-ov (delete-overlay corfu--preview-ov)) - (when corfu--echo-timer (cancel-timer corfu--echo-timer)) - (when corfu--echo-message (corfu--echo-show "")) - (accept-change-group corfu--change-group) - (mapc #'kill-local-variable corfu--state-vars)) - -(defun corfu--completion-message (msg) - "Print completion MSG, do not hang like `completion--message'." - (when (and completion-show-inline-help - (member msg '("No match" "Sole completion"))) - (message msg))) - -(defun corfu--all-sorted-completions (&optional beg end) - "Compute all sorted completions for string between BEG and END." - (or completion-all-sorted-completions - (pcase-let ((`(,base ,all . ,_) (corfu--recompute-candidates - (buffer-substring-no-properties beg end) - (max 0 (- (point) beg)) - minibuffer-completion-table - minibuffer-completion-predicate))) - (when all - (completion--cache-all-sorted-completions - beg end (nconc all base)))))) - -(defun corfu--completion-in-region (&rest args) - "Corfu completion in region function passing ARGS to `completion--in-region'." - (barf-if-buffer-read-only) - (if (not (display-graphic-p)) - ;; XXX Warning this can result in an endless loop when `completion-in-region-function' - ;; is set *globally* to `corfu--completion-in-region'. This should never happen. - (apply (default-value 'completion-in-region-function) args) - ;; Restart the completion. This can happen for example if C-M-/ - ;; (`dabbrev-completion') is pressed while the Corfu popup is already open. - (when (and completion-in-region-mode (not completion-cycling)) - (corfu-quit)) - (prog1 - (cl-letf* ((completion-auto-help nil) - ;; Set the predicate to ensure that `completion-in-region-mode' is enabled. - (completion-in-region-mode-predicate - (or completion-in-region-mode-predicate (lambda () t))) - ;; Overwrite to avoid hanging. - ((symbol-function #'completion--message) - #'corfu--completion-message) - ;; Overwrite for performance and consistency. - ((symbol-function #'completion-all-sorted-completions) - #'corfu--all-sorted-completions)) - (apply #'completion--in-region args)) - (when (and completion-in-region-mode - ;; Do not show Corfu when "trivially" cycling, i.e., - ;; when the completion is finished after the candidate. - (not (and completion-cycling - (pcase-let* ((`(,beg ,end ,table ,pred) completion-in-region--data) - (pt (max 0 (- (point) beg))) - (str (buffer-substring-no-properties beg end)) - (before (substring str 0 pt)) - (after (substring str pt))) - (equal (completion-boundaries before table pred after) '(0 . 0)))))) - (corfu--setup))))) - -(defun corfu--auto-complete (buffer) - "Initiate auto completion after delay in BUFFER." - (setq corfu--auto-timer nil) - (when (and (not completion-in-region-mode) - (eq (current-buffer) buffer)) - (pcase (while-no-input ;; Interruptible capf query - (run-hook-wrapped 'completion-at-point-functions #'corfu--capf-wrapper)) - ((and `(,fun ,beg ,end ,table . ,plist) - (guard (integer-or-marker-p beg)) - (guard (<= beg (point) end)) - (guard - (let ((len (or (plist-get plist :company-prefix-length) (- (point) beg)))) - (or (eq len t) (>= len corfu-auto-prefix))))) - (let ((completion-extra-properties plist) - (completion-in-region-mode-predicate - (lambda () (eq beg (car-safe (funcall fun)))))) - (setq completion-in-region--data `(,(copy-marker beg) ,(copy-marker end t) - ,table ,(plist-get plist :predicate)) - corfu--auto-start (float-time)) - (undo-boundary) ;; Necessary to support `corfu-reset' - (completion-in-region-mode 1) - (corfu--setup) - (corfu--update)))))) - -(defun corfu--auto-post-command () - "Post command hook which initiates auto completion." - (when corfu--auto-timer - (cancel-timer corfu--auto-timer) - (setq corfu--auto-timer nil)) - (when (and (not completion-in-region-mode) - (corfu--match-symbol-p corfu-auto-commands this-command) - (display-graphic-p)) - ;; NOTE: Do not use idle timer since this leads to unacceptable slowdowns, - ;; in particular if flyspell-mode is enabled. - (setq corfu--auto-timer (run-at-time corfu-auto-delay nil - #'corfu--auto-complete - (current-buffer))))) - -;;;###autoload -(define-minor-mode corfu-mode - "Completion Overlay Region FUnction" - :global nil :group 'corfu - (cond - (corfu-mode - ;; FIXME: Install advice which fixes `completion--capf-wrapper', such that - ;; it respects the completion styles for non-exclusive capfs. See FIXME in - ;; the `completion--capf-wrapper' function in minibuffer.el, where the - ;; issue has been mentioned. We never uninstall this advice since the - ;; advice is active *globally*. - (advice-add #'completion--capf-wrapper :around #'corfu--capf-wrapper-advice) - (advice-add #'eldoc-display-message-no-interference-p :before-while #'corfu--allow-eldoc) - (and corfu-auto (add-hook 'post-command-hook #'corfu--auto-post-command nil 'local)) - (setq-local completion-in-region-function #'corfu--completion-in-region)) - (t - (remove-hook 'post-command-hook #'corfu--auto-post-command 'local) - (kill-local-variable 'completion-in-region-function)))) - -(defun corfu--capf-wrapper (fun) - "Wrapper for `completion-at-point' FUN. -Determines if the capf is applicable at the current position." - (pcase - ;; bug#50470: Fix Capfs which illegally modify the buffer or which - ;; illegally call `completion-in-region'. The workaround here has been - ;; proposed @jakanakaevangeli in bug#50470 and is used in - ;; @jakanakaevangeli's capf-autosuggest package. - (catch 'corfu--illegal-completion-in-region - (condition-case nil - (let ((buffer-read-only t) - (inhibit-read-only nil) - (completion-in-region-function - (lambda (beg end coll pred) - (throw 'corfu--illegal-completion-in-region - (list beg end coll :predicate pred))))) - (funcall fun)) - (buffer-read-only nil))) - ((and res `(,beg ,end ,table . ,plist)) - (and (integer-or-marker-p beg) ;; Valid capf result - (<= beg (point) end) ;; Sanity checking - ;; For non-exclusive capfs, check for valid completion. - (or (not (eq 'no (plist-get plist :exclusive))) - (let* ((str (buffer-substring-no-properties beg end)) - (pt (- (point) beg)) - (pred (plist-get plist :predicate)) - (md (completion-metadata (substring str 0 pt) table pred))) - ;; We use `completion-try-completion' to check if there are - ;; completions. The upstream `completion--capf-wrapper' uses - ;; `try-completion' which is incorrect since it only checks for - ;; prefix completions. - (completion-try-completion str table pred pt md))) - (cons fun res))))) - -(defun corfu--capf-wrapper-advice (orig fun which) - "Around advice for `completion--capf-wrapper'. -The ORIG function takes the FUN and WHICH arguments." - (if corfu-mode (corfu--capf-wrapper fun) (funcall orig fun which))) - -;;;###autoload -(define-globalized-minor-mode corfu-global-mode corfu-mode corfu--on :group 'corfu) - -(defun corfu--on () - "Turn `corfu-mode' on." - (unless (or noninteractive - (eq (aref (buffer-name) 0) ?\s) - (memq major-mode corfu-excluded-modes)) - (corfu-mode 1))) - -(defun corfu--allow-eldoc () - "Return non-nil if Corfu is currently not active." - (not (and corfu-mode completion-in-region-mode))) - -;; Emacs 28: Do not show Corfu commands with M-X -(dolist (sym '(corfu-next corfu-previous corfu-first corfu-last corfu-quit corfu-reset - corfu-complete corfu-insert corfu-scroll-up corfu-scroll-down - corfu-show-location corfu-show-documentation)) - (put sym 'completion-predicate #'ignore)) - -(provide 'corfu) -;;; corfu.el ends here diff --git a/elpa/corfu-0.17/corfu.elc b/elpa/corfu-0.17/corfu.elc Binary files differ. diff --git a/elpa/corfu-0.17/corfu.info b/elpa/corfu-0.17/corfu.info @@ -1,327 +0,0 @@ -This is corfu.info, produced by makeinfo version 6.7 from corfu.texi. - -INFO-DIR-SECTION Emacs -START-INFO-DIR-ENTRY -* Corfu: (corfu). Completion Overlay Region FUnction. -END-INFO-DIR-ENTRY - - -File: corfu.info, Node: Top, Next: Introduction, Up: (dir) - -corfu.el - Completion Overlay Region FUnction -********************************************* - -* Menu: - -* Introduction:: -* Features:: -* Installation and Configuration:: -* Key bindings:: -* Complementary packages:: -* Caveats:: -* Contributions:: - -— The Detailed Node Listing — - -Installation and Configuration - -* TAB-and-Go completion:: - - - -File: corfu.info, Node: Introduction, Next: Features, Prev: Top, Up: Top - -1 Introduction -************** - -Corfu enhances the default completion in region function with a -completion overlay. The current candidates are shown in a popup below -or above the point. Corfu is the minimalistic ‘completion-in-region’ -counterpart of the Vertico (https://github.com/minad/vertico) minibuffer -UI. - - Corfu is a minimal package, which relies on the Emacs completion -facilities and concentrates on providing a polished completion UI. -Completions are either provided by commands like ‘dabbrev-completion’ or -by pluggable backends (‘completion-at-point-functions’, Capfs). Most -programming language major modes implement a Capf. Furthermore the -language server packages, Eglot (https://github.com/joaotavora/eglot) -and Lsp-mode (https://github.com/emacs-lsp/lsp-mode), both use Capfs -which talk to the LSP server to retrieve the completions. - - Corfu does not include custom completion backends. In contrast, the -complex Company package includes custom completion backends, which -deviate from the Emacs completion infrastructure. The Emacs built-in -Capfs are mostly sufficient, but a few additional Capfs and completion -functions are provided by the Cape (https://github.com/minad/cape) -package. - - *NOTE*: Corfu uses child frames to show the popup. For now Corfu -falls back to the default setting of the ‘completion-in-region-function’ -on non-graphical displays. You may want to use -‘consult-completion-in-region’. - - <https://github.com/minad/corfu/blob/screenshots/light.png?raw=true> - - <https://github.com/minad/corfu/blob/screenshots/dark.png?raw=true> - - -File: corfu.info, Node: Features, Next: Installation and Configuration, Prev: Introduction, Up: Top - -2 Features -********** - - • Timer-based auto-completions (_off_ by default, set ‘corfu-auto’). - • Popup display with scrollbar indicator and arrow key navigation. - • The popup can be summoned explicitly by pressing ‘TAB’ at any time. - • The current candidate is inserted with ‘TAB’ and selected with - ‘RET’. - • Candidates sorting by prefix, string length and alphabetically. - • The selected candidate is previewed (configuable via - ‘corfu-preview-current’). - • The selected candidate automatically committed on further input by - default (configurable via ‘corfu-commit-predicate’). - • The Orderless (https://github.com/oantolin/orderless) completion - style is supported. The filter string can contain arbitrary - characters, including spaces, if ‘corfu-quit-at-boundary’ is nil. - • Deferred completion style highlighting for performance. - • Jumping to location/documentation of current candidate. - • Show candidate documentation/signature string in the echo area. - • Deprecated candidates are crossed out in the display. - • Support for annotations (‘annotation-function’, - ‘affixation-function’). - • Icons can be provided by an external package via margin formatter - functions. - - -File: corfu.info, Node: Installation and Configuration, Next: Key bindings, Prev: Features, Up: Top - -3 Installation and Configuration -******************************** - -Corfu is available from GNU ELPA -(http://elpa.gnu.org/packages/corfu.html), such that it can be installed -directly via ‘package-install’. After installation, the global minor -mode can be enabled with ‘M-x corfu-global-mode’. In order to configure -Corfu and other packages in your init.el, you may want to use -‘use-package’. - - Corfu is highly flexible and customizable via ‘corfu-*’ customization -variables. For filtering I recommend to give Orderless completion a -try, which is different from the familiar prefix TAB completion. Corfu -can be used with the default completion styles, the use of Orderless is -not a necessity. See also the Corfu Wiki -(https://github.com/minad/corfu/wiki) for additional configuration tips. -In particular the Lsp-mode configuration is documented in the Wiki. - - Here is an example configuration: - - (use-package corfu - ;; Optional customizations - ;; :custom - ;; (corfu-cycle t) ;; Enable cycling for `corfu-next/previous' - ;; (corfu-auto t) ;; Enable auto completion - ;; (corfu-commit-predicate nil) ;; Do not commit selected candidates on next input - ;; (corfu-quit-at-boundary t) ;; Automatically quit at word boundary - ;; (corfu-quit-no-match t) ;; Automatically quit if there is no match - ;; (corfu-preview-current nil) ;; Disable current candidate preview - ;; (corfu-preselect-first nil) ;; Disable candidate preselection - ;; (corfu-echo-documentation nil) ;; Disable documentation in the echo area - ;; (corfu-scroll-margin 5) ;; Use scroll margin - - ;; You may want to enable Corfu only for certain modes. - ;; :hook ((prog-mode . corfu-mode) - ;; (shell-mode . corfu-mode) - ;; (eshell-mode . corfu-mode)) - - ;; Recommended: Enable Corfu globally. - ;; This is recommended since dabbrev can be used globally (M-/). - :init - (corfu-global-mode)) - - ;; Optionally use the `orderless' completion style. See `+orderless-dispatch' - ;; in the Consult wiki for an advanced Orderless style dispatcher. - ;; Enable `partial-completion' for files to allow path expansion. - ;; You may prefer to use `initials' instead of `partial-completion'. - (use-package orderless - :init - ;; Configure a custom style dispatcher (see the Consult wiki) - ;; (setq orderless-style-dispatchers '(+orderless-dispatch) - ;; orderless-component-separator #'orderless-escapable-split-on-space) - (setq completion-styles '(orderless) - completion-category-defaults nil - completion-category-overrides '((file (styles . (partial-completion)))))) - - ;; Use dabbrev with Corfu! - (use-package dabbrev - ;; Swap M-/ and C-M-/ - :bind (("M-/" . dabbrev-completion) - ("C-M-/" . dabbrev-expand))) - - ;; A few more useful configurations... - (use-package emacs - :init - ;; TAB cycle if there are only few candidates - (setq completion-cycle-threshold 3) - - ;; Emacs 28: Hide commands in M-x which do not apply to the current mode. - ;; Corfu commands are hidden, since they are not supposed to be used via M-x. - ;; (setq read-extended-command-predicate - ;; #'command-completion-default-include-p) - - ;; Enable indentation+completion using the TAB key. - ;; `completion-at-point' is often bound to M-TAB. - (setq tab-always-indent 'complete)) - - See also the Corfu Wiki (https://github.com/minad/corfu/wiki) for -additional configuration tips. For more general documentation read the -chapter about completion in the Emacs manual -(https://www.gnu.org/software/emacs/manual/html_node/emacs/Completion.html). -If you want to create your own Capfs, you can find documentation about -completion in the Elisp manual -(https://www.gnu.org/software/emacs/manual/html_node/elisp/Completion.html). - -* Menu: - -* TAB-and-Go completion:: - - -File: corfu.info, Node: TAB-and-Go completion, Up: Installation and Configuration - -3.1 TAB-and-Go completion -========================= - -You may be interested in configuring Corfu in TAB-and-Go style. -Pressing TAB moves to the next candidate and further input will then -commit the selection. - - (use-package corfu - ;; TAB-and-Go customizations - :custom - (corfu-cycle t) ;; Enable cycling for `corfu-next/previous' - (corfu-preselect-first nil) ;; Disable candidate preselection - - ;; Use TAB for cycling, default is `corfu-complete'. - :bind - (:map corfu-map - ("TAB" . corfu-next) - ([tab] . corfu-next) - ("S-TAB" . corfu-previous) - ([backtab] . corfu-previous)) - - :init - (corfu-global-mode)) - - -File: corfu.info, Node: Key bindings, Next: Complementary packages, Prev: Installation and Configuration, Up: Top - -4 Key bindings -************** - -Corfu uses a transient keymap ‘corfu-map’ which is active while the -popup is shown. The keymap defines the following remappings and -bindings: - - • ‘beginning-of-buffer’ -> ‘corfu-first’ - • ‘end-of-buffer’ -> ‘corfu-last’ - • ‘scroll-down-command’ -> ‘corfu-scroll-down’ - • ‘scroll-up-command’ -> ‘corfu-scroll-up’ - • ‘next-line’, ‘down’, ‘M-n’ -> ‘corfu-next’ - • ‘previous-line’, ‘up’, ‘M-p’ -> ‘corfu-previous’ - • ‘completion-at-point’, ‘TAB’ -> ‘corfu-complete’ - • ‘RET’ -> ‘corfu-insert’ - • ‘M-g’ -> ‘corfu-show-location’ - • ‘M-h’ -> ‘corfu-show-documentation’ - • ‘C-g’ -> ‘corfu-quit’ - • ‘keyboard-escape-quit’ -> ‘corfu-reset’ - - -File: corfu.info, Node: Complementary packages, Next: Caveats, Prev: Key bindings, Up: Top - -5 Complementary packages -************************ - -Corfu works well together with all packages providing code completion -via the ‘completion-at-point-functions’. Many modes and packages -already provide a Capf out of the box. Nevertheless you may want to -look into complementary packages to enhance your setup. - - • Orderless (https://github.com/oantolin/orderless): Cofu supports - completion styles, including the advanced Orderless - (https://github.com/oantolin/orderless) completion style, where the - filtering expressions are separated by spaces (see - ‘corfu-quit-at-boundary’). - - • Cape (https://github.com/minad/cape): I collect additional Capf - backends and ‘completion-in-region’ commands in my Cape - (https://github.com/minad/cape) package. The package provides a - file path, a dabbrev completion backend and a backend which allows - you to enter unicode characters in the form of TeX commands. Cape - provides an adapter to reuse Company backends in Corfu. - Furthermore the function ‘cape-super-capf’ can merge/groups - multiple Capfs, such that the candidates of multiple Capfs are - displayed together at the same time. - - • kind-icon (https://github.com/jdtsmith/kind-icon): Icons are - supported by Corfu via an external package. For example the - kind-icon (https://github.com/jdtsmith/kind-icon) package provides - beautifully styled SVG icons based on monochromatic icon sets like - material design. - - • corfu-doc (https://github.com/galeo/corfu-doc): The corfu-doc - package by @galeo allows you to display the candidate documentation - in a popup next to the Corfu popup, similar to ‘company-quickhelp’. - _Note that the corfu-doc package is new and still work in - progress._ - - • Vertico (https://github.com/minad/vertico): You may also want to - look into my Vertico (https://github.com/minad/vertico) package. - Vertico is the minibuffer completion counterpart of Corfu. - - -File: corfu.info, Node: Caveats, Next: Contributions, Prev: Complementary packages, Up: Top - -6 Caveats -********* - -Corfu is robust in most scenarios. There are a few known technical -caveats. - - • Corfu uses child frames to show the popup. For now Corfu falls - back to the default setting of the ‘completion-in-region-function’ - on non-graphical displays. You may want to use - ‘consult-completion-in-region’. - - • Corfu does not sort by history, since ‘completion-at-point’ does - not maintain a history (See branch ‘history’ for a possible - solution). - - -File: corfu.info, Node: Contributions, Prev: Caveats, Up: Top - -7 Contributions -*************** - -Since this package is part of GNU ELPA -(http://elpa.gnu.org/packages/corfu.html) contributions require a -copyright assignment to the FSF. - - - -Tag Table: -Node: Top195 -Node: Introduction597 -Node: Features2280 -Node: Installation and Configuration3684 -Node: TAB-and-Go completion7847 -Node: Key bindings8657 -Node: Complementary packages9609 -Node: Caveats11717 -Node: Contributions12319 - -End Tag Table - - -Local Variables: -coding: utf-8 -End: diff --git a/elpa/corfu-0.20.signed b/elpa/corfu-0.20.signed @@ -0,0 +1 @@ +Good signature from 066DAFCB81E42C40 GNU ELPA Signing Agent (2019) <elpasign@elpa.gnu.org> (trust undefined) created at 2022-03-08T23:05:02+0100 using RSA +\ No newline at end of file diff --git a/elpa/corfu-0.17/LICENSE b/elpa/corfu-0.20/LICENSE diff --git a/elpa/corfu-0.20/README.org b/elpa/corfu-0.20/README.org @@ -0,0 +1,466 @@ +#+title: corfu.el - Completion Overlay Region FUnction +#+author: Daniel Mendler +#+language: en +#+export_file_name: corfu.texi +#+texinfo_dir_category: Emacs +#+texinfo_dir_title: Corfu: (corfu). +#+texinfo_dir_desc: Completion Overlay Region FUnction + +#+html: <a href="https://www.gnu.org/software/emacs/"><img alt="GNU Emacs" src="https://github.com/minad/corfu/blob/screenshots/emacs.svg?raw=true"/></a> +#+html: <a href="http://elpa.gnu.org/packages/corfu.html"><img alt="GNU ELPA" src="https://elpa.gnu.org/packages/corfu.svg"/></a> +#+html: <a href="http://elpa.gnu.org/devel/corfu.html"><img alt="GNU-devel ELPA" src="https://elpa.gnu.org/devel/corfu.svg"/></a> + +* Introduction + + Corfu enhances completion at point with a small completion popup. The current + candidates are shown in a popup below or above the point. Corfu is the + minimalistic ~completion-in-region~ counterpart of the [[https://github.com/minad/vertico][Vertico]] minibuffer UI. + + Corfu is a small package, which relies on the Emacs completion facilities and + concentrates on providing a polished completion UI. Completions are either + provided by commands like ~dabbrev-completion~ or by pluggable backends + (~completion-at-point-functions~, Capfs). Most programming language major modes + implement a Capf. Furthermore the language server packages, [[https://github.com/joaotavora/eglot][Eglot]] and + [[https://github.com/emacs-lsp/lsp-mode][Lsp-mode]], use Capfs which talk to the LSP server to retrieve the completions. + Corfu does not include its own completion backends. The Emacs built-in Capfs + and the Capfs provided by other programming language packages are usually + sufficient. A few additional Capfs and completion utilities are provided by + the [[https://github.com/minad/cape][Cape]] package. + + *NOTE*: Corfu uses child frames to show the popup. For now Corfu falls back to + the default setting of the ~completion-in-region-function~ on non-graphical + displays. + + [[https://github.com/minad/corfu/blob/screenshots/light.png?raw=true]] + + [[https://github.com/minad/corfu/blob/screenshots/dark.png?raw=true]] + +* Features + + - Timer-based auto-completions (/off/ by default, set ~corfu-auto~). + - Popup display with scrollbar indicator and arrow key navigation. + - The popup can be summoned explicitly by pressing =TAB= at any time. + - The current candidate is inserted with =TAB= and selected with =RET=. + - Candidates sorting by prefix, string length and alphabetically. + - The selected candidate is previewed (configurable via ~corfu-preview-current~). + - The selected candidate automatically committed on further input by default. + (configurable via ~corfu-preview-current~). + - The [[https://github.com/oantolin/orderless][Orderless]] completion style is supported. The filter string can contain + arbitrary characters, after inserting a space via =M-SPC= (configurable via + ~corfu-quit-at-boundary~ and ~corfu-separator~). + - Deferred completion style highlighting for performance. + - Jumping to location/documentation of current candidate. + - Support for candidate annotations and documentation in the echo area. + - Deprecated candidates are crossed out in the display. + - Icons can be provided by an external package via margin formatter functions. + +* Installation and Configuration + + Corfu is available from [[http://elpa.gnu.org/packages/corfu.html][GNU ELPA]], such that it can be installed directly via + ~package-install~. After installation, the global minor mode can be enabled with + =M-x corfu-global-mode=. In order to configure Corfu and other packages in your + init.el, you may want to use ~use-package~. + + Corfu is highly flexible and customizable via ~corfu-*~ customization variables, + such that you can adapt it precisely to your requirements. However in order to + quickly try out the Corfu completion package, it should be sufficient to + activate ~corfu-global-mode~. Then you experiment with manual completion for + example in an Elisp buffer or in an Eshell or Shell buffer. For auto + completion, set ~corfu-auto=t~ before turning on ~corfu-global-mode~. + + If you start to configure the package more deeply, I recommend to give the + Orderless completion style a try for filtering. Orderless completion is + different from the familiar prefix TAB completion. Corfu can be used with the + default completion styles. The use of Orderless is not a necessity. See also + the [[https://github.com/minad/corfu/wiki][Corfu Wiki]] for additional configuration tips. In particular the Lsp-mode + configuration is documented in the Wiki. + + Here is an example configuration: + + #+begin_src emacs-lisp + (use-package corfu + ;; Optional customizations + ;; :custom + ;; (corfu-cycle t) ;; Enable cycling for `corfu-next/previous' + ;; (corfu-auto t) ;; Enable auto completion + ;; (corfu-separator ?\s) ;; Orderless field separator + ;; (corfu-quit-at-boundary nil) ;; Never quit at completion boundary + ;; (corfu-quit-no-match nil) ;; Never quit, even if there is no match + ;; (corfu-preview-current nil) ;; Disable current candidate preview + ;; (corfu-preselect-first nil) ;; Disable candidate preselection + ;; (corfu-on-exact-match nil) ;; Configure handling of exact matches + ;; (corfu-echo-documentation nil) ;; Disable documentation in the echo area + ;; (corfu-scroll-margin 5) ;; Use scroll margin + + ;; You may want to enable Corfu only for certain modes. + ;; :hook ((prog-mode . corfu-mode) + ;; (shell-mode . corfu-mode) + ;; (eshell-mode . corfu-mode)) + + ;; Recommended: Enable Corfu globally. + ;; This is recommended since dabbrev can be used globally (M-/). + :init + (corfu-global-mode)) + + ;; Optionally use the `orderless' completion style. See `+orderless-dispatch' + ;; in the Consult wiki for an advanced Orderless style dispatcher. + ;; Enable `partial-completion' for files to allow path expansion. + ;; You may prefer to use `initials' instead of `partial-completion'. + (use-package orderless + :init + ;; Configure a custom style dispatcher (see the Consult wiki) + ;; (setq orderless-style-dispatchers '(+orderless-dispatch) + ;; orderless-component-separator #'orderless-escapable-split-on-space) + (setq completion-styles '(orderless) + completion-category-defaults nil + completion-category-overrides '((file (styles . (partial-completion)))))) + + ;; Use dabbrev with Corfu! + (use-package dabbrev + ;; Swap M-/ and C-M-/ + :bind (("M-/" . dabbrev-completion) + ("C-M-/" . dabbrev-expand))) + + ;; A few more useful configurations... + (use-package emacs + :init + ;; TAB cycle if there are only few candidates + (setq completion-cycle-threshold 3) + + ;; Emacs 28: Hide commands in M-x which do not apply to the current mode. + ;; Corfu commands are hidden, since they are not supposed to be used via M-x. + ;; (setq read-extended-command-predicate + ;; #'command-completion-default-include-p) + + ;; Enable indentation+completion using the TAB key. + ;; `completion-at-point' is often bound to M-TAB. + (setq tab-always-indent 'complete)) + #+end_src + + See also the [[https://github.com/minad/corfu/wiki][Corfu Wiki]] for additional configuration tips. For more general + documentation read the chapter about completion in the [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Completion.html][Emacs manual]]. If you + want to create your own Capfs, you can find documentation about completion in + the [[https://www.gnu.org/software/emacs/manual/html_node/elisp/Completion.html][Elisp manual]]. + +** Auto completion + + Auto completion is disabled by default, but can be enabled by setting + ~corfu-auto=t~. Furthermore you may want to configure Corfu to quit completion + eagerly, such that the completion popup stays out of your way when it + appeared unexpectedly. + + #+begin_src emacs-lisp + ;; Enable auto completion and configure quitting + (setq corfu-auto t + corfu-quit-no-match 'separator) ;; or t + #+end_src + + In general, I recommend to experiment a bit with the various settings and key + bindings to find a configuration which works for you. There is no one size + fits all solution. Some people like auto completion, some like manual + completion, some want to cycle with TAB and some with the arrow keys... + +** Completing with Corfu in the minibuffer + +Corfu can be used for completion in the minibuffer, since it relies on child +frames to display the candidates. By default, ~corfu-global-mode~ does not +activate ~corfu-mode~ in the minibuffer, to avoid interference with specialised +minibuffer completion UIs like Vertico or Mct. However you may still want to +enable Corfu completion for commands like ~M-:~ (~eval-expression~) or ~M-!~ +(~shell-command~), which read from the minibuffer. Activate ~corfu-mode~ only if +~completion-at-point~ is bound in the minibuffer-local keymap to achieve this +effect. + +#+begin_src emacs-lisp + (defun corfu-enable-in-minibuffer () + "Enable Corfu in the minibuffer if `completion-at-point' is bound." + (when (where-is-internal #'completion-at-point (list (current-local-map))) + ;; (setq-local corfu-auto nil) Enable/disable auto completion + (corfu-mode 1))) + (add-hook 'minibuffer-setup-hook #'corfu-enable-in-minibuffer) +#+end_src + +You can also enable Corfu more generally for every minibuffer, as long as no +other completion UI is active. If you use Mct or Vertico as your main minibuffer +completion UI, the following snippet should yield the desired result. + +#+begin_src emacs-lisp + (defun corfu-enable-always-in-minibuffer () + "Enable Corfu in the minibuffer if Vertico/Mct are not active." + (unless (or (bound-and-true-p mct--active) + (bound-and-true-p vertico--input)) + ;; (setq-local corfu-auto nil) Enable/disable auto completion + (corfu-mode 1))) + (add-hook 'minibuffer-setup-hook #'corfu-enable-always-in-minibuffer 1) +#+end_src + +** Completing with Corfu in the Eshell or Shell + +When completing in the Eshell I recommend conservative local settings without +auto completion, such that the completion behavior is similar to widely used +shells like Bash, Zsh or Fish. + +#+begin_src emacs-lisp + (add-hook 'eshell-mode-hook + (lambda () + (setq-local corfu-auto nil) + (corfu-mode))) +#+end_src + +When pressing =RET= while the Corfu popup is visible, the ~corfu-insert~ command +will be invoked. This command does inserts the currently selected candidate, but +it does not send the prompt input to Eshell or the comint process. Therefore you +often have to press =RET= twice which feels like an unnecessary double +confirmation. Fortunately it is easy to improve this! In my configuration I +define the command ~corfu-insert-and-send~ which performs the two steps at once. + +#+begin_src emacs-lisp + (defun corfu-insert-and-send () + (interactive) + ;; 1. First insert the completed candidate + (corfu-insert) + ;; 2. Send the entire prompt input to the shell + (cond + ((and (derived-mode-p 'eshell-mode) (fboundp 'eshell-send-input)) + (eshell-send-input)) + ((derived-mode-p 'comint-mode) + (comint-send-input)))) + + (define-key corfu-map "\r" #'+corfu-insert-and-send) +#+end_src + +Shell completion uses the flexible ~pcomplete~ mechanism internally, which allows +you to program the completions per shell command. If you want to know more, look +into this [[https://www.masteringemacs.org/article/pcomplete-context-sensitive-completion-emacs][blog post]], which shows how to configure pcomplete for git commands. I +recommend the [[https://github.com/JonWaltman/pcmpl-args.el][pcmpl-args]] package which extends Pcomplete with completion support +and helpful annotation support for more commands. Similar to the Fish shell, +pcmpl-args uses man page parsing and --help output parsing to dynamically +generate completions. This package brings Eshell completion to another level! + +Unfortunately Pcomplete has a few technical issues, which we can work around +with the [[https://github.com/minad/cape][Cape]] library (Completion at point extensions). Cape provides wrappers, +which sanitize the pcomplete function. Ideally the bugs in pcomplete should be +fixed upstream. *For now these two advices are strongly recommended to achieve a +sane Eshell experience.* + +#+begin_src emacs-lisp + ;; Silence the pcomplete capf, no errors or messages! + (advice-add 'pcomplete-completions-at-point :around #'cape-wrap-silent) + + ;; Ensure that pcomplete does not write to the buffer + ;; and behaves as a pure `completion-at-point-function'. + (advice-add 'pcomplete-completions-at-point :around #'cape-wrap-purify) +#+end_src + +** Orderless completion + +[[https://github.com/oantolin/orderless][Orderless]] is an advanced completion style that supports multi-component search +filters separated by a configurable character (space, by default). Normally, +entering characters like space which lie outside the completion region +boundaries (words, typically) causes Corfu to quit. This behavior is helpful +with auto-completion, which may pop-up when not desired, e.g. on entering a new +variable name. Just keep typing and Corfu will get out of the way. + +But orderless search terms can contain arbitrary characters; they are also +interpreted as regular expressions. To use orderless, set ~corfu-separator~ (a +space, by default) to the primary character of your orderless component +separator. + +Then, when a new orderless component is desired, use =M-SPC= +(~corfu-insert-separator~) to enter the /first/ component separator in the input, +and arbitrary orderless search terms and new separators can be entered +thereafter. + +To treat the entire input as Orderless input, you can set the customization +option ~corfu-quit-at-boundary=t~. This disables the predicate which checks if the +current completion boundary has been left. In contrast, if you /always/ want to +quit at the boundary, simply set ~corfu-quit-at-boundary=nil~. By default +~corfu-quit-at-boundary~ is set to ~separator~ which quits at completion boundaries +as long as no separator has been inserted with ~corfu-insert-separator~. + +Finally, there exists the user option ~corfu-quit-no-match~ which is set to +=separator= by default. With this setting Corfu stays alive as soon as you start +advanced filtering with a ~corfu-separator~ even if there are no matches, for +example due to a typo. As long as no separator character has been inserted with +~corfu-insert-separator~, Corfu will still quit if there are no matches. This +ensures that the Corfu popup goes away quickly if completion is not possible. + +In the following we show two configurations, one which works best with auto +completion and one which may work better with manual completion if you prefer to +always use =SPC= to separate the Orderless components. + + #+begin_src emacs-lisp + ;; Auto completion example + (use-package corfu + :custom + (corfu-auto t) ;; Enable auto completion + ;; (corfu-separator ?_) ;; Set to orderless separator, if not using space + :bind + ;; Another key binding can be used, such as S-SPC. + ;; (:map corfu-map ("M-SPC" . corfu-insert-separator)) + :init + (corfu-global-mode)) + + ;; Manual completion example + (use-package corfu + :custom + ;; (corfu-separator ?_) ;; Set to orderless separator, if not using space + :bind + ;; Configure SPC for separator insertion + (:map corfu-map ("SPC" . corfu-insert-separator)) + :init + (corfu-global-mode)) +#+end_src + +** TAB-and-Go completion + +You may be interested in configuring Corfu in TAB-and-Go style. Pressing TAB +moves to the next candidate and further input will then commit the selection. + +#+begin_src emacs-lisp + (use-package corfu + ;; TAB-and-Go customizations + :custom + (corfu-cycle t) ;; Enable cycling for `corfu-next/previous' + (corfu-preselect-first nil) ;; Disable candidate preselection + + ;; Use TAB for cycling, default is `corfu-complete'. + :bind + (:map corfu-map + ("TAB" . corfu-next) + ([tab] . corfu-next) + ("S-TAB" . corfu-previous) + ([backtab] . corfu-previous)) + + :init + (corfu-global-mode)) +#+end_src + +** Transfer completion to the minibuffer + +Sometimes it is useful to transfer the Corfu completion session to the +minibuffer, since the minibuffer offers richer interaction features. In +particular, [[https://github.com/oantolin/embark][Embark]] is available in the minibuffer, such that you can act on the +candidates or export/collect the candidates to a separate buffer. Hopefully we +can also add Corfu-support to Embark in the future, such that at least +export/collect is possible directly from Corfu. But in my opinion having the +ability to transfer the Corfu completion to the minibuffer is an even better +feature, since further completion can be performed there. + +The command ~corfu-move-to-minibuffer~ is defined here in terms of +~consult-completion-in-region~, which uses the minibuffer completion UI via +~completing-read~. + +#+begin_src emacs-lisp + (defun corfu-move-to-minibuffer () + (interactive) + (let ((completion-extra-properties corfu--extra) + completion-cycle-threshold completion-cycling) + (apply #'consult-completion-in-region completion-in-region--data))) + (define-key corfu-map "\M-m" #'corfu-move-to-minibuffer) +#+end_src + +* Key bindings + + Corfu uses a transient keymap ~corfu-map~ which is active while the popup is shown. + The keymap defines the following remappings and bindings: + + - ~beginning-of-buffer~ -> ~corfu-first~ + - ~end-of-buffer~ -> ~corfu-last~ + - ~scroll-down-command~ -> ~corfu-scroll-down~ + - ~scroll-up-command~ -> ~corfu-scroll-up~ + - ~next-line~, =down=, =M-n= -> ~corfu-next~ + - ~previous-line~, =up=, =M-p= -> ~corfu-previous~ + - ~completion-at-point~, =TAB= -> ~corfu-complete~ + - =RET= -> ~corfu-insert~ + - =M-g= -> ~corfu-show-location~ + - =M-h= -> ~corfu-show-documentation~ + - =M-SPC= -> ~corfu-insert-separator~ + - =C-g= -> ~corfu-quit~ + - ~keyboard-escape-quit~ -> ~corfu-reset~ + +* Complementary packages + + Corfu works well together with all packages providing code completion via the + ~completion-at-point-functions~. Many modes and packages already provide a Capf + out of the box. Nevertheless you may want to look into complementary packages + to enhance your setup. + + - [[https://github.com/oantolin/orderless][Orderless]]: Corfu supports completion styles, including the advanced + [[https://github.com/oantolin/orderless][Orderless]] completion style, where the filtering expressions are separated by + spaces or another character (see ~corfu-separator~). + + - [[https://github.com/minad/cape][Cape]]: I collect additional Capf backends and =completion-in-region= commands + in my [[https://github.com/minad/cape][Cape]] package. The package provides a file path, a dabbrev completion + backend and a backend which allows you to enter unicode characters in the + form of TeX commands. Cape provides an adapter to reuse Company backends in + Corfu. Furthermore the function ~cape-super-capf~ can merge/groups multiple + Capfs, such that the candidates of multiple Capfs are displayed together at + the same time. + + - [[https://github.com/jdtsmith/kind-icon][kind-icon]]: Icons are supported by Corfu via an external package. For example + the [[https://github.com/jdtsmith/kind-icon][kind-icon]] package provides beautifully styled SVG icons based on + monochromatic icon sets like material design. + + - [[https://github.com/galeo/corfu-doc][corfu-doc]]: The corfu-doc package displays the candidate documentation in a + popup next to the Corfu popup, similar to =company-quickhelp=. + + - [[https://github.com/JonWaltman/pcmpl-args.el][pcmpl-args]]: Extend the Eshell/Shell Pcomplete mechanism with support for many + more commands. Similar to the Fish shell, Pcomplete uses man page parsing to + dynamically retrieve the completions and helpful annotations. This package + brings Eshell completions to another level! + + - [[https://github.com/minad/tempel][Tempel]]: Tiny template/snippet package with templates in Lisp syntax, which + can be used in conjunction with Corfu. + + - [[https://github.com/minad/vertico][Vertico]]: You may also want to look into my [[https://github.com/minad/vertico][Vertico]] package. Vertico is the + minibuffer completion counterpart of Corfu. + +* Alternatives + + - [[https://github.com/company-mode/company-mode][Company]]: Company is a widely used and mature completion package, which + implements a similar interaction model and popup UI as Corfu. While Corfu + relies exclusively on the standard Emacs completion API (Capfs), Company + defines its own API for the backends. Furthermore Company includes its + completion backends, which are incompatible with the Emacs completion + infrastructure. As a result of this design, Company is a more complex + package than Corfu. Company by default uses overlays to display the popup in + contrast to the child frames used by Corfu. Overall both packages work well. + Company is more mature but the integration into Emacs is a bit less tight, + since for example the ~completion-at-point~ command (or the + ~completion-in-region~ function) does not invoke Company. + + - [[https://gitlab.com/protesilaos/mct][Mct]]: Protesilaos' Minibuffer Confines Transcended package supports both + minibuffer completion and completion in region. It reuses the default + completion UI for this purpose and installs a timer which live updates the + completion buffer. The main advantage of Mct is that you work with a regular + Emacs buffer instead of with a popup. You can take advantage of the usual + Emacs commands to navigate in the completions buffer. On top, Mct enhances + the movement such that you can quickly switch between the completions buffer + and the minibuffer or the region which is being completed. Mct does not + support timer-based auto completion, but the integration into Emacs is + naturally tight. + + - [[https://github.com/minad/consult][consult-completion-in-region]]: The Consult package provides the function + ~consult-completion-in-region~ which can be set as + ~completion-in-region-function~ such that it handles ~completion-at-point~. The + function works by transferring the in-buffer completion to the minibuffer. + In the minibuffer, the minibuffer completion UI, for example [[https://github.com/minad/vertico][Vertico]] takes + over. If you prefer to perform all your completions in the minibuffer + ~consult-completion-in-region~ is your best option. + +* Caveats + + Corfu is robust in most scenarios. There are a few known technical caveats. + + - Corfu uses child frames to show the popup. For now Corfu falls back to the + default setting of the ~completion-in-region-function~ on non-graphical + displays. You can use one of the alternatives in terminals. + + - Corfu does not sort by history, since ~completion-at-point~ does not + maintain a history (See branch =history= for a possible solution). + +* Contributions + + Since this package is part of [[http://elpa.gnu.org/packages/corfu.html][GNU ELPA]] contributions require a copyright + assignment to the FSF. diff --git a/elpa/corfu-0.20/corfu-autoloads.el b/elpa/corfu-0.20/corfu-autoloads.el @@ -0,0 +1,60 @@ +;;; corfu-autoloads.el --- automatically extracted autoloads +;; +;;; Code: + +(add-to-list 'load-path (directory-file-name + (or (file-name-directory #$) (car load-path)))) + + +;;;### (autoloads nil "corfu" "corfu.el" (0 0 0 0)) +;;; Generated autoloads from corfu.el + +(autoload 'corfu-mode "corfu" "\ +Completion Overlay Region FUnction. + +If called interactively, enable Corfu mode if ARG is positive, +and disable it if ARG is zero or negative. If called from Lisp, +also enable the mode if ARG is omitted or nil, and toggle it if +ARG is `toggle'; disable the mode otherwise. + +\(fn &optional ARG)" t nil) + +(put 'corfu-global-mode 'globalized-minor-mode t) + +(defvar corfu-global-mode nil "\ +Non-nil if Corfu-Global mode is enabled. +See the `corfu-global-mode' command +for a description of this minor mode. +Setting this variable directly does not take effect; +either customize it (see the info node `Easy Customization') +or call the function `corfu-global-mode'.") + +(custom-autoload 'corfu-global-mode "corfu" nil) + +(autoload 'corfu-global-mode "corfu" "\ +Toggle Corfu mode in all buffers. +With prefix ARG, enable Corfu-Global mode if ARG is positive; +otherwise, disable it. If called from Lisp, enable the mode if +ARG is omitted or nil. + +Corfu mode is enabled in all buffers where +`corfu--on' would do it. +See `corfu-mode' for more information on Corfu mode. + +\(fn &optional ARG)" t nil) + +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "corfu" '("corfu-"))) + +;;;*** + +;;;### (autoloads nil nil ("corfu-pkg.el") (0 0 0 0)) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; coding: utf-8 +;; End: +;;; corfu-autoloads.el ends here diff --git a/elpa/corfu-0.20/corfu-pkg.el b/elpa/corfu-0.20/corfu-pkg.el @@ -0,0 +1,2 @@ +;; Generated package description from corfu.el -*- no-byte-compile: t -*- +(define-package "corfu" "0.20" "Completion Overlay Region FUnction" '((emacs "27.1")) :commit "e2cc92b029bd9334f88d382def7066c35c31b3a9" :authors '(("Daniel Mendler" . "mail@daniel-mendler.de")) :maintainer '("Daniel Mendler" . "mail@daniel-mendler.de") :url "https://github.com/minad/corfu") diff --git a/elpa/corfu-0.20/corfu.el b/elpa/corfu-0.20/corfu.el @@ -0,0 +1,1278 @@ +;;; corfu.el --- Completion Overlay Region FUnction -*- lexical-binding: t -*- + +;; Copyright (C) 2021, 2022 Free Software Foundation, Inc. + +;; Author: Daniel Mendler <mail@daniel-mendler.de> +;; Maintainer: Daniel Mendler <mail@daniel-mendler.de> +;; Created: 2021 +;; Version: 0.20 +;; Package-Requires: ((emacs "27.1")) +;; Homepage: https://github.com/minad/corfu + +;; This file is part of 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: + +;; Corfu enhances the default completion in region function with a +;; completion overlay. The current candidates are shown in a popup +;; below or above the point. Corfu can be considered the minimalistic +;; completion-in-region counterpart of Vertico. + +;;; Code: + +(require 'seq) +(eval-when-compile + (require 'cl-lib) + (require 'subr-x)) + +(defgroup corfu nil + "Completion Overlay Region FUnction." + :group 'convenience + :prefix "corfu-") + +(defcustom corfu-count 10 + "Maximal number of candidates to show." + :type 'integer) + +(defcustom corfu-scroll-margin 2 + "Number of lines at the top and bottom when scrolling. +The value should lie between 0 and corfu-count/2." + :type 'integer) + +(defcustom corfu-min-width 15 + "Popup minimum width in characters." + :type 'integer) + +(defcustom corfu-max-width 100 + "Popup maximum width in characters." + :type 'integer) + +(defcustom corfu-cycle nil + "Enable cycling for `corfu-next' and `corfu-previous'." + :type 'boolean) + +(defcustom corfu-on-exact-match 'insert + "Configure how a single exact match should be handled." + :type '(choice (const insert) (const quit) (const nil))) + +(defcustom corfu-continue-commands + ;; nil is undefined command + '(nil ignore universal-argument universal-argument-more digit-argument + "\\`corfu-" "\\`scroll-other-window") + "Continue Corfu completion after executing these commands." + :type '(repeat (choice regexp symbol))) + +(defcustom corfu-preview-current 'insert + "Preview currently selected candidate. +If the variable has the value `insert', the candidate is automatically +inserted on further input." + :type '(choice boolean (const insert))) + +(defcustom corfu-preselect-first t + "Preselect first candidate." + :type 'boolean) + +(defvar corfu-commit-predicate nil) +(make-obsolete-variable + 'corfu-commit-predicate + "Set `corfu-preview-current' to the value `insert'." + "0.19") + +(defcustom corfu-separator ?\s + "Component separator character. +The character used for separating components in the input. The presence +of this separator character will inhibit quitting at completion +boundaries, so that any further characters can be entered. To enter the +first separator character, call `corfu-insert-separator' (bound to M-SPC +by default). Useful for multi-component completion styles such as +Orderless." + :type 'character) + +(defcustom corfu-quit-at-boundary 'separator + "Automatically quit at completion boundary. +nil: Never quit at completion boundary. +t: Always quit at completion boundary. +separator: Quit at boundary if no `corfu-separator' has been inserted." + :type '(choice boolean (const separator))) + +(defcustom corfu-quit-no-match 'separator + "Automatically quit if no matching candidate is found. +nil: Stay alive even if there is no match. +t: Quit if there is no match. +separator: Only stay alive if there is no match and +`corfu-separator' has been inserted." + :type '(choice boolean (const separator))) + +(defcustom corfu-excluded-modes nil + "List of modes excluded by `corfu-global-mode'." + :type '(repeat symbol)) + +(defcustom corfu-left-margin-width 0.5 + "Width of the left margin in units of the character width." + :type 'float) + +(defcustom corfu-right-margin-width 0.5 + "Width of the right margin in units of the character width." + :type 'float) + +(defcustom corfu-bar-width 0.2 + "Width of the bar in units of the character width." + :type 'float) + +(defcustom corfu-echo-documentation '(1.0 . 0.2) + "Show documentation string in the echo area after that number of seconds. +Set to nil to disable the echo message or to t for an instant message. +The value can be a pair of two floats to specify initial and subsequent +delay." + :type '(choice (const :tag "Never" nil) + (const :tag "Instant" t) + (number :tag "Delay in seconds") + (cons :tag "Two Delays" + (choice :tag "Initial " number)) + (choice :tag "Subsequent" number))) + +(defcustom corfu-margin-formatters nil + "Registry for margin formatter functions. +Each function of the list is called with the completion metadata as +argument until an appropriate formatter is found. The function should +return a formatter function, which takes the candidate string and must +return a string, possibly an icon." + :type 'hook) + +(defcustom corfu-sort-function #'corfu-sort-length-alpha + "Default sorting function, used if no `display-sort-function' is specified." + :type `(choice + (const :tag "No sorting" nil) + (const :tag "By length and alpha" ,#'corfu-sort-length-alpha) + (function :tag "Custom function"))) + +(defcustom corfu-auto-prefix 3 + "Minimum length of prefix for auto completion. +The completion backend can override this with +:company-prefix-length." + :type 'integer) + +(defcustom corfu-auto-delay 0.2 + "Delay for auto completion." + :type 'float) + +(defcustom corfu-auto-commands + '("self-insert-command\\'" + c-electric-colon c-electric-lt-gt c-electric-slash c-scope-operator) + "Commands which initiate auto completion." + :type '(repeat (choice regexp symbol))) + +(defcustom corfu-auto nil + "Enable auto completion." + :type 'boolean) + +(defgroup corfu-faces nil + "Faces used by Corfu." + :group 'corfu + :group 'faces) + +(defface corfu-default + '((((class color) (min-colors 88) (background dark)) :background "#191a1b") + (((class color) (min-colors 88) (background light)) :background "#f0f0f0") + (t :background "gray")) + "Default face used for the popup, in particular the background and foreground color.") + +(defface corfu-current + '((((class color) (min-colors 88) (background dark)) + :background "#00415e" :foreground "white") + (((class color) (min-colors 88) (background light)) + :background "#c0efff" :foreground "black") + (t :background "blue" :foreground "white")) + "Face used to highlight the currently selected candidate.") + +(defface corfu-bar + '((((class color) (min-colors 88) (background dark)) :background "#a8a8a8") + (((class color) (min-colors 88) (background light)) :background "#505050") + (t :background "gray")) + "The background color is used for the scrollbar indicator.") + +(defface corfu-border + '((((class color) (min-colors 88) (background dark)) :background "#323232") + (((class color) (min-colors 88) (background light)) :background "#d7d7d7") + (t :background "gray")) + "The background color used for the thin border.") + +(defface corfu-echo + '((t :inherit completions-annotations)) + "Face used for echo area messages.") + +(defface corfu-annotations + '((t :inherit completions-annotations)) + "Face used for annotations.") + +(defface corfu-deprecated + '((t :inherit shadow :strike-through t)) + "Face used for deprecated candidates.") + +(defvar corfu-map + (let ((map (make-sparse-keymap))) + (define-key map [remap beginning-of-buffer] #'corfu-first) + (define-key map [remap end-of-buffer] #'corfu-last) + (define-key map [remap scroll-down-command] #'corfu-scroll-down) + (define-key map [remap scroll-up-command] #'corfu-scroll-up) + (define-key map [remap next-line] #'corfu-next) + (define-key map [remap previous-line] #'corfu-previous) + (define-key map [remap completion-at-point] #'corfu-complete) + (define-key map [down] #'corfu-next) + (define-key map [up] #'corfu-previous) + (define-key map [remap keyboard-escape-quit] #'corfu-reset) + ;; XXX [tab] is bound because of org-mode + ;; The binding should be removed from org-mode-map. + (define-key map [tab] #'corfu-complete) + (define-key map "\en" #'corfu-next) + (define-key map "\ep" #'corfu-previous) + (define-key map "\C-g" #'corfu-quit) + (define-key map "\r" #'corfu-insert) + (define-key map "\t" #'corfu-complete) + (define-key map "\eg" #'corfu-show-location) + (define-key map "\eh" #'corfu-show-documentation) + (define-key map (concat "\e" " ") #'corfu-insert-separator) ;; Avoid ugly warning + map) + "Corfu keymap used when popup is shown.") + +(defvar corfu--auto-timer nil + "Auto completion timer.") + +(defvar-local corfu--candidates nil + "List of candidates.") + +(defvar-local corfu--metadata nil + "Completion metadata.") + +(defvar-local corfu--base 0 + "Size of the base string, which is concatenated with the candidate.") + +(defvar-local corfu--total 0 + "Length of the candidate list `corfu--candidates'.") + +(defvar-local corfu--highlight #'identity + "Deferred candidate highlighting function.") + +(defvar-local corfu--index -1 + "Index of current candidate or negative for prompt selection.") + +(defvar-local corfu--preselect -1 + "Index of preselected candidate, negative for prompt selection.") + +(defvar-local corfu--scroll 0 + "Scroll position.") + +(defvar-local corfu--input nil + "Cons of last prompt contents and point.") + +(defvar-local corfu--preview-ov nil + "Current candidate overlay.") + +(defvar-local corfu--extra nil + "Extra completion properties.") + +(defvar-local corfu--change-group nil + "Undo change group.") + +(defvar-local corfu--echo-timer nil + "Echo area message timer.") + +(defvar-local corfu--echo-message nil + "Last echo message.") + +(defvar corfu--frame nil + "Popup frame.") + +(defconst corfu--state-vars + '(corfu--base + corfu--candidates + corfu--highlight + corfu--index + corfu--preselect + corfu--scroll + corfu--input + corfu--total + corfu--preview-ov + corfu--extra + corfu--echo-timer + corfu--echo-message + corfu--change-group + corfu--metadata) + "Buffer-local state variables used by Corfu.") + +(defvar corfu--frame-parameters + '((no-accept-focus . t) + (no-focus-on-map . t) + (min-width . t) + (min-height . t) + (width . 0) + (height . 0) + (border-width . 0) + (child-frame-border-width . 1) + (left-fringe . 0) + (right-fringe . 0) + (vertical-scroll-bars . nil) + (horizontal-scroll-bars . nil) + (menu-bar-lines . 0) + (tool-bar-lines . 0) + (tab-bar-lines . 0) + (no-other-frame . t) + (no-other-window . t) + (no-delete-other-windows . t) + (unsplittable . t) + (undecorated . t) + (cursor-type . nil) + (visibility . nil) + (no-special-glyphs . t) + (desktop-dont-save . t)) + "Default child frame parameters.") + +(defvar corfu--buffer-parameters + '((mode-line-format . nil) + (header-line-format . nil) + (tab-line-format . nil) + (tab-bar-format . nil) ;; Emacs 28 tab-bar-format + (frame-title-format . "") + (truncate-lines . t) + (cursor-in-non-selected-windows . nil) + (cursor-type . nil) + (show-trailing-whitespace . nil) + (display-line-numbers . nil) + (left-fringe-width . nil) + (right-fringe-width . nil) + (left-margin-width . 0) + (right-margin-width . 0) + (fringes-outside-margins . 0) + (buffer-read-only . t)) + "Default child frame buffer parameters.") + +(defvar corfu--mouse-ignore-map + (let ((map (make-sparse-keymap))) + (dotimes (i 7) + (dolist (k '(mouse down-mouse drag-mouse double-mouse triple-mouse)) + (define-key map (vector (intern (format "%s-%s" k (1+ i)))) #'ignore))) + map) + "Ignore all mouse clicks.") + +(defun corfu--popup-redirect-focus () + "Redirect focus from popup." + (redirect-frame-focus corfu--frame (frame-parent corfu--frame))) + +(defun corfu--make-buffer (content) + "Create corfu buffer with CONTENT." + (let ((fr face-remapping-alist) + (buffer (get-buffer-create " *corfu*"))) + (with-current-buffer buffer + ;;; XXX HACK install redirect focus hook + (add-hook 'pre-command-hook #'corfu--popup-redirect-focus nil 'local) + ;;; XXX HACK install mouse ignore map + (use-local-map corfu--mouse-ignore-map) + (dolist (var corfu--buffer-parameters) + (set (make-local-variable (car var)) (cdr var))) + (setq-local face-remapping-alist (copy-tree fr)) + (cl-pushnew 'corfu-default (alist-get 'default face-remapping-alist)) + (let ((inhibit-modification-hooks t) + (inhibit-read-only t)) + (erase-buffer) + (insert content) + (goto-char (point-min)))) + buffer)) + +;; Function adapted from posframe.el by tumashu +(defvar x-gtk-resize-child-frames) ;; Not present on non-gtk builds +(defun corfu--make-frame (x y width height content) + "Show child frame at X/Y with WIDTH/HEIGHT and CONTENT." + (let* ((window-min-height 1) + (window-min-width 1) + (x-gtk-resize-child-frames + (let ((case-fold-search t)) + (and + ;; XXX HACK to fix resizing on gtk3/gnome taken from posframe.el + ;; More information: + ;; * https://github.com/minad/corfu/issues/17 + ;; * https://gitlab.gnome.org/GNOME/mutter/-/issues/840 + ;; * https://lists.gnu.org/archive/html/emacs-devel/2020-02/msg00001.html + (string-match-p "gtk3" system-configuration-features) + (string-match-p "gnome\\|cinnamon" (or (getenv "XDG_CURRENT_DESKTOP") + (getenv "DESKTOP_SESSION") "")) + 'resize-mode))) + (after-make-frame-functions) + (edge (window-inside-pixel-edges)) + (ch (default-line-height)) + (border (alist-get 'child-frame-border-width corfu--frame-parameters)) + (x (max border (min (+ (car edge) x (- border)) + (- (frame-pixel-width) width)))) + (yb (+ (cadr edge) (window-tab-line-height) y ch)) + (y (if (> (+ yb (* corfu-count ch) ch ch) (frame-pixel-height)) + (- yb height ch 1) + yb)) + (buffer (corfu--make-buffer content))) + (unless (and (frame-live-p corfu--frame) + (eq (frame-parent corfu--frame) (window-frame))) + (when corfu--frame (delete-frame corfu--frame)) + (setq corfu--frame (make-frame + `((parent-frame . ,(window-frame)) + (minibuffer . ,(minibuffer-window (window-frame))) + (line-spacing . ,line-spacing) + ;; Set `internal-border-width' for Emacs 27 + (internal-border-width . ,border) + ,@corfu--frame-parameters)))) + ;; XXX HACK Setting the same frame-parameter/face-background is not a nop (BUG!). + ;; Check explicitly before applying the setting. + ;; Without the check, the frame flickers on Mac. + ;; XXX HACK We have to apply the face background before adjusting the frame parameter, + ;; otherwise the border is not updated (BUG!). + (let* ((face (if (facep 'child-frame-border) 'child-frame-border 'internal-border)) + (new (face-attribute 'corfu-border :background nil 'default))) + (unless (equal (face-attribute face :background corfu--frame 'default) new) + (set-face-background face new corfu--frame))) + (let ((new (face-attribute 'corfu-default :background nil 'default))) + (unless (equal (frame-parameter corfu--frame 'background-color) new) + (set-frame-parameter corfu--frame 'background-color new))) + (let ((win (frame-root-window corfu--frame))) + (set-window-buffer win buffer) + ;; Mark window as dedicated to prevent frame reuse (#60) + (set-window-dedicated-p win t)) + (set-frame-size corfu--frame width height t) + (if (frame-visible-p corfu--frame) + ;; XXX HACK Avoid flicker when frame is already visible. + ;; Redisplay, wait for resize and then move the frame. + (unless (equal (frame-position corfu--frame) (cons x y)) + (redisplay 'force) + (sleep-for 0.01) + (set-frame-position corfu--frame x y)) + ;; XXX HACK: Force redisplay, otherwise the popup sometimes does not display content. + (set-frame-position corfu--frame x y) + (redisplay 'force) + (make-frame-visible corfu--frame)))) + +(defun corfu--popup-show (pos off width lines &optional curr lo bar) + "Show LINES as popup at POS - OFF. +WIDTH is the width of the popup. +The current candidate CURR is highlighted. +A scroll bar is displayed from LO to LO+BAR." + (let* ((ch (default-line-height)) + (cw (default-font-width)) + (ml (ceiling (* cw corfu-left-margin-width))) + (mr (ceiling (* cw corfu-right-margin-width))) + (bw (ceiling (min mr (* cw corfu-bar-width)))) + (marginl (and (> ml 0) (propertize " " 'display `(space :width (,ml))))) + (marginr (and (> mr 0) (propertize " " 'display `(space :align-to right)))) + (sbar (when (> bw 0) + (concat (propertize " " 'display `(space :align-to (- right (,mr)))) + (propertize " " 'display `(space :width (,(- mr bw)))) + (propertize " " 'face 'corfu-bar 'display `(space :width (,bw)))))) + (row 0) + (pos (posn-x-y (posn-at-point pos))) + (x (or (car pos) 0)) + (y (or (cdr pos) 0))) + (corfu--make-frame + (- x ml (* cw off)) y + (+ (* width cw) ml mr) (* (length lines) ch) + (mapconcat (lambda (line) + (let ((str (concat marginl line + (if (and lo (<= lo row (+ lo bar))) sbar marginr)))) + (when (eq row curr) + (add-face-text-property + 0 (length str) 'corfu-current 'append str)) + (setq row (1+ row)) + str)) + lines "\n")))) + +(defun corfu--popup-hide () + "Hide Corfu popup." + (when (frame-live-p corfu--frame) + (make-frame-invisible corfu--frame) + (with-current-buffer (window-buffer (frame-root-window corfu--frame)) + (let ((inhibit-read-only t)) + (erase-buffer))))) + +(defun corfu--move-to-front (elem list) + "Move ELEM to front of LIST." + (if-let (found (member elem list)) + (let ((head (list (car found)))) + (nconc head (delq (setcar found nil) list))) + list)) + +;; bug#47711: Deferred highlighting for `completion-all-completions' +;; XXX There is one complication: `completion--twq-all' already adds `completions-common-part'. +(defun corfu--all-completions (&rest args) + "Compute all completions for ARGS with deferred highlighting." + (cl-letf* ((orig-pcm (symbol-function #'completion-pcm--hilit-commonality)) + (orig-flex (symbol-function #'completion-flex-all-completions)) + ((symbol-function #'completion-flex-all-completions) + (lambda (&rest args) + ;; Unfortunately for flex we have to undo the deferred highlighting, since flex uses + ;; the completion-score for sorting, which is applied during highlighting. + (cl-letf (((symbol-function #'completion-pcm--hilit-commonality) orig-pcm)) + (apply orig-flex args)))) + ;; Defer the following highlighting functions + (hl #'identity) + ((symbol-function #'completion-hilit-commonality) + (lambda (cands prefix &optional base) + (setq hl (lambda (x) (nconc (completion-hilit-commonality x prefix base) nil))) + (and cands (nconc cands base)))) + ((symbol-function #'completion-pcm--hilit-commonality) + (lambda (pattern cands) + (setq hl (lambda (x) + ;; `completion-pcm--hilit-commonality' sometimes throws an internal error + ;; for example when entering "/sudo:://u". + (condition-case nil + (completion-pcm--hilit-commonality pattern x) + (t x)))) + cands))) + ;; Only advise orderless after it has been loaded to avoid load order issues + (if (and (fboundp 'orderless-highlight-matches) (fboundp 'orderless-pattern-compiler)) + (cl-letf (((symbol-function 'orderless-highlight-matches) + (lambda (pattern cands) + (let ((regexps (orderless-pattern-compiler pattern))) + (setq hl (lambda (x) (orderless-highlight-matches regexps x)))) + cands))) + (cons (apply #'completion-all-completions args) hl)) + (cons (apply #'completion-all-completions args) hl)))) + +(defun corfu--sort-predicate (x y) + "Sorting predicate which compares X and Y." + (or (< (length x) (length y)) (and (= (length x) (length y)) (string< x y)))) + +(defun corfu-sort-length-alpha (list) + "Sort LIST by length and alphabetically." + (sort list #'corfu--sort-predicate)) + +(defmacro corfu--partition! (list form) + "Evaluate FORM for every element and partition LIST." + (let ((head1 (make-symbol "head1")) + (head2 (make-symbol "head2")) + (tail1 (make-symbol "tail1")) + (tail2 (make-symbol "tail2"))) + `(let* ((,head1 (cons nil nil)) + (,head2 (cons nil nil)) + (,tail1 ,head1) + (,tail2 ,head2)) + (while ,list + (if (let ((it (car ,list))) ,form) + (progn + (setcdr ,tail1 ,list) + (pop ,tail1)) + (setcdr ,tail2 ,list) + (pop ,tail2)) + (pop ,list)) + (setcdr ,tail1 (cdr ,head2)) + (setcdr ,tail2 nil) + (setq ,list (cdr ,head1))))) + +(defun corfu--move-prefix-candidates-to-front (field candidates) + "Move CANDIDATES which match prefix of FIELD to the beginning." + (let* ((word (substring field 0 + (seq-position field corfu-separator))) + (len (length word))) + (corfu--partition! + candidates + (and (>= (length it) len) + (eq t (compare-strings word 0 len it 0 len + completion-ignore-case)))))) + +(defun corfu--filter-files (files) + "Filter FILES by `completion-ignored-extensions'." + (let ((re (concat "\\(?:\\(?:\\`\\|/\\)\\.\\.?/\\|" + (regexp-opt completion-ignored-extensions) + "\\)\\'"))) + (or (seq-remove (lambda (x) (string-match-p re x)) files) files))) + +(defun corfu--sort-function () + "Return the sorting function." + (or (corfu--metadata-get 'display-sort-function) corfu-sort-function)) + +(defun corfu--recompute-candidates (str pt table pred) + "Recompute candidates from STR, PT, TABLE and PRED." + (pcase-let* ((before (substring str 0 pt)) + (after (substring str pt)) + (corfu--metadata (completion-metadata before table pred)) + ;; bug#47678: `completion-boundaries` fails for `partial-completion` + ;; if the cursor is moved between the slashes of "~//". + ;; See also vertico.el which has the same issue. + (bounds (or (condition-case nil + (completion-boundaries before table pred after) + (t (cons 0 (length after)))))) + (field (substring str (car bounds) (+ pt (cdr bounds)))) + (completing-file (eq (corfu--metadata-get 'category) 'file)) + (`(,all . ,hl) (corfu--all-completions str table pred pt corfu--metadata)) + (base (or (when-let (z (last all)) (prog1 (cdr z) (setcdr z nil))) 0))) + ;; Filter the ignored file extensions. We cannot use modified predicate for this filtering, + ;; since this breaks the special casing in the `completion-file-name-table' for `file-exists-p' + ;; and `file-directory-p'. + (when completing-file (setq all (corfu--filter-files all))) + (setq all (delete-consecutive-dups (funcall (or (corfu--sort-function) #'identity) all))) + (setq all (corfu--move-prefix-candidates-to-front field all)) + (when (and completing-file (not (string-suffix-p "/" field))) + (setq all (corfu--move-to-front (concat field "/") all))) + (setq all (corfu--move-to-front field all)) + (list base all (length all) hl corfu--metadata + ;; Select the prompt when the input is a valid completion + ;; and if it is not equal to the first candidate. + (if (or (not corfu-preselect-first) (not all) + (and (not (equal field (car all))) + (not (and completing-file (equal (concat field "/") (car all)))) + (test-completion str table pred))) + -1 0)))) + +(defun corfu--update-candidates (str pt table pred) + "Update candidates from STR, PT, TABLE and PRED." + ;; Redisplay such that the input becomes immediately visible before the + ;; expensive candidate recomputation is performed (Issue #48). See also + ;; corresponding vertico#89. + (redisplay) + (pcase (while-no-input (corfu--recompute-candidates str pt table pred)) + ('nil (keyboard-quit)) + (`(,base ,candidates ,total ,hl ,metadata ,preselect) + (setq corfu--input (cons str pt) + corfu--candidates candidates + corfu--base base + corfu--total total + corfu--preselect preselect + corfu--index preselect + corfu--highlight hl + corfu--metadata metadata)))) + +(defun corfu--match-symbol-p (pattern sym) + "Return non-nil if SYM is matching an element of the PATTERN list." + (and (symbolp sym) + (cl-loop for x in pattern + thereis (if (symbolp x) + (eq sym x) + (string-match-p x (symbol-name sym)))))) + +(defun corfu-quit () + "Quit Corfu completion." + (interactive) + (completion-in-region-mode -1)) + +(defun corfu-reset () + "Reset Corfu completion. +This command can be executed multiple times by hammering the ESC key. If a +candidate is selected, unselect the candidate. Otherwise reset the input. If +there hasn't been any input, then quit." + (interactive) + (if (/= corfu--index corfu--preselect) + (progn + (corfu--goto -1) + (setq this-command #'corfu-first)) + ;; Cancel all changes and start new change group. + (cancel-change-group corfu--change-group) + (activate-change-group (setq corfu--change-group (prepare-change-group))) + (when (eq last-command #'corfu-reset) (corfu-quit)))) + +(defun corfu--affixate (cands) + "Annotate CANDS with annotation function." + (setq cands + (if-let (aff (or (corfu--metadata-get 'affixation-function) + (plist-get corfu--extra :affixation-function))) + (funcall aff cands) + (if-let (ann (or (corfu--metadata-get 'annotation-function) + (plist-get corfu--extra :annotation-function))) + (cl-loop for cand in cands collect + (let ((suffix (or (funcall ann cand) ""))) + ;; The default completion UI adds the `completions-annotations' face + ;; if no other faces are present. We use a custom `corfu-annotations' + ;; face to allow further styling which fits better for popups. + (unless (text-property-not-all 0 (length suffix) 'face nil suffix) + (setq suffix (propertize suffix 'face 'corfu-annotations))) + (list cand "" suffix))) + (cl-loop for cand in cands collect (list cand "" ""))))) + (let* ((dep (plist-get corfu--extra :company-deprecated)) + (completion-extra-properties corfu--extra) + (mf (run-hook-with-args-until-success 'corfu-margin-formatters corfu--metadata))) + (cl-loop for x in cands for (c . _) = x do + (when mf + (setf (cadr x) (funcall mf c))) + (when (and dep (funcall dep c)) + (setcar x (setq c (substring c))) + (add-face-text-property 0 (length c) 'corfu-deprecated 'append c))) + (cons mf cands))) + +(defun corfu--metadata-get (prop) + "Return PROP from completion metadata." + ;; Note: Do not use `completion-metadata-get' in order to avoid Marginalia. + ;; The Marginalia annotators are too heavy for the Corfu popup! + (cdr (assq prop corfu--metadata))) + +(defun corfu--format-candidates (cands) + "Format annotated CANDS." + (setq cands + (cl-loop for c in cands collect + (cl-loop for s in c collect + (replace-regexp-in-string "[ \t]*\n[ \t]*" " " s)))) + (let* ((cw (cl-loop for x in cands maximize (string-width (car x)))) + (pw (cl-loop for x in cands maximize (string-width (cadr x)))) + (sw (cl-loop for x in cands maximize (string-width (caddr x)))) + (width (+ pw cw sw)) + ;; -4 because of margins and some additional safety + (max-width (min corfu-max-width (- (frame-width) 4)))) + (when (> width max-width) + (setq sw (max 0 (- max-width pw cw)) + width (+ pw cw sw))) + (when (< width corfu-min-width) + (setq cw (+ cw (- corfu-min-width width)) + width corfu-min-width)) + (setq width (min width max-width)) + (list pw width + (cl-loop for (cand prefix suffix) in cands collect + (truncate-string-to-width + (concat prefix + (make-string (max 0 (- pw (string-width prefix))) ?\s) + cand + (when (/= sw 0) + (make-string + (+ (max 0 (- cw (string-width cand))) + (max 0 (- sw (string-width suffix)))) + ?\s)) + suffix) + width))))) + +(defun corfu--update-scroll () + "Update scroll position." + (let ((off (max (min corfu-scroll-margin (/ corfu-count 2)) 0)) + (corr (if (= corfu-scroll-margin (/ corfu-count 2)) (1- (mod corfu-count 2)) 0))) + (setq corfu--scroll (min (max 0 (- corfu--total corfu-count)) + (max 0 (+ corfu--index off 1 (- corfu-count)) + (min (- corfu--index off corr) corfu--scroll)))))) + +(defun corfu--candidates-popup (pos) + "Show candidates popup at POS." + (corfu--update-scroll) + (pcase-let* ((last (min (+ corfu--scroll corfu-count) corfu--total)) + (bar (ceiling (* corfu-count corfu-count) corfu--total)) + (lo (min (- corfu-count bar 1) (floor (* corfu-count corfu--scroll) corfu--total))) + (`(,mf . ,acands) (corfu--affixate (funcall corfu--highlight + (seq-subseq corfu--candidates corfu--scroll last)))) + (`(,pw ,width ,fcands) (corfu--format-candidates acands)) + ;; Disable the left margin if a margin formatter is active. + (corfu-left-margin-width (if mf 0 corfu-left-margin-width))) + ;; Nonlinearity at the end and the beginning + (when (/= corfu--scroll 0) + (setq lo (max 1 lo))) + (when (/= last corfu--total) + (setq lo (min (- corfu-count bar 2) lo))) + (corfu--popup-show (+ pos corfu--base) pw width fcands (- corfu--index corfu--scroll) + (and (> corfu--total corfu-count) lo) bar))) + +(defun corfu--preview-current (beg end str) + "Show current candidate as overlay given BEG, END and STR." + (when-let (cand (and corfu-preview-current (>= corfu--index 0) + (/= corfu--index corfu--preselect) + (nth corfu--index corfu--candidates))) + (setq corfu--preview-ov (make-overlay beg end nil t t)) + (overlay-put corfu--preview-ov 'priority 1000) + (overlay-put corfu--preview-ov 'window (selected-window)) + (overlay-put corfu--preview-ov + (if (= beg end) 'after-string 'display) + (concat (substring str 0 corfu--base) cand)))) + +(defun corfu--echo-refresh () + "Refresh echo message to prevent flicker during redisplay." + (when corfu--echo-timer + (cancel-timer corfu--echo-timer) + (setq corfu--echo-timer nil)) + (corfu--echo-show corfu--echo-message)) + +(defun corfu--echo-show (&optional msg) + "Show MSG in echo area." + (when (or msg corfu--echo-message) + (setq msg (or msg "") + corfu--echo-message msg) + (corfu--message "%s" (if (text-property-not-all 0 (length msg) 'face nil msg) + msg + (propertize msg 'face 'corfu-echo))))) + +(defun corfu--echo-documentation () + "Show documentation string of current candidate in echo area." + (if-let* ((delay (if (consp corfu-echo-documentation) + (funcall (if corfu--echo-message #'cdr #'car) + corfu-echo-documentation) + corfu-echo-documentation)) + (fun (plist-get corfu--extra :company-docsig)) + (cand (and (>= corfu--index 0) + (nth corfu--index corfu--candidates)))) + (if (or (eq delay t) (<= delay 0)) + (corfu--echo-show (funcall fun cand)) + (when corfu--echo-timer (cancel-timer corfu--echo-timer)) + (setq corfu--echo-timer + (run-at-time delay nil + (lambda () + (corfu--echo-show (funcall fun cand))))) + (corfu--echo-show)) + (corfu--echo-show))) + +(defun corfu--update () + "Refresh Corfu UI." + (pcase-let* ((`(,beg ,end ,table ,pred) completion-in-region--data) + (pt (- (point) beg)) + (str (buffer-substring-no-properties beg end)) + (initializing (not corfu--input))) + (corfu--echo-refresh) + (cond + ;; XXX Guard against errors during candidate generation. + ;; Turn off completion immediately if there are errors + ;; For example dabbrev throws error "No dynamic expansion ... found". + ;; TODO Report this as a bug? Are completion tables supposed to throw errors? + ((condition-case err + ;; Only recompute when input changed + (unless (equal corfu--input (cons str pt)) + (corfu--update-candidates str pt table pred) + nil) + (error (corfu-quit) + (message "Corfu completion error: %s" (error-message-string err))))) + ;; 1) Initializing, no candidates => Quit. Happens during auto completion. + ((and initializing (not corfu--candidates)) + (corfu-quit)) + ;; 2) Single exactly matching candidate and no further completion is possible. + ((and (not (equal str "")) + (equal (car corfu--candidates) str) (not (cdr corfu--candidates)) + (not (consp (completion-try-completion str table pred pt corfu--metadata))) + (or initializing corfu-on-exact-match)) + ;; Quit directly when initializing. This happens during auto completion. + (if (or initializing (eq corfu-on-exact-match 'quit)) + (corfu-quit) + (corfu--done str 'finished))) + ;; 3) There exist candidates => Show candidates popup. + (corfu--candidates + (corfu--candidates-popup beg) + (corfu--preview-current beg end str) + (corfu--echo-documentation) + (redisplay 'force)) ;; XXX HACK Ensure that popup is redisplayed + ;; 4) There are no candidates & corfu-quit-no-match => Confirmation popup. + ((and (not corfu--candidates) + (pcase-exhaustive corfu-quit-no-match + ('t nil) + ('nil t) + ('separator (seq-contains-p (car corfu--input) corfu-separator)))) + (corfu--popup-show beg 0 8 '(#("No match" 0 8 (face italic)))) + (redisplay 'force)) ;; XXX HACK Ensure that popup is redisplayed + (t (corfu-quit))))) + +(defun corfu--pre-command () + "Insert selected candidate unless command is marked to continue completion." + (when corfu--preview-ov + (delete-overlay corfu--preview-ov) + (setq corfu--preview-ov nil)) + (when (and (eq corfu-preview-current 'insert) + (/= corfu--index corfu--preselect) + (not (corfu--match-symbol-p corfu-continue-commands this-command))) + (corfu--insert 'exact))) + +(defun corfu-insert-separator () + "Insert a separator character, inhibiting quit on completion boundary." + (interactive) + (insert corfu-separator)) + +(defun corfu--post-command () + "Refresh Corfu after last command." + (or (pcase completion-in-region--data + (`(,beg ,end . ,_) + (when (let ((pt (point))) + (and (eq (marker-buffer beg) (current-buffer)) + ;; Check ranges + (<= beg pt end) + (save-excursion + (goto-char beg) + (<= (line-beginning-position) pt (line-end-position))) + (or + ;; Check if it is an explicitly listed continue command + (corfu--match-symbol-p corfu-continue-commands this-command) + (and + ;; Check for empty input + (or (not corfu--input) (< beg end)) + ;; Check separator or predicate + (or (not corfu-quit-at-boundary) + (and (eq corfu-quit-at-boundary 'separator) + (or (eq this-command #'corfu-insert-separator) + ;; with separator, any further chars allowed + (seq-contains-p (car corfu--input) corfu-separator))) + (funcall completion-in-region-mode--predicate)))))) + (corfu--update) + t))) + (corfu-quit))) + +(defun corfu--goto (index) + "Go to candidate with INDEX." + (setq corfu--index (max corfu--preselect (min index (1- corfu--total))))) + +(defun corfu-next (&optional n) + "Go forward N candidates." + (interactive "p") + (let ((index (+ corfu--index (or n 1)))) + (corfu--goto + (cond + ((not corfu-cycle) index) + ((= corfu--total 0) -1) + ((< corfu--preselect 0) (1- (mod (1+ index) (1+ corfu--total)))) + (t (mod index corfu--total)))))) + +(defun corfu-previous (&optional n) + "Go backward N candidates." + (interactive "p") + (corfu-next (- (or n 1)))) + +(defun corfu-scroll-down (&optional n) + "Go back by N pages." + (interactive "p") + (corfu--goto (max 0 (- corfu--index (* (or n 1) corfu-count))))) + +(defun corfu-scroll-up (&optional n) + "Go forward by N pages." + (interactive "p") + (corfu-scroll-down (- (or n 1)))) + +(defun corfu-first () + "Go to first candidate, or to the prompt when the first candidate is selected." + (interactive) + (corfu--goto (if (> corfu--index 0) 0 -1))) + +(defun corfu-last () + "Go to last candidate." + (interactive) + (corfu--goto (1- corfu--total))) + +(defun corfu--restore-on-next-command () + "Restore window configuration before next command." + (let ((config (current-window-configuration)) + (other other-window-scroll-buffer) + (restore (make-symbol "corfu--restore"))) + (fset restore + (lambda () + (setq other-window-scroll-buffer other) + (unless (memq this-command '(scroll-other-window scroll-other-window-down)) + (when (memq this-command '(corfu-quit corfu-reset)) + (setq this-command #'ignore)) + (remove-hook 'pre-command-hook restore) + (set-window-configuration config)))) + (add-hook 'pre-command-hook restore))) + +;; Company support, taken from `company.el', see `company-show-doc-buffer'. +(defun corfu-show-documentation () + "Show documentation of current candidate." + (interactive) + (when (< corfu--index 0) + (user-error "No candidate selected")) + (if-let* ((fun (plist-get corfu--extra :company-doc-buffer)) + (res (funcall fun (nth corfu--index corfu--candidates)))) + (let ((buf (or (car-safe res) res))) + (corfu--restore-on-next-command) + (setq other-window-scroll-buffer (get-buffer buf)) + (set-window-start (display-buffer buf t) (or (cdr-safe res) (point-min)))) + (user-error "No documentation available"))) + +;; Company support, taken from `company.el', see `company-show-location'. +(defun corfu-show-location () + "Show location of current candidate." + (interactive) + (when (< corfu--index 0) + (user-error "No candidate selected")) + (if-let* ((fun (plist-get corfu--extra :company-location)) + (loc (funcall fun (nth corfu--index corfu--candidates)))) + (let ((buf (or (and (bufferp (car loc)) (car loc)) (find-file-noselect (car loc) t)))) + (corfu--restore-on-next-command) + (setq other-window-scroll-buffer buf) + (with-selected-window (display-buffer buf t) + (save-restriction + (widen) + (if (bufferp (car loc)) + (goto-char (cdr loc)) + (goto-char (point-min)) + (forward-line (1- (cdr loc)))) + (set-window-start nil (point))))) + (user-error "No candidate location available"))) + +(defun corfu-complete () + "Try to complete current input." + (interactive) + (pcase-let ((`(,beg ,end ,table ,pred) completion-in-region--data)) + (if (>= corfu--index 0) + ;; Continue completion with selected candidate + (corfu--insert nil) + ;; Try to complete the current input string + (let* ((pt (max 0 (- (point) beg))) + (str (buffer-substring-no-properties beg end)) + (metadata (completion-metadata (substring str 0 pt) table pred))) + (pcase (completion-try-completion str table pred pt metadata) + (`(,newstr . ,newpt) + (unless (equal str newstr) + (completion--replace beg end (concat newstr))) + (goto-char (+ beg newpt)))))) + ;; No further completion is possible and the current string is a valid + ;; match, exit with status 'finished. + (let* ((pt (max 0 (- (point) beg))) + (str (buffer-substring-no-properties beg end)) + (metadata (completion-metadata (substring str 0 pt) table pred))) + (when (and (not (consp (completion-try-completion str table pred pt metadata))) + (test-completion str table pred)) + (corfu--done str 'finished))))) + +(defun corfu--insert (status) + "Insert current candidate, exit with STATUS if non-nil." + (pcase-let* ((`(,beg ,end ,table ,pred) completion-in-region--data) + (str (buffer-substring-no-properties beg end))) + ;; Replace if candidate is selected or if current input is not valid completion. + ;; For example str can be a valid path, e.g., ~/dir/. + (when (or (>= corfu--index 0) (equal str "") + (not (test-completion str table pred))) + ;; XXX There is a small bug here, depending on interpretation. + ;; When completing "~/emacs/master/li|/calc" where "|" is the + ;; cursor, then the candidate only includes the prefix + ;; "~/emacs/master/lisp/", but not the suffix "/calc". Default + ;; completion has the same problem when selecting in the + ;; *Completions* buffer. See bug#48356. + (setq str (concat (substring str 0 corfu--base) + (substring-no-properties (nth (max 0 corfu--index) corfu--candidates)))) + (completion--replace beg end str) + (corfu--goto -1)) ;; Reset selection, but continue completion. + (when status (corfu--done str status)))) ;; Exit with status + +(defun corfu--done (str status) + "Call the `:exit-function' with STR and STATUS and exit completion." + (let ((exit (plist-get corfu--extra :exit-function))) + ;; For successfull completions, amalgamate undo operations, + ;; such that completion can be undone in a single step. + (undo-amalgamate-change-group corfu--change-group) + (corfu-quit) + ;; XXX Is the :exit-function handling sufficient? + (when exit (funcall exit str status)))) + +(defun corfu-insert () + "Insert current candidate." + (interactive) + (if (> corfu--total 0) + (corfu--insert 'finished) + (corfu-quit))) + +(defun corfu--setup () + "Setup Corfu completion state." + (setq corfu--extra completion-extra-properties) + (completion-in-region-mode 1) + (undo-boundary) ;; Necessary to support `corfu-reset' + (activate-change-group (setq corfu--change-group (prepare-change-group))) + (setcdr (assq #'completion-in-region-mode minor-mode-overriding-map-alist) corfu-map) + (add-hook 'pre-command-hook #'corfu--pre-command nil 'local) + (add-hook 'post-command-hook #'corfu--post-command) + ;; Disable default post-command handling, since we have our own + ;; checks in `corfu--post-command'. + (remove-hook 'post-command-hook #'completion-in-region--postch) + (let ((sym (make-symbol "corfu--teardown")) + (buf (current-buffer))) + (fset sym (lambda () + ;; Ensure that the teardown runs in the correct buffer, if still alive. + (unless completion-in-region-mode + (remove-hook 'completion-in-region-mode-hook sym) + (with-current-buffer (if (buffer-live-p buf) buf (current-buffer)) + (corfu--teardown))))) + (add-hook 'completion-in-region-mode-hook sym))) + +(defun corfu--teardown () + "Teardown Corfu." + ;; Redisplay such that the input becomes immediately visible before the popup + ;; hiding, which is slow (Issue #48). See also corresponding vertico#89. + (redisplay) + (corfu--popup-hide) + (remove-hook 'pre-command-hook #'corfu--pre-command 'local) + (remove-hook 'post-command-hook #'corfu--post-command) + (when corfu--preview-ov (delete-overlay corfu--preview-ov)) + (when corfu--echo-timer (cancel-timer corfu--echo-timer)) + (corfu--echo-show) + (accept-change-group corfu--change-group) + (mapc #'kill-local-variable corfu--state-vars)) + +(defun corfu--in-region (beg end table &optional pred) + "Corfu completion in region function. +See `completion-in-region' for the arguments BEG, END, TABLE, PRED." + (barf-if-buffer-read-only) + (if (not (display-graphic-p)) + ;; XXX Warning this can result in an endless loop when `completion-in-region-function' + ;; is set *globally* to `corfu--in-region'. This should never happen. + (funcall (default-value 'completion-in-region-function) beg end table pred) + ;; Restart the completion. This can happen for example if C-M-/ + ;; (`dabbrev-completion') is pressed while the Corfu popup is already open. + (when completion-in-region-mode (corfu-quit)) + (let* ((pt (max 0 (- (point) beg))) + (str (buffer-substring-no-properties beg end)) + (before (substring str 0 pt)) + (metadata (completion-metadata before table pred)) + (exit (plist-get completion-extra-properties :exit-function)) + (threshold (completion--cycle-threshold metadata)) + (completion-in-region-mode-predicate + (or completion-in-region-mode-predicate (lambda () t)))) + (pcase (completion-try-completion str table pred pt metadata) + ('nil (corfu--message "No match") nil) + ('t + (goto-char end) + (corfu--message "Sole match") + (when exit (funcall exit str 'finished)) + t) + (`(,newstr . ,newpt) + (pcase-let ((`(,base ,candidates ,total . ,_) + (corfu--recompute-candidates str pt table pred))) + (setq beg (copy-marker beg) + end (copy-marker end t) + completion-in-region--data (list beg end table pred)) + (unless (equal str newstr) + (completion--replace beg end (concat newstr))) + (goto-char (+ beg newpt)) + (if (= total 1) + (when exit + (funcall exit newstr + ;; If completion is finished and cannot be further completed, + ; return 'finished. Otherwise return 'exact. + (if (eq (try-completion (car candidates) table pred) t) + 'finished 'exact))) + (if (not (and threshold (or (eq threshold t) (>= threshold total)))) + (corfu--setup) + (corfu--cycle-candidates total candidates (+ base beg) end) + ;; Do not show Corfu when "trivially" cycling, i.e., + ;; when the completion is finished after the candidate. + (unless (equal (completion-boundaries + (buffer-substring-no-properties beg end) + table pred "") '(0 . 0)) + (corfu--setup))))) + t))))) + +(defun corfu--message (&rest msg) + "Show completion MSG." + (let (message-log-max) (apply #'message msg))) + +(defun corfu--cycle-candidates (total cands beg end) + "Cycle between TOTAL number of CANDS. +See `completion-in-region' for the arguments BEG, END, TABLE, PRED." + (let* ((idx 0) + (map (make-sparse-keymap)) + (replace (lambda () + (interactive) + (completion--replace beg end (concat (nth idx cands))) + (corfu--message "Cycling %d/%d..." (1+ idx) total) + (setq idx (mod (1+ idx) total)) + (set-transient-map map)))) + (define-key map [remap completion-at-point] replace) + (define-key map [remap corfu-complete] replace) + (define-key map (vector last-command-event) replace) + (funcall replace))) + +(defun corfu--auto-complete (tick) + "Initiate auto completion if TICK did not change." + (setq corfu--auto-timer nil) + (when (and (not completion-in-region-mode) (equal tick (corfu--auto-tick))) + (pcase (while-no-input ;; Interruptible capf query + (run-hook-wrapped 'completion-at-point-functions #'corfu--capf-wrapper)) + (`(,fun ,beg ,end ,table . ,plist) + (let ((completion-in-region-mode-predicate + (lambda () (eq beg (car-safe (funcall fun))))) + (completion-extra-properties plist)) + (setq completion-in-region--data + (list (copy-marker beg) (copy-marker end t) table + (plist-get plist :predicate))) + (corfu--setup) + (corfu--update)))))) + +(defun corfu--auto-post-command () + "Post command hook which initiates auto completion." + (when corfu--auto-timer + (cancel-timer corfu--auto-timer) + (setq corfu--auto-timer nil)) + (when (and (not completion-in-region-mode) + (not defining-kbd-macro) + (corfu--match-symbol-p corfu-auto-commands this-command) + (display-graphic-p)) + ;; NOTE: Do not use idle timer since this leads to unacceptable slowdowns, + ;; in particular if flyspell-mode is enabled. + (setq corfu--auto-timer + (run-at-time corfu-auto-delay nil + #'corfu--auto-complete (corfu--auto-tick))))) + +(defun corfu--auto-tick () + "Return the current tick/status of the buffer. +Auto completion is only performed if the tick did not change." + (list (current-buffer) (buffer-chars-modified-tick) (point))) + +;;;###autoload +(define-minor-mode corfu-mode + "Completion Overlay Region FUnction." + :global nil :group 'corfu + (cond + (corfu-mode + ;; FIXME: Install advice which fixes `completion--capf-wrapper', such that + ;; it respects the completion styles for non-exclusive capfs. See FIXME in + ;; the `completion--capf-wrapper' function in minibuffer.el, where the + ;; issue has been mentioned. We never uninstall this advice since the + ;; advice is active *globally*. + (advice-add #'completion--capf-wrapper :around #'corfu--capf-wrapper-advice) + (advice-add #'eldoc-display-message-no-interference-p :before-while #'corfu--allow-eldoc) + (and corfu-auto (add-hook 'post-command-hook #'corfu--auto-post-command nil 'local)) + (setq-local completion-in-region-function #'corfu--in-region)) + (t + (remove-hook 'post-command-hook #'corfu--auto-post-command 'local) + (kill-local-variable 'completion-in-region-function)))) + +(defun corfu--capf-wrapper (fun &optional prefix) + "Wrapper for `completion-at-point' FUN. +The wrapper determines if the capf is applicable at the current position +and performs sanity checking on the returned result. PREFIX is a prefix +length override, set to t for manual completion." + (pcase (funcall fun) + ((and res `(,beg ,end ,table . ,plist)) + (and (integer-or-marker-p beg) ;; Valid capf result + (<= beg (point) end) ;; Sanity checking + ;; When auto completing, check the prefix length! + (let ((len (or prefix + (plist-get plist :company-prefix-length) + (- (point) beg)))) + (or (eq len t) (>= len corfu-auto-prefix))) + ;; For non-exclusive capfs, check for valid completion. + (or (not (eq 'no (plist-get plist :exclusive))) + (let* ((str (buffer-substring-no-properties beg end)) + (pt (- (point) beg)) + (pred (plist-get plist :predicate)) + (md (completion-metadata (substring str 0 pt) table pred))) + ;; We use `completion-try-completion' to check if there are + ;; completions. The upstream `completion--capf-wrapper' uses + ;; `try-completion' which is incorrect since it only checks for + ;; prefix completions. + (completion-try-completion str table pred pt md))) + (cons fun res))))) + +(defun corfu--capf-wrapper-advice (orig fun which) + "Around advice for `completion--capf-wrapper'. +The ORIG function takes the FUN and WHICH arguments." + (if corfu-mode (corfu--capf-wrapper fun t) (funcall orig fun which))) + +;;;###autoload +(define-globalized-minor-mode corfu-global-mode corfu-mode corfu--on :group 'corfu) + +(defun corfu--on () + "Turn `corfu-mode' on." + (unless (or noninteractive + (eq (aref (buffer-name) 0) ?\s) + (memq major-mode corfu-excluded-modes)) + (corfu-mode 1))) + +(defun corfu--allow-eldoc () + "Return non-nil if Corfu is currently not active." + (not (and corfu-mode completion-in-region-mode))) + +;; Emacs 28: Do not show Corfu commands with M-X +(dolist (sym '(corfu-next corfu-previous corfu-first corfu-last corfu-quit corfu-reset + corfu-complete corfu-insert corfu-scroll-up corfu-scroll-down + corfu-show-location corfu-show-documentation corfu-insert-separator)) + (put sym 'completion-predicate #'ignore)) + +(provide 'corfu) +;;; corfu.el ends here diff --git a/elpa/corfu-0.20/corfu.elc b/elpa/corfu-0.20/corfu.elc Binary files differ. diff --git a/elpa/corfu-0.20/corfu.info b/elpa/corfu-0.20/corfu.info @@ -0,0 +1,611 @@ +This is corfu.info, produced by makeinfo version 6.7 from corfu.texi. + +INFO-DIR-SECTION Emacs +START-INFO-DIR-ENTRY +* Corfu: (corfu). Completion Overlay Region FUnction. +END-INFO-DIR-ENTRY + + +File: corfu.info, Node: Top, Next: Introduction, Up: (dir) + +corfu.el - Completion Overlay Region FUnction +********************************************* + +* Menu: + +* Introduction:: +* Features:: +* Installation and Configuration:: +* Key bindings:: +* Complementary packages:: +* Alternatives:: +* Caveats:: +* Contributions:: + +— The Detailed Node Listing — + +Installation and Configuration + +* Auto completion:: +* Completing with Corfu in the minibuffer:: +* Completing with Corfu in the Eshell or Shell:: +* Orderless completion:: +* TAB-and-Go completion:: +* Transfer completion to the minibuffer:: + + + +File: corfu.info, Node: Introduction, Next: Features, Prev: Top, Up: Top + +1 Introduction +************** + +Corfu enhances completion at point with a small completion popup. The +current candidates are shown in a popup below or above the point. Corfu +is the minimalistic ‘completion-in-region’ counterpart of the Vertico +(https://github.com/minad/vertico) minibuffer UI. + + Corfu is a small package, which relies on the Emacs completion +facilities and concentrates on providing a polished completion UI. +Completions are either provided by commands like ‘dabbrev-completion’ or +by pluggable backends (‘completion-at-point-functions’, Capfs). Most +programming language major modes implement a Capf. Furthermore the +language server packages, Eglot (https://github.com/joaotavora/eglot) +and Lsp-mode (https://github.com/emacs-lsp/lsp-mode), use Capfs which +talk to the LSP server to retrieve the completions. Corfu does not +include its own completion backends. The Emacs built-in Capfs and the +Capfs provided by other programming language packages are usually +sufficient. A few additional Capfs and completion utilities are +provided by the Cape (https://github.com/minad/cape) package. + + *NOTE*: Corfu uses child frames to show the popup. For now Corfu +falls back to the default setting of the ‘completion-in-region-function’ +on non-graphical displays. + + <https://github.com/minad/corfu/blob/screenshots/light.png?raw=true> + + <https://github.com/minad/corfu/blob/screenshots/dark.png?raw=true> + + +File: corfu.info, Node: Features, Next: Installation and Configuration, Prev: Introduction, Up: Top + +2 Features +********** + + • Timer-based auto-completions (_off_ by default, set ‘corfu-auto’). + • Popup display with scrollbar indicator and arrow key navigation. + • The popup can be summoned explicitly by pressing ‘TAB’ at any time. + • The current candidate is inserted with ‘TAB’ and selected with + ‘RET’. + • Candidates sorting by prefix, string length and alphabetically. + • The selected candidate is previewed (configurable via + ‘corfu-preview-current’). + • The selected candidate automatically committed on further input by + default. (configurable via ‘corfu-preview-current’). + • The Orderless (https://github.com/oantolin/orderless) completion + style is supported. The filter string can contain arbitrary + characters, after inserting a space via ‘M-SPC’ (configurable via + ‘corfu-quit-at-boundary’ and ‘corfu-separator’). + • Deferred completion style highlighting for performance. + • Jumping to location/documentation of current candidate. + • Support for candidate annotations and documentation in the echo + area. + • Deprecated candidates are crossed out in the display. + • Icons can be provided by an external package via margin formatter + functions. + + +File: corfu.info, Node: Installation and Configuration, Next: Key bindings, Prev: Features, Up: Top + +3 Installation and Configuration +******************************** + +Corfu is available from GNU ELPA +(http://elpa.gnu.org/packages/corfu.html), such that it can be installed +directly via ‘package-install’. After installation, the global minor +mode can be enabled with ‘M-x corfu-global-mode’. In order to configure +Corfu and other packages in your init.el, you may want to use +‘use-package’. + + Corfu is highly flexible and customizable via ‘corfu-*’ customization +variables, such that you can adapt it precisely to your requirements. +However in order to quickly try out the Corfu completion package, it +should be sufficient to activate ‘corfu-global-mode’. Then you +experiment with manual completion for example in an Elisp buffer or in +an Eshell or Shell buffer. For auto completion, set ‘corfu-auto=t’ +before turning on ‘corfu-global-mode’. + + If you start to configure the package more deeply, I recommend to +give the Orderless completion style a try for filtering. Orderless +completion is different from the familiar prefix TAB completion. Corfu +can be used with the default completion styles. The use of Orderless is +not a necessity. See also the Corfu Wiki +(https://github.com/minad/corfu/wiki) for additional configuration tips. +In particular the Lsp-mode configuration is documented in the Wiki. + + Here is an example configuration: + + (use-package corfu + ;; Optional customizations + ;; :custom + ;; (corfu-cycle t) ;; Enable cycling for `corfu-next/previous' + ;; (corfu-auto t) ;; Enable auto completion + ;; (corfu-separator ?\s) ;; Orderless field separator + ;; (corfu-quit-at-boundary nil) ;; Never quit at completion boundary + ;; (corfu-quit-no-match nil) ;; Never quit, even if there is no match + ;; (corfu-preview-current nil) ;; Disable current candidate preview + ;; (corfu-preselect-first nil) ;; Disable candidate preselection + ;; (corfu-on-exact-match nil) ;; Configure handling of exact matches + ;; (corfu-echo-documentation nil) ;; Disable documentation in the echo area + ;; (corfu-scroll-margin 5) ;; Use scroll margin + + ;; You may want to enable Corfu only for certain modes. + ;; :hook ((prog-mode . corfu-mode) + ;; (shell-mode . corfu-mode) + ;; (eshell-mode . corfu-mode)) + + ;; Recommended: Enable Corfu globally. + ;; This is recommended since dabbrev can be used globally (M-/). + :init + (corfu-global-mode)) + + ;; Optionally use the `orderless' completion style. See `+orderless-dispatch' + ;; in the Consult wiki for an advanced Orderless style dispatcher. + ;; Enable `partial-completion' for files to allow path expansion. + ;; You may prefer to use `initials' instead of `partial-completion'. + (use-package orderless + :init + ;; Configure a custom style dispatcher (see the Consult wiki) + ;; (setq orderless-style-dispatchers '(+orderless-dispatch) + ;; orderless-component-separator #'orderless-escapable-split-on-space) + (setq completion-styles '(orderless) + completion-category-defaults nil + completion-category-overrides '((file (styles . (partial-completion)))))) + + ;; Use dabbrev with Corfu! + (use-package dabbrev + ;; Swap M-/ and C-M-/ + :bind (("M-/" . dabbrev-completion) + ("C-M-/" . dabbrev-expand))) + + ;; A few more useful configurations... + (use-package emacs + :init + ;; TAB cycle if there are only few candidates + (setq completion-cycle-threshold 3) + + ;; Emacs 28: Hide commands in M-x which do not apply to the current mode. + ;; Corfu commands are hidden, since they are not supposed to be used via M-x. + ;; (setq read-extended-command-predicate + ;; #'command-completion-default-include-p) + + ;; Enable indentation+completion using the TAB key. + ;; `completion-at-point' is often bound to M-TAB. + (setq tab-always-indent 'complete)) + + See also the Corfu Wiki (https://github.com/minad/corfu/wiki) for +additional configuration tips. For more general documentation read the +chapter about completion in the Emacs manual +(https://www.gnu.org/software/emacs/manual/html_node/emacs/Completion.html). +If you want to create your own Capfs, you can find documentation about +completion in the Elisp manual +(https://www.gnu.org/software/emacs/manual/html_node/elisp/Completion.html). + +* Menu: + +* Auto completion:: +* Completing with Corfu in the minibuffer:: +* Completing with Corfu in the Eshell or Shell:: +* Orderless completion:: +* TAB-and-Go completion:: +* Transfer completion to the minibuffer:: + + +File: corfu.info, Node: Auto completion, Next: Completing with Corfu in the minibuffer, Up: Installation and Configuration + +3.1 Auto completion +=================== + +Auto completion is disabled by default, but can be enabled by setting +‘corfu-auto=t’. Furthermore you may want to configure Corfu to quit +completion eagerly, such that the completion popup stays out of your way +when it appeared unexpectedly. + + ;; Enable auto completion and configure quitting + (setq corfu-auto t + corfu-quit-no-match 'separator) ;; or t + + In general, I recommend to experiment a bit with the various settings +and key bindings to find a configuration which works for you. There is +no one size fits all solution. Some people like auto completion, some +like manual completion, some want to cycle with TAB and some with the +arrow keys... + + +File: corfu.info, Node: Completing with Corfu in the minibuffer, Next: Completing with Corfu in the Eshell or Shell, Prev: Auto completion, Up: Installation and Configuration + +3.2 Completing with Corfu in the minibuffer +=========================================== + +Corfu can be used for completion in the minibuffer, since it relies on +child frames to display the candidates. By default, ‘corfu-global-mode’ +does not activate ‘corfu-mode’ in the minibuffer, to avoid interference +with specialised minibuffer completion UIs like Vertico or Mct. However +you may still want to enable Corfu completion for commands like ‘M-:’ +(‘eval-expression’) or ‘M-!’ (‘shell-command’), which read from the +minibuffer. Activate ‘corfu-mode’ only if ‘completion-at-point’ is +bound in the minibuffer-local keymap to achieve this effect. + + (defun corfu-enable-in-minibuffer () + "Enable Corfu in the minibuffer if `completion-at-point' is bound." + (when (where-is-internal #'completion-at-point (list (current-local-map))) + ;; (setq-local corfu-auto nil) Enable/disable auto completion + (corfu-mode 1))) + (add-hook 'minibuffer-setup-hook #'corfu-enable-in-minibuffer) + + You can also enable Corfu more generally for every minibuffer, as +long as no other completion UI is active. If you use Mct or Vertico as +your main minibuffer completion UI, the following snippet should yield +the desired result. + + (defun corfu-enable-always-in-minibuffer () + "Enable Corfu in the minibuffer if Vertico/Mct are not active." + (unless (or (bound-and-true-p mct--active) + (bound-and-true-p vertico--input)) + ;; (setq-local corfu-auto nil) Enable/disable auto completion + (corfu-mode 1))) + (add-hook 'minibuffer-setup-hook #'corfu-enable-always-in-minibuffer 1) + + +File: corfu.info, Node: Completing with Corfu in the Eshell or Shell, Next: Orderless completion, Prev: Completing with Corfu in the minibuffer, Up: Installation and Configuration + +3.3 Completing with Corfu in the Eshell or Shell +================================================ + +When completing in the Eshell I recommend conservative local settings +without auto completion, such that the completion behavior is similar to +widely used shells like Bash, Zsh or Fish. + + (add-hook 'eshell-mode-hook + (lambda () + (setq-local corfu-auto nil) + (corfu-mode))) + + When pressing ‘RET’ while the Corfu popup is visible, the +‘corfu-insert’ command will be invoked. This command does inserts the +currently selected candidate, but it does not send the prompt input to +Eshell or the comint process. Therefore you often have to press ‘RET’ +twice which feels like an unnecessary double confirmation. Fortunately +it is easy to improve this! In my configuration I define the command +‘corfu-insert-and-send’ which performs the two steps at once. + + (defun corfu-insert-and-send () + (interactive) + ;; 1. First insert the completed candidate + (corfu-insert) + ;; 2. Send the entire prompt input to the shell + (cond + ((and (derived-mode-p 'eshell-mode) (fboundp 'eshell-send-input)) + (eshell-send-input)) + ((derived-mode-p 'comint-mode) + (comint-send-input)))) + + (define-key corfu-map "\r" #'+corfu-insert-and-send) + + Shell completion uses the flexible ‘pcomplete’ mechanism internally, +which allows you to program the completions per shell command. If you +want to know more, look into this blog post +(https://www.masteringemacs.org/article/pcomplete-context-sensitive-completion-emacs), +which shows how to configure pcomplete for git commands. I recommend +the pcmpl-args (https://github.com/JonWaltman/pcmpl-args.el) package +which extends Pcomplete with completion support and helpful annotation +support for more commands. Similar to the Fish shell, pcmpl-args uses +man page parsing and –help output parsing to dynamically generate +completions. This package brings Eshell completion to another level! + + Unfortunately Pcomplete has a few technical issues, which we can work +around with the Cape (https://github.com/minad/cape) library (Completion +at point extensions). Cape provides wrappers, which sanitize the +pcomplete function. Ideally the bugs in pcomplete should be fixed +upstream. *For now these two advices are strongly recommended to +achieve a sane Eshell experience.* + + ;; Silence the pcomplete capf, no errors or messages! + (advice-add 'pcomplete-completions-at-point :around #'cape-wrap-silent) + + ;; Ensure that pcomplete does not write to the buffer + ;; and behaves as a pure `completion-at-point-function'. + (advice-add 'pcomplete-completions-at-point :around #'cape-wrap-purify) + + +File: corfu.info, Node: Orderless completion, Next: TAB-and-Go completion, Prev: Completing with Corfu in the Eshell or Shell, Up: Installation and Configuration + +3.4 Orderless completion +======================== + +Orderless (https://github.com/oantolin/orderless) is an advanced +completion style that supports multi-component search filters separated +by a configurable character (space, by default). Normally, entering +characters like space which lie outside the completion region boundaries +(words, typically) causes Corfu to quit. This behavior is helpful with +auto-completion, which may pop-up when not desired, e.g. on entering a +new variable name. Just keep typing and Corfu will get out of the way. + + But orderless search terms can contain arbitrary characters; they are +also interpreted as regular expressions. To use orderless, set +‘corfu-separator’ (a space, by default) to the primary character of your +orderless component separator. + + Then, when a new orderless component is desired, use ‘M-SPC’ +(‘corfu-insert-separator’) to enter the _first_ component separator in +the input, and arbitrary orderless search terms and new separators can +be entered thereafter. + + To treat the entire input as Orderless input, you can set the +customization option ‘corfu-quit-at-boundary=t’. This disables the +predicate which checks if the current completion boundary has been left. +In contrast, if you _always_ want to quit at the boundary, simply set +‘corfu-quit-at-boundary=nil’. By default ‘corfu-quit-at-boundary’ is +set to ‘separator’ which quits at completion boundaries as long as no +separator has been inserted with ‘corfu-insert-separator’. + + Finally, there exists the user option ‘corfu-quit-no-match’ which is +set to ‘separator’ by default. With this setting Corfu stays alive as +soon as you start advanced filtering with a ‘corfu-separator’ even if +there are no matches, for example due to a typo. As long as no +separator character has been inserted with ‘corfu-insert-separator’, +Corfu will still quit if there are no matches. This ensures that the +Corfu popup goes away quickly if completion is not possible. + + In the following we show two configurations, one which works best +with auto completion and one which may work better with manual +completion if you prefer to always use ‘SPC’ to separate the Orderless +components. + + ;; Auto completion example + (use-package corfu + :custom + (corfu-auto t) ;; Enable auto completion + ;; (corfu-separator ?_) ;; Set to orderless separator, if not using space + :bind + ;; Another key binding can be used, such as S-SPC. + ;; (:map corfu-map ("M-SPC" . corfu-insert-separator)) + :init + (corfu-global-mode)) + + ;; Manual completion example + (use-package corfu + :custom + ;; (corfu-separator ?_) ;; Set to orderless separator, if not using space + :bind + ;; Configure SPC for separator insertion + (:map corfu-map ("SPC" . corfu-insert-separator)) + :init + (corfu-global-mode)) + + +File: corfu.info, Node: TAB-and-Go completion, Next: Transfer completion to the minibuffer, Prev: Orderless completion, Up: Installation and Configuration + +3.5 TAB-and-Go completion +========================= + +You may be interested in configuring Corfu in TAB-and-Go style. +Pressing TAB moves to the next candidate and further input will then +commit the selection. + + (use-package corfu + ;; TAB-and-Go customizations + :custom + (corfu-cycle t) ;; Enable cycling for `corfu-next/previous' + (corfu-preselect-first nil) ;; Disable candidate preselection + + ;; Use TAB for cycling, default is `corfu-complete'. + :bind + (:map corfu-map + ("TAB" . corfu-next) + ([tab] . corfu-next) + ("S-TAB" . corfu-previous) + ([backtab] . corfu-previous)) + + :init + (corfu-global-mode)) + + +File: corfu.info, Node: Transfer completion to the minibuffer, Prev: TAB-and-Go completion, Up: Installation and Configuration + +3.6 Transfer completion to the minibuffer +========================================= + +Sometimes it is useful to transfer the Corfu completion session to the +minibuffer, since the minibuffer offers richer interaction features. In +particular, Embark (https://github.com/oantolin/embark) is available in +the minibuffer, such that you can act on the candidates or +export/collect the candidates to a separate buffer. Hopefully we can +also add Corfu-support to Embark in the future, such that at least +export/collect is possible directly from Corfu. But in my opinion +having the ability to transfer the Corfu completion to the minibuffer is +an even better feature, since further completion can be performed there. + + The command ‘corfu-move-to-minibuffer’ is defined here in terms of +‘consult-completion-in-region’, which uses the minibuffer completion UI +via ‘completing-read’. + + (defun corfu-move-to-minibuffer () + (interactive) + (let ((completion-extra-properties corfu--extra) + completion-cycle-threshold completion-cycling) + (apply #'consult-completion-in-region completion-in-region--data))) + (define-key corfu-map "\M-m" #'corfu-move-to-minibuffer) + + +File: corfu.info, Node: Key bindings, Next: Complementary packages, Prev: Installation and Configuration, Up: Top + +4 Key bindings +************** + +Corfu uses a transient keymap ‘corfu-map’ which is active while the +popup is shown. The keymap defines the following remappings and +bindings: + + • ‘beginning-of-buffer’ -> ‘corfu-first’ + • ‘end-of-buffer’ -> ‘corfu-last’ + • ‘scroll-down-command’ -> ‘corfu-scroll-down’ + • ‘scroll-up-command’ -> ‘corfu-scroll-up’ + • ‘next-line’, ‘down’, ‘M-n’ -> ‘corfu-next’ + • ‘previous-line’, ‘up’, ‘M-p’ -> ‘corfu-previous’ + • ‘completion-at-point’, ‘TAB’ -> ‘corfu-complete’ + • ‘RET’ -> ‘corfu-insert’ + • ‘M-g’ -> ‘corfu-show-location’ + • ‘M-h’ -> ‘corfu-show-documentation’ + • ‘M-SPC’ -> ‘corfu-insert-separator’ + • ‘C-g’ -> ‘corfu-quit’ + • ‘keyboard-escape-quit’ -> ‘corfu-reset’ + + +File: corfu.info, Node: Complementary packages, Next: Alternatives, Prev: Key bindings, Up: Top + +5 Complementary packages +************************ + +Corfu works well together with all packages providing code completion +via the ‘completion-at-point-functions’. Many modes and packages +already provide a Capf out of the box. Nevertheless you may want to +look into complementary packages to enhance your setup. + + • Orderless (https://github.com/oantolin/orderless): Corfu supports + completion styles, including the advanced Orderless + (https://github.com/oantolin/orderless) completion style, where the + filtering expressions are separated by spaces or another character + (see ‘corfu-separator’). + + • Cape (https://github.com/minad/cape): I collect additional Capf + backends and ‘completion-in-region’ commands in my Cape + (https://github.com/minad/cape) package. The package provides a + file path, a dabbrev completion backend and a backend which allows + you to enter unicode characters in the form of TeX commands. Cape + provides an adapter to reuse Company backends in Corfu. + Furthermore the function ‘cape-super-capf’ can merge/groups + multiple Capfs, such that the candidates of multiple Capfs are + displayed together at the same time. + + • kind-icon (https://github.com/jdtsmith/kind-icon): Icons are + supported by Corfu via an external package. For example the + kind-icon (https://github.com/jdtsmith/kind-icon) package provides + beautifully styled SVG icons based on monochromatic icon sets like + material design. + + • corfu-doc (https://github.com/galeo/corfu-doc): The corfu-doc + package displays the candidate documentation in a popup next to the + Corfu popup, similar to ‘company-quickhelp’. + + • pcmpl-args (https://github.com/JonWaltman/pcmpl-args.el): Extend + the Eshell/Shell Pcomplete mechanism with support for many more + commands. Similar to the Fish shell, Pcomplete uses man page + parsing to dynamically retrieve the completions and helpful + annotations. This package brings Eshell completions to another + level! + + • Tempel (https://github.com/minad/tempel): Tiny template/snippet + package with templates in Lisp syntax, which can be used in + conjunction with Corfu. + + • Vertico (https://github.com/minad/vertico): You may also want to + look into my Vertico (https://github.com/minad/vertico) package. + Vertico is the minibuffer completion counterpart of Corfu. + + +File: corfu.info, Node: Alternatives, Next: Caveats, Prev: Complementary packages, Up: Top + +6 Alternatives +************** + + • Company (https://github.com/company-mode/company-mode): Company is + a widely used and mature completion package, which implements a + similar interaction model and popup UI as Corfu. While Corfu + relies exclusively on the standard Emacs completion API (Capfs), + Company defines its own API for the backends. Furthermore Company + includes its completion backends, which are incompatible with the + Emacs completion infrastructure. As a result of this design, + Company is a more complex package than Corfu. Company by default + uses overlays to display the popup in contrast to the child frames + used by Corfu. Overall both packages work well. Company is more + mature but the integration into Emacs is a bit less tight, since + for example the ‘completion-at-point’ command (or the + ‘completion-in-region’ function) does not invoke Company. + + • Mct (https://gitlab.com/protesilaos/mct): Protesilaos’ Minibuffer + Confines Transcended package supports both minibuffer completion + and completion in region. It reuses the default completion UI for + this purpose and installs a timer which live updates the completion + buffer. The main advantage of Mct is that you work with a regular + Emacs buffer instead of with a popup. You can take advantage of + the usual Emacs commands to navigate in the completions buffer. On + top, Mct enhances the movement such that you can quickly switch + between the completions buffer and the minibuffer or the region + which is being completed. Mct does not support timer-based auto + completion, but the integration into Emacs is naturally tight. + + • consult-completion-in-region (https://github.com/minad/consult): + The Consult package provides the function + ‘consult-completion-in-region’ which can be set as + ‘completion-in-region-function’ such that it handles + ‘completion-at-point’. The function works by transferring the + in-buffer completion to the minibuffer. In the minibuffer, the + minibuffer completion UI, for example Vertico + (https://github.com/minad/vertico) takes over. If you prefer to + perform all your completions in the minibuffer + ‘consult-completion-in-region’ is your best option. + + +File: corfu.info, Node: Caveats, Next: Contributions, Prev: Alternatives, Up: Top + +7 Caveats +********* + +Corfu is robust in most scenarios. There are a few known technical +caveats. + + • Corfu uses child frames to show the popup. For now Corfu falls + back to the default setting of the ‘completion-in-region-function’ + on non-graphical displays. You can use one of the alternatives in + terminals. + + • Corfu does not sort by history, since ‘completion-at-point’ does + not maintain a history (See branch ‘history’ for a possible + solution). + + +File: corfu.info, Node: Contributions, Prev: Caveats, Up: Top + +8 Contributions +*************** + +Since this package is part of GNU ELPA +(http://elpa.gnu.org/packages/corfu.html) contributions require a +copyright assignment to the FSF. + + + +Tag Table: +Node: Top195 +Node: Introduction794 +Node: Features2318 +Node: Installation and Configuration3705 +Node: Auto completion8565 +Node: Completing with Corfu in the minibuffer9414 +Node: Completing with Corfu in the Eshell or Shell11271 +Node: Orderless completion14224 +Node: TAB-and-Go completion17332 +Node: Transfer completion to the minibuffer18217 +Node: Key bindings19556 +Node: Complementary packages20559 +Node: Alternatives23106 +Node: Caveats25539 +Node: Contributions26125 + +End Tag Table + + +Local Variables: +coding: utf-8 +End: diff --git a/elpa/corfu-0.17/dir b/elpa/corfu-0.20/dir diff --git a/elpa/go-mode-1.5.0/go-mode-autoloads.el b/elpa/go-mode-1.5.0/go-mode-autoloads.el @@ -1,107 +0,0 @@ -;;; go-mode-autoloads.el --- automatically extracted autoloads -;; -;;; Code: - -(add-to-list 'load-path (directory-file-name - (or (file-name-directory #$) (car load-path)))) - - -;;;### (autoloads nil "go-mode" "go-mode.el" (0 0 0 0)) -;;; Generated autoloads from go-mode.el - -(autoload 'go-mode "go-mode" "\ -Major mode for editing Go source text. - -This mode provides (not just) basic editing capabilities for -working with Go code. It offers almost complete syntax -highlighting, indentation that is almost identical to gofmt and -proper parsing of the buffer content to allow features such as -navigation by function, manipulation of comments or detection of -strings. - -In addition to these core features, it offers various features to -help with writing Go code. You can directly run buffer content -through gofmt, read godoc documentation from within Emacs, modify -and clean up the list of package imports or interact with the -Playground (uploading and downloading pastes). - -The following extra functions are defined: - -- `gofmt' -- `godoc' and `godoc-at-point' -- `go-import-add' -- `go-remove-unused-imports' -- `go-goto-arguments' -- `go-goto-docstring' -- `go-goto-function' -- `go-goto-function-name' -- `go-goto-imports' -- `go-goto-return-values' -- `go-goto-method-receiver' -- `go-play-buffer' and `go-play-region' -- `go-download-play' -- `godef-describe' and `godef-jump' -- `go-coverage' -- `go-set-project' -- `go-reset-gopath' - -If you want to automatically run `gofmt' before saving a file, -add the following hook to your emacs configuration: - -\(add-hook 'before-save-hook #'gofmt-before-save) - -If you want to use `godef-jump' instead of etags (or similar), -consider binding godef-jump to `M-.', which is the default key -for `find-tag': - -\(add-hook 'go-mode-hook (lambda () - (local-set-key (kbd \"M-.\") #'godef-jump))) - -Please note that godef is an external dependency. You can install -it with - -go get github.com/rogpeppe/godef - - -If you're looking for even more integration with Go, namely -on-the-fly syntax checking, auto-completion and snippets, it is -recommended that you look at flycheck -\(see URL `https://github.com/flycheck/flycheck') or flymake in combination -with goflymake (see URL `https://github.com/dougm/goflymake'), gocode -\(see URL `https://github.com/nsf/gocode'), go-eldoc -\(see URL `github.com/syohex/emacs-go-eldoc') and yasnippet-go -\(see URL `https://github.com/dominikh/yasnippet-go') - -\(fn)" t nil) - -(add-to-list 'auto-mode-alist (cons "\\.go\\'" 'go-mode)) - -(autoload 'gofmt-before-save "go-mode" "\ -Add this to .emacs to run gofmt on the current buffer when saving: -\(add-hook 'before-save-hook 'gofmt-before-save). - -Note that this will cause ‘go-mode’ to get loaded the first time -you save any file, kind of defeating the point of autoloading." t nil) - -(autoload 'godoc "go-mode" "\ -Show Go documentation for QUERY, much like \\<go-mode-map>\\[man]. - -\(fn QUERY)" t nil) - -(autoload 'go-download-play "go-mode" "\ -Download a paste from the playground and insert it in a Go buffer. -Tries to look for a URL at point. - -\(fn URL)" t nil) - -(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "go-mode" '("go-" "god" "gofmt"))) - -;;;*** - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; coding: utf-8 -;; End: -;;; go-mode-autoloads.el ends here diff --git a/elpa/go-mode-1.5.0/go-mode-pkg.el b/elpa/go-mode-1.5.0/go-mode-pkg.el @@ -1,2 +0,0 @@ -;;; Generated package description from go-mode.el -*- no-byte-compile: t -*- -(define-package "go-mode" "1.5.0" "Major mode for the Go programming language" 'nil :commit "35f6826e435c3004dabf134d0f2ae2f31ea7b6a2" :authors '(("The go-mode Authors")) :maintainer '("The go-mode Authors") :keywords '("languages" "go") :url "https://github.com/dominikh/go-mode.el") diff --git a/elpa/go-mode-1.5.0/go-mode.el b/elpa/go-mode-1.5.0/go-mode.el @@ -1,2011 +0,0 @@ -;;; go-mode.el --- Major mode for the Go programming language - -;;; Commentary: - -;; Copyright 2013 The go-mode Authors. All rights reserved. -;; Use of this source code is governed by a BSD-style -;; license that can be found in the LICENSE file. - -;; Author: The go-mode Authors -;; Version: 1.5.0 -;; Package-Version: 1.5.0 -;; Package-Commit: 35f6826e435c3004dabf134d0f2ae2f31ea7b6a2 -;; Keywords: languages go -;; URL: https://github.com/dominikh/go-mode.el -;; -;; This file is not part of GNU Emacs. - -;;; Code: - -(require 'cl-lib) -(require 'compile) -(require 'etags) -(require 'ffap) -(require 'find-file) -(require 'ring) -(require 'url) -(require 'xref nil :noerror) ; xref is new in Emacs 25.1 - - -(eval-when-compile - (defmacro go--forward-word (&optional arg) - (if (fboundp 'forward-word-strictly) - `(forward-word-strictly ,arg) - `(forward-word ,arg)))) - -(defun go--delete-whole-line (&optional arg) - "Delete the current line without putting it in the `kill-ring'. -Derived from function `kill-whole-line'. ARG is defined as for that -function." - (setq arg (or arg 1)) - (if (and (> arg 0) - (eobp) - (save-excursion (forward-visible-line 0) (eobp))) - (signal 'end-of-buffer nil)) - (if (and (< arg 0) - (bobp) - (save-excursion (end-of-visible-line) (bobp))) - (signal 'beginning-of-buffer nil)) - (cond ((zerop arg) - (delete-region (progn (forward-visible-line 0) (point)) - (progn (end-of-visible-line) (point)))) - ((< arg 0) - (delete-region (progn (end-of-visible-line) (point)) - (progn (forward-visible-line (1+ arg)) - (unless (bobp) - (backward-char)) - (point)))) - (t - (delete-region (progn (forward-visible-line 0) (point)) - (progn (forward-visible-line arg) (point)))))) - -(defun go-goto-opening-parenthesis (&optional _legacy-unused) - "Move up one level of parentheses." - ;; The old implementation of go-goto-opening-parenthesis had an - ;; optional argument to speed up the function. It didn't change the - ;; function's outcome. - - ;; Silently fail if there's no matching opening parenthesis. - (condition-case nil - (backward-up-list) - (scan-error nil))) - - -(defconst go-dangling-operators-regexp "[^-]-\\|[^+]\\+\\|[/*&><.=|^]") -(defconst go--max-dangling-operator-length 2 - "The maximum length of dangling operators. -This must be at least the length of the longest string matched by -‘go-dangling-operators-regexp.’, and must be updated whenever -that constant is changed.") - -(defconst go-identifier-regexp "[[:word:][:multibyte:]]+") -(defconst go-type-name-no-prefix-regexp "\\(?:[[:word:][:multibyte:]]+\\.\\)?[[:word:][:multibyte:]]+") -(defconst go-qualified-identifier-regexp (concat go-identifier-regexp "\\." go-identifier-regexp)) -(defconst go-label-regexp go-identifier-regexp) -(defconst go-type-regexp "[[:word:][:multibyte:]*]+") -(defconst go-func-regexp (concat "\\_<func\\_>\\s *\\(" go-identifier-regexp "\\)")) -(defconst go-func-meth-regexp (concat - "\\_<func\\_>\\s *\\(?:(\\s *" - "\\(" go-identifier-regexp "\\s +\\)?" go-type-regexp - "\\s *)\\s *\\)?\\(" - go-identifier-regexp - "\\)(")) - -(defconst go-builtins - '("append" "cap" "close" "complex" "copy" - "delete" "imag" "len" "make" "new" - "panic" "print" "println" "real" "recover") - "All built-in functions in the Go language. Used for font locking.") - -(defconst go-mode-keywords - '("break" "default" "func" "interface" "select" - "case" "defer" "go" "map" "struct" - "chan" "else" "goto" "package" "switch" - "const" "fallthrough" "if" "range" "type" - "continue" "for" "import" "return" "var") - "All keywords in the Go language. Used for font locking.") - -(defconst go-constants '("nil" "true" "false" "iota")) -(defconst go-type-name-regexp (concat "\\(?:[*(]\\)*\\(\\(?:" go-identifier-regexp "\\.\\)?" go-identifier-regexp "\\)")) - -;; Maximum number of identifiers that can be highlighted as type names -;; in one function type/declaration. -(defconst go--font-lock-func-param-num-groups 16) - -(defvar go-dangling-cache) -(defvar go-godoc-history nil) -(defvar go--coverage-current-file-name) - -(defgroup go nil - "Major mode for editing Go code." - :link '(url-link "https://github.com/dominikh/go-mode.el") - :group 'languages) - -(defgroup go-cover nil - "Options specific to `cover`." - :group 'go) - -(defgroup godoc nil - "Options specific to `godoc'." - :group 'go) - -(defcustom go-fontify-function-calls t - "Fontify function and method calls if this is non-nil." - :type 'boolean - :group 'go) - -(defcustom go-mode-hook nil - "Hook called by `go-mode'." - :type 'hook - :group 'go) - -(defcustom go-command "go" - "The 'go' command. -Some users have multiple Go development trees and invoke the 'go' -tool via a wrapper that sets GOROOT and GOPATH based on the -current directory. Such users should customize this variable to -point to the wrapper script." - :type 'string - :group 'go) - -(defcustom gofmt-command "gofmt" - "The 'gofmt' command. -Some users may replace this with 'goimports' -from https://golang.org/x/tools/cmd/goimports." - :type 'string - :group 'go) - -(defcustom gofmt-args nil - "Additional arguments to pass to gofmt." - :type '(repeat string) - :group 'go) - -(defcustom gofmt-show-errors 'buffer - "Where to display gofmt error output. -It can either be displayed in its own buffer, in the echo area, or not at all. - -Please note that Emacs outputs to the echo area when writing -files and will overwrite gofmt's echo output if used from inside -a `before-save-hook'." - :type '(choice - (const :tag "Own buffer" buffer) - (const :tag "Echo area" echo) - (const :tag "None" nil)) - :group 'go) - -(defcustom godef-command "godef" - "The 'godef' command." - :type 'string - :group 'go) - -(defcustom go-other-file-alist - '(("_test\\.go\\'" (".go")) - ("\\.go\\'" ("_test.go"))) - "See the documentation of `ff-other-file-alist' for details." - :type '(repeat (list regexp (choice (repeat string) function))) - :group 'go) - -(defcustom go-packages-function 'go-packages-native - "Function called by `go-packages' to determine the list of available packages. -This is used in e.g. tab completion in `go-import-add'. - -This package provides two functions: `go-packages-native' uses -elisp to find all .a files in all /pkg/ directories. -`go-packages-go-list' uses 'go list all' to determine all Go -packages. `go-packages-go-list' generally produces more accurate -results, but can be slower than `go-packages-native'." - :type 'function - :package-version '(go-mode . 1.4.0) - :group 'go) - -(defcustom go-guess-gopath-functions (list #'go-godep-gopath - #'go-wgo-gopath - #'go-gb-gopath - #'go-plain-gopath) - "Functions to call in sequence to detect a project's GOPATH. - -The functions in this list will be called one after another, -until a function returns non-nil. The order of the functions in -this list is important, as some project layouts may superficially -look like others. For example, a subset of wgo projects look like -gb projects. That's why we need to detect wgo first, to avoid -mis-identifying them as gb projects." - :type '(repeat function) - :group 'go) - -(defcustom godoc-command "go doc" - "Which executable to use for `godoc'. -This can either be 'godoc' or 'go doc', both as an absolute path -or an executable in PATH." - :type 'string - :group 'go) - -(defcustom godoc-and-godef-command "godoc" - "Which executable to use for `godoc' in `godoc-and-godef-command'. -Must be 'godoc' and not 'go doc' and can be an absolute path or -an executable in PATH." - :type 'string - :group 'go) - -(defcustom godoc-use-completing-read nil - "Provide auto-completion for godoc. -Only really desirable when using `godoc' instead of `go doc'." - :type 'boolean - :group 'godoc) - -(defcustom godoc-at-point-function #'godoc-and-godef - "Function to call to display the documentation for an -identifier at a given position. - -This package provides two functions: `godoc-and-godef' uses a -combination of godef and godoc to find the documentation. This -approach has several caveats. See its documentation for more -information. The second function, `godoc-gogetdoc' uses an -additional tool that correctly determines the documentation for -any identifier. It provides better results than -`godoc-and-godef'." - :type 'function - :group 'godoc) - -(defun godoc-and-godef (point) - "Use a combination of godef and godoc to guess the documentation at POINT. - -Due to a limitation in godoc, it is not possible to differentiate -between functions and methods, which may cause `godoc-at-point' -to display more documentation than desired. Furthermore, it -doesn't work on package names or variables. - -Consider using ‘godoc-gogetdoc’ instead for more accurate results." - (condition-case nil - (let* ((output (godef--call point)) - (file (car output)) - (name-parts (split-string (cadr output) " ")) - (first (car name-parts))) - (if (not (godef--successful-p file)) - (message "%s" (godef--error file)) - (go--godoc (format "%s %s" - (file-name-directory file) - (if (or (string= first "type") (string= first "const")) - (cadr name-parts) - (car name-parts))) - godoc-and-godef-command))) - (file-error (message "Could not run godef binary")))) - -(defun godoc-gogetdoc (point) - "Use the gogetdoc tool to find the documentation for an identifier at POINT. - -You can install gogetdoc with 'go get -u github.com/zmb3/gogetdoc'." - (if (not (buffer-file-name (go--coverage-origin-buffer))) - ;; TODO: gogetdoc supports unsaved files, but not introducing - ;; new artifical files, so this limitation will stay for now. - (error "Cannot use gogetdoc on a buffer without a file name")) - (let ((posn (format "%s:#%d" (shell-quote-argument (file-truename buffer-file-name)) (1- (position-bytes point)))) - (out (godoc--get-buffer "<at point>"))) - (with-current-buffer (get-buffer-create "*go-gogetdoc-input*") - (setq buffer-read-only nil) - (erase-buffer) - (go--insert-modified-files) - (call-process-region (point-min) (point-max) "gogetdoc" nil out nil - "-modified" - (format "-pos=%s" posn))) - (with-current-buffer out - (goto-char (point-min)) - (godoc-mode) - (display-buffer (current-buffer) t)))) - -(defun go--kill-new-message (url) - "Make URL the latest kill and print a message." - (kill-new url) - (message "%s" url)) - -(defcustom go-play-browse-function 'go--kill-new-message - "Function to call with the Playground URL. -See `go-play-region' for more details." - :type '(choice - (const :tag "Nothing" nil) - (const :tag "Kill + Message" go--kill-new-message) - (const :tag "Browse URL" browse-url) - (function :tag "Call function")) - :group 'go) - -(defcustom go-coverage-display-buffer-func 'display-buffer-reuse-window - "How `go-coverage' should display the coverage buffer. -See `display-buffer' for a list of possible functions." - :type 'function - :group 'go-cover) - -(defface go-coverage-untracked - '((t (:foreground "#505050"))) - "Coverage color of untracked code." - :group 'go-cover) - -(defface go-coverage-0 - '((t (:foreground "#c00000"))) - "Coverage color for uncovered code." - :group 'go-cover) -(defface go-coverage-1 - '((t (:foreground "#808080"))) - "Coverage color for covered code with weight 1." - :group 'go-cover) -(defface go-coverage-2 - '((t (:foreground "#748c83"))) - "Coverage color for covered code with weight 2." - :group 'go-cover) -(defface go-coverage-3 - '((t (:foreground "#689886"))) - "Coverage color for covered code with weight 3." - :group 'go-cover) -(defface go-coverage-4 - '((t (:foreground "#5ca489"))) - "Coverage color for covered code with weight 4." - :group 'go-cover) -(defface go-coverage-5 - '((t (:foreground "#50b08c"))) - "Coverage color for covered code with weight 5." - :group 'go-cover) -(defface go-coverage-6 - '((t (:foreground "#44bc8f"))) - "Coverage color for covered code with weight 6." - :group 'go-cover) -(defface go-coverage-7 - '((t (:foreground "#38c892"))) - "Coverage color for covered code with weight 7." - :group 'go-cover) -(defface go-coverage-8 - '((t (:foreground "#2cd495"))) - "Coverage color for covered code with weight 8. -For mode=set, all covered lines will have this weight." - :group 'go-cover) -(defface go-coverage-9 - '((t (:foreground "#20e098"))) - "Coverage color for covered code with weight 9." - :group 'go-cover) -(defface go-coverage-10 - '((t (:foreground "#14ec9b"))) - "Coverage color for covered code with weight 10." - :group 'go-cover) -(defface go-coverage-covered - '((t (:foreground "#2cd495"))) - "Coverage color of covered code." - :group 'go-cover) - -(defvar go-mode-syntax-table - (let ((st (make-syntax-table))) - (modify-syntax-entry ?+ "." st) - (modify-syntax-entry ?- "." st) - (modify-syntax-entry ?% "." st) - (modify-syntax-entry ?& "." st) - (modify-syntax-entry ?| "." st) - (modify-syntax-entry ?^ "." st) - (modify-syntax-entry ?! "." st) - (modify-syntax-entry ?= "." st) - (modify-syntax-entry ?< "." st) - (modify-syntax-entry ?> "." st) - (modify-syntax-entry ?/ ". 124b" st) - (modify-syntax-entry ?* ". 23" st) - (modify-syntax-entry ?\n "> b" st) - (modify-syntax-entry ?\" "\"" st) - (modify-syntax-entry ?\' "\"" st) - (modify-syntax-entry ?` "\"" st) - (modify-syntax-entry ?\\ "\\" st) - ;; TODO make _ a symbol constituent now that xemacs is gone - (modify-syntax-entry ?_ "w" st) - - st) - "Syntax table for Go mode.") - -(defun go--build-font-lock-keywords () - ;; we cannot use 'symbols in regexp-opt because GNU Emacs <24 - ;; doesn't understand that - (append - `((go--match-func - ,@(mapcar (lambda (x) `(,x font-lock-type-face)) - (number-sequence 1 go--font-lock-func-param-num-groups))) - (,(concat "\\_<" (regexp-opt go-mode-keywords t) "\\_>") . font-lock-keyword-face) - (,(concat "\\(\\_<" (regexp-opt go-builtins t) "\\_>\\)[[:space:]]*(") 1 font-lock-builtin-face) - (,(concat "\\_<" (regexp-opt go-constants t) "\\_>") . font-lock-constant-face) - (,go-func-regexp 1 font-lock-function-name-face)) ;; function (not method) name - - (if go-fontify-function-calls - `((,(concat "\\(" go-identifier-regexp "\\)[[:space:]]*(") 1 font-lock-function-name-face) ;; function call/method name - (,(concat "[^[:word:][:multibyte:]](\\(" go-identifier-regexp "\\))[[:space:]]*(") 1 font-lock-function-name-face)) ;; bracketed function call - `((,go-func-meth-regexp 2 font-lock-function-name-face))) ;; method name - - `( - ("\\(`[^`]*`\\)" 1 font-lock-multiline) ;; raw string literal, needed for font-lock-syntactic-keywords - (,(concat "\\_<type\\_>[[:space:]]+\\([^[:space:](]+\\)") 1 font-lock-type-face) ;; types - (,(concat "\\_<type\\_>[[:space:]]+" go-identifier-regexp "[[:space:]]*" go-type-name-regexp) 1 font-lock-type-face) ;; types - (,(concat "[^[:word:][:multibyte:]]\\[\\([[:digit:]]+\\|\\.\\.\\.\\)?\\]" go-type-name-regexp) 2 font-lock-type-face) ;; Arrays/slices - (,(concat "\\(" go-identifier-regexp "\\)" "{") 1 font-lock-type-face) - (,(concat "\\_<map\\_>\\[[^]]+\\]" go-type-name-regexp) 1 font-lock-type-face) ;; map value type - (,(concat "\\_<map\\_>\\[" go-type-name-regexp) 1 font-lock-type-face) ;; map key type - (,(concat "\\_<chan\\_>[[:space:]]*\\(?:<-[[:space:]]*\\)?" go-type-name-regexp) 1 font-lock-type-face) ;; channel type - (,(concat "\\_<\\(?:new\\|make\\)\\_>\\(?:[[:space:]]\\|)\\)*(" go-type-name-regexp) 1 font-lock-type-face) ;; new/make type - ;; TODO do we actually need this one or isn't it just a function call? - (,(concat "\\.\\s *(" go-type-name-regexp) 1 font-lock-type-face) ;; Type conversion - ;; Like the original go-mode this also marks compound literal - ;; fields. There, it was marked as to fix, but I grew quite - ;; accustomed to it, so it'll stay for now. - (,(concat "^[[:space:]]*\\(" go-label-regexp "\\)[[:space:]]*:\\(\\S.\\|$\\)") 1 font-lock-constant-face) ;; Labels and compound literal fields - (,(concat "\\_<\\(goto\\|break\\|continue\\)\\_>[[:space:]]*\\(" go-label-regexp "\\)") 2 font-lock-constant-face)))) ;; labels in goto/break/continue - -(let ((m (define-prefix-command 'go-goto-map))) - (define-key m "a" #'go-goto-arguments) - (define-key m "d" #'go-goto-docstring) - (define-key m "f" #'go-goto-function) - (define-key m "i" #'go-goto-imports) - (define-key m "m" #'go-goto-method-receiver) - (define-key m "n" #'go-goto-function-name) - (define-key m "r" #'go-goto-return-values)) - -(defvar go-mode-map - (let ((m (make-sparse-keymap))) - (unless (boundp 'electric-indent-chars) - (define-key m "}" #'go-mode-insert-and-indent) - (define-key m ")" #'go-mode-insert-and-indent)) - (define-key m (kbd "C-c C-a") #'go-import-add) - (define-key m (kbd "C-c C-j") #'godef-jump) - (define-key m (kbd "C-x 4 C-c C-j") #'godef-jump-other-window) - (define-key m (kbd "C-c C-d") #'godef-describe) - (define-key m (kbd "C-c C-f") 'go-goto-map) - m) - "Keymap used by ‘go-mode’.") - -(easy-menu-define go-mode-menu go-mode-map - "Menu for Go mode." - '("Go" - ["Describe Expression" godef-describe t] - ["Jump to Definition" godef-jump t] - "---" - ["Add Import" go-import-add t] - ["Remove Unused Imports" go-remove-unused-imports t] - ["Go to Imports" go-goto-imports t] - "---" - ("Playground" - ["Send Buffer" go-play-buffer t] - ["Send Region" go-play-region t] - ["Download" go-download-play t]) - "---" - ["Coverage" go-coverage t] - ["Gofmt" gofmt t] - ["Godoc" godoc t] - "---" - ["Customize Mode" (customize-group 'go) t])) - -(defun go-mode-insert-and-indent (key) - "Invoke the global binding of KEY, then reindent the line." - - (interactive (list (this-command-keys))) - (call-interactively (lookup-key (current-global-map) key)) - (indent-according-to-mode)) - -(defmacro go-paren-level () - `(car (syntax-ppss))) - -(defmacro go-in-string-or-comment-p () - `(nth 8 (syntax-ppss))) - -(defmacro go-in-string-p () - `(nth 3 (syntax-ppss))) - -(defmacro go-in-comment-p () - `(nth 4 (syntax-ppss))) - -(defmacro go-goto-beginning-of-string-or-comment () - `(goto-char (nth 8 (syntax-ppss)))) - -(defun go--backward-irrelevant (&optional stop-at-string) - "Skip backwards over any characters that are irrelevant for -indentation and related tasks. - -It skips over whitespace, comments, cases and labels and, if -STOP-AT-STRING is not true, over strings." - - (let (pos (start-pos (point))) - (skip-chars-backward "\n\s\t") - (if (and (save-excursion (beginning-of-line) (go-in-string-p)) - (= (char-before) ?`) - (not stop-at-string)) - (backward-char)) - (if (and (go-in-string-p) - (not stop-at-string)) - (go-goto-beginning-of-string-or-comment)) - (if (looking-back "\\*/" (line-beginning-position)) - (backward-char)) - (if (go-in-comment-p) - (go-goto-beginning-of-string-or-comment)) - (setq pos (point)) - (beginning-of-line) - (if (or (looking-at (concat "^" go-label-regexp ":")) - (looking-at "^[[:space:]]*\\(case .+\\|default\\):")) - (end-of-line 0) - (goto-char pos)) - (if (/= start-pos (point)) - (go--backward-irrelevant stop-at-string)) - (/= start-pos (point)))) - -(defun go--buffer-narrowed-p () - "Return non-nil if the current buffer is narrowed." - (/= (buffer-size) - (- (point-max) - (point-min)))) - -(defun go-previous-line-has-dangling-op-p () - "Return non-nil if the current line is a continuation line." - (let* ((cur-line (line-number-at-pos)) - (val (gethash cur-line go-dangling-cache 'nope))) - (if (or (go--buffer-narrowed-p) (equal val 'nope)) - (save-excursion - (beginning-of-line) - (go--backward-irrelevant t) - (setq val (looking-back go-dangling-operators-regexp - (- (point) go--max-dangling-operator-length))) - (if (not (go--buffer-narrowed-p)) - (puthash cur-line val go-dangling-cache)))) - val)) - -(defun go--at-function-definition () - "Return non-nil if point is on the opening curly brace of a -function definition. - -We do this by first calling (beginning-of-defun), which will take -us to the start of *some* function. We then look for the opening -curly brace of that function and compare its position against the -curly brace we are checking. If they match, we return non-nil." - (if (= (char-after) ?\{) - (save-excursion - (let ((old-point (point)) - start-nesting) - (beginning-of-defun) - (when (looking-at "func ") - (setq start-nesting (go-paren-level)) - (skip-chars-forward "^{") - (while (> (go-paren-level) start-nesting) - (forward-char) - (skip-chars-forward "^{") 0) - (if (and (= (go-paren-level) start-nesting) (= old-point (point))) - t)))))) - -(defun go--indentation-for-opening-parenthesis () - "Return the semantic indentation for the current opening parenthesis. - -If point is on an opening curly brace and said curly brace -belongs to a function declaration, the indentation of the func -keyword will be returned. Otherwise the indentation of the -current line will be returned." - (save-excursion - (if (go--at-function-definition) - (progn - (beginning-of-defun) - (current-indentation)) - (current-indentation)))) - -(defun go-indentation-at-point () - (save-excursion - (let (start-nesting) - (back-to-indentation) - (setq start-nesting (go-paren-level)) - - (cond - ((go-in-string-p) - (current-indentation)) - ((looking-at "[])}]") - (go-goto-opening-parenthesis) - (if (go-previous-line-has-dangling-op-p) - (- (current-indentation) tab-width) - (go--indentation-for-opening-parenthesis))) - ((progn (go--backward-irrelevant t) - (looking-back go-dangling-operators-regexp - (- (point) go--max-dangling-operator-length))) - ;; only one nesting for all dangling operators in one operation - (if (go-previous-line-has-dangling-op-p) - (current-indentation) - (+ (current-indentation) tab-width))) - ((zerop (go-paren-level)) - 0) - ((progn (go-goto-opening-parenthesis) (< (go-paren-level) start-nesting)) - (if (go-previous-line-has-dangling-op-p) - (current-indentation) - (+ (go--indentation-for-opening-parenthesis) tab-width))) - (t - (current-indentation)))))) - -(defun go-mode-indent-line () - (interactive) - (let (indent - shift-amt - (pos (- (point-max) (point))) - (point (point)) - (beg (line-beginning-position))) - (back-to-indentation) - (if (go-in-string-or-comment-p) - (goto-char point) - (setq indent (go-indentation-at-point)) - (if (looking-at (concat go-label-regexp ":\\([[:space:]]*/.+\\)?$\\|case .+:\\|default:")) - (cl-decf indent tab-width)) - (setq shift-amt (- indent (current-column))) - (if (zerop shift-amt) - nil - (delete-region beg (point)) - (indent-to indent)) - ;; If initial point was within line's indentation, - ;; position after the indentation. Else stay at same point in text. - (if (> (- (point-max) pos) (point)) - (goto-char (- (point-max) pos)))))) - -(defun go-beginning-of-defun (&optional count) - (unless (bolp) - (end-of-line)) - (setq count (or count 1)) - (let (first failure) - (dotimes (i (abs count)) - (setq first t) - (while (and (not failure) - (or first (go-in-string-or-comment-p))) - (if (>= count 0) - (progn - (go--backward-irrelevant) - (if (not (re-search-backward go-func-meth-regexp nil t)) - (setq failure t))) - (if (looking-at go-func-meth-regexp) - (forward-char)) - (if (not (re-search-forward go-func-meth-regexp nil t)) - (setq failure t))) - (setq first nil))) - (if (< count 0) - (beginning-of-line)) - (not failure))) - -(defun go-end-of-defun () - (let (orig-level) - ;; It can happen that we're not placed before a function by emacs - (if (not (looking-at "func")) - (go-beginning-of-defun -1)) - ;; Find the { that starts the function, i.e., the next { that isn't - ;; preceded by struct or interface, or a comment or struct tag. BUG: - ;; breaks if there's a comment between the struct/interface keyword and - ;; bracket, like this: - ;; - ;; struct /* why? */ { - (while (progn - (skip-chars-forward "^{") - (forward-char) - (or (go-in-string-or-comment-p) - (looking-back "\\(struct\\|interface\\)\\s-*{" - (line-beginning-position))))) - (setq orig-level (go-paren-level)) - (while (>= (go-paren-level) orig-level) - (skip-chars-forward "^}") - (forward-char)))) - -(defun go--find-enclosing-parentheses (position) - "Return points of outermost '(' and ')' surrounding POSITION if -such parentheses exist. - -If outermost '(' exists but ')' does not, it returns the next blank -line or end-of-buffer position instead of the position of the closing -parenthesis. - -If the starting parenthesis is not found, it returns (POSITION -POSITION)." - (save-excursion - (let (beg end) - (goto-char position) - (while (> (go-paren-level) 0) - (re-search-backward "[(\\[{]" nil t) - (when (looking-at "(") - (setq beg (point)))) - (if (null beg) - (list position position) - (goto-char position) - (while (and (> (go-paren-level) 0) - (search-forward ")" nil t))) - (when (> (go-paren-level) 0) - (unless (re-search-forward "^[[:space:]]*$" nil t) - (goto-char (point-max)))) - (list beg (point)))))) - -(defun go--search-next-comma (end) - "Search forward from point for a comma whose nesting level is -the same as point. If it reaches the end of line or a closing -parenthesis before a comma, it stops at it." - (let ((orig-level (go-paren-level))) - (while (and (< (point) end) - (or (looking-at "[^,)\n]") - (> (go-paren-level) orig-level))) - (forward-char)) - (when (and (looking-at ",") - (< (point) (1- end))) - (forward-char)))) - -(defun go--looking-at-keyword () - (and (looking-at (concat "\\(" go-identifier-regexp "\\)")) - (member (match-string 1) go-mode-keywords))) - -(defun go--match-func (end) - "Search for identifiers used as type names from a function -parameter list, and set the identifier positions as the results -of last search. Return t if search succeeded." - (when (re-search-forward "\\_<func\\_>" end t) - (let ((regions (go--match-func-type-names end))) - (if (null regions) - ;; Nothing to highlight. This can happen if the current func - ;; is "func()". Try next one. - (go--match-func end) - ;; There are something to highlight. Set those positions as - ;; last search results. - (setq regions (go--filter-match-data regions end)) - (when regions - (set-match-data (go--make-match-data regions)) - t))))) - -(defun go--match-func-type-names (end) - (cond - ;; Function declaration (e.g. "func foo(") - ((looking-at (concat "[[:space:]\n]*" go-identifier-regexp "[[:space:]\n]*(")) - (goto-char (match-end 0)) - (nconc (go--match-parameter-list end) - (go--match-function-result end))) - ;; Method declaration, function literal, or function type - ((looking-at "[[:space:]]*(") - (goto-char (match-end 0)) - (let ((regions (go--match-parameter-list end))) - ;; Method declaration (e.g. "func (x y) foo(") - (when (looking-at (concat "[[:space:]]*" go-identifier-regexp "[[:space:]\n]*(")) - (goto-char (match-end 0)) - (setq regions (nconc regions (go--match-parameter-list end)))) - (nconc regions (go--match-function-result end)))))) - -(defun go--parameter-list-type (end) - "Return `present' if the parameter list has names, or `absent' if not. -Assumes point is at the beginning of a parameter list, just -after '('." - (save-excursion - (skip-chars-forward "[:space:]\n" end) - (cond ((> (point) end) - nil) - ((looking-at (concat go-identifier-regexp "[[:space:]\n]*,")) - (goto-char (match-end 0)) - (go--parameter-list-type end)) - ((or (looking-at go-qualified-identifier-regexp) - (looking-at (concat go-type-name-no-prefix-regexp "[[:space:]\n]*\\(?:)\\|\\'\\)")) - (go--looking-at-keyword) - (looking-at "[*\\[]\\|\\.\\.\\.\\|\\'")) - 'absent) - (t 'present)))) - -(defconst go--opt-dotdotdot-regexp "\\(?:\\.\\.\\.\\)?") -(defconst go--parameter-type-regexp - (concat go--opt-dotdotdot-regexp "[[:space:]*\n]*\\(" go-type-name-no-prefix-regexp "\\)[[:space:]\n]*\\([,)]\\|\\'\\)")) -(defconst go--func-type-in-parameter-list-regexp - (concat go--opt-dotdotdot-regexp "[[:space:]*\n]*\\(\\_<func\\_>" "\\)")) - -(defun go--match-parameters-common (identifier-regexp end) - (let ((acc ()) - (start -1)) - (while (progn (skip-chars-forward "[:space:]\n" end) - (and (not (looking-at "\\(?:)\\|\\'\\)")) - (< start (point)) - (<= (point) end))) - (setq start (point)) - (cond - ((looking-at (concat identifier-regexp go--parameter-type-regexp)) - (setq acc (nconc acc (list (match-beginning 1) (match-end 1)))) - (goto-char (match-beginning 2))) - ((looking-at (concat identifier-regexp go--func-type-in-parameter-list-regexp)) - (goto-char (match-beginning 1)) - (setq acc (nconc acc (go--match-func-type-names end))) - (go--search-next-comma end)) - (t - (go--search-next-comma end)))) - (when (and (looking-at ")") - (< (point) end)) - (forward-char)) - acc)) - -(defun go--match-parameters-with-identifier-list (end) - (go--match-parameters-common - (concat go-identifier-regexp "[[:space:]\n]+") - end)) - -(defun go--match-parameters-without-identifier-list (end) - (go--match-parameters-common "" end)) - -(defun go--filter-match-data (regions end) - "Remove points from REGIONS if they are beyond END. -REGIONS are a list whose size is multiple of 2. Element 2n is beginning of a -region and 2n+1 is end of it. - -This function is used to make sure we don't override end point -that `font-lock-mode' gave to us." - (when regions - (let* ((vec (vconcat regions)) - (i 0) - (len (length vec))) - (while (and (< i len) - (<= (nth i regions) end) - (<= (nth (1+ i) regions) end)) - (setq i (+ i 2))) - (cond ((= i len) - regions) - ((zerop i) - nil) - (t - (butlast regions (- (length regions) i))))))) - -(defun go--make-match-data (regions) - (let ((deficit (- (* 2 go--font-lock-func-param-num-groups) - (length regions)))) - (when (> deficit 0) - (let ((last (car (last regions)))) - (setq regions (nconc regions (make-list deficit last)))))) - `(,(car regions) ,@(last regions) ,@regions)) - -(defun go--match-parameter-list (end) - "Return a list of identifier positions that are used as type -names in a function parameter list, assuming point is at the -beginning of a parameter list. Return nil if the text after -point does not look like a parameter list. - -Set point to end of closing parenthesis on success. - -In Go, the names must either all be present or all be absent -within a list of parameters. - -Parsing a parameter list is a little bit complicated because we -have to scan through the parameter list to determine whether or -not the list has names. Until a type name is found or reaching -end of a parameter list, we are not sure which form the parameter -list is. - -For example, X and Y are type names in a parameter list \"(X, -Y)\" but are parameter names in \"(X, Y int)\". We cannot say if -X is a type name until we see int after Y. - -Note that even \"(int, float T)\" is a valid parameter -list. Builtin type names are not reserved words. In this example, -int and float are parameter names and only T is a type name. - -In this function, we first scan the parameter list to see if the -list has names, and then handle it accordingly." - (let ((name (go--parameter-list-type end))) - (cond ((eq name 'present) - (go--match-parameters-with-identifier-list end)) - ((eq name 'absent) - (go--match-parameters-without-identifier-list end)) - (t nil)))) - -(defun go--match-function-result (end) - "Return a list of identifier positions that are used as type -names in a function result, assuming point is at the beginning of -a result. - -Function result is a unparenthesized type or a parameter list." - (cond ((and (looking-at (concat "[[:space:]*]*\\(" go-type-name-no-prefix-regexp "\\)")) - (not (member (match-string 1) go-mode-keywords))) - (list (match-beginning 1) (match-end 1))) - ((looking-at "[[:space:]]*(") - (goto-char (match-end 0)) - (go--match-parameter-list end)) - (t nil))) - -(defun go--reset-dangling-cache-before-change (&optional _beg _end) - "Reset `go-dangling-cache'. - -This is intended to be called from `before-change-functions'." - (setq go-dangling-cache (make-hash-table :test 'eql))) - -;;;###autoload -(define-derived-mode go-mode prog-mode "Go" - "Major mode for editing Go source text. - -This mode provides (not just) basic editing capabilities for -working with Go code. It offers almost complete syntax -highlighting, indentation that is almost identical to gofmt and -proper parsing of the buffer content to allow features such as -navigation by function, manipulation of comments or detection of -strings. - -In addition to these core features, it offers various features to -help with writing Go code. You can directly run buffer content -through gofmt, read godoc documentation from within Emacs, modify -and clean up the list of package imports or interact with the -Playground (uploading and downloading pastes). - -The following extra functions are defined: - -- `gofmt' -- `godoc' and `godoc-at-point' -- `go-import-add' -- `go-remove-unused-imports' -- `go-goto-arguments' -- `go-goto-docstring' -- `go-goto-function' -- `go-goto-function-name' -- `go-goto-imports' -- `go-goto-return-values' -- `go-goto-method-receiver' -- `go-play-buffer' and `go-play-region' -- `go-download-play' -- `godef-describe' and `godef-jump' -- `go-coverage' -- `go-set-project' -- `go-reset-gopath' - -If you want to automatically run `gofmt' before saving a file, -add the following hook to your emacs configuration: - -\(add-hook 'before-save-hook #'gofmt-before-save) - -If you want to use `godef-jump' instead of etags (or similar), -consider binding godef-jump to `M-.', which is the default key -for `find-tag': - -\(add-hook 'go-mode-hook (lambda () - (local-set-key (kbd \"M-.\") #'godef-jump))) - -Please note that godef is an external dependency. You can install -it with - -go get github.com/rogpeppe/godef - - -If you're looking for even more integration with Go, namely -on-the-fly syntax checking, auto-completion and snippets, it is -recommended that you look at flycheck -\(see URL `https://github.com/flycheck/flycheck') or flymake in combination -with goflymake \(see URL `https://github.com/dougm/goflymake'), gocode -\(see URL `https://github.com/nsf/gocode'), go-eldoc -\(see URL `github.com/syohex/emacs-go-eldoc') and yasnippet-go -\(see URL `https://github.com/dominikh/yasnippet-go')" - - ;; Font lock - (set (make-local-variable 'font-lock-defaults) - '(go--build-font-lock-keywords)) - - ;; Indentation - (set (make-local-variable 'indent-line-function) #'go-mode-indent-line) - - ;; Comments - (set (make-local-variable 'comment-start) "// ") - (set (make-local-variable 'comment-end) "") - (set (make-local-variable 'comment-use-syntax) t) - (set (make-local-variable 'comment-start-skip) "\\(//+\\|/\\*+\\)\\s *") - - (set (make-local-variable 'beginning-of-defun-function) #'go-beginning-of-defun) - (set (make-local-variable 'end-of-defun-function) #'go-end-of-defun) - - (set (make-local-variable 'parse-sexp-lookup-properties) t) - (set (make-local-variable 'syntax-propertize-function) #'go-propertize-syntax) - - (if (boundp 'electric-indent-chars) - (set (make-local-variable 'electric-indent-chars) '(?\n ?} ?\)))) - - (set (make-local-variable 'compilation-error-screen-columns) nil) - - (set (make-local-variable 'go-dangling-cache) (make-hash-table :test 'eql)) - (add-hook 'before-change-functions #'go--reset-dangling-cache-before-change t t) - - ;; ff-find-other-file - (setq ff-other-file-alist 'go-other-file-alist) - - (setq imenu-generic-expression - '(("type" "^type *\\([^ \t\n\r\f]*\\)" 1) - ("func" "^func *\\(.*\\) {" 1))) - (imenu-add-to-menubar "Index") - - ;; Go style - (setq indent-tabs-mode t) - - ;; Handle unit test failure output in compilation-mode - ;; - ;; Note that we add our entry to the beginning of - ;; compilation-error-regexp-alist. In older versions of Emacs, the - ;; list was processed from the end, and we would've wanted to add - ;; ours last. But at some point this changed, and now the list is - ;; processed from the beginning. It's important that our entry comes - ;; before gnu, because gnu matches go test output, but includes the - ;; leading whitespace in the file name. - ;; - ;; http://lists.gnu.org/archive/html/bug-gnu-emacs/2001-12/msg00674.html - ;; documents the old, reverseed order. - (when (and (boundp 'compilation-error-regexp-alist) - (boundp 'compilation-error-regexp-alist-alist)) - (add-to-list 'compilation-error-regexp-alist 'go-test) - (add-to-list 'compilation-error-regexp-alist-alist - '(go-test . ("^\t+\\([^()\t\n]+\\):\\([0-9]+\\):? .*$" 1 2)) t))) - -;;;###autoload -(add-to-list 'auto-mode-alist (cons "\\.go\\'" 'go-mode)) - -(defun go--apply-rcs-patch (patch-buffer) - "Apply an RCS-formatted diff from PATCH-BUFFER to the current buffer." - (let ((target-buffer (current-buffer)) - ;; Relative offset between buffer line numbers and line numbers - ;; in patch. - ;; - ;; Line numbers in the patch are based on the source file, so - ;; we have to keep an offset when making changes to the - ;; buffer. - ;; - ;; Appending lines decrements the offset (possibly making it - ;; negative), deleting lines increments it. This order - ;; simplifies the forward-line invocations. - (line-offset 0)) - (save-excursion - (with-current-buffer patch-buffer - (goto-char (point-min)) - (while (not (eobp)) - (unless (looking-at "^\\([ad]\\)\\([0-9]+\\) \\([0-9]+\\)") - (error "Invalid rcs patch or internal error in go--apply-rcs-patch")) - (forward-line) - (let ((action (match-string 1)) - (from (string-to-number (match-string 2))) - (len (string-to-number (match-string 3)))) - (cond - ((equal action "a") - (let ((start (point))) - (forward-line len) - (let ((text (buffer-substring start (point)))) - (with-current-buffer target-buffer - (cl-decf line-offset len) - (goto-char (point-min)) - (forward-line (- from len line-offset)) - (insert text))))) - ((equal action "d") - (with-current-buffer target-buffer - (go--goto-line (- from line-offset)) - (cl-incf line-offset len) - (go--delete-whole-line len))) - (t - (error "Invalid rcs patch or internal error in go--apply-rcs-patch"))))))))) - -(defun gofmt--is-goimports-p () - (string-equal (file-name-base gofmt-command) "goimports")) - -(defun gofmt () - "Format the current buffer according to the gofmt tool." - (interactive) - (let ((tmpfile (make-temp-file "gofmt" nil ".go")) - (patchbuf (get-buffer-create "*Gofmt patch*")) - (errbuf (if gofmt-show-errors (get-buffer-create "*Gofmt Errors*"))) - (coding-system-for-read 'utf-8) - (coding-system-for-write 'utf-8) - our-gofmt-args) - - (unwind-protect - (save-restriction - (widen) - (if errbuf - (with-current-buffer errbuf - (setq buffer-read-only nil) - (erase-buffer))) - (with-current-buffer patchbuf - (erase-buffer)) - - (write-region nil nil tmpfile) - - (when (and (gofmt--is-goimports-p) buffer-file-name) - (setq our-gofmt-args - (append our-gofmt-args - ;; srcdir, despite its name, supports - ;; accepting a full path, and some features - ;; of goimports rely on knowing the full - ;; name. - (list "-srcdir" (file-truename buffer-file-name))))) - (setq our-gofmt-args (append our-gofmt-args - gofmt-args - (list "-w" tmpfile))) - (message "Calling gofmt: %s %s" gofmt-command our-gofmt-args) - ;; We're using errbuf for the mixed stdout and stderr output. This - ;; is not an issue because gofmt -w does not produce any stdout - ;; output in case of success. - (if (zerop (apply #'call-process gofmt-command nil errbuf nil our-gofmt-args)) - (progn - (if (zerop (call-process-region (point-min) (point-max) "diff" nil patchbuf nil "-n" "-" tmpfile)) - (message "Buffer is already gofmted") - (go--apply-rcs-patch patchbuf) - (message "Applied gofmt")) - (if errbuf (gofmt--kill-error-buffer errbuf))) - (message "Could not apply gofmt") - (if errbuf (gofmt--process-errors (buffer-file-name) tmpfile errbuf)))) - - (kill-buffer patchbuf) - (delete-file tmpfile)))) - - -(defun gofmt--process-errors (filename tmpfile errbuf) - (with-current-buffer errbuf - (if (eq gofmt-show-errors 'echo) - (progn - (message "%s" (buffer-string)) - (gofmt--kill-error-buffer errbuf)) - ;; Convert the gofmt stderr to something understood by the compilation mode. - (goto-char (point-min)) - (if (save-excursion - (save-match-data - (search-forward "flag provided but not defined: -srcdir" nil t))) - (insert "Your version of goimports is too old and doesn't support vendoring. Please update goimports!\n\n")) - (insert "gofmt errors:\n") - (let ((truefile - (if (gofmt--is-goimports-p) - (concat (file-name-directory filename) (file-name-nondirectory tmpfile)) - tmpfile))) - (while (search-forward-regexp (concat "^\\(" (regexp-quote truefile) "\\):") nil t) - (replace-match (file-name-nondirectory filename) t t nil 1))) - (compilation-mode) - (display-buffer errbuf)))) - -(defun gofmt--kill-error-buffer (errbuf) - (let ((win (get-buffer-window errbuf))) - (if win - (quit-window t win) - (kill-buffer errbuf)))) - -;;;###autoload -(defun gofmt-before-save () - "Add this to .emacs to run gofmt on the current buffer when saving: -\(add-hook 'before-save-hook 'gofmt-before-save). - -Note that this will cause ‘go-mode’ to get loaded the first time -you save any file, kind of defeating the point of autoloading." - - (interactive) - (when (eq major-mode 'go-mode) (gofmt))) - -(defun godoc--read-query () - "Read a godoc query from the minibuffer." - (if godoc-use-completing-read - (completing-read "godoc; " - (go-packages) nil nil nil 'go-godoc-history) - (read-from-minibuffer "godoc: " nil nil nil 'go-godoc-history))) - -(defun godoc--get-buffer (query) - "Get an empty buffer for a godoc QUERY." - (let* ((buffer-name (concat "*godoc " query "*")) - (buffer (get-buffer buffer-name))) - ;; Kill the existing buffer if it already exists. - (when buffer (kill-buffer buffer)) - (get-buffer-create buffer-name))) - -(defun godoc--buffer-sentinel (proc event) - "Sentinel function run when godoc command completes." - (with-current-buffer (process-buffer proc) - (cond ((string= event "finished\n") ;; Successful exit. - (goto-char (point-min)) - (godoc-mode) - (display-buffer (current-buffer) t)) - ((/= (process-exit-status proc) 0) ;; Error exit. - (let ((output (buffer-string))) - (kill-buffer (current-buffer)) - (message (concat "godoc: " output))))))) - -(define-derived-mode godoc-mode special-mode "Godoc" - "Major mode for showing Go documentation." - (view-mode-enter)) - -;;;###autoload -(defun godoc (query) - "Show Go documentation for QUERY, much like \\<go-mode-map>\\[man]." - (interactive (list (godoc--read-query))) - (go--godoc query godoc-command)) - -(defun go--godoc (query command) - (unless (string= query "") - (set-process-sentinel - (start-process-shell-command "godoc" (godoc--get-buffer query) - (concat command " " query)) - 'godoc--buffer-sentinel) - nil)) - -(defun godoc-at-point (point) - "Show Go documentation for the identifier at POINT. - -It uses `godoc-at-point-function' to look up the documentation." - (interactive "d") - (funcall godoc-at-point-function point)) - -(defun go-goto-imports () - "Move point to the block of imports. - -If using - - import ( - \"foo\" - \"bar\" - ) - -it will move point directly behind the last import. - -If using - - import \"foo\" - import \"bar\" - -it will move point to the next line after the last import. - -If no imports can be found, point will be moved after the package -declaration." - (interactive) - ;; FIXME if there's a block-commented import before the real - ;; imports, we'll jump to that one. - - ;; Generally, this function isn't very forgiving. it'll bark on - ;; extra whitespace. It works well for clean code. - (let ((old-point (point))) - (goto-char (point-min)) - (cond - ((re-search-forward "^import ()" nil t) - (backward-char 1) - 'block-empty) - ((re-search-forward "^import ([^)]+)" nil t) - (backward-char 2) - 'block) - ((re-search-forward "\\(^import \\([^\"]+ \\)?\"[^\"]+\"\n?\\)+" nil t) - 'single) - ((re-search-forward "^[[:space:]\n]*package .+?\n" nil t) - (message "No imports found, moving point after package declaration") - 'none) - (t - (goto-char old-point) - (message "No imports or package declaration found. Is this really a Go file?") - 'fail)))) - -(defun go-play-buffer () - "Like `go-play-region', but acts on the entire buffer." - (interactive) - (go-play-region (point-min) (point-max))) - -(defun go-play-region (start end) - "Send the region between START and END to the Playground. -If non-nil `go-play-browse-function' is called with the -Playground URL." - (interactive "r") - (let* ((url-request-method "POST") - (url-request-extra-headers - '(("Content-Type" . "application/x-www-form-urlencoded"))) - (url-request-data - (encode-coding-string - (buffer-substring-no-properties start end) - 'utf-8)) - (content-buf (url-retrieve - "https://play.golang.org/share" - (lambda (arg) - (cond - ((equal :error (car arg)) - (signal 'go-play-error (cdr arg))) - (t - (re-search-forward "\n\n") - (let ((url (format "https://play.golang.org/p/%s" - (buffer-substring (point) (point-max))))) - (when go-play-browse-function - (funcall go-play-browse-function url))))))))))) - -;;;###autoload -(defun go-download-play (url) - "Download a paste from the playground and insert it in a Go buffer. -Tries to look for a URL at point." - (interactive (list (read-from-minibuffer "Playground URL: " (ffap-url-p (ffap-string-at-point 'url))))) - (with-current-buffer - (let ((url-request-method "GET") url-request-data url-request-extra-headers) - (url-retrieve-synchronously (concat url ".go"))) - (let ((buffer (generate-new-buffer (concat (car (last (split-string url "/"))) ".go")))) - (goto-char (point-min)) - (re-search-forward "\n\n") - (copy-to-buffer buffer (point) (point-max)) - (kill-buffer) - (with-current-buffer buffer - (go-mode) - (switch-to-buffer buffer))))) - -(defun go-propertize-syntax (start end) - (save-excursion - (goto-char start) - (while (search-forward "\\" end t) - (put-text-property (1- (point)) (point) 'syntax-table (if (= (char-after) ?`) '(1) '(9)))))) - -(defun go-import-add (arg import) - "Add a new IMPORT to the list of imports. - -When called with a prefix ARG asks for an alternative name to -import the package as. - -If no list exists yet, one will be created if possible. - -If an identical import has been commented, it will be -uncommented, otherwise a new import will be added." - - ;; - If there's a matching `// import "foo"`, uncomment it - ;; - If we're in an import() block and there's a matching `"foo"`, uncomment it - ;; - Otherwise add a new import, with the appropriate syntax - (interactive - (list - current-prefix-arg - (replace-regexp-in-string "^[\"']\\|[\"']$" "" (completing-read "Package: " (go-packages))))) - (save-excursion - (let (as line import-start) - (if arg - (setq as (read-from-minibuffer "Import as: "))) - (if as - (setq line (format "%s \"%s\"" as import)) - (setq line (format "\"%s\"" import))) - - (goto-char (point-min)) - (if (re-search-forward (concat "^[[:space:]]*//[[:space:]]*import " line "$") nil t) - (uncomment-region (line-beginning-position) (line-end-position)) - (cl-case (go-goto-imports) - ('fail (message "Could not find a place to add import.")) - ('block-empty - (insert "\n\t" line "\n")) - ('block - (save-excursion - (re-search-backward "^import (") - (setq import-start (point))) - (if (re-search-backward (concat "^[[:space:]]*//[[:space:]]*" line "$") import-start t) - (uncomment-region (line-beginning-position) (line-end-position)) - (insert "\n\t" line))) - ('single (insert "import " line "\n")) - ('none (insert "\nimport (\n\t" line "\n)\n"))))))) - -(defun go-root-and-paths () - (let* ((output (process-lines go-command "env" "GOROOT" "GOPATH")) - (root (car output)) - (paths (split-string (cadr output) path-separator))) - (cons root paths))) - -(defun go--string-prefix-p (s1 s2 &optional ignore-case) - "Return non-nil if S1 is a prefix of S2. -If IGNORE-CASE is non-nil, the comparison is case-insensitive." - (eq t (compare-strings s1 nil nil - s2 0 (length s1) ignore-case))) - -(defun go--directory-dirs (dir) - "Recursively return all subdirectories in DIR." - (if (file-directory-p dir) - (let ((dir (directory-file-name dir)) - (dirs '()) - (files (directory-files dir nil nil t))) - (dolist (file files) - (unless (member file '("." "..")) - (let ((file (concat dir "/" file))) - (if (file-directory-p file) - (setq dirs (append (cons file - (go--directory-dirs file)) - dirs)))))) - dirs) - '())) - - -(defun go-packages () - (funcall go-packages-function)) - -(defun go-packages-native () - "Return a list of all installed Go packages. -It looks for archive files in /pkg/." - (sort - (delete-dups - (cl-mapcan - (lambda (topdir) - (let ((pkgdir (concat topdir "/pkg/"))) - (cl-mapcan (lambda (dir) - (mapcar (lambda (file) - (let ((sub (substring file (length pkgdir) -2))) - (unless (or (go--string-prefix-p "obj/" sub) (go--string-prefix-p "tool/" sub)) - (mapconcat #'identity (cdr (split-string sub "/")) "/")))) - (if (file-directory-p dir) - (directory-files dir t "\\.a$")))) - (if (file-directory-p pkgdir) - (go--directory-dirs pkgdir))))) - (go-root-and-paths))) - #'string<)) - -(defun go-packages-go-list () - "Return a list of all Go packages, using `go list'." - (process-lines go-command "list" "-e" "all")) - -(defun go-unused-imports-lines () - (reverse (remove nil - (mapcar - (lambda (line) - (when (string-match "^\\(.+\\):\\([[:digit:]]+\\): imported and not used: \".+\".*$" line) - (let ((error-file-name (match-string 1 line)) - (error-line-num (match-string 2 line))) - (if (string= (file-truename error-file-name) (file-truename buffer-file-name)) - (string-to-number error-line-num))))) - (split-string (shell-command-to-string - (concat go-command - (if (string-match "_test\\.go$" buffer-file-truename) - " test -c" - (concat " build -o " null-device)) - " -gcflags=-e" - " " - (shell-quote-argument (file-truename buffer-file-name)))) "\n"))))) - -(defun go-remove-unused-imports (arg) - "Remove all unused imports. -If ARG is non-nil, unused imports will be commented, otherwise -they will be removed completely." - (interactive "P") - (save-excursion - (let ((cur-buffer (current-buffer)) flymake-state lines) - (when (boundp 'flymake-mode) - (setq flymake-state flymake-mode) - (flymake-mode-off)) - (save-some-buffers nil (lambda () (equal cur-buffer (current-buffer)))) - (if (buffer-modified-p) - (message "Cannot operate on unsaved buffer") - (setq lines (go-unused-imports-lines)) - (dolist (import lines) - (go--goto-line import) - (beginning-of-line) - (if arg - (comment-region (line-beginning-position) (line-end-position)) - (go--delete-whole-line))) - (message "Removed %d imports" (length lines))) - (if flymake-state (flymake-mode-on))))) - -(defun godef--find-file-line-column (specifier other-window) - "Given a file name in the format of `filename:line:column', -visit FILENAME and go to line LINE and column COLUMN." - (if (not (string-match "\\(.+\\):\\([0-9]+\\):\\([0-9]+\\)" specifier)) - ;; We've only been given a directory name - (funcall (if other-window #'find-file-other-window #'find-file) specifier) - (let ((filename (match-string 1 specifier)) - (line (string-to-number (match-string 2 specifier))) - (column (string-to-number (match-string 3 specifier)))) - (funcall (if other-window #'find-file-other-window #'find-file) filename) - (go--goto-line line) - (beginning-of-line) - (forward-char (1- column)) - (if (buffer-modified-p) - (message "Buffer is modified, file position might not have been correct"))))) - -(defun godef--call (point) - "Call godef, acquiring definition position and expression -description at POINT." - (if (not (buffer-file-name (go--coverage-origin-buffer))) - (error "Cannot use godef on a buffer without a file name") - (let ((outbuf (generate-new-buffer "*godef*")) - (coding-system-for-read 'utf-8) - (coding-system-for-write 'utf-8)) - (prog2 - (call-process-region (point-min) - (point-max) - godef-command - nil - outbuf - nil - "-i" - "-t" - "-f" - (file-truename (buffer-file-name (go--coverage-origin-buffer))) - "-o" - (number-to-string (position-bytes point))) - (with-current-buffer outbuf - (split-string (buffer-substring-no-properties (point-min) (point-max)) "\n")) - (kill-buffer outbuf))))) - -(defun godef--successful-p (output) - (not (or (string= "-" output) - (string= "godef: no identifier found" output) - (go--string-prefix-p "godef: no declaration found for " output) - (go--string-prefix-p "error finding import path for " output)))) - -(defun godef--error (output) - (cond - ((godef--successful-p output) - nil) - ((string= "-" output) - "godef: expression is not defined anywhere") - (t - output))) - -(defun godef-describe (point) - "Describe the expression at POINT." - (interactive "d") - (condition-case nil - (let ((description (cdr (butlast (godef--call point) 1)))) - (if (not description) - (message "No description found for expression at point") - (message "%s" (mapconcat #'identity description "\n")))) - (file-error (message "Could not run godef binary")))) - -(defun godef-jump (point &optional other-window) - "Jump to the definition of the expression at POINT." - (interactive "d") - (condition-case nil - (let ((file (car (godef--call point)))) - (if (not (godef--successful-p file)) - (message "%s" (godef--error file)) - (push-mark) - (if (eval-when-compile (fboundp 'xref-push-marker-stack)) - ;; TODO: Integrate this facility with XRef. - (xref-push-marker-stack) - (ring-insert find-tag-marker-ring (point-marker))) - (godef--find-file-line-column file other-window))) - (file-error (message "Could not run godef binary")))) - -(defun godef-jump-other-window (point) - (interactive "d") - (godef-jump point t)) - -(defun go--goto-line (line) - (goto-char (point-min)) - (forward-line (1- line))) - -(defun go--line-column-to-point (line column) - (save-excursion - (go--goto-line line) - (forward-char (1- column)) - (point))) - -(cl-defstruct go--covered - start-line start-column end-line end-column covered count) - -(defun go--coverage-file () - "Return the coverage file to use, either by reading it from the -current coverage buffer or by prompting for it." - (if (boundp 'go--coverage-current-file-name) - go--coverage-current-file-name - (read-file-name "Coverage file: " nil nil t))) - -(defun go--coverage-origin-buffer () - "Return the buffer to base the coverage on." - (or (buffer-base-buffer) (current-buffer))) - -(defun go--coverage-face (count divisor) - "Return the intensity face for COUNT when using DIVISOR -to scale it to a range [0,10]. - -DIVISOR scales the absolute cover count to values from 0 to 10. -For DIVISOR = 0 the count will always translate to 8." - (let* ((norm (cond - ((= count 0) - -0.1) ;; Uncovered code, set to -0.1 so n becomes 0. - ((= divisor 0) - 0.8) ;; covermode=set, set to 0.8 so n becomes 8. - (t - (/ (log count) divisor)))) - (n (1+ (floor (* norm 9))))) ;; Convert normalized count [0,1] to intensity [0,10] - (concat "go-coverage-" (number-to-string n)))) - -(defun go--coverage-make-overlay (range divisor) - "Create a coverage overlay for a RANGE of covered/uncovered code. -Use DIVISOR to scale absolute counts to a [0,10] scale." - (let* ((count (go--covered-count range)) - (face (go--coverage-face count divisor)) - (ov (make-overlay (go--line-column-to-point (go--covered-start-line range) - (go--covered-start-column range)) - (go--line-column-to-point (go--covered-end-line range) - (go--covered-end-column range))))) - - (overlay-put ov 'face face) - (overlay-put ov 'help-echo (format "Count: %d" count)))) - -(defun go--coverage-clear-overlays () - "Remove existing overlays and put a single untracked overlay -over the entire buffer." - (remove-overlays) - (overlay-put (make-overlay (point-min) (point-max)) - 'face - 'go-coverage-untracked)) - -(defun go--coverage-parse-file (coverage-file file-name) - "Parse COVERAGE-FILE and extract coverage information and -divisor for FILE-NAME." - (let (ranges - (max-count 0)) - (with-temp-buffer - (insert-file-contents coverage-file) - (go--goto-line 2) ;; Skip over mode - (while (not (eobp)) - (let* ((parts (split-string (buffer-substring (point-at-bol) (point-at-eol)) ":")) - (file (car parts)) - (rest (split-string (nth 1 parts) "[., ]"))) - - (cl-destructuring-bind - (start-line start-column end-line end-column num count) - (mapcar #'string-to-number rest) - - (when (string= (file-name-nondirectory file) file-name) - (if (> count max-count) - (setq max-count count)) - (push (make-go--covered :start-line start-line - :start-column start-column - :end-line end-line - :end-column end-column - :covered (/= count 0) - :count count) - ranges))) - - (forward-line))) - - (list ranges (if (> max-count 0) (log max-count) 0))))) - -(defun go-coverage (&optional coverage-file) - "Open a clone of the current buffer and overlay it with -coverage information gathered via go test -coverprofile=COVERAGE-FILE. - -If COVERAGE-FILE is nil, it will either be inferred from the -current buffer if it's already a coverage buffer, or be prompted -for." - (interactive) - (let* ((cur-buffer (current-buffer)) - (origin-buffer (go--coverage-origin-buffer)) - (gocov-buffer-name (concat (buffer-name origin-buffer) "<gocov>")) - (coverage-file (or coverage-file (go--coverage-file))) - (ranges-and-divisor (go--coverage-parse-file - coverage-file - (file-name-nondirectory (buffer-file-name origin-buffer)))) - (cov-mtime (nth 5 (file-attributes coverage-file))) - (cur-mtime (nth 5 (file-attributes (buffer-file-name origin-buffer))))) - - (if (< (float-time cov-mtime) (float-time cur-mtime)) - (message "Coverage file is older than the source file.")) - - (with-current-buffer (or (get-buffer gocov-buffer-name) - (make-indirect-buffer origin-buffer gocov-buffer-name t)) - (set (make-local-variable 'go--coverage-current-file-name) coverage-file) - - (save-excursion - (go--coverage-clear-overlays) - (dolist (range (car ranges-and-divisor)) - (go--coverage-make-overlay range (cadr ranges-and-divisor)))) - - (if (not (eq cur-buffer (current-buffer))) - (display-buffer (current-buffer) `(,go-coverage-display-buffer-func)))))) - -(defun go-goto-function (&optional arg) - "Go to the function defintion (named or anonymous) surrounding point. - -If we are on a docstring, follow the docstring down. -If no function is found, assume that we are at the top of a file -and search forward instead. - -If point is looking at the func keyword of an anonymous function, -go to the surrounding function. - -If ARG is non-nil, anonymous functions are ignored." - (interactive "P") - (let ((p (point))) - (cond - ((save-excursion - (beginning-of-line) - (looking-at "^//")) - ;; In case we are looking at the docstring, move on forward until we are - ;; not anymore - (beginning-of-line) - (while (looking-at "^//") - (forward-line 1)) - ;; If we are still not looking at a function, retry by calling self again. - (when (not (looking-at "\\<func\\>")) - (go-goto-function arg))) - - ;; If we're already looking at an anonymous func, look for the - ;; surrounding function. - ((and (looking-at "\\<func\\>") - (not (looking-at "^func\\>"))) - (re-search-backward "\\<func\\>" nil t)) - - ((not (looking-at "\\<func\\>")) - ;; If point is on the "func" keyword, step back a word and retry - (if (string= (symbol-name (symbol-at-point)) "func") - (backward-word) - ;; If we are not looking at the beginning of a function line, do a regexp - ;; search backwards - (re-search-backward "\\<func\\>" nil t)) - - ;; If nothing is found, assume that we are at the top of the file and - ;; should search forward instead. - (when (not (looking-at "\\<func\\>")) - (re-search-forward "\\<func\\>" nil t) - (go--forward-word -1)) - - ;; If we have landed at an anonymous function, it is possible that we - ;; were not inside it but below it. If we were not inside it, we should - ;; go to the containing function. - (while (and (not (go--in-function-p p)) - (not (looking-at "^func\\>"))) - (go-goto-function arg))))) - - (cond - ((go-in-comment-p) - ;; If we are still in a comment, redo the call so that we get out of it. - (go-goto-function arg)) - - ((and (looking-at "\\<func(") arg) - ;; If we are looking at an anonymous function and a prefix argument has - ;; been supplied, redo the call so that we skip the anonymous function. - (go-goto-function arg)))) - -(defun go--goto-opening-curly-brace () - ;; Find the { that starts the function, i.e., the next { that isn't - ;; preceded by struct or interface, or a comment or struct tag. BUG: - ;; breaks if there's a comment between the struct/interface keyword and - ;; bracket, like this: - ;; - ;; struct /* why? */ { - (go--goto-return-values) - (while (progn - (skip-chars-forward "^{") - (forward-char) - (or (go-in-string-or-comment-p) - (looking-back "\\(struct\\|interface\\)\\s-*{" - (line-beginning-position))))) - (backward-char)) - -(defun go--in-function-p (compare-point) - "Return t if COMPARE-POINT lies inside the function immediately surrounding point." - (save-excursion - (when (not (looking-at "\\<func\\>")) - (go-goto-function)) - (let ((start (point))) - (go--goto-opening-curly-brace) - - (unless (looking-at "{") - (error "Expected to be looking at opening curly brace")) - (forward-list 1) - (and (>= compare-point start) - (<= compare-point (point)))))) - -(defun go-goto-function-name (&optional arg) - "Go to the name of the current function. - -If the function is a test, place point after 'Test'. -If the function is anonymous, place point on the 'func' keyword. - -If ARG is non-nil, anonymous functions are skipped." - (interactive "P") - (when (not (looking-at "\\<func\\>")) - (go-goto-function arg)) - ;; If we are looking at func( we are on an anonymous function and - ;; nothing else should be done. - (when (not (looking-at "\\<func(")) - (let ((words 1) - (chars 1)) - (when (looking-at "\\<func (") - (setq words 3 - chars 2)) - (go--forward-word words) - (forward-char chars) - (when (looking-at "Test") - (forward-char 4))))) - -(defun go-goto-arguments (&optional arg) - "Go to the arguments of the current function. - -If ARG is non-nil, anonymous functions are skipped." - (interactive "P") - (go-goto-function-name arg) - (go--forward-word 1) - (forward-char 1)) - -(defun go--goto-return-values (&optional arg) - "Go to the declaration of return values for the current function." - (go-goto-arguments arg) - (backward-char) - (forward-list) - (forward-char)) - -(defun go-goto-return-values (&optional arg) - "Go to the return value declaration of the current function. - -If there are multiple ones contained in a parenthesis, enter the parenthesis. -If there is none, make space for one to be added. - -If ARG is non-nil, anonymous functions are skipped." - (interactive "P") - (go--goto-return-values arg) - - ;; Opening parenthesis, enter it - (when (looking-at "(") - (forward-char 1)) - - ;; No return arguments, add space for adding - (when (looking-at "{") - (insert " ") - (backward-char 1))) - -(defun go-goto-method-receiver (&optional arg) - "Go to the receiver of the current method. - -If there is none, add parenthesis to add one. - -Anonymous functions cannot have method receivers, so when this is called -interactively anonymous functions will be skipped. If called programmatically, -an error is raised unless ARG is non-nil." - (interactive "P") - - (when (and (not (called-interactively-p 'interactive)) - (not arg) - (go--in-anonymous-funcion-p)) - (error "Anonymous functions cannot have method receivers")) - - (go-goto-function t) ; Always skip anonymous functions - (forward-char 5) - (when (not (looking-at "(")) - (save-excursion - (insert "() "))) - (forward-char 1)) - -(defun go-goto-docstring (&optional arg) - "Go to the top of the docstring of the current function. - -If there is none, add one beginning with the name of the current function. - -Anonymous functions do not have docstrings, so when this is called -interactively anonymous functions will be skipped. If called programmatically, -an error is raised unless ARG is non-nil." - (interactive "P") - - (when (and (not (called-interactively-p 'interactive)) - (not arg) - (go--in-anonymous-funcion-p)) - (error "Anonymous functions do not have docstrings")) - - (go-goto-function t) - (forward-line -1) - (beginning-of-line) - - (while (looking-at "^//") - (forward-line -1)) - (forward-line 1) - (beginning-of-line) - - (cond - ;; If we are looking at an empty comment, add a single space in front of it. - ((looking-at "^//$") - (forward-char 2) - (insert (format " %s " (go--function-name t)))) - ;; If we are not looking at the function signature, we are looking at a docstring. - ;; Move to the beginning of the first word of it. - ((not (looking-at "^func")) - (forward-char 3)) - ;; If we are still at the function signature, we should add a new docstring. - (t - (forward-line -1) - (newline) - (insert "// ") - (insert (go--function-name t))))) - -(defun go--function-name (&optional arg) - "Return the name of the surrounding function. - -If ARG is non-nil, anonymous functions will be ignored and the -name returned will be that of the top-level function. If ARG is -nil and the surrounding function is anonymous, nil will be -returned." - (when (or (not (go--in-anonymous-funcion-p)) - arg) - (save-excursion - (go-goto-function-name t) - (symbol-name (symbol-at-point))))) - -(defun go--in-anonymous-funcion-p () - "Return t if point is inside an anonymous function, nil otherwise." - (save-excursion - (go-goto-function) - (looking-at "\\<func("))) - -(defun go-guess-gopath (&optional buffer) - "Determine a suitable GOPATH for BUFFER, or the current buffer if BUFFER is nil. - -This function supports gb-based projects as well as Godep, in -addition to ordinary uses of GOPATH." - (with-current-buffer (or buffer (current-buffer)) - (let ((gopath (cl-some (lambda (el) (funcall el)) - go-guess-gopath-functions))) - (if gopath - (mapconcat - (lambda (el) (file-truename el)) - gopath - path-separator))))) - -(defun go-plain-gopath () - "Detect a normal GOPATH, by looking for the first `src' -directory up the directory tree." - (let ((d (locate-dominating-file buffer-file-name "src"))) - (if d - (list d)))) - -(defun go-godep-gopath () - "Detect a Godeps workspace by looking for Godeps/_workspace up -the directory tree. The result is combined with that of -`go-plain-gopath'." - (let* ((d (locate-dominating-file buffer-file-name "Godeps")) - (workspace (concat d - (file-name-as-directory "Godeps") - (file-name-as-directory "_workspace")))) - (if (and d - (file-exists-p workspace)) - (list workspace - (locate-dominating-file buffer-file-name "src"))))) - -(defun go-gb-gopath () - "Detect a gb project." - (or (go--gb-vendor-gopath) - (go--gb-vendor-gopath-reverse))) - -(defun go--gb-vendor-gopath () - (let* ((d (locate-dominating-file buffer-file-name "src")) - (vendor (concat d (file-name-as-directory "vendor")))) - (if (and d - (file-exists-p vendor)) - (list d vendor)))) - -(defun go--gb-vendor-gopath-reverse () - (let* ((d (locate-dominating-file buffer-file-name "vendor")) - (src (concat d (file-name-as-directory "src")))) - (if (and d - (file-exists-p src)) - (list d (concat d - (file-name-as-directory "vendor")))))) - -(defun go-wgo-gopath () - "Detect a wgo project." - (or (go--wgo-gocfg "src") - (go--wgo-gocfg "vendor"))) - -(defun go--wgo-gocfg (needle) - (let* ((d (locate-dominating-file buffer-file-name needle)) - (gocfg (concat d (file-name-as-directory ".gocfg")))) - (if (and d - (file-exists-p gocfg)) - (with-temp-buffer - (insert-file-contents (concat gocfg "gopaths")) - (append - (mapcar (lambda (el) (concat d (file-name-as-directory el))) (split-string (buffer-string) "\n" t)) - (list (go-original-gopath))))))) - -(defun go-set-project (&optional buffer) - "Set GOPATH based on `go-guess-gopath' for BUFFER, or the current buffer if BUFFER is nil. - -If go-guess-gopath returns nil, that is if it couldn't determine -a valid value for GOPATH, GOPATH will be set to the initial value -of when Emacs was started. - -This function can for example be used as a -projectile-switch-project-hook, or simply be called manually when -switching projects." - (interactive) - (let ((gopath (or (go-guess-gopath buffer) - (go-original-gopath)))) - (setenv "GOPATH" gopath) - (message "Set GOPATH to %s" gopath))) - -(defun go-reset-gopath () - "Reset GOPATH to the value it had when Emacs started." - (interactive) - (let ((gopath (go-original-gopath))) - (setenv "GOPATH" gopath) - (message "Set GOPATH to %s" gopath))) - -(defun go-original-gopath () - "Return the original value of GOPATH from when Emacs was started." - (let ((process-environment initial-environment)) (getenv "GOPATH"))) - -(defun go--insert-modified-files () - "Insert the contents of each modified Go buffer into the -current buffer in the format specified by guru's -modified flag." - (mapc #'(lambda (b) - (and (buffer-modified-p b) - (buffer-file-name b) - (string= (file-name-extension (buffer-file-name b)) "go") - (go--insert-modified-file (buffer-file-name b) b))) - (buffer-list))) - -(defun go--insert-modified-file (name buffer) - (insert (format "%s\n%d\n" name (go--buffer-size-bytes buffer))) - (insert-buffer-substring buffer)) - -(defun go--buffer-size-bytes (&optional buffer) - (message "buffer; %s" buffer) - "Return the number of bytes in the current buffer. -If BUFFER, return the number of characters in that buffer instead." - (with-current-buffer (or buffer (current-buffer)) - (1- (position-bytes (point-max))))) - - -(provide 'go-mode) - -;;; go-mode.el ends here diff --git a/elpa/go-mode-1.5.0/go-mode.elc b/elpa/go-mode-1.5.0/go-mode.elc Binary files differ. diff --git a/elpa/go-mode-1.6.0.signed b/elpa/go-mode-1.6.0.signed @@ -0,0 +1 @@ +Good signature from 066DAFCB81E42C40 GNU ELPA Signing Agent (2019) <elpasign@elpa.gnu.org> (trust undefined) created at 2022-01-15T11:05:04+0100 using RSA +\ No newline at end of file diff --git a/elpa/go-mode-1.6.0/.github/workflows/emacs.yml b/elpa/go-mode-1.6.0/.github/workflows/emacs.yml @@ -0,0 +1,17 @@ +name: Emacs CI +on: [push, pull_request] +jobs: + + test: + name: Test + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: Install emacs + run: sudo apt-get update && sudo apt-get install -y emacs + + - name: Check out code into the Go module directory + uses: actions/checkout@v1 + + - name: Test + run: cd test; emacs --batch -q -l ert -l ../go-mode.el $(for t in *-test.el; do echo -n "-l $t "; done) -f ert-run-tests-batch-and-exit diff --git a/elpa/go-mode-1.6.0/.mailmap b/elpa/go-mode-1.6.0/.mailmap @@ -0,0 +1 @@ +Dominik Honnef <dominik@honnef.co> <dominikh@fork-bomb.org> diff --git a/elpa/go-mode-1.6.0/AUTHORS b/elpa/go-mode-1.6.0/AUTHORS @@ -0,0 +1,50 @@ +Aaron France <aaron.l.france@gmail.com> +Alan Donovan <adonovan@google.com> +Alan Donovan <alan@alandonovan.net> +Andrew Gerrand <adg@golang.org> +Austin Clements <aclements@csail.mit.edu> +Ben Fried <ben.fried@gmail.com> +Bobby Powers <bobbypowers@gmail.com> +Charles Lee <zombie.fml@gmail.com> +Daniel Morsing <daniel.morsing@gmail.com> +Dominik Honnef <dominik.honnef@gmail.com> +Dominik Honnef <dominik@honnef.co> +Eric Eisner <eric.d.eisner@gmail.com> +Erin Keenan <erinok@gmail.com> +Evan Martin <evan.martin@gmail.com> +Felix Lange <fjl@twurst.com> +Florian Weimer <fw@deneb.enyo.de> +Istvan Marko <mi-git@kismala.com> +Iwasaki Yudai <yudai.iwasaki@ntti3.com> +James Aguilar <jaguilar@google.com> +Jan Newmarch <jan.newmarch@gmail.com> +Jean-Marc Eurin <jmeurin@google.com> +Jeff Hodges <jeff@somethingsimilar.com> +Juergen Hoetzel <juergen@archlinux.org> +Kevin Ballard <kevin@sb.org> +Konstantin Shaposhnikov <k.shaposhnikov@gmail.com> +Lowe Thiderman <lowe.thiderman@gmail.com> +Mark Petrovic <mark.petrovic@xoom.com> +Mats Lidell <mats.lidell@cag.se> +Matt Armstrong <marmstrong@google.com> +Peter Kleiweg <pkleiweg@xs4all.nl> +Philipp Stephani <phst@google.com> +Quan Yong Zhai <qyzhai@gmail.com> +Robert Zaremba <robert.zaremba@zoho.com> +Rui Ueyama <ruiu@google.com> +Russ Cox <rsc@golang.org> +Ryan Barrett <ryanb@google.com> +Rüdiger Sonderfeld <ruediger@c-plusplus.net> +Sameer Ajmani <sameer@golang.org> +Scott Lawrence <bytbox@gmail.com> +Steven Elliot Harris <seharris@gmail.com> +Syohei YOSHIDA <syohex@gmail.com> +Taiki Sugawara <buzz.taiki@gmail.com> +Viacheslav Chimishuk <vchimishuk@yandex-team.ru> +Will <will@glozer.net> +Yasuyuki Oka <yasuyk@gmail.com> +Yutian Li <hotpxless@gmail.com> +Zac Bergquist <zbergquist99@gmail.com> +kostya-sh <kostya-sh@users.noreply.github.com> +nverno <noah.v.peart@gmail.com> +nwidger <niels.widger@gmail.com> diff --git a/elpa/go-mode-1.6.0/AUTHORS.old b/elpa/go-mode-1.6.0/AUTHORS.old @@ -0,0 +1,31 @@ +# This file contains all the authors that contributed to go-mode while +# it was still part of the Go distribution. Most of these authors are +# not tracked in this repository's history. For a complete list of +# authors, see the AUTHORS file instead. + +Aaron France <aaron.l.france@gmail.com> +Alan Donovan <adonovan@google.com> +Austin Clements <aclements@csail.mit.edu> +Ben Fried <ben.fried@gmail.com> +Bobby Powers <bobbypowers@gmail.com> +Charles Lee <zombie.fml@gmail.com> +Dominik Honnef <dominik@honnef.co> +Eric Eisner <eric.d.eisner@gmail.com> +Evan Martin <evan.martin@gmail.com> +Florian Weimer <fw@deneb.enyo.de> +Istvan Marko <mi-git@kismala.com> +James Aguilar <jaguilar@google.com> +Jan Newmarch <jan.newmarch@gmail.com> +Jean-Marc Eurin <jmeurin@google.com> +Kevin Ballard <kevin@sb.org> +Mats Lidell <mats.lidell@cag.se> +Peter Kleiweg <pkleiweg@xs4all.nl> +Quan Yong Zhai <qyzhai@gmail.com> +Robert Zaremba <robert.zaremba@zoho.com> +Rui Ueyama <ruiu@google.com> +Russ Cox <rsc@golang.org> +Ryan Barrett <ryanb@google.com> +Sameer Ajmani <sameer@golang.org> +Scott Lawrence <bytbox@gmail.com> +Steven Elliot Harris <seharris@gmail.com> +Yasuyuki Oka <yasuyk@gmail.com> diff --git a/elpa/go-mode-1.6.0/LICENSE b/elpa/go-mode-1.6.0/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2014 The go-mode Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of the copyright holder nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/elpa/go-mode-1.6.0/NEWS b/elpa/go-mode-1.6.0/NEWS @@ -0,0 +1,134 @@ +go-mode-1.4.0 (2016/05/12) + + * Fix minor bugs in fontification. + + * Use unwind-protect in gofmt, ensuring that temporary files will be + removed in all cases. + + * Improve go-remove-unused-imports: don't fail to remove unused + imports because of compilation errors in other files. + + * Add new commands for jumping to various parts of function + declarations: + + - go-goto-arguments (C-c C-f a) + - go-goto-docstring (C-c C-f d) + - go-goto-function (C-c C-f f) + - go-goto-function-name (C-c C-f n) + - go-goto-return-values (C-c C-f r) + - go-goto-method-receiver (C-c C-f m) + + Thanks to Lowe Thiderman for contributing these commands. + + * Add new customizable variable go-packages-function, which allows + choosing between different ways of finding installed packages. + Currently, go-packages-native (the default) and go-packages-go-list + are provided. + + * Automatically detect if goimports is used instead of gofmt and pass + the -srcdir flag, enabling support for vendoring. + + * Add new customizable variable gofmt-args, a list of strings that + will be passed to gofmt as additional arguments. Primarily this + allows using the -s flag with gofmt. + + * Add detection of GOPATH, Godep, wgo and gb. A new non-interactive + function go-guess-gopath will try a list of functions (the + customizable variable go-guess-gopath-functions) to detect a + suitable value for GOPATH. A new interactive command go-set-project + uses the result of go-guess-gopath to actually set GOPATH. This + interactive function could be used from inside a + projectile-switch-project-hook, directory variables or some other + way of invoking per-project code. + + * Add new command go-reset-gopath, which resets GOPATH to the value + it had when Emacs started. + + * Add customizable variable godoc-command, which allows choosing + between using godoc and go doc, and generally specifying + alternative paths to those tools. + + It defaults to go doc, which differs from prior versions which always + used godoc. + + * Add customizable variable godoc-use-completing-read, which allows + turning on or off the completion of import paths in the godoc + command. This is only really useful when using godoc instead of go + doc, and thus defaults to off. + + * Fix parsing of test output in compilation-mode for new versions of + Emacs. It's probably broken in older versions now. + + * Add support for electric-indent-mode and only reindent for closing + parens, not colons, commas or equal signs. + + * Avoid warnings by the byte compiler -- Use cl-lib instead of cl, + and utilize a macro to conditionally compile fallback code. + + * Use null-device instead of /dev/null to work better on Windows. + + * Add new customizable variable godoc-at-point-function, which allows + choosing between strategies for finding an identifier's + documentation. + + * Add new function godoc-and-godef, which is the default function + used for godoc-at-point and matches the previous behaviour. + + * Add new customizable variable godoc-and-godef-command, which allows + specifying the path to the godoc binary. + + * Add the function godoc-gogetdoc, which provides godoc-at-point + functionality by using the third party tool gogetdoc. + + * The godoc command no longer defaults to the symbol under point. It + rarely defaulted to a useful value. If you're interested in + documentation for the identifier at point, use godoc-at-point + instead. + + * Set compilation-error-screen-columns to nil in go-mode buffers. Go + uses tabs for indentation, and go/parser-based tools emit column + numbers in terms of characters, where a tab is one character wide. + Emacs defaults to interpreting columns as screen columns, where a + tab is tab-width columns wide. This breaks jumping to the right + columns from compilation-mode + + * Use HTTPS when talking to the Go Playground. Not only is it safer, + it is required nowadays. + +go-mode-1.3.1 (2015-07-03) + + * The 1.3.0 release forgot to update the version in the package + metadata. This version fixes that. + +go-mode-1.3.0 (2015-05-03) + + * Add a new minor mode godoc-mode, used instead of view-mode when + displaying godoc output. + +go-mode-1.2.1 (2015-04-10) + + * After using godef-jump, don't temporarily mark old buffer as + active. + +go-mode-1.2.0 (2015/03/22): + + * Add a menu for go-mode. + + * Add a new variable go-play-browsw-function that controls the + behaviour of go-play-region and go-play-buffer. + + * Fix minor bug in fontification. + + * Fix minor bug in go-remove-unused-imports that sometimes prevented + it from removing unused imports. + +go-mode-1.1.0 (2015-02-25): + + * Add a new variable godef-command, allowing customization of the + godef command. + + * Automatically hide the gofmt error window after fixing errors. + +go-mode-1.0.0 (2014-11-28): + + * First release since go-mode was removed from the Go distribution. diff --git a/elpa/go-mode-1.6.0/README.md b/elpa/go-mode-1.6.0/README.md @@ -0,0 +1,178 @@ +This is go-mode, the Emacs mode for editing Go code. + +It is a complete rewrite of the go-mode that shipped with Go 1.0.3 and +before, and was part of Go 1.1 until Go 1.3. Beginning with Go 1.4, +editor integration will not be part of the Go distribution anymore, +making this repository the canonical place for go-mode. + + +# Features + +In addition to normal features, such as fontification and indentation, +and close integration with familiar Emacs functionality (for example +syntax-based navigation like `beginning-of-defun`), go-mode comes with +the following extra features to provide an improved experience: + +- Integration with `gofmt` by providing a command of the same name, + and `gofmt-before-save`, which can be used in a hook to format Go + buffers before saving them. + - Setting the `gofmt-command` variable also allows using + `goimports`. + - Setting the `gofmt-args` variable with a list of arguments allows + using e.g. `gofmt -s`. +- Integration with `godoc` via the functions `godoc` and + `godoc-at-point`. +- Integration with the Playground + - `go-play-buffer` and `go-play-region` to send code to the + Playground + - `go-download-play` to download a Playground entry into a new + buffer +- Managing imports + - A function for jumping to the file's imports (`go-goto-imports` - + `C-c C-f i`) + - A function for adding imports, including tab completion + (`go-import-add`, bound to `C-c C-a`) + - A function for removing or commenting unused imports + (`go-remove-unused-imports`) + - It is recommended that you use `goimports` or the + `organize-imports` feature of `gopls` to manage + adding/removing/organizing imports automatically. +- Integration with godef + - `godef-describe` (`C-c C-d`) to describe expressions + - `godef-jump` (`C-c C-j`) and `godef-jump-other-window` (`C-x 4 C-c + C-j`) to jump to declarations + - This requires you to install godef via `go get + github.com/rogpeppe/godef`. +- Basic support for imenu (functions and variables) +- Built-in support for displaying code coverage as calculated by `go + test` (`go-coverage`) +- Several functions for jumping to and manipulating the individual + parts of function signatures. These functions support anonymous + functions, but are smart enough to skip them when required (e.g. + when jumping to a method receiver or docstring.) + - Jump to the argument list (`go-goto-arguments` - `C-c C-f a`) + - Jump to the docstring, create it if it does not exist yet + (`go-goto-docstring` - `C-c C-f d`). + - Jump to the function keyword (`go-goto-function` - `C-c C-f f`) + - Jump to the function name (`go-goto-function-name` - `C-c C-f n`) + - Jump to the return values (`go-goto-return-values` - `C-c C-f r`) + - Jump to the method receiver, adding a pair of parentheses if no + method receiver exists (`go-goto-method-receiver` - `C-c C-f m`). + + All of these functions accept a prefix argument (`C-u`), causing + them to skip anonymous functions. +- GOPATH detection – the function `go-guess-gopath` will guess a + suitable value for GOPATH, based on gb or wgo projects, Godeps and + src folders for plain GOPATH workspaces. The command + `go-set-project` uses the return value of `go-guess-gopath` to set + the GOPATH environment variable. + + You can either call `go-set-project` manually, or integrate it with + Projectile's project switching hooks, or any other means of + switching projects you may employ. + +# Installation + +## MELPA + +The recommended way of installing go-mode is via +[ELPA](http://www.emacswiki.org/emacs/ELPA), the Emacs package +manager, and the +[MELPA Stable repository](http://emacsredux.com/blog/2014/05/16/melpa-stable/), which provides +an up-to-date version of go-mode. + +If you're not familiar with ELPA yet, consider reading +[this guide](http://ergoemacs.org/emacs/emacs_package_system.html). + +## Manual + + + +To install go-mode manually, check out the `go-mode.el` repository in +a directory of your choice, add it to your load path and configure +Emacs to automatically load it when opening a `.go` file: + + (add-to-list 'load-path "/place/where/you/put/it/") + (autoload 'go-mode "go-mode" nil t) + (add-to-list 'auto-mode-alist '("\\.go\\'" . go-mode)) + +Either evaluate the statements with `C-x C-e`, or restart Emacs. + +# Other extensions + +There are several third party extensions that can enhance the Go +experience in Emacs. + +## Gopls integration + +[Gopls](https://github.com/golang/tools/blob/master/gopls/README.md) +is the official language server protocol (lsp) implementation provided +by the Go team. It is intended to replace the existing third party +tools for code formatting (gofmt), automatic imports (goimports), code +navigation (godef/guru), type and function descriptions (godoc/godef), +error checking, auto completion (gocode), variable and type renaming +(rename), and more. Once gopls is stable the older tools will no +longer be supported. + +Gopls is a supported backend for +[lsp-mode](https://github.com/emacs-lsp/lsp-mode). It will be used +automatically by lsp-mode if `gopls` is found in your PATH. You can +install gopls via: `go get golang.org/x/tools/gopls@latest`. To enable +lsp-mode for go buffers: + + (add-hook 'go-mode-hook 'lsp-deferred) + + +## Syntax/error checking + +There are two ways of using flymake with Go: + +1. [goflymake](https://github.com/dougm/goflymake), which internally +uses `go build` to capture all errors that a regular compilation would +also produce +2. [flymake-go](http://marmalade-repo.org/packages/flymake-go) for a +more lightweight solution that only uses `gofmt` and as such is only +able to catch syntax errors. Unlike goflymake, however, it does not +require an additional executable. + +Additionally, there is +[flycheck](https://github.com/flycheck/flycheck), a modern replacement +for flymake, which comes with built-in support for Go. In addition to +using `go build` or `gofmt`, it also has support for `go vet`, +`golint` and `errcheck`. + +## Auto completion + +For auto completion, take a look at +[gocode](https://github.com/nsf/gocode). + +## eldoc + +https://github.com/syohex/emacs-go-eldoc provides eldoc functionality +for go-mode. + +## Snippets + +I maintain a set of YASnippet snippets for go-mode at +https://github.com/dominikh/yasnippet-go + +## Integration with errcheck + +https://github.com/dominikh/go-errcheck.el provides integration with +[errcheck](https://github.com/kisielk/errcheck). + +# Stability + +go-mode.el has regular, tagged releases and is part of the MELPA +Stable repository. These tagged releases are intended to provide a +stable experience. APIs added in tagged releases will usually not be +removed or changed in future releases. + +Changes made on the master branch, which is tracked by the normal +MELPA repository, however, are under active development. New APIs are +experimental and may be changed or removed before the next release. +Furthermore, there is a higher chance for bugs. + +If you want a stable experience, use MELPA Stable. If you want cutting +edge features, or "beta-test" future releases, use MELPA or the master +branch. diff --git a/elpa/go-mode-1.6.0/go-guru.el b/elpa/go-mode-1.6.0/go-guru.el @@ -0,0 +1,561 @@ +;;; go-guru.el --- Integration of the Go 'guru' analysis tool into Emacs. + +;; Copyright 2016 The Go Authors. All rights reserved. +;; Use of this source code is governed by a BSD-style +;; license that can be found in the LICENSE file. + +;; Version: 0.1 +;; Package-Requires: ((go-mode "1.3.1") (cl-lib "0.5")) +;; Keywords: tools + +;;; Commentary: + +;; To enable the Go guru in Emacs, use this command to download, +;; build, and install the tool in $GOROOT/bin: +;; +;; $ go get golang.org/x/tools/cmd/guru +;; +;; Verify that the tool is on your $PATH: +;; +;; $ guru -help +;; Go source code guru. +;; Usage: guru [flags] <mode> <position> +;; ... +;; +;; Then copy this file to a directory on your `load-path', +;; and add this to your ~/.emacs: +;; +;; (require 'go-guru) +;; +;; Inside a buffer of Go source code, select an expression of +;; interest, and type `C-c C-o d' (for "describe") or run one of the +;; other go-guru-xxx commands. If you use `menu-bar-mode', these +;; commands are available from the Guru menu. +;; +;; To enable identifier highlighting mode in a Go source buffer, use: +;; +;; (go-guru-hl-identifier-mode) +;; +;; To enable it automatically in all Go source buffers, +;; add this to your ~/.emacs: +;; +;; (add-hook 'go-mode-hook #'go-guru-hl-identifier-mode) +;; +;; See http://golang.org/s/using-guru for more information about guru. + +;;; Code: + +(require 'compile) +(require 'easymenu) +(require 'go-mode) +(require 'json) +(require 'simple) +(require 'cl-lib) + +(defgroup go-guru nil + "Options specific to the Go guru." + :group 'go) + +(defcustom go-guru-command "guru" + "The Go guru command." + :type 'string + :group 'go-guru) + +(defcustom go-guru-scope "" + "The scope of the analysis. See `go-guru-set-scope'." + :type 'string + :group 'go-guru) + +(defvar go-guru--scope-history + nil + "History of values supplied to `go-guru-set-scope'.") + +(defcustom go-guru-build-tags '() + "Build tags passed to guru." + :type '(repeat string) + :group 'go-guru) + +(defface go-guru-hl-identifier-face + '((t (:inherit highlight))) + "Face used for highlighting identifiers in `go-guru-hl-identifier'." + :group 'go-guru) + +(defcustom go-guru-debug nil + "Print debug messages when running guru." + :type 'boolean + :group 'go-guru) + +(defcustom go-guru-hl-identifier-idle-time 0.5 + "How long to wait after user input before highlighting the current identifier." + :type 'float + :group 'go-guru) + +(defvar go-guru--current-hl-identifier-idle-time + 0 + "The current delay for hl-identifier-mode.") + +(defvar go-guru--hl-identifier-timer + nil + "The global timer used for highlighting identifiers.") + +(defvar go-guru--last-enclosing + nil + "The remaining enclosing regions of the previous go-expand-region invocation.") + +;; Extend go-mode-map. +(let ((m (define-prefix-command 'go-guru-map))) + (define-key m "d" #'go-guru-describe) + (define-key m "f" #'go-guru-freevars) + (define-key m "i" #'go-guru-implements) + (define-key m "c" #'go-guru-peers) ; c for channel + (define-key m "r" #'go-guru-referrers) + (define-key m "j" #'go-guru-definition) ; j for jump + (define-key m "p" #'go-guru-pointsto) + (define-key m "s" #'go-guru-callstack) ; s for stack + (define-key m "e" #'go-guru-whicherrs) ; e for error + (define-key m "<" #'go-guru-callers) + (define-key m ">" #'go-guru-callees) + (define-key m "x" #'go-guru-expand-region)) ;; x for expand + +(define-key go-mode-map (kbd "C-c C-o") 'go-guru-map) + +(easy-menu-define go-guru-mode-menu go-mode-map + "Menu for Go Guru." + '("Guru" + ["Jump to Definition" go-guru-definition t] + ["Show Referrers" go-guru-referrers t] + ["Show Free Names" go-guru-freevars t] + ["Describe Expression" go-guru-describe t] + ["Show Implements" go-guru-implements t] + "---" + ["Show Callers" go-guru-callers t] + ["Show Callees" go-guru-callees t] + ["Show Callstack" go-guru-callstack t] + "---" + ["Show Points-To" go-guru-pointsto t] + ["Show Which Errors" go-guru-whicherrs t] + ["Show Channel Peers" go-guru-peers t] + "---" + ["Set pointer analysis scope..." go-guru-set-scope t])) + +(defun go-guru--read-scope () + "Read go-guru-scope from the minibuffer." + (completing-read-multiple "guru-scope (comma-separated): " + (go-packages) nil nil nil 'go-guru--scope-history)) + +(eval-when-compile (require 'subr-x)) + +;;;###autoload +(defun go-guru-set-scope () + "Set the scope for the Go guru, prompting the user to edit the previous scope. + +The scope restricts analysis to the specified packages. +Its value is a comma-separated list of patterns of these forms: + golang.org/x/tools/cmd/guru # a single package + golang.org/x/tools/... # all packages beneath dir + ... # the entire workspace. + +A pattern preceded by '-' is negative, so the scope + encoding/...,-encoding/xml +matches all encoding packages except encoding/xml." + (interactive) + (let ((scope (go-guru--read-scope))) + (setq go-guru-scope (string-join scope ",")))) + +(defun go-guru--set-scope-if-empty () + (if (string-equal "" go-guru-scope) + (go-guru-set-scope))) + +(defun go-guru--json (mode) + "Execute the Go guru in the specified MODE, passing it the +selected region of the current buffer, requesting JSON output. +Parse and return the resulting JSON object." + ;; A "what" query works even in a buffer without a file name. + (let* ((filename (file-truename (or buffer-file-name "synthetic.go"))) + (cmd (go-guru--command mode filename '("-json"))) + (buf (current-buffer)) + ;; Use temporary buffers to avoid conflict with go-guru--start. + (json-buffer (generate-new-buffer "*go-guru-json-output*")) + (input-buffer (generate-new-buffer "*go-guru-json-input*"))) + (unwind-protect + ;; Run guru, feeding it the input buffer (modified files). + (with-current-buffer input-buffer + (go-guru--insert-modified-files) + (unless (buffer-file-name buf) + (go-guru--insert-modified-file filename buf)) + (let ((exitcode (apply #'call-process-region + (append (list (point-min) + (point-max) + (car cmd) ; guru + nil ; delete + json-buffer ; output + nil) ; display + (cdr cmd))))) ; args + (with-current-buffer json-buffer + (unless (zerop exitcode) + ;; Failed: use buffer contents (sans final \n) as an error. + (error "%s" (buffer-substring (point-min) (1- (point-max))))) + ;; Success: parse JSON. + (goto-char (point-min)) + (json-read)))) + ;; Clean up temporary buffers. + (kill-buffer json-buffer) + (kill-buffer input-buffer)))) + +(define-compilation-mode go-guru-output-mode "Go guru" + "Go guru output mode is a variant of `compilation-mode' for the +output of the Go guru tool." + (set (make-local-variable 'compilation-error-screen-columns) nil) + (set (make-local-variable 'compilation-filter-hook) #'go-guru--compilation-filter-hook) + (set (make-local-variable 'compilation-start-hook) #'go-guru--compilation-start-hook)) + +(defun go-guru--compilation-filter-hook () + "Post-process a blob of input to the go-guru-output buffer." + ;; For readability, truncate each "file:line:col:" prefix to a fixed width. + ;; If the prefix is longer than 20, show "…/last/19chars.go". + ;; This usually includes the last segment of the package name. + ;; Hide the line and column numbers. + (let ((start compilation-filter-start) + (end (point))) + (goto-char start) + (unless (bolp) + ;; TODO(adonovan): not quite right: the filter may be called + ;; with chunks of output containing incomplete lines. Moving to + ;; beginning-of-line may cause duplicate post-processing. + (beginning-of-line)) + (setq start (point)) + (while (< start end) + (let ((p (search-forward ": " end t))) + (if (null p) + (setq start end) ; break out of loop + (setq p (1- p)) ; exclude final space + (let* ((posn (buffer-substring-no-properties start p)) + (flen (cl-search ":" posn)) ; length of filename + (filename (if (< flen 19) + (substring posn 0 flen) + (concat "…" (substring posn (- flen 19) flen))))) + (put-text-property start p 'display filename) + (forward-line 1) + (setq start (point)))))))) + +(defun go-guru--compilation-start-hook (proc) + "Erase default output header inserted by `compilation-mode'." + (with-current-buffer (process-buffer proc) + (let ((inhibit-read-only t)) + (goto-char (point-min)) + (delete-region (point) (point-max))))) + +(defun go-guru--start (mode) + "Start an asynchronous Go guru process for the specified query +MODE, passing it the selected region of the current buffer, and +feeding its standard input with the contents of all modified Go +buffers. Its output is handled by `go-guru-output-mode', a +variant of `compilation-mode'." + (or buffer-file-name + (error "Cannot use guru on a buffer without a file name")) + (let* ((filename (file-truename buffer-file-name)) + (cmd (mapconcat #'shell-quote-argument (go-guru--command mode filename) " ")) + (process-connection-type nil) ; use pipe (not pty) so EOF closes stdin + (procbuf (compilation-start cmd 'go-guru-output-mode))) + (with-current-buffer procbuf + (setq truncate-lines t)) ; the output is neater without line wrapping + (with-current-buffer (get-buffer-create "*go-guru-input*") + (erase-buffer) + (go-guru--insert-modified-files) + (process-send-region procbuf (point-min) (point-max)) + (process-send-eof procbuf)) + procbuf)) + +(defun go-guru--command (mode filename &optional flags) + "Return a command and argument list for a Go guru query of MODE, passing it +the selected region of the current buffer. FILENAME is the +effective name of the current buffer." + (let* ((posn (if (use-region-p) + (format "%s:#%d,#%d" + filename + (1- (position-bytes (region-beginning))) + (1- (position-bytes (region-end)))) + (format "%s:#%d" + filename + (1- (position-bytes (point)))))) + (cmd (append (list go-guru-command + "-modified" + "-scope" go-guru-scope + (format "-tags=%s" (mapconcat 'identity go-guru-build-tags " "))) + flags + (list mode + posn)))) + ;; Log the command to *Messages*, for debugging. + (when go-guru-debug + (message "go-guru--command: %s" cmd) + (message nil)) ; clear/shrink minibuffer + cmd)) + +(defun go-guru--insert-modified-files () + "Insert the contents of each modified Go buffer into the +current buffer in the format specified by guru's -modified flag." + (mapc #'(lambda (b) + (and (buffer-modified-p b) + (buffer-file-name b) + (string= (file-name-extension (buffer-file-name b)) "go") + (go-guru--insert-modified-file (buffer-file-name b) b))) + (buffer-list))) + +(defun go-guru--insert-modified-file (name buffer) + (insert (format "%s\n%d\n" name (go-guru--buffer-size-bytes buffer))) + (insert-buffer-substring buffer)) + +(defun go-guru--buffer-size-bytes (&optional buffer) + "Return the number of bytes in the current buffer. +If BUFFER, return the number of characters in that buffer instead." + (with-current-buffer (or buffer (current-buffer)) + (string-bytes (buffer-substring (point-min) + (point-max))))) + +(defun go-guru--goto-byte (offset) + "Go to the OFFSETth byte in the buffer." + (goto-char (byte-to-position offset))) + +(defun go-guru--goto-byte-column (offset) + "Go to the OFFSETth byte in the current line." + (goto-char (byte-to-position (+ (position-bytes (point-at-bol)) (1- offset))))) + +(defun go-guru--goto-pos (posn other-window) + "Find the file containing the position POSN (of the form `file:line:col') +set the point to it, switching the current buffer." + (let ((file-line-pos (split-string posn ":"))) + (funcall (if other-window #'find-file-other-window #'find-file) (car file-line-pos)) + (goto-char (point-min)) + (forward-line (1- (string-to-number (cadr file-line-pos)))) + (go-guru--goto-byte-column (string-to-number (cl-caddr file-line-pos))))) + +(defun go-guru--goto-pos-no-file (posn) + "Given `file:line:col', go to the line and column. The file +component will be ignored." + (let ((file-line-pos (split-string posn ":"))) + (goto-char (point-min)) + (forward-line (1- (string-to-number (cadr file-line-pos)))) + (go-guru--goto-byte-column (string-to-number (cl-caddr file-line-pos))))) + +;;;###autoload +(defun go-guru-callees () + "Show possible callees of the function call at the current point." + (interactive) + (go-guru--set-scope-if-empty) + (go-guru--start "callees")) + +;;;###autoload +(defun go-guru-callers () + "Show the set of callers of the function containing the current point." + (interactive) + (go-guru--set-scope-if-empty) + (go-guru--start "callers")) + +;;;###autoload +(defun go-guru-callstack () + "Show an arbitrary path from a root of the call graph to the +function containing the current point." + (interactive) + (go-guru--set-scope-if-empty) + (go-guru--start "callstack")) + +;;;###autoload +(defun go-guru-definition (&optional other-window) + "Jump to the definition of the selected identifier." + (interactive) + (or buffer-file-name + (error "Cannot use guru on a buffer without a file name")) + (let* ((res (go-guru--json "definition")) + (desc (cdr (assoc 'desc res)))) + (push-mark) + (if (eval-when-compile (fboundp 'xref-push-marker-stack)) + ;; TODO: Integrate this facility with XRef. + (xref-push-marker-stack) + (ring-insert find-tag-marker-ring (point-marker))) + (go-guru--goto-pos (cdr (assoc 'objpos res)) other-window) + (message "%s" desc))) + +;;;###autoload +(defun go-guru-definition-other-window () + "Jump to the defintion of the selected identifier in another window" + (interactive) + (go-guru-definition t)) + +;;;###autoload +(defun go-guru-describe () + "Describe the selected syntax, its kind, type and methods." + (interactive) + (go-guru--start "describe")) + +;;;###autoload +(defun go-guru-pointsto () + "Show what the selected expression points to." + (interactive) + (go-guru--set-scope-if-empty) + (go-guru--start "pointsto")) + +;;;###autoload +(defun go-guru-implements () + "Describe the 'implements' relation for types in the package +containing the current point." + (interactive) + (go-guru--start "implements")) + +;;;###autoload +(defun go-guru-freevars () + "Enumerate the free variables of the current selection." + (interactive) + (go-guru--start "freevars")) + +;;;###autoload +(defun go-guru-peers () + "Enumerate the set of possible corresponding sends/receives for +this channel receive/send operation." + (interactive) + (go-guru--set-scope-if-empty) + (go-guru--start "peers")) + +;;;###autoload +(defun go-guru-referrers () + "Enumerate all references to the object denoted by the selected +identifier." + (interactive) + (go-guru--start "referrers")) + +;;;###autoload +(defun go-guru-whicherrs () + "Show globals, constants and types to which the selected +expression (of type 'error') may refer." + (interactive) + (go-guru--set-scope-if-empty) + (go-guru--start "whicherrs")) + +(defun go-guru-what () + "Run a 'what' query and return the parsed JSON response as an +association list." + (go-guru--json "what")) + +(defun go-guru--hl-symbols (posn face id) + "Highlight the symbols at the positions POSN by creating +overlays with face FACE. The attribute 'go-guru-overlay on the +overlays will be set to ID." + (save-excursion + (mapc (lambda (pos) + (go-guru--goto-pos-no-file pos) + (let ((x (make-overlay (point) (+ (point) (length (current-word)))))) + (overlay-put x 'go-guru-overlay id) + (overlay-put x 'face face))) + posn))) + +;;;###autoload +(defun go-guru-unhighlight-identifiers () + "Remove highlights from previously highlighted identifier." + (remove-overlays nil nil 'go-guru-overlay 'sameid)) + +;;;###autoload +(defun go-guru-hl-identifier () + "Highlight all instances of the identifier under point. Removes +highlights from previously highlighted identifier." + (interactive) + (go-guru-unhighlight-identifiers) + (go-guru--hl-identifier)) + +;;;###autoload +(define-minor-mode go-guru-hl-identifier-mode + "Highlight instances of the identifier at point after a short +timeout." + :group 'go-guru + (if go-guru-hl-identifier-mode + (progn + (go-guru--hl-set-timer) + ;; Unhighlight if point moves off identifier + (add-hook 'post-command-hook #'go-guru--hl-identifiers-post-command-hook nil t) + ;; Unhighlight any time the buffer changes + (add-hook 'before-change-functions #'go-guru--hl-identifiers-before-change-function nil t)) + (remove-hook 'post-command-hook #'go-guru--hl-identifiers-post-command-hook t) + (remove-hook 'before-change-functions #'go-guru--hl-identifiers-before-change-function t) + (go-guru-unhighlight-identifiers))) + +(defun go-guru--hl-identifier () + "Highlight all instances of the identifier under point." + (let ((posn (cdr (assoc 'sameids (go-guru-what))))) + (go-guru--hl-symbols posn 'go-guru-hl-identifier-face 'sameid))) + +(defun go-guru--hl-identifiers-function () + "Function run after an idle timeout, highlighting the +identifier at point, if necessary." + (when go-guru-hl-identifier-mode + (unless (go-guru--on-overlay-p 'sameid) + ;; Ignore guru errors. Otherwise, we might end up with an error + ;; every time the timer runs, e.g. because of a malformed + ;; buffer. + (condition-case nil + (go-guru-hl-identifier) + (error nil))) + (unless (eq go-guru--current-hl-identifier-idle-time go-guru-hl-identifier-idle-time) + (go-guru--hl-set-timer)))) + +(defun go-guru--hl-set-timer () + (if go-guru--hl-identifier-timer + (cancel-timer go-guru--hl-identifier-timer)) + (setq go-guru--current-hl-identifier-idle-time go-guru-hl-identifier-idle-time) + (setq go-guru--hl-identifier-timer (run-with-idle-timer + go-guru-hl-identifier-idle-time + t + #'go-guru--hl-identifiers-function))) + +(defun go-guru--on-overlay-p (id) + "Return whether point is on a guru overlay of type ID." + (cl-find-if (lambda (el) (eq (overlay-get el 'go-guru-overlay) id)) (overlays-at (point)))) + +(defun go-guru--hl-identifiers-post-command-hook () + (if (and go-guru-hl-identifier-mode + (not (go-guru--on-overlay-p 'sameid))) + (go-guru-unhighlight-identifiers))) + +(defun go-guru--hl-identifiers-before-change-function (_beg _end) + (go-guru-unhighlight-identifiers)) + +;; TODO(dominikh): a future feature may be to cycle through all uses +;; of an identifier. + +(defun go-guru--enclosing () + "Return a list of enclosing regions." + (cdr (assoc 'enclosing (go-guru-what)))) + +(defun go-guru--enclosing-unique () + "Return a list of enclosing regions, with duplicates removed. +Two regions are considered equal if they have the same start and +end point." + (let ((enclosing (go-guru--enclosing))) + (cl-remove-duplicates enclosing + :from-end t + :test (lambda (a b) + (and (= (cdr (assoc 'start a)) + (cdr (assoc 'start b))) + (= (cdr (assoc 'end a)) + (cdr (assoc 'end b)))))))) + +(defun go-guru-expand-region () + "Expand region to the next enclosing syntactic unit." + (interactive) + (let* ((enclosing (if (eq last-command #'go-guru-expand-region) + go-guru--last-enclosing + (go-guru--enclosing-unique))) + (block (if (> (length enclosing) 0) (elt enclosing 0)))) + (when block + (go-guru--goto-byte (1+ (cdr (assoc 'start block)))) + (set-mark (byte-to-position (1+ (cdr (assoc 'end block))))) + (setq go-guru--last-enclosing (cl-subseq enclosing 1)) + (message "Region: %s" (cdr (assoc 'desc block))) + (setq deactivate-mark nil)))) + + +(provide 'go-guru) + +;; Local Variables: +;; indent-tabs-mode: t +;; tab-width: 8 +;; End: + +;;; go-guru.el ends here diff --git a/elpa/go-mode-1.6.0/go-guru.elc b/elpa/go-mode-1.6.0/go-guru.elc Binary files differ. diff --git a/elpa/go-mode-1.6.0/go-mode-autoloads.el b/elpa/go-mode-1.6.0/go-mode-autoloads.el @@ -0,0 +1,213 @@ +;;; go-mode-autoloads.el --- automatically extracted autoloads +;; +;;; Code: + +(add-to-list 'load-path (directory-file-name + (or (file-name-directory #$) (car load-path)))) + + +;;;### (autoloads nil "go-guru" "go-guru.el" (0 0 0 0)) +;;; Generated autoloads from go-guru.el + +(autoload 'go-guru-set-scope "go-guru" "\ +Set the scope for the Go guru, prompting the user to edit the previous scope. + +The scope restricts analysis to the specified packages. +Its value is a comma-separated list of patterns of these forms: + golang.org/x/tools/cmd/guru # a single package + golang.org/x/tools/... # all packages beneath dir + ... # the entire workspace. + +A pattern preceded by '-' is negative, so the scope + encoding/...,-encoding/xml +matches all encoding packages except encoding/xml." t nil) + +(autoload 'go-guru-callees "go-guru" "\ +Show possible callees of the function call at the current point." t nil) + +(autoload 'go-guru-callers "go-guru" "\ +Show the set of callers of the function containing the current point." t nil) + +(autoload 'go-guru-callstack "go-guru" "\ +Show an arbitrary path from a root of the call graph to the +function containing the current point." t nil) + +(autoload 'go-guru-definition "go-guru" "\ +Jump to the definition of the selected identifier. + +\(fn &optional OTHER-WINDOW)" t nil) + +(autoload 'go-guru-definition-other-window "go-guru" "\ +Jump to the defintion of the selected identifier in another window" t nil) + +(autoload 'go-guru-describe "go-guru" "\ +Describe the selected syntax, its kind, type and methods." t nil) + +(autoload 'go-guru-pointsto "go-guru" "\ +Show what the selected expression points to." t nil) + +(autoload 'go-guru-implements "go-guru" "\ +Describe the 'implements' relation for types in the package +containing the current point." t nil) + +(autoload 'go-guru-freevars "go-guru" "\ +Enumerate the free variables of the current selection." t nil) + +(autoload 'go-guru-peers "go-guru" "\ +Enumerate the set of possible corresponding sends/receives for +this channel receive/send operation." t nil) + +(autoload 'go-guru-referrers "go-guru" "\ +Enumerate all references to the object denoted by the selected +identifier." t nil) + +(autoload 'go-guru-whicherrs "go-guru" "\ +Show globals, constants and types to which the selected +expression (of type 'error') may refer." t nil) + +(autoload 'go-guru-unhighlight-identifiers "go-guru" "\ +Remove highlights from previously highlighted identifier." nil nil) + +(autoload 'go-guru-hl-identifier "go-guru" "\ +Highlight all instances of the identifier under point. Removes +highlights from previously highlighted identifier." t nil) + +(autoload 'go-guru-hl-identifier-mode "go-guru" "\ +Highlight instances of the identifier at point after a short +timeout. + +If called interactively, enable Go-Guru-Hl-Identifier mode if ARG +is positive, and disable it if ARG is zero or negative. If +called from Lisp, also enable the mode if ARG is omitted or nil, +and toggle it if ARG is `toggle'; disable the mode otherwise. + +\(fn &optional ARG)" t nil) + +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "go-guru" '("go-guru-"))) + +;;;*** + +;;;### (autoloads nil "go-mode" "go-mode.el" (0 0 0 0)) +;;; Generated autoloads from go-mode.el + +(autoload 'go-mode "go-mode" "\ +Major mode for editing Go source text. + +This mode provides (not just) basic editing capabilities for +working with Go code. It offers almost complete syntax +highlighting, indentation that is almost identical to gofmt and +proper parsing of the buffer content to allow features such as +navigation by function, manipulation of comments or detection of +strings. + +In addition to these core features, it offers various features to +help with writing Go code. You can directly run buffer content +through gofmt, read godoc documentation from within Emacs, modify +and clean up the list of package imports or interact with the +Playground (uploading and downloading pastes). + +The following extra functions are defined: + +- `gofmt' +- `godoc' and `godoc-at-point' +- `go-import-add' +- `go-remove-unused-imports' +- `go-goto-arguments' +- `go-goto-docstring' +- `go-goto-function' +- `go-goto-function-name' +- `go-goto-imports' +- `go-goto-return-values' +- `go-goto-method-receiver' +- `go-play-buffer' and `go-play-region' +- `go-download-play' +- `godef-describe' and `godef-jump' +- `go-coverage' +- `go-set-project' +- `go-reset-gopath' + +If you want to automatically run `gofmt' before saving a file, +add the following hook to your emacs configuration: + +\(add-hook 'before-save-hook #'gofmt-before-save) + +If you want to use `godef-jump' instead of etags (or similar), +consider binding godef-jump to `M-.', which is the default key +for `find-tag': + +\(add-hook 'go-mode-hook (lambda () + (local-set-key (kbd \"M-.\") #'godef-jump))) + +Please note that godef is an external dependency. You can install +it with + +go get github.com/rogpeppe/godef + + +If you're looking for even more integration with Go, namely +on-the-fly syntax checking, auto-completion and snippets, it is +recommended that you look at flycheck +\(see URL `https://github.com/flycheck/flycheck') or flymake in combination +with goflymake (see URL `https://github.com/dougm/goflymake'), gocode +\(see URL `https://github.com/nsf/gocode'), go-eldoc +\(see URL `github.com/syohex/emacs-go-eldoc') and yasnippet-go +\(see URL `https://github.com/dominikh/yasnippet-go') + +\(fn)" t nil) + +(add-to-list 'auto-mode-alist (cons "\\.go\\'" 'go-mode)) + +(autoload 'gofmt-before-save "go-mode" "\ +Add this to .emacs to run gofmt on the current buffer when saving: +\(add-hook 'before-save-hook 'gofmt-before-save). + +Note that this will cause ‘go-mode’ to get loaded the first time +you save any file, kind of defeating the point of autoloading." t nil) + +(autoload 'godoc "go-mode" "\ +Show Go documentation for QUERY, much like \\<go-mode-map>\\[man]. + +\(fn QUERY)" t nil) + +(autoload 'go-download-play "go-mode" "\ +Download a paste from the playground and insert it in a Go buffer. +Tries to look for a URL at point. + +\(fn URL)" t nil) + +(autoload 'go-dot-mod-mode "go-mode" "\ +A major mode for editing go.mod files. + +\(fn)" t nil) + +(add-to-list 'auto-mode-alist '("go\\.mod\\'" . go-dot-mod-mode)) + +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "go-mode" '("go-" "god" "gofmt"))) + +;;;*** + +;;;### (autoloads nil "go-rename" "go-rename.el" (0 0 0 0)) +;;; Generated autoloads from go-rename.el + +(autoload 'go-rename "go-rename" "\ +Rename the entity denoted by the identifier at point, using +the `gorename' tool. With FORCE, call `gorename' with the +`-force' flag. + +\(fn NEW-NAME &optional FORCE)" t nil) + +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "go-rename" '("go-"))) + +;;;*** + +;;;### (autoloads nil nil ("go-mode-pkg.el") (0 0 0 0)) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; coding: utf-8 +;; End: +;;; go-mode-autoloads.el ends here diff --git a/elpa/go-mode-1.6.0/go-mode-pkg.el b/elpa/go-mode-1.6.0/go-mode-pkg.el @@ -0,0 +1,2 @@ +;; Generated package description from go-mode.el -*- no-byte-compile: t -*- +(define-package "go-mode" "1.6.0" "Major mode for the Go programming language" '((emacs "26.1")) :commit "3273fcece5d9ab7edd4f15b2d6bce61f4e5a0666" :authors '(("The go-mode Authors")) :maintainer '("The go-mode Authors") :keywords '("languages" "go") :url "https://github.com/dominikh/go-mode.el") diff --git a/elpa/go-mode-1.6.0/go-mode.el b/elpa/go-mode-1.6.0/go-mode.el @@ -0,0 +1,3047 @@ +;;; go-mode.el --- Major mode for the Go programming language + +;;; Commentary: + +;; Copyright 2013 The go-mode Authors. All rights reserved. +;; Use of this source code is governed by a BSD-style +;; license that can be found in the LICENSE file. + +;; Author: The go-mode Authors +;; Version: 1.6.0 +;; Keywords: languages go +;; Package-Requires: ((emacs "26.1")) +;; URL: https://github.com/dominikh/go-mode.el +;; +;; This file is not part of GNU Emacs. + +;;; Code: + +(require 'cl-lib) +(require 'compile) +(require 'etags) +(require 'ffap) +(require 'find-file) +(require 'ring) +(require 'url) +(require 'xref) + + +(eval-when-compile + (defmacro go--forward-word (&optional arg) + (if (fboundp 'forward-word-strictly) + `(forward-word-strictly ,arg) + `(forward-word ,arg)))) + +(defun go--delete-whole-line (&optional arg) + "Delete the current line without putting it in the `kill-ring'. +Derived from function `kill-whole-line'. ARG is defined as for that +function." + (setq arg (or arg 1)) + (if (and (> arg 0) + (eobp) + (save-excursion (forward-visible-line 0) (eobp))) + (signal 'end-of-buffer nil)) + (if (and (< arg 0) + (bobp) + (save-excursion (end-of-visible-line) (bobp))) + (signal 'beginning-of-buffer nil)) + (cond ((zerop arg) + (delete-region (progn (forward-visible-line 0) (point)) + (progn (end-of-visible-line) (point)))) + ((< arg 0) + (delete-region (progn (end-of-visible-line) (point)) + (progn (forward-visible-line (1+ arg)) + (unless (bobp) + (backward-char)) + (point)))) + (t + (delete-region (progn (forward-visible-line 0) (point)) + (progn (forward-visible-line arg) (point)))))) + +(defun go-goto-opening-parenthesis (&optional _legacy-unused) + "Move up one level of parentheses. + +Return non-nil if there was a paren to move up to." + ;; The old implementation of go-goto-opening-parenthesis had an + ;; optional argument to speed up the function. It didn't change the + ;; function's outcome. + + ;; Silently fail if there's no matching opening parenthesis. + (let ((open-char (nth 1 (syntax-ppss)))) + (when open-char + (goto-char open-char)))) + + +(defconst go-dangling-operators-regexp "[^-]-\\|[^+]\\+\\|[/*&><.=|^]") +(defconst go--max-dangling-operator-length 2 + "The maximum length of dangling operators. +This must be at least the length of the longest string matched by +‘go-dangling-operators-regexp’ and must be updated whenever that +constant is changed.") + +(defconst go-identifier-regexp "[[:word:][:multibyte:]]+") +(defconst go-type-name-no-prefix-regexp "\\(?:[[:word:][:multibyte:]]+\\.\\)?[[:word:][:multibyte:]]+") +(defconst go-qualified-identifier-regexp (concat go-identifier-regexp "\\." go-identifier-regexp)) +(defconst go-label-regexp go-identifier-regexp) +(defconst go-type-regexp "[[:word:][:multibyte:]*]+") +(defconst go-func-regexp (concat "\\_<func\\_>\\s *\\(" go-identifier-regexp "\\)")) +(defconst go-func-meth-regexp (concat + "\\_<func\\_>\\s *\\(?:(\\s *" + "\\(" go-identifier-regexp "\\s +\\)?" go-type-regexp + "\\s *)\\s *\\)?\\(" + go-identifier-regexp + "\\)(")) + +(defconst go--comment-start-regexp "[[:space:]]*\\(?:/[/*]\\)") +(defconst go--case-regexp "\\([[:space:]]*case\\([[:space:]]\\|$\\)\\)") +(defconst go--case-or-default-regexp (concat "\\(" go--case-regexp "\\|" "[[:space:]]*default:\\)")) + +(defconst go-builtins + '("append" "cap" "close" "complex" "copy" + "delete" "imag" "len" "make" "new" + "panic" "print" "println" "real" "recover") + "All built-in functions in the Go language. Used for font locking.") + +(defconst go-mode-keywords + '("break" "default" "func" "interface" "select" + "case" "defer" "go" "map" "struct" + "chan" "else" "goto" "package" "switch" + "const" "fallthrough" "if" "range" "type" + "continue" "for" "import" "return" "var") + "All keywords in the Go language. Used for font locking.") + +(defconst go-constants '("nil" "true" "false" "iota")) +(defconst go-type-name-regexp (concat "\\**\\(\\(?:" go-identifier-regexp "\\.\\)?" go-identifier-regexp "\\)")) + +(defvar go-dangling-cache) +(defvar go-godoc-history nil) +(defvar go--coverage-current-file-name) + +(defgroup go nil + "Major mode for editing Go code." + :link '(url-link "https://github.com/dominikh/go-mode.el") + :group 'languages) + +(defgroup go-cover nil + "Options specific to `cover`." + :group 'go) + +(defgroup godoc nil + "Options specific to `godoc'." + :group 'go) + +(defcustom go-fontify-function-calls t + "Fontify function and method calls if this is non-nil." + :type 'boolean + :group 'go) + +(defcustom go-fontify-variables t + "Fontify variable declarations if this is non-nil." + :type 'boolean + :group 'go) + +(defcustom go-mode-hook nil + "Hook called by `go-mode'." + :type 'hook + :group 'go) + +(defcustom go-command "go" + "The 'go' command. +Some users have multiple Go development trees and invoke the 'go' +tool via a wrapper that sets GOROOT and GOPATH based on the +current directory. Such users should customize this variable to +point to the wrapper script." + :type 'string + :group 'go) + +(defcustom gofmt-command "gofmt" + "The 'gofmt' command. +Some users may replace this with 'goimports' +from https://golang.org/x/tools/cmd/goimports." + :type 'string + :group 'go) + +(defcustom gofmt-args nil + "Additional arguments to pass to gofmt." + :type '(repeat string) + :group 'go) + +(defcustom gofmt-show-errors 'buffer + "Where to display gofmt error output. +It can either be displayed in its own buffer, in the echo area, or not at all. + +Please note that Emacs outputs to the echo area when writing +files and will overwrite gofmt's echo output if used from inside +a `before-save-hook'." + :type '(choice + (const :tag "Own buffer" buffer) + (const :tag "Echo area" echo) + (const :tag "None" nil)) + :group 'go) + +(defcustom godef-command "godef" + "The 'godef' command." + :type 'string + :group 'go) + +(defcustom go-other-file-alist + '(("_test\\.go\\'" (".go")) + ("\\.go\\'" ("_test.go"))) + "See the documentation of `ff-other-file-alist' for details." + :type '(repeat (list regexp (choice (repeat string) function))) + :group 'go) + +(defcustom go-packages-function 'go-packages-go-list + "Function called by `go-packages' to determine the list of available packages. +This is used in e.g. tab completion in `go-import-add'. + +This package provides two functions: `go-packages-go-list' uses +'go list all' to determine all Go packages. `go-packages-native' uses +elisp to find all .a files in all /pkg/ directories. +`go-packages-native' is obsolete as it doesn't behave correctly with +the Go build cache or Go modules." + :type 'function + :package-version '(go-mode . 1.4.0) + :group 'go) + +(defcustom go-guess-gopath-functions (list #'go-plain-gopath) + "Functions to call in sequence to detect a project's GOPATH. + +The functions in this list will be called one after another, +until a function returns non-nil. The order of the functions in +this list is important, as some project layouts may superficially +look like others." + :type '(repeat function) + :group 'go) + +(defcustom go-confirm-playground-uploads t + "Ask before uploading code to the public Go Playground. + +Set this to nil to upload without prompting. +" + :type 'boolean + :group 'go) + +(defcustom godoc-command "go doc" + "Which executable to use for `godoc'. +This can be either an absolute path or an executable in PATH." + :type 'string + :group 'go) + +(defcustom godoc-and-godef-command "go doc" + "Which executable to use for `godoc-and-godef'. +This can be either an absolute path or an executable in PATH." + :type 'string + :group 'go) + +(defcustom godoc-use-completing-read nil + "Provide auto-completion for godoc. +Only really desirable when using `godoc' instead of `go doc'." + :type 'boolean + :group 'godoc) + +(defcustom godoc-reuse-buffer nil + "Reuse a single *godoc* buffer to display godoc-at-point calls. +The default behavior is to open a separate buffer for each call." + :type 'boolean + :group 'godoc) + +(defcustom godoc-at-point-function #'godoc-and-godef + "Function to call to display the documentation for an +identifier at a given position. + +This package provides two functions: `godoc-and-godef' uses a +combination of godef and godoc to find the documentation. This +approach has several caveats. See its documentation for more +information. The second function, `godoc-gogetdoc' uses an +additional tool that correctly determines the documentation for +any identifier. It provides better results than +`godoc-and-godef'." + :type 'function + :group 'godoc) + +(defun godoc-and-godef (point) + "Use a combination of godef and godoc to guess the documentation at POINT. + +Due to a limitation in godoc, it is not possible to differentiate +between functions and methods, which may cause `godoc-at-point' +to display more documentation than desired. Furthermore, it +doesn't work on package names or variables. + +Consider using ‘godoc-gogetdoc’ instead for more accurate results." + (condition-case nil + (let* ((output (godef--call point)) + (file (car output)) + (name-parts (split-string (cadr output) " ")) + (first (car name-parts))) + (if (not (godef--successful-p file)) + (message "%s" (godef--error file)) + (go--godoc (format "%s %s" + (file-name-directory file) + (if (or (string= first "type") (string= first "const")) + (cadr name-parts) + (car name-parts))) + godoc-and-godef-command))) + (file-error (message "Could not run godef binary")))) + +(defun godoc-gogetdoc (point) + "Use the gogetdoc tool to find the documentation for an identifier at POINT. + +You can install gogetdoc with 'go get -u github.com/zmb3/gogetdoc'." + (if (not (buffer-file-name (go--coverage-origin-buffer))) + ;; TODO: gogetdoc supports unsaved files, but not introducing + ;; new artificial files, so this limitation will stay for now. + (error "Cannot use gogetdoc on a buffer without a file name")) + (let ((posn (format "%s:#%d" (file-truename buffer-file-name) (1- (position-bytes point)))) + (out (godoc--get-buffer "<at point>"))) + (with-temp-buffer + (go--insert-modified-files) + (call-process-region (point-min) (point-max) "gogetdoc" nil out nil + "-modified" + (format "-pos=%s" posn))) + (with-current-buffer out + (goto-char (point-min)) + (godoc-mode) + (display-buffer (current-buffer) t)))) + +(defun go--kill-new-message (url) + "Make URL the latest kill and print a message." + (kill-new url) + (message "%s" url)) + +(defcustom go-play-browse-function 'go--kill-new-message + "Function to call with the Playground URL. +See `go-play-region' for more details." + :type '(choice + (const :tag "Nothing" nil) + (const :tag "Kill + Message" go--kill-new-message) + (const :tag "Browse URL" browse-url) + (function :tag "Call function")) + :group 'go) + +(defcustom go-coverage-display-buffer-func 'display-buffer-reuse-window + "How `go-coverage' should display the coverage buffer. +See `display-buffer' for a list of possible functions." + :type 'function + :group 'go-cover) + +(defface go-coverage-untracked + '((t (:foreground "#505050"))) + "Coverage color of untracked code." + :group 'go-cover) + +(defface go-coverage-0 + '((t (:foreground "#c00000"))) + "Coverage color for uncovered code." + :group 'go-cover) +(defface go-coverage-1 + '((t (:foreground "#808080"))) + "Coverage color for covered code with weight 1." + :group 'go-cover) +(defface go-coverage-2 + '((t (:foreground "#748c83"))) + "Coverage color for covered code with weight 2." + :group 'go-cover) +(defface go-coverage-3 + '((t (:foreground "#689886"))) + "Coverage color for covered code with weight 3." + :group 'go-cover) +(defface go-coverage-4 + '((t (:foreground "#5ca489"))) + "Coverage color for covered code with weight 4." + :group 'go-cover) +(defface go-coverage-5 + '((t (:foreground "#50b08c"))) + "Coverage color for covered code with weight 5." + :group 'go-cover) +(defface go-coverage-6 + '((t (:foreground "#44bc8f"))) + "Coverage color for covered code with weight 6." + :group 'go-cover) +(defface go-coverage-7 + '((t (:foreground "#38c892"))) + "Coverage color for covered code with weight 7." + :group 'go-cover) +(defface go-coverage-8 + '((t (:foreground "#2cd495"))) + "Coverage color for covered code with weight 8. +For mode=set, all covered lines will have this weight." + :group 'go-cover) +(defface go-coverage-9 + '((t (:foreground "#20e098"))) + "Coverage color for covered code with weight 9." + :group 'go-cover) +(defface go-coverage-10 + '((t (:foreground "#14ec9b"))) + "Coverage color for covered code with weight 10." + :group 'go-cover) +(defface go-coverage-covered + '((t (:foreground "#2cd495"))) + "Coverage color of covered code." + :group 'go-cover) + +(defvar go-mode-syntax-table + (let ((st (make-syntax-table))) + (modify-syntax-entry ?+ "." st) + (modify-syntax-entry ?- "." st) + (modify-syntax-entry ?% "." st) + (modify-syntax-entry ?& "." st) + (modify-syntax-entry ?| "." st) + (modify-syntax-entry ?^ "." st) + (modify-syntax-entry ?! "." st) + (modify-syntax-entry ?= "." st) + (modify-syntax-entry ?< "." st) + (modify-syntax-entry ?> "." st) + (modify-syntax-entry ?/ ". 124b" st) + (modify-syntax-entry ?* ". 23" st) + (modify-syntax-entry ?\n "> b" st) + (modify-syntax-entry ?\" "\"" st) + (modify-syntax-entry ?\' "\"" st) + (modify-syntax-entry ?` "\"" st) + (modify-syntax-entry ?\\ "\\" st) + ;; TODO make _ a symbol constituent now that xemacs is gone + (modify-syntax-entry ?_ "w" st) + + st) + "Syntax table for Go mode.") + +(defun go--fontify-type-switch-case-pre () + "Move point to line following the end of case statement. + +This is used as an anchored font lock keyword PRE-MATCH-FORM. We +expand the font lock region to include multiline type switch case +statements." + (save-excursion + (beginning-of-line) + (while (or (looking-at "[[:space:]]*\\($\\|//\\)") (go--line-suffix-p ",")) + (forward-line)) + (when (go--line-suffix-p ":") + (forward-line)) + (point))) + +(defun go--build-font-lock-keywords () + ;; we cannot use 'symbols in regexp-opt because GNU Emacs <24 + ;; doesn't understand that + (append + `( + ;; Match param lists in func signatures. This uses the + ;; MATCH-ANCHORED format (see `font-lock-keywords' docs). + ;; + ;; Parent/anchor match. It matches the param list opening "(". + (go--match-param-start + ;; Sub-matcher that matches individual params in the param list. + (go--fontify-param + ;; Pre-match form that runs before the first sub-match. + (go--fontify-param-pre) + ;; Post-match form that runs after last sub-match. + (go--fontify-param-post) + ;; Subexp 1 is the param variable name, if any. + (1 font-lock-variable-name-face nil t) + ;; Subexp 2 is the param type name, if any. We set the LAXMATCH + ;; flag to allow optional regex groups. + (2 font-lock-type-face nil t))) + + ;; Special case to match non-parenthesized function results. For + ;; example, "func(i int) string". + (go--match-single-func-result 1 font-lock-type-face) + + ;; Match name+type pairs, such as "foo bar" in "var foo bar". + (go--match-ident-type-pair 2 font-lock-type-face) + + ;; An anchored matcher for type switch case clauses. + (go--match-type-switch-case + (go--fontify-type-switch-case + (go--fontify-type-switch-case-pre) + nil + (1 font-lock-type-face))) + + ;; Match variable names in var decls, constant names in const + ;; decls, and type names in type decls. + (go--match-decl + (1 font-lock-variable-name-face nil t) + (2 font-lock-constant-face nil t) + (3 font-lock-type-face nil t)) + + (,(concat "\\_<" (regexp-opt go-mode-keywords t) "\\_>") . font-lock-keyword-face) + (,(concat "\\(\\_<" (regexp-opt go-builtins t) "\\_>\\)[[:space:]]*(") 1 font-lock-builtin-face) + (,(concat "\\_<" (regexp-opt go-constants t) "\\_>") . font-lock-constant-face) + + ;; Function (not method) name + (,go-func-regexp 1 font-lock-function-name-face)) + + (if go-fontify-function-calls + ;; Function call/method name + `((,(concat "\\(" go-identifier-regexp "\\)[[:space:]]*(") 1 font-lock-function-name-face) + ;; Bracketed function call + (,(concat "[^[:word:][:multibyte:]](\\(" go-identifier-regexp "\\))[[:space:]]*(") 1 font-lock-function-name-face)) + ;; Method name + `((,go-func-meth-regexp 2 font-lock-function-name-face))) + + `( + ;; Raw string literal, needed for font-lock-syntactic-keywords + ("\\(`[^`]*`\\)" 1 font-lock-multiline) + + ;; RHS of type alias. + (go--match-type-alias 2 font-lock-type-face) + + ;; Arrays/slices: []<type> | [123]<type> | [some.Const]<type> | [someConst]<type> | [...]<type> + (,(concat "\\(?:^\\|[^[:word:][:multibyte:]]\\)\\[\\(?:[[:digit:]]+\\|" go-qualified-identifier-regexp "\\|" go-identifier-regexp "\\|\\.\\.\\.\\)?\\]" go-type-name-regexp) 1 font-lock-type-face) + + ;; Unary "!" + ("\\(!\\)[^=]" 1 font-lock-negation-char-face) + + ;; Composite literal type + (,(concat go-type-name-regexp "{") 1 font-lock-type-face) + + ;; Map value type + (go--match-map-value 1 font-lock-type-face) + + ;; Map key type + (,(concat "\\_<map\\_>\\[" go-type-name-regexp) 1 font-lock-type-face) + + ;; Channel type + (,(concat "\\_<chan\\_>[[:space:]]*\\(?:<-[[:space:]]*\\)?" go-type-name-regexp) 1 font-lock-type-face) + + ;; "new()"/"make()" type + (,(concat "\\_<\\(?:new\\|make\\)\\_>\\(?:[[:space:]]\\|)\\)*(" go-type-name-regexp) 1 font-lock-type-face) + + ;; Type assertion + (,(concat "\\.\\s *(" go-type-name-regexp) 1 font-lock-type-face) + + ;; Composite literal field names and label definitions. + (go--match-ident-colon 1 font-lock-constant-face) + + ;; Labels in goto/break/continue + (,(concat "\\_<\\(?:goto\\|break\\|continue\\)\\_>[[:space:]]*\\(" go-label-regexp "\\)") 1 font-lock-constant-face)))) + +(let ((m (define-prefix-command 'go-goto-map))) + (define-key m "a" #'go-goto-arguments) + (define-key m "d" #'go-goto-docstring) + (define-key m "f" #'go-goto-function) + (define-key m "i" #'go-goto-imports) + (define-key m "m" #'go-goto-method-receiver) + (define-key m "n" #'go-goto-function-name) + (define-key m "r" #'go-goto-return-values)) + +(defvar go-mode-map + (let ((m (make-sparse-keymap))) + (unless (boundp 'electric-indent-chars) + (define-key m "}" #'go-mode-insert-and-indent) + (define-key m ")" #'go-mode-insert-and-indent)) + (define-key m (kbd "C-c C-a") #'go-import-add) + (define-key m (kbd "C-c C-j") #'godef-jump) + (define-key m (kbd "C-x 4 C-c C-j") #'godef-jump-other-window) + (define-key m (kbd "C-c C-d") #'godef-describe) + (define-key m (kbd "C-c C-f") 'go-goto-map) + m) + "Keymap used by ‘go-mode’.") + +(easy-menu-define go-mode-menu go-mode-map + "Menu for Go mode." + '("Go" + ["Describe Expression" godef-describe t] + ["Jump to Definition" godef-jump t] + "---" + ["Add Import" go-import-add t] + ["Remove Unused Imports" go-remove-unused-imports t] + ["Go to Imports" go-goto-imports t] + "---" + ("Playground" + ["Send Buffer" go-play-buffer t] + ["Send Region" go-play-region t] + ["Download" go-download-play t]) + "---" + ["Coverage" go-coverage t] + ["Gofmt" gofmt t] + ["Godoc" godoc t] + "---" + ["Customize Mode" (customize-group 'go) t])) + +(defun go-mode-insert-and-indent (key) + "Invoke the global binding of KEY, then reindent the line." + + (interactive (list (this-command-keys))) + (call-interactively (lookup-key (current-global-map) key)) + (indent-according-to-mode)) + +(defmacro go-paren-level () + `(car (syntax-ppss))) + +(defmacro go-in-string-or-comment-p () + `(nth 8 (syntax-ppss))) + +(defmacro go-in-string-p () + `(nth 3 (syntax-ppss))) + +(defmacro go-in-comment-p () + `(nth 4 (syntax-ppss))) + +(defmacro go-goto-beginning-of-string-or-comment () + `(goto-char (nth 8 (syntax-ppss)))) + +(defun go--backward-irrelevant (&optional stop-at-string) + "Skip backwards over any characters that are irrelevant for +indentation and related tasks. + +It skips over whitespace, comments, cases and labels and, if +STOP-AT-STRING is not true, over strings." + + (let (pos (start-pos (point))) + (skip-chars-backward "\n\s\t") + (if (and (save-excursion (beginning-of-line) (go-in-string-p)) + (= (char-before) ?`) + (not stop-at-string)) + (backward-char)) + (if (and (go-in-string-p) + (not stop-at-string)) + (go-goto-beginning-of-string-or-comment)) + (if (looking-back "\\*/" (line-beginning-position)) + (backward-char)) + (if (go-in-comment-p) + (go-goto-beginning-of-string-or-comment)) + (setq pos (point)) + (beginning-of-line) + (if (or (looking-at (concat "^" go-label-regexp ":")) + (looking-at "^[[:space:]]*\\(case .+\\|default\\):")) + (end-of-line 0) + (goto-char pos)) + (if (/= start-pos (point)) + (go--backward-irrelevant stop-at-string)) + (/= start-pos (point)))) + +(defun go--buffer-narrowed-p () + "Return non-nil if the current buffer is narrowed." + (/= (buffer-size) + (- (point-max) + (point-min)))) + +(defun go-previous-line-has-dangling-op-p () + "Return non-nil if the current line is a continuation line. +The return value is cached based on the current `line-beginning-position'." + (let* ((line-begin (line-beginning-position)) + (val (gethash line-begin go-dangling-cache 'nope))) + (when (or (go--buffer-narrowed-p) (equal val 'nope)) + (save-excursion + (go--forward-line -1) + (if (go--current-line-has-dangling-op-p) + (setq val (line-end-position)) + (setq val nil)) + + (if (not (go--buffer-narrowed-p)) + (puthash line-begin val go-dangling-cache)))) + val)) + +(defun go--current-line-has-dangling-op-p () + "Return non-nil if current line ends in a dangling operator. +The return value is not cached." + (or + (and + (go--line-suffix-p go-dangling-operators-regexp) + + ;; "=" does not behave like a dangling operator in decl statements. + (not (go--line-suffix-p "\\(?:var\\|type\\|const\\)[[:space:]].*=")) + + ;; Don't mistake "1234." for a dangling operator. + (not (go--line-suffix-p "[[:space:]]-?[[:digit:]][_0-9]*\\."))) + + ;; treat comma as dangling operator in certain cases + (and + (go--line-suffix-p ",") + (save-excursion (end-of-line) (go--commas-indent-p))))) + + +(defun go--commas-indent-p () + "Return non-nil if in a context where dangling commas indent next line." + (not (or + (go--open-paren-position) + (go--in-composite-literal-p) + (go--in-case-clause-list-p) + (go--in-struct-definition-p)))) + +(defun go--in-case-clause-list-p () + "Return non-nil if inside a multi-line case cause list. + +This function is only concerned with list items on lines after the +case keyword. It returns nil for the case line itself." + (save-excursion + (beginning-of-line) + (when (not (looking-at go--case-or-default-regexp)) + (let (saw-colon) + ;; optionally skip line with the colon + (when (go--line-suffix-p ":") + (setq saw-colon t) + (forward-line -1)) + + ;; go backwards while at a comment or a line ending in comma + (while (and + (or + (go--boring-line-p) + (go--line-suffix-p ",")) + (not (looking-at go--case-regexp)) + (go--forward-line -1))) + + (and + (looking-at-p go--case-regexp) + ;; we weren't in case list if first line ended in colon + ;; and the "case" line ended in colon + (not (and saw-colon (looking-at ".*:[[:space:]]*$")))))))) + +(defun go--in-composite-literal-p () + "Return non-nil if point is in a composite literal." + (save-excursion + (save-match-data + (and + (go-goto-opening-parenthesis) + + ;; Opening paren-like character is a curly. + (eq (char-after) ?{) + + (or + ;; Curly is preceded by non space (e.g. "Foo{"), definitely + ;; composite literal. + (zerop (skip-syntax-backward " ")) + + ;; Curly preceded by comma or semicolon. This is a composite + ;; literal with implicit type name. + (looking-back "[,:]" (1- (point))) + + ;; If we made it to the beginning of line we are either a naked + ;; block or a composite literal with implicit type name. If we + ;; are the latter, we must be contained in another composite + ;; literal. + (and (bolp) (go--in-composite-literal-p))))))) + +(defun go--in-paren-with-prefix-p (paren prefix) + (save-excursion + (and + (go-goto-opening-parenthesis) + (eq (char-after) paren) + (skip-syntax-backward " ") + (> (point) (length prefix)) + (string= prefix (buffer-substring (- (point) (length prefix)) (point)))))) + +(defun go--in-struct-definition-p () + "Return non-nil if point is inside a struct definition." + (go--in-paren-with-prefix-p ?{ "struct")) + +(defun go--in-interface-p () + "Return non-nil if point is inside an interface definition." + (go--in-paren-with-prefix-p ?{ "interface")) + + +(defun go--in-type-switch-p () + "Return non-nil if point is inside a type switch statement." + (go--in-paren-with-prefix-p ?{ ".(type)")) + +(defun go--fill-prefix () + "Return fill prefix for following comment paragraph." + (save-excursion + (beginning-of-line) + + ;; Skip over empty lines and empty comment openers/closers. + (while (and + (or (go--empty-line-p) (go--boring-comment-p)) + (zerop (forward-line 1)))) + + ;; If we are in a block comment, set prefix based on first line + ;; with content. + (if (go-in-comment-p) + (progn + (looking-at "[[:space:]]*") + (match-string-no-properties 0)) + + ;; Else if we are looking at the start of an interesting comment, our + ;; prefix is the comment opener and any space following. + (if (looking-at (concat go--comment-start-regexp "[[:space:]]*")) + ;; Replace "/*" opener with spaces so following lines don't + ;; get "/*" prefix. + (replace-regexp-in-string "/\\*" " " + (match-string-no-properties 0)))))) + +(defun go--open-paren-position () + "Return non-nil if point is between '(' and ')'. + +The return value is the position of the opening paren." + (save-excursion + (let ((start-paren-level (go-paren-level))) + (and + (go-goto-opening-parenthesis) + + ;; opening paren-like character is actually a paren + (eq (char-after) ?\() + + ;; point is before the closing paren + (< (go-paren-level) start-paren-level) + + (point))))) + +(defun go-indentation-at-point () + "Return the appropriate indentation for the current line." + (save-excursion + (beginning-of-line) + + (if (go-in-comment-p) + (go--multiline-comment-indent) + (go--indentation-at-point)))) + +;; It's unfortunate that the user cannot reindent the current line to +;; align with the previous line; however, if they could, then people +;; who use reindent-then-newline-and-indent wouldn't be able to +;; explicitly indent lines inside comments. +(defun go--multiline-comment-indent () + "Return the appropriate indent inside multiline comment. + +Assumes point is at beginning of line within comment. This +function has basic logic to indent as you add new lines to a +multiline comment, and to line up all the `*' if each line starts +with `*'. The gofmt behavior for multiline comments is +surprisingly complex and strange/buggy, so we just aim to do +something simple rather than encode all the subtle behavior." + (let* (;; Indent of current line. + (indent (current-indentation)) + ;; Indent of opening "/*". + start-indent + ;; Default indent to use based on preceding context. + natural-indent + ;; Non-nil means keep existing indent and give up calculating indent. + give-up + ;; Whether all comment lines (except first) begin with "*". + (all-star t)) + + (save-excursion + (go-goto-beginning-of-string-or-comment) + + (setq start-indent (current-indentation)) + + ;; If other stuff precedes start of multiline comment, give up. + (setq give-up (/= (current-column) start-indent)) + + ;; Skip "/*". + (forward-char 2) + + (skip-syntax-forward " ") + + (if (not (eolp)) + ;; If we aren't at EOL, we have content on the first line. + ;; Base our natural indent on that. + (setq natural-indent (current-column)) + ;; Otherwise default to 1 space beyond "/*". + (setq natural-indent (+ start-indent 3))) + + (let (done) + (while (not done) + (setq done (or (looking-at ".*\\*/") (not (zerop (forward-line))))) + (setq all-star (and all-star (looking-at "[[:space:]]*\\*")))))) + + ;; If previous line has comment content, use its indent as our + ;; natural indent. + (save-excursion + (when (zerop (forward-line -1)) + (beginning-of-line) + (when (and (go-in-comment-p) (> (current-indentation) 0)) + (setq natural-indent (current-indentation))))) + + (cond + (give-up indent) + + (all-star (1+ start-indent)) + + ;; Closing "*/" with no preceding content always lines up with "/*". + ((looking-at "[[:space:]]*\\*/") start-indent) + + ;; If the line is already indented, leave it. + (t (if (zerop indent) natural-indent indent))))) + +(defun go--indentation-at-point () + "Return the appropriate indentation for the current non-comment line. + +This function works by walking a line's characters backwards. When it +encounters a closing paren or brace it bounces to the corresponding +opener. If it arrives at the beginning of the line you are indenting, +it moves to the end of the previous line if the current line is a +continuation line, else it moves to the containing opening paren or +brace. If it arrives at the beginning of a line other than the line +you are indenting, it will continue to the previous dangling line if +the line you are indenting was not a continuation line, otherwise it +is done." + (save-excursion + (beginning-of-line) + + (let ( + ;; Beginning of our starting line. + (start-line (point)) + + ;; Whether this is our first iteration of the outer while loop. + (first t) + + ;; Whether we start in a block (i.e. our first line is not a + ;; continuation line and is in an "if", "for", etc. block). + (in-block) + + ;; Our desired indent relative to our ending line's indent. + (indent 0)) + + ;; Skip leading whitespace. + (skip-syntax-forward " ") + + ;; Decrement indent if the first character on the line is a closer. + (when (or (eq (char-after) ?\)) (eq (char-after) ?})) + (cl-decf indent tab-width)) + + (while (or + ;; Always run the first iteration so we process empty lines. + first + + ;; Otherwise stop if we are at the start of a line. + (not (bolp))) + (setq first nil) + + (cl-case (char-before) + + ;; We have found a closer (paren or brace). + ((?\) ?}) + (backward-char) + (let ((bol (line-beginning-position))) + + ;; Jump back to corresponding opener. + (go-goto-opening-parenthesis) + + ;; Here we decrement the indent if we are closing an indented + ;; expression. In other words, the closer's line was indented + ;; relative to the opener's line, and that indent should not + ;; be inherited by our starting line. + (when (and + ;; We care about dangling expressions, not child blocks. + (not in-block) + + ;; Opener and closer aren't on same line. + (< (point) bol) + + (go-previous-line-has-dangling-op-p) + + ;; Opener is at same paren level as start of line (ignore sub-expressions). + (eq (go-paren-level) (save-excursion (beginning-of-line) (go-paren-level))) + + ;; This dangling line opened indent relative to previous dangling line. + (go--continuation-line-indents-p)) + (cl-decf indent tab-width)))) + + ;; Brackets don't affect indentation, so just skip them. + ((?\]) + (backward-char))) + + ;; Skip non-closers since we are only interested in closing parens/braces. + (skip-syntax-backward "^)" (line-beginning-position)) + + (when (go-in-string-or-comment-p) + (go-goto-beginning-of-string-or-comment)) + + ;; At the beginning of the starting line. + (when (= start-line (point)) + + ;; We are a continuation line. + (if (go-previous-line-has-dangling-op-p) + (progn + ;; Presume a continuation line always gets an extra indent. + ;; We reduce the indent after the loop, if necessary. + (cl-incf indent tab-width) + + ;; Go to the end of the dangling line. + (goto-char (go-previous-line-has-dangling-op-p))) + + ;; If we aren't a continuation line and we have an enclosing paren + ;; or brace, jump to opener and increment our indent. + (when (go-goto-opening-parenthesis) + (setq in-block (go--flow-block-p)) + (cl-incf indent tab-width)))) + + ;; If we started in a child block we must follow dangling lines + ;; until they don't dangle anymore. This is to handle cases like: + ;; + ;; if foo || + ;; foo && + ;; foo { + ;; X + ;; + ;; There can be an arbitrary number of indents, so we must go back to + ;; the "if" to determine the indent of "X". + (when (and in-block (bolp) (go-previous-line-has-dangling-op-p)) + (goto-char (go-previous-line-has-dangling-op-p)))) + + ;; If our ending line is a continuation line but doesn't open + ;; an extra indent, reduce indent. We tentatively gave indents to all + ;; dangling lines and all lines inside open parens, so here we take that + ;; indent back. + ;; + ;; 1 + 1 + + ;; ending line 1 + foo( 1 + foo( + ;; starting line 1, becomes 1, + ;; ) ) + ;; + ;; + ;; 1 + 1 + + ;; ending line 1 + becomes 1 + + ;; starting line 1 1 + (when (and + (go-previous-line-has-dangling-op-p) + (not (go--continuation-line-indents-p))) + (cl-decf indent tab-width)) + + ;; Apply our computed indent relative to the indent of the + ;; ending line, or 0 if we are at the top level. + (if (and + (= 0 (go-paren-level)) + (not (go-previous-line-has-dangling-op-p))) + indent + (+ indent (current-indentation)))))) + +(defconst go--operator-chars "*/%<>&\\^+\\-|=!,." + "Individual characters that appear in operators. +Comma and period are included because they can be dangling operators, so +they need to be considered by `go--continuation-line-indents-p'") + +(defun go--operator-precedence (op) + "Go operator precedence (higher binds tighter)." + (cl-case (intern op) + (\. 7) ; "." in "foo.bar", binds tightest + (! 6) + ((* / % << >> & &^) 5) + ((+ - | ^) 4) + ((== != < <= > >=) 3) + (&& 2) + (|| 1) + (t 0))) + +(defun go--flow-block-p () + "Return whether looking at a { that opens a control flow block. + +We check for a { that is preceded by a space and is not a func +literal opening brace." + (save-excursion + (when (and + (eq (char-after) ?{) + (not (zerop (skip-syntax-backward " ")))) + + (let ((eol (line-end-position)) + (level (go-paren-level)) + (found-func-literal)) + + (beginning-of-line) + + ;; See if we find any "func" keywords on this line at the same paren + ;; level as the curly. + (while (and + (not found-func-literal) + (re-search-forward "\\_<func\\_>" eol t)) + (setq found-func-literal (and + (= level (go-paren-level)) + (not (go-in-string-or-comment-p))))) + (not found-func-literal))))) + +(defun go--continuation-line-indents-p () + "Return non-nil if the current continuation line opens an additional indent. + +This function works by looking at the Go operators used on the current +line. If all the operators bind tighter than the previous line's +dangling operator and the current line ends in a dangling operator or +open paren, the next line will have an additional indent. + +For example: +foo || + foo && // this continuation line opens another indent + foo +" + (save-excursion + (let (prev-op (all-tighter t)) + + ;; Record the dangling operator from previous line. + (save-excursion + (goto-char (go-previous-line-has-dangling-op-p)) + (go--end-of-line) + (skip-syntax-backward " ") + (let ((end (point))) + (skip-chars-backward go--operator-chars) + (setq prev-op (buffer-substring-no-properties (point) end)))) + + (beginning-of-line) + + (when (or + ;; We can only open indent if we have a dangling operator, or + (go--current-line-has-dangling-op-p) + + (save-excursion + (go--end-of-line) + (backward-char) + (or + ;; Line ends in a "(" or ",", or + (eq (char-after) ?\() + (eq (char-after) ?,) + + ;; Line ends in a "{" that isn't a control block. + (and + (eq (char-after) ?{) + (not (go--flow-block-p)))))) + + (let ((prev-precedence (go--operator-precedence prev-op)) + (start-depth (go-paren-level)) + (line-start (line-beginning-position))) + + (end-of-line) + + ;; While we haven't found a looser operator and are on the starting line... + (while (and all-tighter (> (point) line-start)) + + ;; Skip over non-operator characters. + (skip-chars-backward (concat "^" go--operator-chars) line-start) + + (let ((end (point))) + (cond + ;; Ignore sub-expressions at different paren levels. + ((/= (go-paren-level) start-depth) + (skip-syntax-backward "^()")) + + ((go-in-string-or-comment-p) + (go-goto-beginning-of-string-or-comment)) + + ;; We found an operator. Check if it has lower precedence. + ((/= (skip-chars-backward go--operator-chars) 0) + (when (>= + prev-precedence + (go--operator-precedence (buffer-substring (point) end))) + (setq all-tighter nil))))))) + all-tighter)))) + +(defun go--end-of-line () + "Move to the end of the code on the current line. +Point will be left before any trailing comments. Point will be left +after the opening backtick of multiline strings." + (end-of-line) + (let ((keep-going t)) + (while keep-going + (skip-syntax-backward " ") + (when (looking-back "\\*/" (- (point) 2)) + ;; back up so we are in the /* comment */ + (backward-char)) + (if (go-in-comment-p) + (go-goto-beginning-of-string-or-comment) + (setq keep-going nil)))) + (when (go-in-string-p) + (go-goto-beginning-of-string-or-comment) + ;; forward one so point is after the opening "`" + (forward-char))) + +(defun go--line-suffix-p (re) + "Return non-nil if RE matches the end of the line starting from `point'. + +Trailing whitespace, trailing comments and trailing multiline strings are +ignored." + (let ((start (point)) + (end (save-excursion (go--end-of-line) (point)))) + (when (< start end) + (string-match-p + (concat "\\(?:" re "\\)$") + (buffer-substring-no-properties start end))))) + +(defun go--boring-line-p () + "Return non-nil if the current line probably doesn't impact indentation. + +A boring line is one that starts with a comment, is empty, is part of a +multiline comment, or starts and ends in a multiline string." + (or + (looking-at (concat go--comment-start-regexp "\\|[[:space:]]*$")) + (go-in-comment-p) + (and (go-in-string-p) (save-excursion (end-of-line) (go-in-string-p))))) + +(defun go--forward-line (&optional count) + "Like `forward-line' but skip comments and empty lines. + +Return non-nil if point changed lines." + (let (moved) + (while (and + (zerop (forward-line count)) + (setq moved t) + (go--boring-line-p)) + (setq count (if (and count (< count 0 )) -1 1))) + moved)) + +(defun go--case-comment-p (indent) + "Return non-nil if looking at a comment attached to a case statement. + +INDENT is the normal indent of this line, i.e. that of the case body." + (when (and + (> (current-indentation) 0) + (looking-at go--comment-start-regexp)) + + (let (switch-before + case-after + has-case-aligned-preceding-comment) + + (save-excursion + ;; Search for previous case-aligned comment. + (while (and + (zerop (forward-line -1)) + (cond + ((looking-at "^[[:space:]]*$")) + + ((looking-at go--comment-start-regexp) + (when (= (current-indentation) (- indent tab-width)) + (setq has-case-aligned-preceding-comment t)) + t) + + ((go-in-comment-p))))) + + ;; Record if a switch (or select) precedes us. + (setq switch-before (looking-at "^[[:space:]]*\\(switch\\|select\\)[[:space:]]"))) + + ;; Record if first proceeding non-comment line is a case statement. + (save-excursion + (while (and + (zerop (forward-line 1)) + (or + (looking-at go--comment-start-regexp) + (looking-at "^[[:space:]]*$") + (go-in-comment-p)))) + + (setq case-after (looking-at go--case-or-default-regexp))) + + (and + ;; a "case" statement comes after our comment + case-after + + (or + ;; "switch" statement precedes us, always align with "case" + switch-before + + ;; a preceding comment is aligned with "case", we should too + has-case-aligned-preceding-comment + + ;; other cases are ambiguous, so if comment is currently + ;; aligned with "case", leave it that way + (= (current-indentation) (- indent tab-width))))))) + +(defun go-mode-indent-line () + (interactive) + (let (indent + ;; case sensitively match "case", "default", etc. + (case-fold-search nil) + (pos (- (point-max) (point))) + (point (point)) + (beg (line-beginning-position)) + (non-tab-indents 0)) + (back-to-indentation) + (if (go-in-string-p) + (goto-char point) + (setq indent (go-indentation-at-point)) + (when (or + (and + (looking-at (concat go-label-regexp ":\\([[:space:]]*/.+\\)?$\\|" go--case-or-default-regexp)) + ;; don't think last part of multiline case statement is a label + (not (go-previous-line-has-dangling-op-p)) + (not (go--in-case-clause-list-p)) + (not (go--in-composite-literal-p))) + + ;; comment attached above a "case" statement + (go--case-comment-p indent)) + (cl-decf indent tab-width)) + + ;; Don't do anything if current indent is correct. + (when (/= indent (current-column)) + ;; Don't use tabs for indenting beyond "/*" in multiline + ;; comments. They don't play well with gofmt. + (when (go-in-comment-p) + (save-excursion + (go-goto-beginning-of-string-or-comment) + (when (> indent (current-indentation)) + (setq non-tab-indents (- indent (current-indentation))) + (setq indent (current-indentation))))) + + (delete-region beg (point)) + (indent-to indent) + (insert-char ? non-tab-indents)) + + ;; If initial point was within line's indentation, + ;; position after the indentation. Else stay at same point in text. + (if (> (- (point-max) pos) (point)) + (goto-char (- (point-max) pos)))))) + +(defun go-beginning-of-defun (&optional count) + (unless (bolp) + (end-of-line)) + (setq count (or count 1)) + (let (first failure) + (dotimes (i (abs count)) + (setq first t) + (while (and (not failure) + (or first (go-in-string-or-comment-p))) + (if (>= count 0) + (progn + (go--backward-irrelevant) + (if (not (re-search-backward go-func-meth-regexp nil t)) + (setq failure t))) + (if (looking-at go-func-meth-regexp) + (forward-char)) + (if (not (re-search-forward go-func-meth-regexp nil t)) + (setq failure t))) + (setq first nil))) + (if (< count 0) + (beginning-of-line)) + (not failure))) + +(defun go-end-of-defun () + (let (orig-level) + ;; It can happen that we're not placed before a function by emacs + (if (not (looking-at "func")) + (go-beginning-of-defun -1)) + ;; Find the { that starts the function, i.e., the next { that isn't + ;; preceded by struct or interface, or a comment or struct tag. BUG: + ;; breaks if there's a comment between the struct/interface keyword and + ;; bracket, like this: + ;; + ;; struct /* why? */ { + (while (progn + (skip-chars-forward "^{") + (forward-char) + (or (go-in-string-or-comment-p) + (looking-back "\\(struct\\|interface\\)\\s-*{" + (line-beginning-position))))) + (setq orig-level (go-paren-level)) + (while (>= (go-paren-level) orig-level) + (skip-chars-forward "^}") + (forward-char)))) + + +(defvar go--fontify-param-has-name nil + "Whether the current params list has names. + +This is used during fontification of function signatures.") + +(defvar go--fontify-param-beg nil + "Position of \"(\" starting param list. + +This is used during fontification of function signatures.") + +(defun go--fontify-param-pre () + "Set `go--fontify-param-has-name' and `go--fontify-param-beg' appropriately. + +This is used as an anchored font lock keyword PRE-MATCH-FORM. We +must set `go--fontify-param-has-name' ahead of time because you +can't know if the param list is types only or names and types +until you see the end. For example: + +// types only +func foo(int, string) {} + +// names and types (don't know so until you see the \"int\"). +func foo(i, j int) {} +" + (setq go--fontify-param-has-name (eq + (go--parameter-list-type (point-max)) + 'present)) + + ;; Remember where our match started so we can continue our search + ;; from here. + (setq go--fontify-param-beg (point)) + + ;; Return position of closing paren so we process the entire + ;; multiline param list. + (save-excursion + (let ((depth (go-paren-level))) + ;; First check that our paren is closed by the end of the file. This + ;; avoids expanding the fontification region to the entire file when you + ;; have an unclosed paren at file scope. + (when (save-excursion + (goto-char (1+ (buffer-size))) + (< (go-paren-level) depth)) + (while (and + (re-search-forward ")" nil t) + (>= (go-paren-level) depth))))) + (point))) + +(defun go--fontify-param-post () + "Move point back to opening paren. + +This is used as an anchored font lock keyword POST-MATCH-FORM. We +move point back to the opening \"(\" so we find nested param +lists. +" + (goto-char go--fontify-param-beg)) + +(defun go--match-param-start (end) + "Search for the starting of param lists. + +Search for the opening `(' of function signature param lists. +This covers the func receiver, params, and results. Interface +declarations are also included." + (let (found-match) + (while (and + (not found-match) + (re-search-forward (concat "\\(\\_<" go-identifier-regexp "\\)?(") end t)) + (when (not (go-in-string-or-comment-p)) + (save-excursion + (goto-char (match-beginning 0)) + + (let ((name (match-string 1))) + (when name + ;; We are in a param list if "func" preceded the "(" (i.e. + ;; func literal), or if we are in an interface + ;; declaration, e.g. "interface { foo(i int) }". + (setq found-match (or (string= name "func") (go--in-interface-p)))) + + ;; Otherwise we are in a param list if our "(" is preceded + ;; by ") " or "func ". + (when (and (not found-match) (not (zerop (skip-syntax-backward " ")))) + (setq found-match (or + (eq (char-before) ?\)) + (looking-back "\\_<func" (- (point) 4))))))))) + found-match)) + + +(defconst go--named-param-re + (concat "[[:space:]\n]*\\(" go-identifier-regexp "\\)\\(?:[[:space:]]+\\(?:\\.\\.\\.\\)?" go-type-name-regexp "[[:space:]]*[,)]\\)?") + "Regexp to match named param such as \"s *string\" in: + +func(i int, s *string) { }") + +(defconst go--unnamed-param-re + (concat "\\(\\)[[:space:]\n]*\\(?:\\.\\.\\.\\)?" go-type-name-regexp "[[:space:]]*[,)]") + "Regexp to match unnamed param such as \"*string\" in: + +func(int, *string) { } + +We start with an empty subexp since our font lock keyword expects +subexp 1 to a variable name, but we have no variable.") + +(defun go--fontify-param (end) + "Match a param within a param list. + +Our parent font lock matcher is anchored to the beginning of the +param list. `go--fontify-param-has-name' has been set +appropriately. We match the next param and advance point to after +the next comma or to the closing paren." + (let (found-match done) + ;; We loop until match because there are some params that we can't + ;; handle (but we may need to handle subsequent params). For + ;; example: + ;; + ;; // We don't handle the interface, so we must skip it and handle + ;; // "string". + ;; func(int, interface { foo() }, string) + (while (and (not found-match) (not done)) + (if go--fontify-param-has-name + (when (looking-at go--named-param-re) + (when (not go-fontify-variables) + (let ((md (match-data))) + (setf (nth 2 md) nil (nth 3 md) nil) + (set-match-data md))) + (setq found-match t)) + (when (looking-at go--unnamed-param-re) + (setq found-match t))) + + ;; Advance to next comma. We are done if there are no more commas. + (setq done (not (go--search-next-comma end)))) + found-match)) + +(defun go--search-next-comma (end) + "Search forward from point for a comma whose nesting level is +the same as point. If it reaches a closing parenthesis before a +comma, it stops at it. Return non-nil if comma was found." + (let ((orig-level (go-paren-level))) + (while (and (< (point) end) + (or (looking-at-p "[^,)]") + (> (go-paren-level) orig-level))) + (forward-char)) + (when (and (looking-at-p ",") + (< (point) (1- end))) + (forward-char) + t))) + +(defun go--looking-at-keyword () + (and (looking-at (concat "\\(" go-identifier-regexp "\\)")) + (member (match-string 1) go-mode-keywords))) + +(defun go--match-type-switch-case (end) + "Match a \"case\" clause within a type switch." + (let (found-match) + (while (and + (not found-match) + + ;; Search for "case" statements. + (re-search-forward "^[[:space:]]*case " end t)) + + ;; Make sure we are in a type switch statement. + (setq found-match (go--in-type-switch-p))) + found-match)) + +(defun go--fontify-type-switch-case (end) + "Match a single type within a type switch case." + (let (found-match done) + ;; Loop until we find a match because we must skip types we don't + ;; handle, such as "interface { foo() }". + (while (and (not found-match) (not done)) + (when (looking-at (concat "\\(?:[[:space:]]*\\|//.*\\|\n\\)*" go-type-name-regexp "[[:space:]]*[,:]")) + (goto-char (match-end 1)) + (unless (member (match-string 1) go-constants) + (setq found-match t))) + (setq done (not (go--search-next-comma end)))) + found-match)) + +(defun go--containing-decl () + "Return containing decl kind var|const|type, if any." + (save-match-data + (or + (save-excursion + (and + (go-goto-opening-parenthesis) + (eq (char-after) ?\() + (skip-syntax-backward " ") + (skip-syntax-backward "w") + (looking-at "\\(var\\|const\\|type\\)[[:space:]]") + (match-string-no-properties 1))) + + (save-excursion + (let ((depth (go-paren-level))) + (beginning-of-line) + (and + (= (go-paren-level) depth) + (looking-at "[[:space:]]*\\(var\\|const\\|type\\)[[:space:]]") + (match-string-no-properties 1))))))) + +(defconst go--decl-ident-re (concat "\\(?:^\\|[[:space:]]\\)\\(\\(\\(" go-identifier-regexp "\\)\\)\\)\\_>")) + +(defun go--match-decl (end) + "Match identifiers in \"var\", \"type\" and \"const\" decls, as +well as \":=\" assignments. + +In order to only scan once, the regex has three subexpressions +that match the same identifier. Depending on the kind of +containing decl we zero out the subexpressions so the right one +gets highlighted by the font lock keyword." + (let (found-match decl) + (while (and + (not found-match) + (re-search-forward go--decl-ident-re end t)) + + (save-excursion + ;; Skip keywords. + (cond + ((member (match-string 1) go-mode-keywords)) + + ((and + ;; We are in a decl of some kind. + (setq decl (go--containing-decl)) + + ;; We aren't on right side of equals sign. + (not (go--looking-back-p "="))) + + (setq found-match t) + + ;; Unset match data subexpressions that don't apply based on + ;; the decl kind. + (let ((md (match-data))) + (cond + ((string= decl "var") + (setf (nth 4 md) nil (nth 5 md) nil (nth 6 md) nil (nth 7 md) nil) + (when (not go-fontify-variables) + (setf (nth 2 md) nil (nth 3 md) nil))) + ((string= decl "const") + (setf (nth 2 md) nil (nth 3 md) nil (nth 6 md) nil (nth 7 md) nil)) + ((string= decl "type") + (setf (nth 2 md) nil (nth 3 md) nil (nth 4 md) nil (nth 5 md) nil))) + (set-match-data md))) + + (go-fontify-variables + (save-match-data + ;; Left side of ":=" assignment. + (when (looking-at ".*:=") + (let ((depth (go-paren-level))) + (goto-char (match-end 0)) + ;; Make sure the ":=" isn't in a comment or a sub-block. + (setq found-match (and + (not (go-in-string-or-comment-p)) + (= depth (go-paren-level))))))))))) + found-match)) + +(defun go--looking-back-p (re) + "Return non-nil if RE matches beginning of line to point. + +RE is not anchored automatically." + (string-match-p + re + (buffer-substring-no-properties (point) (line-beginning-position)))) + + +(defconst go--ident-type-pair-re (concat "\\_<\\(" go-identifier-regexp "\\)[[:space:]]+" go-type-name-regexp)) + +(defun go--match-ident-type-pair (end) + "Search for identifier + type-name pairs. + +For example, this looks for the \"foo bar\" in \"var foo bar\", +yielding match-data for \"bar\" since that is a type name to be +fontified. This approach matches type names in var and const +decls, and in struct definitions. Return non-nil if search +succeeds." + (let (found-match) + (while (and + (not found-match) + (re-search-forward go--ident-type-pair-re end t)) + + ;; Make sure the neither match is a keyword. + (if (member (match-string 2) go-mode-keywords) + (goto-char (match-end 2)) + (if (member (match-string 1) go-mode-keywords) + (goto-char (match-end 1)) + (setq found-match t)))) + + found-match)) + +(defconst go--single-func-result-re (concat ")[[:space:]]+" go-type-name-regexp "\\(?:$\\|[[:space:]),]\\)")) + +(defun go--match-single-func-result (end) + "Match single result types. + +Parenthetical result lists are handled by the param list keyword, +so we need a separate keyword to handle singular result types +such as \"string\" in: + +func foo(i int) string" + (let (found-match) + (while (and + (not found-match) + (re-search-forward go--single-func-result-re end t)) + (when (not (member (match-string 1) go-mode-keywords)) + (setq found-match t) + (goto-char (match-end 1)))) + found-match)) + +(defconst go--type-alias-re + (concat "^[[:space:]]*\\(type\\)?[[:space:]]*" go-identifier-regexp "[[:space:]]*=[[:space:]]*" go-type-name-regexp)) + +(defun go--match-type-alias (end) + "Search for type aliases. + +We are looking for the right-hand-side of the type alias" + (let (found-match) + (while (and + (not found-match) + (re-search-forward go--type-alias-re end t)) + ;; Either line started with "type", or we are in a "type" block. + (setq found-match (or + (match-string 1) + (go--in-paren-with-prefix-p ?\( "type")))) + found-match)) + + +(defconst go--map-value-re + (concat "\\_<map\\_>\\[\\(?:\\[[^]]*\\]\\)*[^]]*\\]" go-type-name-regexp)) + +(defun go--match-map-value (end) + "Search for map value types." + (when (re-search-forward go--map-value-re end t) + ;; Move point to beginning of map value in case value itself is + ;; also a map (we will match it next iteration). + (goto-char (match-beginning 1)) + t)) + +(defconst go--label-re (concat "\\(" go-label-regexp "\\):")) + +(defun go--match-ident-colon (end) + "Search for composite literal field names and label definitions." + (let (found-match) + (while (and + (not found-match) + (re-search-forward go--label-re end t)) + + (save-excursion + (goto-char (match-beginning 1)) + (skip-syntax-backward " ") + + (setq found-match (or + ;; We are a label/field name if we are at the + ;; beginning of the line. + (bolp) + + ;; Composite literal field names, e.g. "Foo{Bar:". Note + ;; that this gives false positives for literal maps, + ;; arrays, and slices. + (and + (or (eq (char-before) ?,) (eq (char-before) ?{)) + (go--in-composite-literal-p)))))) + + found-match)) + +(defun go--parameter-list-type (end) + "Return `present' if the parameter list has names, or `absent' if not. +Assumes point is at the beginning of a parameter list, just +after '('." + (save-excursion + (skip-chars-forward "[:space:]\n" end) + (cond ((> (point) end) + nil) + ((looking-at (concat go-identifier-regexp "[[:space:]\n]*,")) + (goto-char (match-end 0)) + (go--parameter-list-type end)) + ((or (looking-at go-qualified-identifier-regexp) + (looking-at (concat go-type-name-no-prefix-regexp "[[:space:]\n]*\\(?:)\\|\\'\\)")) + (go--looking-at-keyword) + (looking-at "[*\\[]\\|\\.\\.\\.\\|\\'")) + 'absent) + (t 'present)))) + +(defun go--reset-dangling-cache-before-change (&optional _beg _end) + "Reset `go-dangling-cache'. + +This is intended to be called from `before-change-functions'." + (setq go-dangling-cache (make-hash-table :test 'eql))) + +(defun go--electric-indent-function (inserted-char) + (let ((prev (char-before (1- (point))))) + (cond + ;; Indent after starting/ending a comment. This is handy for + ;; comments above "case" statements and closing multiline + ;; comments. + ((or + (and (eq inserted-char ?/) (eq prev ?/)) + (and (eq inserted-char ?/) (eq prev ?*)) + (and (eq inserted-char ?*) (eq prev ?/))) + 'do-indent) + + ((eq inserted-char ? ) + (and + (eq prev ?e) + (eq (char-before (- (point) 2)) ?s) + (eq (char-before (- (point) 3)) ?a) + (eq (char-before (- (point) 4)) ?c))) + + ;; Trick electric-indent-mode into indenting inside multiline + ;; comments. + ((and (eq inserted-char ?\n) (go-in-comment-p)) + 'do-indent)))) + +(defun go--comment-region (beg end &optional arg) + "Switch to block comment when commenting a partial line." + (save-excursion + (goto-char beg) + (let ((beg-bol (line-beginning-position))) + (goto-char end) + (if (and + ;; beg and end are on the same line + (eq (line-beginning-position) beg-bol) + ;; end is not at end of line + (not (eq end (line-end-position)))) + (let ((comment-start "/* ") + (comment-end " */") + (comment-padding "")) + (comment-region-default beg end arg)) + (comment-region-default beg end arg))))) + +;;;###autoload +(define-derived-mode go-mode prog-mode "Go" + "Major mode for editing Go source text. + +This mode provides (not just) basic editing capabilities for +working with Go code. It offers almost complete syntax +highlighting, indentation that is almost identical to gofmt and +proper parsing of the buffer content to allow features such as +navigation by function, manipulation of comments or detection of +strings. + +In addition to these core features, it offers various features to +help with writing Go code. You can directly run buffer content +through gofmt, read godoc documentation from within Emacs, modify +and clean up the list of package imports or interact with the +Playground (uploading and downloading pastes). + +The following extra functions are defined: + +- `gofmt' +- `godoc' and `godoc-at-point' +- `go-import-add' +- `go-remove-unused-imports' +- `go-goto-arguments' +- `go-goto-docstring' +- `go-goto-function' +- `go-goto-function-name' +- `go-goto-imports' +- `go-goto-return-values' +- `go-goto-method-receiver' +- `go-play-buffer' and `go-play-region' +- `go-download-play' +- `godef-describe' and `godef-jump' +- `go-coverage' +- `go-set-project' +- `go-reset-gopath' + +If you want to automatically run `gofmt' before saving a file, +add the following hook to your emacs configuration: + +\(add-hook 'before-save-hook #'gofmt-before-save) + +If you want to use `godef-jump' instead of etags (or similar), +consider binding godef-jump to `M-.', which is the default key +for `find-tag': + +\(add-hook 'go-mode-hook (lambda () + (local-set-key (kbd \"M-.\") #'godef-jump))) + +Please note that godef is an external dependency. You can install +it with + +go get github.com/rogpeppe/godef + + +If you're looking for even more integration with Go, namely +on-the-fly syntax checking, auto-completion and snippets, it is +recommended that you look at flycheck +\(see URL `https://github.com/flycheck/flycheck') or flymake in combination +with goflymake (see URL `https://github.com/dougm/goflymake'), gocode +\(see URL `https://github.com/nsf/gocode'), go-eldoc +\(see URL `github.com/syohex/emacs-go-eldoc') and yasnippet-go +\(see URL `https://github.com/dominikh/yasnippet-go')" + + ;; Font lock + (setq font-lock-defaults '(go--build-font-lock-keywords)) + (setq font-lock-multiline t) + + ;; Indentation + (set (make-local-variable 'indent-line-function) #'go-mode-indent-line) + + ;; Comments + (set (make-local-variable 'comment-start) "// ") + (set (make-local-variable 'comment-end) "") + (set (make-local-variable 'comment-use-syntax) t) + (set (make-local-variable 'comment-start-skip) "\\(//+\\|/\\*+\\)\\s *") + (set (make-local-variable 'comment-region-function) #'go--comment-region) + ;; Set comment-multi-line to t so that comment-indent-new-line + ;; doesn't use one /* */ per line. Thanks to comment-use-syntax, + ;; Emacs is smart enough to still insert new // for single-line + ;; comments. + (set (make-local-variable 'comment-multi-line) t) + + (set (make-local-variable 'beginning-of-defun-function) #'go-beginning-of-defun) + (set (make-local-variable 'end-of-defun-function) #'go-end-of-defun) + (setq-local paragraph-start + (concat "[[:space:]]*\\(?:" + comment-start-skip + "\\|\\*/?[[:space:]]*\\|\\)$")) + (setq-local paragraph-separate paragraph-start) + (setq-local fill-paragraph-function #'go-fill-paragraph) + (setq-local fill-forward-paragraph-function #'go--fill-forward-paragraph) + (setq-local adaptive-fill-function #'go--find-fill-prefix) + (setq-local adaptive-fill-first-line-regexp "") + (setq-local comment-line-break-function #'go--comment-indent-new-line) + + (set (make-local-variable 'parse-sexp-lookup-properties) t) + (set (make-local-variable 'syntax-propertize-function) #'go-propertize-syntax) + + (when (boundp 'electric-indent-chars) + (set (make-local-variable 'electric-indent-chars) '(?\n ?} ?\) ?:)) + (add-hook 'electric-indent-functions #'go--electric-indent-function nil t)) + + (set (make-local-variable 'compilation-error-screen-columns) nil) + + (set (make-local-variable 'go-dangling-cache) (make-hash-table :test 'eql)) + (add-hook 'before-change-functions #'go--reset-dangling-cache-before-change t t) + + ;; ff-find-other-file + (setq ff-other-file-alist 'go-other-file-alist) + + (setq imenu-generic-expression + '(("type" "^type *\\([^ \t\n\r\f]*\\)" 1) + ("func" "^func *\\(.*\\) {" 1))) + (imenu-add-to-menubar "Index") + + ;; Go style + (setq indent-tabs-mode t) + + ;; Handle unit test failure output in compilation-mode + ;; + ;; Note that we add our entry to the beginning of + ;; compilation-error-regexp-alist. In older versions of Emacs, the + ;; list was processed from the end, and we would've wanted to add + ;; ours last. But at some point this changed, and now the list is + ;; processed from the beginning. It's important that our entry comes + ;; before gnu, because gnu matches go test output, but includes the + ;; leading whitespace in the file name. + ;; + ;; http://lists.gnu.org/archive/html/bug-gnu-emacs/2001-12/msg00674.html + ;; documents the old, reversed order. + (when (and (boundp 'compilation-error-regexp-alist) + (boundp 'compilation-error-regexp-alist-alist)) + (add-to-list 'compilation-error-regexp-alist 'go-test) + (add-to-list 'compilation-error-regexp-alist-alist + '(go-test . ("^\\s-+\\([^()\t\n]+\\):\\([0-9]+\\):? .*$" 1 2)) t))) + +;;;###autoload +(add-to-list 'auto-mode-alist (cons "\\.go\\'" 'go-mode)) + +(defun go--apply-rcs-patch (patch-buffer) + "Apply an RCS-formatted diff from PATCH-BUFFER to the current buffer." + (let ((target-buffer (current-buffer)) + ;; Relative offset between buffer line numbers and line numbers + ;; in patch. + ;; + ;; Line numbers in the patch are based on the source file, so + ;; we have to keep an offset when making changes to the + ;; buffer. + ;; + ;; Appending lines decrements the offset (possibly making it + ;; negative), deleting lines increments it. This order + ;; simplifies the forward-line invocations. + (line-offset 0) + (column (current-column))) + (save-excursion + (with-current-buffer patch-buffer + (goto-char (point-min)) + (while (not (eobp)) + (unless (looking-at "^\\([ad]\\)\\([0-9]+\\) \\([0-9]+\\)") + (error "Invalid rcs patch or internal error in go--apply-rcs-patch")) + (forward-line) + (let ((action (match-string 1)) + (from (string-to-number (match-string 2))) + (len (string-to-number (match-string 3)))) + (cond + ((equal action "a") + (let ((start (point))) + (forward-line len) + (let ((text (buffer-substring start (point)))) + (with-current-buffer target-buffer + (cl-decf line-offset len) + (goto-char (point-min)) + (forward-line (- from len line-offset)) + (insert text))))) + ((equal action "d") + (with-current-buffer target-buffer + (go--goto-line (- from line-offset)) + (cl-incf line-offset len) + (go--delete-whole-line len))) + (t + (error "Invalid rcs patch or internal error in go--apply-rcs-patch"))))))) + (move-to-column column))) + +(defun gofmt--is-goimports-p () + (string-equal (file-name-base gofmt-command) "goimports")) + +(defun gofmt () + "Format the current buffer according to the formatting tool. + +The tool used can be set via ‘gofmt-command’ (default: gofmt) and additional +arguments can be set as a list via ‘gofmt-args’." + (interactive) + (let ((tmpfile (make-nearby-temp-file "gofmt" nil ".go")) + (patchbuf (get-buffer-create "*Gofmt patch*")) + (errbuf (if gofmt-show-errors (get-buffer-create "*Gofmt Errors*"))) + (coding-system-for-read 'utf-8) + (coding-system-for-write 'utf-8) + our-gofmt-args) + + (unwind-protect + (save-restriction + (widen) + (if errbuf + (with-current-buffer errbuf + (setq buffer-read-only nil) + (erase-buffer))) + (with-current-buffer patchbuf + (erase-buffer)) + + (write-region nil nil tmpfile) + + (when (and (gofmt--is-goimports-p) buffer-file-name) + (setq our-gofmt-args + (append our-gofmt-args + ;; srcdir, despite its name, supports + ;; accepting a full path, and some features + ;; of goimports rely on knowing the full + ;; name. + (list "-srcdir" (file-local-name + (file-truename buffer-file-name)))))) + (setq our-gofmt-args + (append our-gofmt-args gofmt-args + (list "-w" (file-local-name tmpfile)))) + (message "Calling gofmt: %s %s" gofmt-command our-gofmt-args) + ;; We're using errbuf for the mixed stdout and stderr output. This + ;; is not an issue because gofmt -w does not produce any stdout + ;; output in case of success. + (if (zerop (apply #'process-file gofmt-command nil errbuf nil our-gofmt-args)) + (progn + ;; There is no remote variant of ‘call-process-region’, but we + ;; can invoke diff locally, and the results should be the same. + (if (zerop (let ((local-copy (file-local-copy tmpfile))) + (unwind-protect + (call-process-region + (point-min) (point-max) "diff" nil patchbuf + nil "-n" "-" (or local-copy tmpfile)) + (when local-copy (delete-file local-copy))))) + (message "Buffer is already gofmted") + (go--apply-rcs-patch patchbuf) + (message "Applied gofmt")) + (if errbuf (gofmt--kill-error-buffer errbuf))) + (message "Could not apply gofmt") + (if errbuf (gofmt--process-errors (buffer-file-name) tmpfile errbuf)))) + + (kill-buffer patchbuf) + (delete-file tmpfile)))) + + +(defun gofmt--process-errors (filename tmpfile errbuf) + (with-current-buffer errbuf + (if (eq gofmt-show-errors 'echo) + (progn + (message "%s" (buffer-string)) + (gofmt--kill-error-buffer errbuf)) + ;; Convert the gofmt stderr to something understood by the compilation mode. + (goto-char (point-min)) + (if (save-excursion + (save-match-data + (search-forward "flag provided but not defined: -srcdir" nil t))) + (insert "Your version of goimports is too old and doesn't support vendoring. Please update goimports!\n\n")) + (insert "gofmt errors:\n") + (let ((truefile + (if (gofmt--is-goimports-p) + (concat (file-name-directory filename) (file-name-nondirectory tmpfile)) + tmpfile))) + (while (search-forward-regexp + (concat "^\\(" (regexp-quote (file-local-name truefile)) + "\\):") + nil t) + (replace-match (file-name-nondirectory filename) t t nil 1))) + (compilation-mode) + (display-buffer errbuf)))) + +(defun gofmt--kill-error-buffer (errbuf) + (let ((win (get-buffer-window errbuf))) + (if win + (quit-window t win) + (kill-buffer errbuf)))) + +;;;###autoload +(defun gofmt-before-save () + "Add this to .emacs to run gofmt on the current buffer when saving: +\(add-hook 'before-save-hook 'gofmt-before-save). + +Note that this will cause ‘go-mode’ to get loaded the first time +you save any file, kind of defeating the point of autoloading." + + (interactive) + (when (eq major-mode 'go-mode) (gofmt))) + +(defun godoc--read-query () + "Read a godoc query from the minibuffer." + (if godoc-use-completing-read + (completing-read "godoc; " + (go-packages) nil nil nil 'go-godoc-history) + (read-from-minibuffer "godoc: " nil nil nil 'go-godoc-history))) + +(defun godoc--buffer-name (query) + "Determine the name to use for the output buffer of a given godoc QUERY." + (if godoc-reuse-buffer + "*godoc*" + (concat "*godoc " query "*"))) + +(defun godoc--get-buffer (query) + "Get an empty buffer for a godoc QUERY." + (let* ((buffer-name (godoc--buffer-name query)) + (buffer (get-buffer buffer-name))) + ;; Kill the existing buffer if it already exists. + (when buffer (kill-buffer buffer)) + (get-buffer-create buffer-name))) + +(defun godoc--buffer-sentinel (proc event) + "Sentinel function run when godoc command completes." + (with-current-buffer (process-buffer proc) + (cond ((string= event "finished\n") ;; Successful exit. + (goto-char (point-min)) + (godoc-mode) + (display-buffer (current-buffer) t)) + ((/= (process-exit-status proc) 0) ;; Error exit. + (let ((output (buffer-string))) + (kill-buffer (current-buffer)) + (message (concat "godoc: " output))))))) + +(define-derived-mode godoc-mode special-mode "Godoc" + "Major mode for showing Go documentation." + (view-mode-enter)) + +;;;###autoload +(defun godoc (query) + "Show Go documentation for QUERY, much like \\<go-mode-map>\\[man]." + (interactive (list (godoc--read-query))) + (go--godoc query godoc-command)) + +(defun go--godoc (query command) + (unless (string= query "") + (set-process-sentinel + (start-process-shell-command "godoc" (godoc--get-buffer query) + (concat command " " query)) + 'godoc--buffer-sentinel) + nil)) + +(defun godoc-at-point (point) + "Show Go documentation for the identifier at POINT. + +It uses `godoc-at-point-function' to look up the documentation." + (interactive "d") + (funcall godoc-at-point-function point)) + +(defun go-goto-imports () + "Move point to the block of imports. + +If using + + import ( + \"foo\" + \"bar\" + ) + +it will move point directly behind the last import. + +If using + + import \"foo\" + import \"bar\" + +it will move point to the next line after the last import. + +If no imports can be found, point will be moved after the package +declaration." + (interactive) + ;; FIXME if there's a block-commented import before the real + ;; imports, we'll jump to that one. + + ;; Generally, this function isn't very forgiving. it'll bark on + ;; extra whitespace. It works well for clean code. + (let ((old-point (point))) + (goto-char (point-min)) + (cond + ((re-search-forward "^import ()" nil t) + (backward-char 1) + 'block-empty) + ((re-search-forward "^import ([^)]+)" nil t) + (backward-char 2) + 'block) + ((re-search-forward "\\(^import \\([^\"]+ \\)?\"[^\"]+\"\n?\\)+" nil t) + 'single) + ((re-search-forward "^[[:space:]\n]*package .+?\n" nil t) + (message "No imports found, moving point after package declaration") + 'none) + (t + (goto-char old-point) + (message "No imports or package declaration found. Is this really a Go file?") + 'fail)))) + +(defun go-play-buffer () + "Like `go-play-region', but acts on the entire buffer." + (interactive) + (go-play-region (point-min) (point-max))) + +(defun go-play-region (start end) + "Send the region between START and END to the Playground. +If non-nil `go-play-browse-function' is called with the +Playground URL. + +By default this function will prompt to confirm you want to upload +code to the Playground. You can disable the confirmation by setting +`go-confirm-playground-uploads' to nil. +" + (interactive "r") + (if (and go-confirm-playground-uploads + (not (yes-or-no-p "Upload to public Go Playground? "))) + (message "Upload aborted") + (let* ((url-request-method "POST") + (url-request-extra-headers + '(("Content-Type" . "text/plain; charset=UTF-8"))) + (url-request-data + (encode-coding-string + (buffer-substring-no-properties start end) + 'utf-8)) + + (content-buf (url-retrieve + "https://play.golang.org/share" + (lambda (arg) + (cond + ((equal :error (car arg)) + (signal 'go-play-error (cdr arg))) + (t + (re-search-forward "\n\n") + (let ((url (format "https://play.golang.org/p/%s" + (buffer-substring (point) (point-max))))) + (when go-play-browse-function + (funcall go-play-browse-function url)))))))))))) + +;;;###autoload +(defun go-download-play (url) + "Download a paste from the playground and insert it in a Go buffer. +Tries to look for a URL at point." + (interactive (list (read-from-minibuffer "Playground URL: " (ffap-url-p (ffap-string-at-point 'url))))) + (with-current-buffer + (let ((url-request-method "GET") url-request-data url-request-extra-headers) + (url-retrieve-synchronously (concat url ".go"))) + (let ((buffer (generate-new-buffer (concat (car (last (split-string url "/"))) ".go")))) + (goto-char (point-min)) + (re-search-forward "\n\n") + (copy-to-buffer buffer (point) (point-max)) + (kill-buffer) + (with-current-buffer buffer + (go-mode) + (switch-to-buffer buffer))))) + +(defun go-propertize-syntax (start end) + (save-excursion + (goto-char start) + (while (search-forward "\\" end t) + (put-text-property (1- (point)) (point) 'syntax-table (if (= (char-after) ?`) '(1) '(9)))))) + +(defun go-import-add (arg import) + "Add a new IMPORT to the list of imports. + +When called with a prefix ARG asks for an alternative name to +import the package as. + +If no list exists yet, one will be created if possible. + +If an identical import has been commented, it will be +uncommented, otherwise a new import will be added." + + ;; - If there's a matching `// import "foo"`, uncomment it + ;; - If we're in an import() block and there's a matching `"foo"`, uncomment it + ;; - Otherwise add a new import, with the appropriate syntax + (interactive + (list + current-prefix-arg + (replace-regexp-in-string "^[\"']\\|[\"']$" "" (completing-read "Package: " (go-packages))))) + (save-excursion + (let (as line import-start) + (if arg + (setq as (read-from-minibuffer "Import as: "))) + (if as + (setq line (format "%s \"%s\"" as import)) + (setq line (format "\"%s\"" import))) + + (goto-char (point-min)) + (if (re-search-forward (concat "^[[:space:]]*//[[:space:]]*import " line "$") nil t) + (uncomment-region (line-beginning-position) (line-end-position)) + (cl-case (go-goto-imports) + ('fail (message "Could not find a place to add import.")) + ('block-empty + (insert "\n\t" line "\n")) + ('block + (save-excursion + (re-search-backward "^import (") + (setq import-start (point))) + (if (re-search-backward (concat "^[[:space:]]*//[[:space:]]*" line "$") import-start t) + (uncomment-region (line-beginning-position) (line-end-position)) + (insert "\n\t" line))) + ('single (insert "import " line "\n")) + ('none (insert "\nimport (\n\t" line "\n)\n"))))))) + +(defun go-root-and-paths () + (let* ((output (process-lines go-command "env" "GOROOT" "GOPATH")) + (root (car output)) + (paths (split-string (cadr output) path-separator))) + (cons root paths))) + +(defun go--string-prefix-p (s1 s2 &optional ignore-case) + "Return non-nil if S1 is a prefix of S2. +If IGNORE-CASE is non-nil, the comparison is case-insensitive." + (eq t (compare-strings s1 nil nil + s2 0 (length s1) ignore-case))) + +(defun go--directory-dirs (dir) + "Recursively return all subdirectories in DIR." + (if (file-directory-p dir) + (let ((dir (directory-file-name dir)) + (dirs '()) + (files (directory-files dir nil nil t))) + (dolist (file files) + (unless (member file '("." "..")) + (let ((file (concat dir "/" file))) + (if (and (file-directory-p file) + (not (file-symlink-p file))) + (setq dirs (append (cons file + (go--directory-dirs file)) + dirs)))))) + dirs) + '())) + + +(defun go-packages () + (funcall go-packages-function)) + +(defun go-packages-native () + "Return a list of all installed Go packages. Obsolete. +It looks for archive files in /pkg/. This strategy does not work +well with the Go build cache or Go modules. + +You should use `go-packages-go-list' instead." + (sort + (delete-dups + (cl-mapcan + (lambda (topdir) + (let ((pkgdir (concat topdir "/pkg/"))) + (cl-mapcan (lambda (dir) + (mapcar (lambda (file) + (let ((sub (substring file (length pkgdir) -2))) + (unless (or (go--string-prefix-p "obj/" sub) (go--string-prefix-p "tool/" sub)) + (mapconcat #'identity (cdr (split-string sub "/")) "/")))) + (if (file-directory-p dir) + (directory-files dir t "\\.a$")))) + (if (file-directory-p pkgdir) + (go--directory-dirs pkgdir))))) + (go-root-and-paths))) + #'string<)) + +(defun go-packages-go-list () + "Return a list of all Go packages, using `go list'." + (process-lines go-command "list" "-e" "all")) + +(defun go-unused-imports-lines () + (reverse (remove nil + (mapcar + (lambda (line) + (when (string-match "^\\(.+\\):\\([[:digit:]]+\\):\\([[:digit:]]+\\): imported and not used: \".+\".*$" line) + (let ((error-file-name (match-string 1 line)) + (error-line-num (match-string 2 line))) + (if (string= (file-truename error-file-name) (file-truename buffer-file-name)) + (string-to-number error-line-num))))) + (split-string (shell-command-to-string + (concat go-command + (if (string-match "_test\\.go$" buffer-file-truename) + " test -c" + (concat " build -o " null-device)) + " -gcflags=-e" + " " + (shell-quote-argument (file-truename buffer-file-name)))) "\n"))))) + +(defun go-remove-unused-imports (arg) + "Remove all unused imports. +If ARG is non-nil, unused imports will be commented, otherwise +they will be removed completely." + (interactive "P") + (save-excursion + (let ((cur-buffer (current-buffer)) flymake-state lines) + (when (boundp 'flymake-mode) + (setq flymake-state flymake-mode) + (flymake-mode -1)) + (save-some-buffers nil (lambda () (equal cur-buffer (current-buffer)))) + (if (buffer-modified-p) + (message "Cannot operate on unsaved buffer") + (setq lines (go-unused-imports-lines)) + (dolist (import lines) + (go--goto-line import) + (beginning-of-line) + (if arg + (comment-region (line-beginning-position) (line-end-position)) + (go--delete-whole-line))) + (message "Removed %d imports" (length lines))) + (if flymake-state (flymake-mode 1))))) + +(defun godef--find-file-line-column (specifier other-window) + "Given a file name in the format of `filename:line:column', +visit FILENAME and go to line LINE and column COLUMN." + (if (not (string-match "\\(.+\\):\\([0-9]+\\):\\([0-9]+\\)" specifier)) + ;; We've only been given a directory name + (funcall (if other-window #'find-file-other-window #'find-file) specifier) + (let ((filename (match-string 1 specifier)) + (line (string-to-number (match-string 2 specifier))) + (column (string-to-number (match-string 3 specifier)))) + (funcall (if other-window #'find-file-other-window #'find-file) filename) + (go--goto-line line) + (beginning-of-line) + (forward-char (1- column)) + (if (buffer-modified-p) + (message "Buffer is modified, file position might not have been correct"))))) + +(defun godef--call (point) + "Call godef, acquiring definition position and expression +description at POINT." + (if (not (buffer-file-name (go--coverage-origin-buffer))) + (error "Cannot use godef on a buffer without a file name") + (let ((outbuf (generate-new-buffer "*godef*")) + (coding-system-for-read 'utf-8) + (coding-system-for-write 'utf-8)) + (prog2 + (call-process-region (point-min) + (point-max) + godef-command + nil + outbuf + nil + "-i" + "-t" + "-f" + (file-truename (buffer-file-name (go--coverage-origin-buffer))) + "-o" + ;; Emacs point and byte positions are 1-indexed. + (number-to-string (1- (position-bytes point)))) + (with-current-buffer outbuf + (split-string (buffer-substring-no-properties (point-min) (point-max)) "\n")) + (kill-buffer outbuf))))) + +(defun godef--successful-p (output) + (not (or (string= "-" output) + (string= "godef: no identifier found" output) + (string= "godef: no object" output) + (go--string-prefix-p "godef: no declaration found for " output) + (go--string-prefix-p "error finding import path for " output)))) + +(defun godef--error (output) + (cond + ((godef--successful-p output) + nil) + ((string= "-" output) + "godef: expression is not defined anywhere") + (t + output))) + +(defun godef-describe (point) + "Describe the expression at POINT." + (interactive "d") + (condition-case nil + (let ((description (cdr (butlast (godef--call point) 1)))) + (if (not description) + (message "No description found for expression at point") + (message "%s" (mapconcat #'identity description "\n")))) + (file-error (message "Could not run godef binary")))) + +(defun godef-jump (point &optional other-window) + "Jump to the definition of the expression at POINT." + (interactive "d") + (condition-case nil + (let ((file (car (godef--call point)))) + (if (not (godef--successful-p file)) + (message "%s" (godef--error file)) + (push-mark) + ;; TODO: Integrate this facility with XRef. + (xref-push-marker-stack) + (godef--find-file-line-column file other-window))) + (file-error (message "Could not run godef binary")))) + +(defun godef-jump-other-window (point) + (interactive "d") + (godef-jump point t)) + +(defun go--goto-line (line) + (goto-char (point-min)) + (forward-line (1- line))) + +(defun go--line-column-to-point (line column) + (save-excursion + (go--goto-line line) + (forward-char (1- column)) + (point))) + +(cl-defstruct go--covered + start-line start-column end-line end-column covered count) + +(defun go--coverage-file () + "Return the coverage file to use, either by reading it from the +current coverage buffer or by prompting for it." + (if (boundp 'go--coverage-current-file-name) + go--coverage-current-file-name + (read-file-name "Coverage file: " nil nil t))) + +(defun go--coverage-origin-buffer () + "Return the buffer to base the coverage on." + (or (buffer-base-buffer) (current-buffer))) + +(defun go--coverage-face (count divisor) + "Return the intensity face for COUNT when using DIVISOR +to scale it to a range [0,10]. + +DIVISOR scales the absolute cover count to values from 0 to 10. +For DIVISOR = 0 the count will always translate to 8." + (let* ((norm (cond + ((= count 0) + -0.1) ;; Uncovered code, set to -0.1 so n becomes 0. + ((= divisor 0) + 0.8) ;; covermode=set, set to 0.8 so n becomes 8. + (t + (/ (log count) divisor)))) + (n (1+ (floor (* norm 9))))) ;; Convert normalized count [0,1] to intensity [0,10] + (concat "go-coverage-" (number-to-string n)))) + +(defun go--coverage-make-overlay (range divisor) + "Create a coverage overlay for a RANGE of covered/uncovered code. +Use DIVISOR to scale absolute counts to a [0,10] scale." + (let* ((count (go--covered-count range)) + (face (go--coverage-face count divisor)) + (ov (make-overlay (go--line-column-to-point (go--covered-start-line range) + (go--covered-start-column range)) + (go--line-column-to-point (go--covered-end-line range) + (go--covered-end-column range))))) + + (overlay-put ov 'face face) + (overlay-put ov 'help-echo (format "Count: %d" count)))) + +(defun go--coverage-clear-overlays () + "Remove existing overlays and put a single untracked overlay +over the entire buffer." + (remove-overlays) + (overlay-put (make-overlay (point-min) (point-max)) + 'face + 'go-coverage-untracked)) + +(defun go--coverage-parse-file (coverage-file file-name) + "Parse COVERAGE-FILE and extract coverage information and +divisor for FILE-NAME." + (let (ranges + (max-count 0)) + (with-temp-buffer + (insert-file-contents coverage-file) + (go--goto-line 2) ;; Skip over mode + (while (not (eobp)) + (let* ((parts (split-string (buffer-substring (point-at-bol) (point-at-eol)) ":")) + (file (car parts)) + (rest (split-string (nth 1 parts) "[., ]"))) + + (cl-destructuring-bind + (start-line start-column end-line end-column num count) + (mapcar #'string-to-number rest) + + (when (string= (file-name-nondirectory file) file-name) + (if (> count max-count) + (setq max-count count)) + (push (make-go--covered :start-line start-line + :start-column start-column + :end-line end-line + :end-column end-column + :covered (/= count 0) + :count count) + ranges))) + + (forward-line))) + + (list ranges (if (> max-count 0) (log max-count) 0))))) + +(defun go-coverage (&optional coverage-file) + "Open a clone of the current buffer and overlay it with +coverage information gathered via go test -coverprofile=COVERAGE-FILE. + +If COVERAGE-FILE is nil, it will either be inferred from the +current buffer if it's already a coverage buffer, or be prompted +for." + (interactive) + (let* ((cur-buffer (current-buffer)) + (origin-buffer (go--coverage-origin-buffer)) + (gocov-buffer-name (concat (buffer-name origin-buffer) "<gocov>")) + (coverage-file (or coverage-file (go--coverage-file))) + (ranges-and-divisor (go--coverage-parse-file + coverage-file + (file-name-nondirectory (buffer-file-name origin-buffer)))) + (cov-mtime (nth 5 (file-attributes coverage-file))) + (cur-mtime (nth 5 (file-attributes (buffer-file-name origin-buffer))))) + + (if (< (float-time cov-mtime) (float-time cur-mtime)) + (message "Coverage file is older than the source file.")) + + (with-current-buffer (or (get-buffer gocov-buffer-name) + (make-indirect-buffer origin-buffer gocov-buffer-name t)) + (set (make-local-variable 'go--coverage-current-file-name) coverage-file) + + (save-excursion + (go--coverage-clear-overlays) + (dolist (range (car ranges-and-divisor)) + (go--coverage-make-overlay range (cadr ranges-and-divisor)))) + + (if (not (eq cur-buffer (current-buffer))) + (display-buffer (current-buffer) `(,go-coverage-display-buffer-func)))))) + +(defun go-goto-function (&optional arg) + "Go to the function definition (named or anonymous) surrounding point. + +If we are on a docstring, follow the docstring down. +If no function is found, assume that we are at the top of a file +and search forward instead. + +If point is looking at the func keyword of an anonymous function, +go to the surrounding function. + +If ARG is non-nil, anonymous functions are ignored." + (interactive "P") + (let ((p (point))) + (cond + ((save-excursion + (beginning-of-line) + (looking-at "^//")) + ;; In case we are looking at the docstring, move on forward until we are + ;; not anymore + (beginning-of-line) + (while (looking-at "^//") + (forward-line 1)) + ;; If we are still not looking at a function, retry by calling self again. + (when (not (looking-at "\\<func\\>")) + (go-goto-function arg))) + + ;; If we're already looking at an anonymous func, look for the + ;; surrounding function. + ((and (looking-at "\\<func\\>") + (not (looking-at "^func\\>"))) + (re-search-backward "\\<func\\>" nil t)) + + ((not (looking-at "\\<func\\>")) + ;; If point is on the "func" keyword, step back a word and retry + (if (string= (symbol-name (symbol-at-point)) "func") + (backward-word) + ;; If we are not looking at the beginning of a function line, do a regexp + ;; search backwards + (re-search-backward "\\<func\\>" nil t)) + + ;; If nothing is found, assume that we are at the top of the file and + ;; should search forward instead. + (when (not (looking-at "\\<func\\>")) + (re-search-forward "\\<func\\>" nil t) + (go--forward-word -1)) + + ;; If we have landed at an anonymous function, it is possible that we + ;; were not inside it but below it. If we were not inside it, we should + ;; go to the containing function. + (while (and (not (go--in-function-p p)) + (not (looking-at "^func\\>"))) + (go-goto-function arg))))) + + (cond + ((go-in-comment-p) + ;; If we are still in a comment, redo the call so that we get out of it. + (go-goto-function arg)) + + ((and (looking-at "\\<func(") arg) + ;; If we are looking at an anonymous function and a prefix argument has + ;; been supplied, redo the call so that we skip the anonymous function. + (go-goto-function arg)))) + +(defun go--goto-opening-curly-brace () + ;; Find the { that starts the function, i.e., the next { that isn't + ;; preceded by struct or interface, or a comment or struct tag. BUG: + ;; breaks if there's a comment between the struct/interface keyword and + ;; bracket, like this: + ;; + ;; struct /* why? */ { + (go--goto-return-values) + (while (progn + (skip-chars-forward "^{") + (forward-char) + (or (go-in-string-or-comment-p) + (looking-back "\\(struct\\|interface\\)\\s-*{" + (line-beginning-position))))) + (backward-char)) + +(defun go--in-function-p (compare-point) + "Return t if COMPARE-POINT is inside the function immediately surrounding point." + (save-excursion + (when (not (looking-at "\\<func\\>")) + (go-goto-function)) + (let ((start (point))) + (go--goto-opening-curly-brace) + + (unless (looking-at "{") + (error "Expected to be looking at opening curly brace")) + (forward-list 1) + (and (>= compare-point start) + (<= compare-point (point)))))) + +(defun go-goto-function-name (&optional arg) + "Go to the name of the current function. + +If the function is a test, place point after 'Test'. +If the function is anonymous, place point on the 'func' keyword. + +If ARG is non-nil, anonymous functions are skipped." + (interactive "P") + (when (not (looking-at "\\<func\\>")) + (go-goto-function arg)) + ;; If we are looking at func( we are on an anonymous function and + ;; nothing else should be done. + (when (not (looking-at "\\<func(")) + (let ((words 1) + (chars 1)) + (when (looking-at "\\<func (") + (setq words 3 + chars 2)) + (go--forward-word words) + (forward-char chars) + (when (looking-at "Test") + (forward-char 4))))) + +(defun go-goto-arguments (&optional arg) + "Go to the arguments of the current function. + +If ARG is non-nil, anonymous functions are skipped." + (interactive "P") + (go-goto-function-name arg) + (go--forward-word 1) + (forward-char 1)) + +(defun go--goto-return-values (&optional arg) + "Go to the declaration of return values for the current function." + (go-goto-arguments arg) + (backward-char) + (forward-list) + (forward-char)) + +(defun go-goto-return-values (&optional arg) + "Go to the return value declaration of the current function. + +If there are multiple ones contained in a parenthesis, enter the parenthesis. +If there is none, make space for one to be added. + +If ARG is non-nil, anonymous functions are skipped." + (interactive "P") + (go--goto-return-values arg) + + ;; Opening parenthesis, enter it + (when (looking-at "(") + (forward-char 1)) + + ;; No return arguments, add space for adding + (when (looking-at "{") + (insert " ") + (backward-char 1))) + +(defun go-goto-method-receiver (&optional arg) + "Go to the receiver of the current method. + +If there is none, add parenthesis to add one. + +Anonymous functions cannot have method receivers, so when this is called +interactively anonymous functions will be skipped. If called programmatically, +an error is raised unless ARG is non-nil." + (interactive "P") + + (when (and (not (called-interactively-p 'interactive)) + (not arg) + (go--in-anonymous-funcion-p)) + (error "Anonymous functions cannot have method receivers")) + + (go-goto-function t) ; Always skip anonymous functions + (forward-char 5) + (when (not (looking-at "(")) + (save-excursion + (insert "() "))) + (forward-char 1)) + +(defun go-goto-docstring (&optional arg) + "Go to the top of the docstring of the current function. + +If there is none, add one beginning with the name of the current function. + +Anonymous functions do not have docstrings, so when this is called +interactively anonymous functions will be skipped. If called programmatically, +an error is raised unless ARG is non-nil." + (interactive "P") + + (when (and (not (called-interactively-p 'interactive)) + (not arg) + (go--in-anonymous-funcion-p)) + (error "Anonymous functions do not have docstrings")) + + (go-goto-function t) + (forward-line -1) + (beginning-of-line) + + (while (looking-at "^//") + (forward-line -1)) + (forward-line 1) + (beginning-of-line) + + (cond + ;; If we are looking at an empty comment, add a single space in front of it. + ((looking-at "^//$") + (forward-char 2) + (insert (format " %s " (go--function-name t)))) + ;; If we are not looking at the function signature, we are looking at a docstring. + ;; Move to the beginning of the first word of it. + ((not (looking-at "^func")) + (forward-char 3)) + ;; If we are still at the function signature, we should add a new docstring. + (t + (forward-line -1) + (newline) + (insert "// ") + (insert (go--function-name t))))) + +(defun go--function-name (&optional arg) + "Return the name of the surrounding function. + +If ARG is non-nil, anonymous functions will be ignored and the +name returned will be that of the top-level function. If ARG is +nil and the surrounding function is anonymous, nil will be +returned." + (when (or (not (go--in-anonymous-funcion-p)) + arg) + (save-excursion + (go-goto-function-name t) + (symbol-name (symbol-at-point))))) + +(defun go--in-anonymous-funcion-p () + "Return t if point is inside an anonymous function, nil otherwise." + (save-excursion + (go-goto-function) + (looking-at "\\<func("))) + +(defun go-guess-gopath (&optional buffer) + "Determine a suitable GOPATH for BUFFER, or the current buffer if BUFFER is nil." + (with-current-buffer (or buffer (current-buffer)) + (let ((gopath (cl-some (lambda (el) (funcall el)) + go-guess-gopath-functions))) + (if gopath + (mapconcat + (lambda (el) (file-truename el)) + gopath + path-separator))))) + +(defun go-plain-gopath () + "Detect a normal GOPATH, by looking for the first `src' +directory up the directory tree." + (let ((d (locate-dominating-file buffer-file-name "src"))) + (if d + (list d)))) + +(defun go-set-project (&optional buffer) + "Set GOPATH based on `go-guess-gopath' for BUFFER. +Set it to the current buffer if BUFFER is nil. + +If go-guess-gopath returns nil, that is if it couldn't determine +a valid value for GOPATH, GOPATH will be set to the initial value +of when Emacs was started. + +This function can for example be used as a +projectile-switch-project-hook, or simply be called manually when +switching projects." + (interactive) + (let ((gopath (or (go-guess-gopath buffer) + (go-original-gopath)))) + (setenv "GOPATH" gopath) + (message "Set GOPATH to %s" gopath))) + +(defun go-reset-gopath () + "Reset GOPATH to the value it had when Emacs started." + (interactive) + (let ((gopath (go-original-gopath))) + (setenv "GOPATH" gopath) + (message "Set GOPATH to %s" gopath))) + +(defun go-original-gopath () + "Return the original value of GOPATH from when Emacs was started." + (let ((process-environment initial-environment)) (getenv "GOPATH"))) + +(defun go--insert-modified-files () + "Insert the contents of each modified Go buffer into the +current buffer in the format specified by guru's -modified flag." + (mapc #'(lambda (b) + (and (buffer-modified-p b) + (buffer-file-name b) + (string= (file-name-extension (buffer-file-name b)) "go") + (go--insert-modified-file (buffer-file-name b) b))) + (buffer-list))) + +(defun go--insert-modified-file (name buffer) + (insert (format "%s\n%d\n" name (go--buffer-size-bytes buffer))) + (insert-buffer-substring buffer)) + +(defun go--buffer-size-bytes (&optional buffer) + (message "buffer; %s" buffer) + "Return the number of bytes in the current buffer. +If BUFFER, return the number of characters in that buffer instead." + (with-current-buffer (or buffer (current-buffer)) + (1- (position-bytes (point-max))))) + +(defvar go-dot-mod-mode-map + (let ((map (make-sparse-keymap))) + map) + "Keymap for `go-dot-mod-mode'.") + +(defvar go-dot-mod-mode-syntax-table + (let ((st (make-syntax-table))) + ;; handle '//' comment syntax + (modify-syntax-entry ?/ ". 124b" st) + (modify-syntax-entry ?\n "> b" st) + st) + "Syntax table for `go-dot-mod-mode'.") + +(defconst go-dot-mod-mode-keywords + '("module" "go" "require" "replace" "exclude") + "All keywords for go.mod files. Used for font locking.") + +(defgroup go-dot-mod nil + "Options specific to `go-dot-mod-mode`." + :group 'go) + +(defface go-dot-mod-module-name '((t :inherit default)) + "Face for module name in \"require\" list." + :group 'go-dot-mod) + +(defface go-dot-mod-module-version '((t :inherit default)) + "Face for module version in \"require\" list." + :group 'go-dot-mod) + +(defface go-dot-mod-module-semver '((t :inherit go-dot-mod-module-version)) + "Face for module semver in \"require\" list." + :group 'go-dot-mod) + + +(defvar go-dot-mod-font-lock-keywords + `( + (,(concat "^\\s-*\\(" (regexp-opt go-dot-mod-mode-keywords t) "\\)\\s-") 1 font-lock-keyword-face) + ("\\(?:^\\|=>\\)\\s-*\\([^[:space:]\n()]+\\)\\(?:\\s-+\\(v[0-9]+\\.[0-9]+\\.[0-9]+\\)\\([^[:space:]\n]*\\)\\)?" (1 'go-dot-mod-module-name) (2 'go-dot-mod-module-semver nil t) (3 'go-dot-mod-module-version nil t))) + "Keyword highlighting specification for `go-dot-mod-mode'.") + +;;;###autoload +(define-derived-mode go-dot-mod-mode fundamental-mode "Go Mod" + "A major mode for editing go.mod files." + :syntax-table go-dot-mod-mode-syntax-table + (set (make-local-variable 'comment-start) "// ") + (set (make-local-variable 'comment-end) "") + (set (make-local-variable 'comment-use-syntax) t) + (set (make-local-variable 'comment-start-skip) "\\(//+\\)\\s *") + + (set (make-local-variable 'font-lock-defaults) + '(go-dot-mod-font-lock-keywords)) + (set (make-local-variable 'indent-line-function) 'go-mode-indent-line) + + ;; Go style + (setq indent-tabs-mode t) + + ;; we borrow the go-mode-indent function so we need this buffer cache + (set (make-local-variable 'go-dangling-cache) (make-hash-table :test 'eql)) + (add-hook 'before-change-functions #'go--reset-dangling-cache-before-change t t)) + +;;;###autoload +(add-to-list 'auto-mode-alist '("go\\.mod\\'" . go-dot-mod-mode)) + +;; The following functions were copied (and modified) from rust-mode.el. +;; +;; Copyright (c) 2015 The Rust Project Developers +;; +;; Permission is hereby granted, free of charge, to any +;; person obtaining a copy of this software and associated +;; documentation files (the "Software"), to deal in the +;; Software without restriction, including without +;; limitation the rights to use, copy, modify, merge, +;; publish, distribute, sublicense, and/or sell copies of +;; the Software, and to permit persons to whom the Software +;; is furnished to do so, subject to the following +;; conditions: +;; +;; The above copyright notice and this permission notice +;; shall be included in all copies or substantial portions +;; of the Software. + +(defun go--fill-prefix-for-comment-start (line-start) + "Determine what to use for `fill-prefix' based on the text at LINE-START." + (let ((result + ;; Replace /* with same number of spaces + (replace-regexp-in-string + "\\(?:/\\*+?\\)[!*]?" + (lambda (s) + (let ((offset (if (eq t + (compare-strings "/*" nil nil + s + (- (length s) 2) + (length s))) + 1 2))) + (make-string (1+ (- (length s) offset)) ?\x20))) + line-start))) + ;; Make sure we've got at least one space at the end + (if (not (= (aref result (- (length result) 1)) ?\x20)) + (setq result (concat result " "))) + result)) + +(defun go--in-comment-paragraph (body) + ;; We might move the point to fill the next comment, but we don't want it + ;; seeming to jump around on the user + (save-excursion + ;; If we're outside of a comment, with only whitespace and then a comment + ;; in front, jump to the comment and prepare to fill it. + (when (not (go-in-comment-p)) + (beginning-of-line) + (when (looking-at (concat "[[:space:]\n]*" comment-start-skip)) + (goto-char (match-end 0)))) + + ;; If we're at the beginning of a comment paragraph with nothing but + ;; whitespace til the next line, jump to the next line so that we use the + ;; existing prefix to figure out what the new prefix should be, rather than + ;; inferring it from the comment start. + (while (save-excursion + (end-of-line) + (and (go-in-comment-p) + (save-excursion + (beginning-of-line) + (looking-at paragraph-start)) + (looking-at "[[:space:]]*$") + (nth 4 (syntax-ppss (line-beginning-position 2))))) + (goto-char (line-beginning-position 2))) + + ;; If we're on the last line of a multiline-style comment that started + ;; above, back up one line so we don't mistake the * of the */ that ends + ;; the comment for a prefix. + (when (save-excursion + (and (nth 4 (syntax-ppss (line-beginning-position 1))) + (looking-at "[[:space:]]*\\*/"))) + (goto-char (line-end-position 0))) + (funcall body))) + +(defun go--with-comment-fill-prefix (body) + (let* + ((line-string (buffer-substring-no-properties + (line-beginning-position) (line-end-position))) + (line-comment-start + (when (go-in-comment-p) + (cond + ;; If we're inside the comment and see a * prefix, use it + ((string-match "^\\([[:space:]]*\\*+[[:space:]]*\\)" + line-string) + (match-string 1 line-string)) + ;; If we're at the start of a comment, figure out what prefix + ;; to use for the subsequent lines after it + ((string-match (concat "[[:space:]]*" comment-start-skip) line-string) + (go--fill-prefix-for-comment-start + (match-string 0 line-string)))))) + (fill-prefix + (or line-comment-start + fill-prefix))) + (funcall body))) + +(defun go--find-fill-prefix () + (go--in-comment-paragraph + (lambda () + (go--with-comment-fill-prefix + (lambda () + fill-prefix))))) + +(defun go-fill-paragraph (&rest args) + "Special wrapping for `fill-paragraph'. +This handles multi-line comments with a * prefix on each line." + (go--in-comment-paragraph + (lambda () + (go--with-comment-fill-prefix + (lambda () + (let + ((fill-paragraph-function + (if (not (eq fill-paragraph-function 'go-fill-paragraph)) + fill-paragraph-function)) + (fill-paragraph-handle-comment t)) + (apply 'fill-paragraph args) + t)))))) + +(defun go--do-auto-fill (&rest args) + "Special wrapping for `do-auto-fill'. +This handles multi-line comments with a * prefix on each line." + (go--with-comment-fill-prefix + (lambda () + (apply 'do-auto-fill args) + t))) + +(defun go--fill-forward-paragraph (arg) + ;; This is to work around some funny behavior when a paragraph separator is + ;; at the very top of the file and there is a fill prefix. + (let ((fill-prefix nil)) (forward-paragraph arg))) + +(defun go--comment-indent-new-line (&optional arg) + (go--with-comment-fill-prefix + (lambda () (comment-indent-new-line arg)))) + + + +(provide 'go-mode) + +;;; go-mode.el ends here diff --git a/elpa/go-mode-1.6.0/go-mode.elc b/elpa/go-mode-1.6.0/go-mode.elc Binary files differ. diff --git a/elpa/go-mode-1.6.0/go-rename.el b/elpa/go-mode-1.6.0/go-rename.el @@ -0,0 +1,110 @@ +;;; go-rename.el --- Integration of the 'gorename' tool into Emacs. + +;; Copyright 2014 The Go Authors. All rights reserved. +;; Use of this source code is governed by a BSD-style +;; license that can be found in the LICENSE file. + +;; Version: 0.1 +;; Package-Requires: ((go-mode "1.3.1")) +;; Keywords: tools + +;;; Commentary: + +;; To install: + +;; % go get golang.org/x/tools/cmd/gorename +;; % go build golang.org/x/tools/cmd/gorename +;; % mv gorename $HOME/bin/ # or elsewhere on $PATH + +;; The go-rename-command variable can be customized to specify an +;; alternative location for the installed command. + +;;; Code: + +(require 'cl-lib) +(require 'compile) +(require 'go-mode) +(require 'thingatpt) + +(defgroup go-rename nil + "Options specific to the Go rename." + :group 'go) + +(defcustom go-rename-command "gorename" + "The `gorename' command; by the default, $PATH is searched." + :type 'string + :group 'go-rename) + +;;;###autoload +(defun go-rename (new-name &optional force) + "Rename the entity denoted by the identifier at point, using +the `gorename' tool. With FORCE, call `gorename' with the +`-force' flag." + (interactive (list + (if (and buffer-file-name (not (buffer-modified-p))) + (read-string "New name: " (thing-at-point 'symbol))) + current-prefix-arg)) + (if (not buffer-file-name) + (error "Cannot use go-rename on a buffer without a file name")) + ;; It's not sufficient to save the current buffer if modified, + ;; since if gofmt-before-save is on the before-save-hook, + ;; saving will disturb the selected region. + (if (buffer-modified-p) + (error "Please save the current buffer before invoking go-rename")) + ;; Prompt-save all other modified Go buffers, since they might get written. + (save-some-buffers nil #'(lambda () + (and (buffer-file-name) + (string= (file-name-extension (buffer-file-name)) ".go")))) + (let* ((posflag (format "-offset=%s:#%d" + buffer-file-name + (1- (position-bytes (point))))) + (env-vars (go-root-and-paths)) + (goroot-env (concat "GOROOT=" (car env-vars))) + (gopath-env (concat "GOPATH=" (mapconcat #'identity (cdr env-vars) ":"))) + success) + (with-current-buffer (get-buffer-create "*go-rename*") + (setq buffer-read-only nil) + (erase-buffer) + (let ((args (append (list go-rename-command nil t nil posflag "-to" new-name) (if force '("-force"))))) + ;; Log the command to *Messages*, for debugging. + (message "Command: %s:" args) + (message "Running gorename...") + ;; Use dynamic binding to modify/restore the environment + (setq success (zerop (let ((process-environment (cl-list* goroot-env gopath-env process-environment))) + (apply #'call-process args)))) + (insert "\n") + (compilation-mode) + (setq compilation-error-screen-columns nil) + + ;; On success, print the one-line result in the message bar, + ;; and hide the *go-rename* buffer. + (if success + (progn + (message "%s" (go--buffer-string-no-trailing-space)) + (gofmt--kill-error-buffer (current-buffer))) + ;; failure + (let ((w (display-buffer (current-buffer)))) + (message "gorename exited") + (set-window-point w (point-min))))))) + + ;; Reload the modified files, saving line/col. + ;; (Don't restore the point since the text has changed.) + ;; + ;; TODO(adonovan): should we also do this for all other files + ;; that were updated (the tool can print them)? + (let ((line (line-number-at-pos)) + (col (current-column))) + (revert-buffer t t t) ; safe, because we just saved it + (goto-char (point-min)) + (forward-line (1- line)) + (forward-char col))) + + +(defun go--buffer-string-no-trailing-space () + (replace-regexp-in-string "[\t\n ]*\\'" + "" + (buffer-substring (point-min) (point-max)))) + +(provide 'go-rename) + +;;; go-rename.el ends here diff --git a/elpa/go-mode-1.6.0/go-rename.elc b/elpa/go-mode-1.6.0/go-rename.elc Binary files differ. diff --git a/elpa/go-mode-1.6.0/test/go-comment-test.el b/elpa/go-mode-1.6.0/test/go-comment-test.el @@ -0,0 +1,52 @@ +;;; go-comment-test.el + +;; Copyright 2020 The go-mode Authors. All rights reserved. Use of +;; this source code is governed by a BSD-style license that can be +;; found in the LICENSE file. + +(require 'ert) +(require 'go-mode) +(require 'cl-lib) + +(ert-deftest go--comment-region () + (go--should-comment + " +<var foo int +>" + " +// var foo int +") + + (go--should-comment + " +<// var foo int +>" + " +var foo int +") + + (go--should-comment + "var <foo> int" + "var /* foo */ int") + + (go--should-comment + "var </* foo */> int" + "var foo int")) + +(defun go--should-comment (got expected) + "Run `comment-dwim' against GOT and make sure it matches EXPECTED. + +<> in GOT represents point. If they aren't next to each other, then it +represents point and mark to test the region based comment-region." + (with-temp-buffer + (go-mode) + (transient-mark-mode) + (insert got) + (goto-char (point-min)) + (let ((beg (progn (search-forward "<") (delete-char -1) (point))) + (end (progn (search-forward ">") (delete-char -1) (point)))) + (when (/= beg end) + (set-mark beg)) + (goto-char end) + (call-interactively 'comment-dwim) + (should (string= (buffer-string) expected))))) diff --git a/elpa/go-mode-1.6.0/test/go-comment-test.elc b/elpa/go-mode-1.6.0/test/go-comment-test.elc Binary files differ. diff --git a/elpa/go-mode-1.6.0/test/go-fill-paragraph-test.el b/elpa/go-mode-1.6.0/test/go-fill-paragraph-test.el @@ -0,0 +1,224 @@ +;;; go-fill-paragraph-test.el + +;; Copyright 2019 The go-mode Authors. All rights reserved. +;; Use of this source code is governed by a BSD-style +;; license that can be found in the LICENSE file. + +(require 'ert) +(require 'go-mode) + +(defun go--should-fill (got expected) + "Run `fill-paragraph' against GOT and make sure it matches EXPECTED. + +<> in GOT represents point. If they aren't next to each other, then it +represents point and mark to test the region based fill-paragraph." + (with-temp-buffer + (go-mode) + (transient-mark-mode) + (insert got) + (goto-char (point-min)) + (let ((beg (progn (search-forward "<") (delete-char -1) (point))) + (end (progn (search-forward ">") (delete-char -1) (point)))) + (when (/= beg end) + (set-mark beg)) + (goto-char end) + (call-interactively 'fill-paragraph) + (should (string= (buffer-string) expected))))) + +(ert-deftest go--fill-paragraph-single () + (go--should-fill + " +func main() { +<> // Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +}" + + " +func main() { + // Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do + // eiusmod tempor incididunt ut labore et dolore magna aliqua. +}" + )) + +(ert-deftest go--fill-paragraph-single-region () + (go--should-fill + " +func main() { +< // Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +>}" + + " +func main() { + // Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do + // eiusmod tempor incididunt ut labore et dolore magna aliqua. +}" + )) + +(ert-deftest go--fill-paragraph-block () + (go--should-fill + " +func main() { +<> /* Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. */ +}" + + " +func main() { + /* Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua. */ +}" + )) + +(ert-deftest go--fill-paragraph-block-region () + (go--should-fill + " +func main() { +< /* Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. */ +>}" + + " +func main() { + /* Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua. */ +}" + )) + +(ert-deftest go--fill-paragraph-block-empty-first () + (go--should-fill + " +func main() { +<> /* + Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + */ +}" + + " +func main() { + /* + Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed + do eiusmod tempor incididunt ut labore et dolore magna aliqua. + */ +}" + )) + +(ert-deftest go--fill-paragraph-block-empty-first-region () + (go--should-fill + " +func main() { +< /* + Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + */ +>}" + + " +func main() { + /* + Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed + do eiusmod tempor incididunt ut labore et dolore magna aliqua. + */ +}" + )) + + +(ert-deftest go--fill-paragraph-block-offset () + (go--should-fill + " +func main() { +<> /* + Lorem ipsum dolor sit amet, consectetur adipisicing elit, + sed do eiusmod tempor incididunt ut labore + et dolore magna aliqua. + */ +}" + + " +func main() { + /* + Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed + do eiusmod tempor incididunt ut labore et dolore magna + aliqua. + */ +}" + )) + +(ert-deftest go--fill-paragraph-block-region () + (go--should-fill + " +func main() { +< /* Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. */ +>}" + + " +func main() { + /* Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua. */ +}" + )) + +(ert-deftest go--fill-paragraph-single-artful () + (go--should-fill + " +func main() { + ///////////////////// +<> // Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + ///////////////////// +}" + + " +func main() { + ///////////////////// + // Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do + // eiusmod tempor incididunt ut labore et dolore magna aliqua. + ///////////////////// +}" + )) + +(ert-deftest go--fill-paragraph-single-artful-region () + (go--should-fill + " +func main() { +< ///////////////////// + // Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + ///////////////////// +>}" + + " +func main() { + ///////////////////// + // Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do + // eiusmod tempor incididunt ut labore et dolore magna aliqua. + ///////////////////// +}" + )) + +(ert-deftest go--fill-paragraph-code-region () + (go--should-fill + " +func main() { +< if something() { + somethingElse() + } +>}" + + ;; important thing is we don't get stuck in an infinite loop + " +func main() { + if something() { somethingElse() } +}" + )) + + +(ert-deftest go--fill-paragraph-bob () + (go--should-fill + "<>// Lorem +// ipsum." + "// Lorem ipsum." + ) + + (go--should-fill + "<>/* + Lorem + ipsum. +*/" + "/* + Lorem ipsum. +*/" + )) diff --git a/elpa/go-mode-1.6.0/test/go-fill-paragraph-test.elc b/elpa/go-mode-1.6.0/test/go-fill-paragraph-test.elc Binary files differ. diff --git a/elpa/go-mode-1.6.0/test/go-font-lock-test.el b/elpa/go-mode-1.6.0/test/go-font-lock-test.el @@ -0,0 +1,290 @@ +;;; go-font-lock-test.el + +;; Copyright 2019 The go-mode Authors. All rights reserved. Use of +;; this source code is governed by a BSD-style license that can be +;; found in the LICENSE file. + +(require 'ert) +(require 'go-mode) +(require 'cl-lib) + +(ert-deftest go--fontify-signature () + (go--should-fontify "KfuncK FfooF() { }") + (go--should-fontify "KfuncK FfooF(TaT) { }") + (go--should-fontify "KfuncK FfooF(TaT, TbT) { }") + (go--should-fontify "KfuncK FfooF(TaT) TaT { }") + (go--should-fontify "KfuncK FfooF(VaV TbT) (VaV TbT) { }") + (go--should-fontify "KfuncK FfooF(VaV, VbV TcT) (VaV TbT, VcV TdT) { }") + + (go--should-fontify "KfuncK (TbT) FfooF(VaV, VbV TcT) TdT { }") + (go--should-fontify "KfuncK (VaV TbT) FfooF(VaV TbT) (TdT) { }") + + (go--should-fontify "VfooV := KfuncK(VaV TbT) TcT { }") + + (go--should-fontify "KfuncK(...TintT) { }") + (go--should-fontify "KfuncK(VaV ...TintT) { }") + (go--should-fontify "KfuncK(VaV ...KinterfaceK{}) { }") + + (go--should-fontify "KfuncK(KinterfaceK { FfooF() }, TstringT) KinterfaceK{}") + + (go--should-fontify "KfuncK(VaV TbT, VcV KfuncK(VdV *TeT) TdT) TfT") + (go--should-fontify "KfuncK(VaV KfuncK() TbT, VcV TdT)") + + (go--should-fontify " +KfuncK FfooF( + VaV TcatT, VbV KinterfaceK { FbarkF() }, + VcV TbananaT, +) ( + VwhyV TdothisT, + VjustV TstopT, +) { }") + + (go--should-fontify " +D// DQ +QD// DQ( +QKfuncK (VfV TintT) {} +")) + +(ert-deftest go--fontify-struct () + (go--should-fontify "KstructK { i TintT }") + (go--should-fontify "KstructK { a, b TintT }") + + (go--should-fontify " +KstructK { + a TboolT + c KstructK { f *Tfoo.ZebraT } +}")) + +(ert-deftest go--fontify-interface () + (go--should-fontify " +KinterfaceK { + FfooF(VaV, VbV TcT) *TstringT +}") + + (go--should-fontify " +KinterfaceK { + FfooF(KinterfaceK { FaF() TintT }) (VcV TdT) +}") + + (go--should-fontify " +KmapK[TstringT]KinterfaceK{}{ + S`foo`S: foo.FbarF(baz), +}")) + + +(ert-deftest go--fontify-type-switch () + (go--should-fontify " +KswitchK foo.(KtypeK) { +KcaseK TstringT, *Tfoo.ZebraT, [2]TbyteT: +KcaseK CnilC: +KcaseK TfooT, TbarT, D// DQhi +Q + D// DQthere +Q TbazT, TquxT: +KdefaultK: +}") + + (go--should-fontify " +KswitchK foo.(KtypeK) { +KcaseK KinterfaceK { FfooF(TintT, TstringT) }, KstructK { i, j TintT }, TstringT: +}") + + (go--should-fontify " +KswitchK 123 { +KcaseK string: +}")) + +(ert-deftest go--fontify-composite-literal () + (go--should-fontify "TfooT{") + (go--should-fontify "[]TfooT{") + (go--should-fontify "Tfoo.ZarT{") + (go--should-fontify "[]Tfoo.ZarT{") + + (go--should-fontify "TfooT{CbarC:baz, CquxC: 123}") + + (go--should-fontify "TfooT{ +CbarC: baz, +}") + + (go--should-fontify "[]TfooT{{ +CbarC: baz, +}, { +CbarC: baz, +}}") + + (go--should-fontify "TsomeMapT{ +foo.Zar: baz, +a + b: 3, +a-b: 4, +}")) + +(ert-deftest go--fontify-slices-arrays-maps () + (go--should-fontify "[]TfooT") + (go--should-fontify "[]Tfoo.ZarT") + (go--should-fontify "[]*Tfoo.ZarT") + + (go--should-fontify "[123]TfooT") + (go--should-fontify "[...]TfooT") + (go--should-fontify "[foo.Zar]TfooT") + (go--should-fontify "D/*DQhi*/Q[1]*TfooT") + + (go--should-fontify "KmapK[*Tfoo.ZarT]*Tbar.ZarT") + (go--should-fontify "[]KmapK[TfooT]TbarT") + (go--should-fontify "KmapK[[1][2][three]*Tfoo.ZarT][four][]*Tbar.ZarT") + (go--should-fontify "KmapK[TstringT]KmapK[TstringT]Tfloat64T") + (go--should-fontify "KmapK[[2][c]*TintT]TboolT")) + +(ert-deftest go--fontify-negation () + ;; Fontify unary "!". + (go--should-fontify "N!Nfoo") + + ;; Alternate fontification with multiple "!". + (go--should-fontify "N!N!foo") + (go--should-fontify "N!N!N!Nfoo") + + ;; Don't fontify "!=" operator. + (go--should-fontify "foo != bar")) + + +(ert-deftest go--fontify-type-decl () + (go--should-fontify "KtypeK TfooT TbarT") + (go--should-fontify "KtypeK TfooT Tbar.ZarT") + (go--should-fontify "KtypeK TfooT KstructK { }") + (go--should-fontify "KtypeK TfooT = Tbar.ZarT") + (go--should-fontify "KtypeK TfooT = KmapK[TstringT]TstringT") + + (go--should-fontify " +KtypeK ( + TfooT TbarT + TfooT KstructK {} + TfooT = *Tbar.ZarT +)")) + +(ert-deftest go--fontify-var-decl () + (go--should-fontify "KvarK VfooV = bar") + (go--should-fontify "KvarK VfooV, VbarV = bar, baz") + (go--should-fontify "KvarK VfooV TbarT D// DQcoolQ") + (go--should-fontify "KvarK VfooV TbarT = baz") + (go--should-fontify "KvarK VfooV KstructK { i TintT } = baz") + (go--should-fontify "KvarK VfooV []*Tfoo.ZarT D// DQcoolQ") + + (go--should-fontify " +KvarK ( + VfooV TbarT + VfooV KfuncK(ViV TintT) + VfooV = bar + VfooV TbarT = baz + VfooV, VbarV = baz, qux + VfooV, VbarV TbazT = qux, zorb +)")) + +(ert-deftest go--fontify-const-decl () + (go--should-fontify "KconstK CfooC, CbarC = 123, 456 D// D") + (go--should-fontify "KconstK CfooC, CbarC TbazT = 123, 456") + (go--should-fontify " +KconstK ( + CaC = 1 + CaC TintT = 1 + CaC, CbC TintT = 1, 2 +)")) + +(ert-deftest go--fontify-labels () + (go--should-fontify " +CfooC: +KforK { + KcontinueK CfooC + KbreakK CfooC + KgotoK CfooC +} +")) + +(ert-deftest go--fontify-assign () + (go--should-fontify "VfooV := bar") + (go--should-fontify "foo = bar D// DQ:=Q") + (go--should-fontify "VfooV, VbarV := baz, qux") + (go--should-fontify "foo, bar = baz, qux") + (go--should-fontify "KfuncK FfooF(ViV TintT) { VbarV := baz }")) + +(ert-deftest go--fontify-index-multiply () + (go--should-fontify "foo[1]*10 + 1") + (go--should-fontify "foo[1]*foo[2] + 1")) + +(ert-deftest go--fontify-go-dot-mod () + (go--should-fontify " +KmoduleK foo + +KgoK 1.13 + +KrequireK ( + Nexample.com/require/go/bananaN Sv12.34.56SV-1234-456abcV D// DQindirect +Q Nnoslash.devN Sv1.2.3S +) + +KreplaceK ( + Nfoo.example.com/barN Sv1.2.3S => Nfoo.example.com/barN Sv1.2.3S + Nexample.com/foo/barN => Nexample.com/baz/barN Sv0.0.0SV-20201112005413-933910cbaea0V +) +" 'go-dot-mod-mode)) + +(defun go--should-match-face (want-face) + (let ((got-face (get-text-property (point) 'face))) + (if (not (eq got-face want-face)) + (progn + (message "char '%s' (%s): wanted %s, got %s" (char-to-string (char-after)) (point) want-face got-face) + nil) + t))) + +(defun go--should-fontify (contents &optional mode) + "Verify fontification. + +CONTENTS is a template that uses single capital letters to +represent expected font lock face names. For example: + +BmakeB([]TintT, 0) + +expects \"make\" to be a (B)uiltin and \"int\" to be a (T)type." + (with-temp-buffer + (setq mode (or mode 'go-mode)) + (funcall mode) + (insert contents) + (goto-char (point-min)) + + ;; First pass through buffer looks for the face tags. We delete + ;; the tags and record the expected face ranges in `faces'. + (let ((case-fold-search nil) faces start start-pos) + (while (re-search-forward "[TBKCFSNVDQ]" nil t) + (let ((found-char (char-before))) + (backward-delete-char 1) + (if start + (progn + (should (= found-char start)) + (let ((face (cl-case found-char + (?T 'font-lock-type-face) + (?B 'font-lock-builtin-face) + (?K 'font-lock-keyword-face) + (?C 'font-lock-constant-face) + (?F 'font-lock-function-name-face) + (?S (if (eq mode 'go-mode) 'font-lock-string-face 'go-dot-mod-module-semver)) + (?N (if (eq mode 'go-mode) 'font-lock-negation-char-face 'go-dot-mod-module-name)) + (?V (if (eq mode 'go-mode) 'font-lock-variable-name-face 'go-dot-mod-module-version)) + (?D 'font-lock-comment-delimiter-face) + (?Q 'font-lock-comment-face)))) + (setq faces (append faces `((,face ,start-pos ,(point)))))) + (setq start nil)) + (setq start found-char) + (setq start-pos (point))))) + + ;; Fontify buffer now that we have removed the tags. + (font-lock-fontify-buffer) + (goto-char (point-min)) + + ;; Go through buffer one character at a time making sure the + ;; character's face is correct. + (let ((face (pop faces))) + (while (not (eobp)) + (while (and face (>= (point) (nth 2 face))) + (setq face (pop faces))) + (if (and face (>= (point) (nth 1 face))) + (should (go--should-match-face (nth 0 face))) + (should (go--should-match-face nil))) + (forward-char)))))) diff --git a/elpa/go-mode-1.6.0/test/go-font-lock-test.elc b/elpa/go-mode-1.6.0/test/go-font-lock-test.elc Binary files differ. diff --git a/elpa/go-mode-1.6.0/test/go-indentation-test.el b/elpa/go-mode-1.6.0/test/go-indentation-test.el @@ -0,0 +1,124 @@ +;;; go-indentation-test.el + +;; Copyright 2019 The go-mode Authors. All rights reserved. +;; Use of this source code is governed by a BSD-style +;; license that can be found in the LICENSE file. + +(require 'ert) +(require 'go-mode) + +(ert-deftest go--indent-line () + (dolist (file (directory-files (expand-file-name "testdata/indentation_tests/") t ".*\\.go$")) + (with-temp-buffer + (go-mode) + (insert-file-contents file) + (let ((contents-before-indent (buffer-string)) (inhibit-message t)) + (indent-region (point-min) (point-max) nil) + (should (string= contents-before-indent (buffer-string))))))) + +(ert-deftest go-dot-mod--indent-line () + (with-temp-buffer + (go-dot-mod-mode) + (insert-file-contents "testdata/indentation_tests/go.mod") + (let ((contents-before-indent (buffer-string)) (inhibit-message t)) + (indent-region (point-min) (point-max) nil) + (should (string= contents-before-indent (buffer-string)))))) + +(defun go--should-indent (input expected) + "Run `indent-region' against INPUT and make sure it matches EXPECTED." + (with-temp-buffer + (go-mode) + (insert input) + (let ((inhibit-message t)) + (indent-region (point-min) (point-max)) + (should (string= (buffer-string) expected))))) + +(ert-deftest go--indent-top-level () + (go--should-indent + " +package foo + var foo = 123 + + 456 + + 789 +" + + " +package foo +var foo = 123 + + 456 + + 789 +" + )) + +(ert-deftest go--indent-multiline-comment () + (go--should-indent + " +{ + /* +a + */ +} +" + + " +{ + /* + a + */ +} +") + + (go--should-indent + " +{ + /* LISTEN +a + */ +} +" + + " +{ + /* LISTEN + a + */ +} +") + + (go--should-indent + " +{ + /* c + c +c + */ +} +" + + " +{ + /* c + c + c + */ +} +") + + (go--should-indent + " +{ + /* cool + * cat + * + */ +} +" + + " +{ + /* cool + * cat + * + */ +} +")) diff --git a/elpa/go-mode-1.6.0/test/go-indentation-test.elc b/elpa/go-mode-1.6.0/test/go-indentation-test.elc Binary files differ. diff --git a/elpa/go-mode-1.6.0/test/testdata/behaviour_tests/gh-16.go b/elpa/go-mode-1.6.0/test/testdata/behaviour_tests/gh-16.go @@ -0,0 +1,6 @@ +package magiccomma + +/* +extern void CallMyFunction(void* pfoo); +*/ +import "C" diff --git a/elpa/go-mode-1.6.0/test/testdata/indentation_tests/comments.go b/elpa/go-mode-1.6.0/test/testdata/indentation_tests/comments.go @@ -0,0 +1,7 @@ +package comments + +func _() { + if foo { + X /* why */ /* do this */ + } +} diff --git a/elpa/go-mode-1.6.0/test/testdata/indentation_tests/composite_literal.go b/elpa/go-mode-1.6.0/test/testdata/indentation_tests/composite_literal.go @@ -0,0 +1,34 @@ +package indentation_tests + +func _() { + w := struct { + foo int + }{ + nil, + foo(func() { + foo++ + }), + } + + map[string]func(f Foo, b *Bar){ + "foo": func(f Foo, b *Bar) { + println("hi") + }, + "bar": func(f Foo, b *Bar) { + println("there") + }, + } + + Foo{ + Bar: func() { + switch { + case baz: + } + }, + + "bar": { + "foo", + "bar", + }, + } +} diff --git a/elpa/go-mode-1.6.0/test/testdata/indentation_tests/composite_literal_key.go b/elpa/go-mode-1.6.0/test/testdata/indentation_tests/composite_literal_key.go @@ -0,0 +1,5 @@ +func _() { + Foo{ + Bar: + } +} diff --git a/elpa/go-mode-1.6.0/test/testdata/indentation_tests/dangling_decls.go b/elpa/go-mode-1.6.0/test/testdata/indentation_tests/dangling_decls.go @@ -0,0 +1,47 @@ +package main + +var foo = +// hello +123 + +var foo = 123 + + // hello + 123 + +const foo = +// hello +123 + +const foo = 123 + + // hello + 123 + +type foo = +// hello +int + +func main() { + var foo = + // hello + 123 + + var foo = 123 + + // hello + 123 + + const foo = + // hello + 123 + + const foo = 123 + + // hello + 123 + + type foo = + // hello + int + + foo := + // hello + 123 +} diff --git a/elpa/go-mode-1.6.0/test/testdata/indentation_tests/dangling_operator.go b/elpa/go-mode-1.6.0/test/testdata/indentation_tests/dangling_operator.go @@ -0,0 +1,294 @@ +package main + +func init() { + a := + 123 + + print(123, + 456, + 789, + ) + + c := + "foo" + + d := // meh + 123 + + e := /* meh + another meh + */ + 123 + + println(123, /* foo + bar */ + 456, + ) + + 1 || + 1 || print(1, + 2, + ) + + 1 || + 1 && print(1, + 2, + ) + + f := + print(1, + 2, + ) + + 1 + + foo( + 1) + + foo + + 1 + + (1 + + 1) + (1 + + 1) + + 1 + + 1 + foo( + 1, + ) + + foo( + 1 && foo( + 1, + ), + ) + + g := + int64(4 * + 3 * + 1) + + i := + "" != "" || + true == false || + false == false + + a, b := + 1, + 2 + + a, + b := 1, 2 + + { + a, b := 1, + 2 + } + + 1 + foo( + 3, + ) + + foo && + foo && (foo && + foo) + + foo(1 + + 3 + + 4, + ) + + 1 + + 1 + + 1 + + (1 + + 1) + + 1 + (1 + + 1) + + 1 + (1 + + 1) + + 1 + + 1 + (1 + + 1) + (1 + + 1) + + 1 + + ((1 + + 1) + 1) + (1 + + 1) + + 1 + + ((1 + + 1) + + 1) + (1 + + 1) + + 1 + + ((1 + + 1) + + 1) + (1 + + 1) + + 1 + + (1 + (1 + + 1) + (1 + + 1)) + + 1 + + ((1 + + 1) + + 1) + + 1 + (1 + 1) + + (1 + + 1) + + 1 + + 1 + (1 + + 1) + + 1 + + 1 + + 1 + (1 + + 1) + + 1 + + (1 + foo(1+ + 1)) + + (1 && + (2 && + (3 && + 4))) && + 5 + + Foo{1 + + 2, + 3, + } + + 1 + (1 + + (1 + (1 + + 1))) + + 1 + (1 + (1 + (1 + + 1) + + 1) + + 1) + + 1 + + 1 + Foo{1 + + 1} + + 1 + + FOo{ + 1, + } + + // foo ends the dangle, -indent + 1 + + 1 + foo( + 1, + ) + + 1 + + foo( + 1, + ) + + 1 + + (1 + + 1) + + 1 + + 1 + (1 + + 1) + + 1 + + 1 + 1 + + 1 + + 1 + + (2 + + (3 + 4)) + foo( + 1, + ) + + 1 + + (1 + foo( + 1, + )) + + 1 + + (2 + + (3 + 4)) + + 1 + + 1 + + (2 + + 3) + foo( + 1, + 2) + + foo + + foo && + (foo && (bar && baz) && + qux) && + hi + + foo(1, + bar( + 1, + foo(2, + 1))) + + foo + + foo && + f(bar && (foo && + baz)) && + qux + + foo(1+`, +lol`+ + 123, + 456) + + "hi" + `, +lol` + + "there" + + foo /* hi */ + bar + + 1 + // hi + 2 + + 3 + + 1 + /* hi */ + 2 + + 3 + + foo || + foo && + foo( + 123, + ) + + foo || + foo && + foo{ + { + foo: bar, + }, + } + + foo. + bar. + baz. + qux + + return 123, + 456 +} diff --git a/elpa/go-mode-1.6.0/test/testdata/indentation_tests/function_call.go b/elpa/go-mode-1.6.0/test/testdata/indentation_tests/function_call.go @@ -0,0 +1,80 @@ +package indentation_tests + +func _() { + foo(bar( + baz(func() { + qux.hi = "there" + }), + baz(func() { + qux.hi = "there" + }), + )) + + switch { + case foo: + } + + unrelated(t) + foo([]int{ + 123, + }, func() { + return + }) + + + foo( + func() { + func() { + } + }) + + foo( + foo( + 1, + )) + + + foo( + 1, + ) + + foo( + foo( + 1, + )) + + foo( + foo( + 1, + ), + ) + + foo(foo( + 1, + )) + + foo(1 + + 2) + + + foo(foo( + 1, + ), + ) + + foo. + bar(func(i int) (a b) { + + }) + + foo || + bar && + baz(func() { + X + }) + + foo && + func() bool { + return X + }() +} diff --git a/elpa/go-mode-1.6.0/test/testdata/indentation_tests/gh-10.go b/elpa/go-mode-1.6.0/test/testdata/indentation_tests/gh-10.go @@ -0,0 +1,6 @@ +package gh10 + +func foo() string { + s := `foo` + return s +} diff --git a/elpa/go-mode-1.6.0/test/testdata/indentation_tests/gh-11.go b/elpa/go-mode-1.6.0/test/testdata/indentation_tests/gh-11.go @@ -0,0 +1,7 @@ +package gh11 + +func init() { + g(someSillyLongExpression(param1, param2, param3), + "boo") + x := 42 +} diff --git a/elpa/go-mode-1.6.0/test/testdata/indentation_tests/gh-13.go b/elpa/go-mode-1.6.0/test/testdata/indentation_tests/gh-13.go @@ -0,0 +1,9 @@ +package gh13 + +func init() { + f := + print(1, + 2, + 3, + ) +} diff --git a/elpa/go-mode-1.6.0/test/testdata/indentation_tests/gh-14.go b/elpa/go-mode-1.6.0/test/testdata/indentation_tests/gh-14.go @@ -0,0 +1,7 @@ +package gh14 + +func bar() string { + s := `foo +bar` + return s +} diff --git a/elpa/go-mode-1.6.0/test/testdata/indentation_tests/gh-15.go b/elpa/go-mode-1.6.0/test/testdata/indentation_tests/gh-15.go @@ -0,0 +1,12 @@ +package gh15 + +func somewhatLongFunctionName( + arg1 package1.RatherLongTypeName, arg3 struct{}) { + return +} + +func foo(arg1 type1, + arg2 type2, + arg3 type3) { + return +} diff --git a/elpa/go-mode-1.6.0/test/testdata/indentation_tests/gh-9.go b/elpa/go-mode-1.6.0/test/testdata/indentation_tests/gh-9.go @@ -0,0 +1,7 @@ +package gh9 + +func x() string { + s := f(` +foo`) + return s +} diff --git a/elpa/go-mode-1.6.0/test/testdata/indentation_tests/go.mod b/elpa/go-mode-1.6.0/test/testdata/indentation_tests/go.mod @@ -0,0 +1,19 @@ +module my/thing + +// comment +go 1.12 + +require other/thing v1.0.2 +require new/thing/v2 v2.3.4 +exclude old/thing v1.2.3 +replace bad/thing v1.4.5 => good/thing v1.4.5 + +require ( + // comment inside block + new/thing v2.3.4 + old/thing v1.2.3 +) + +replace ( + bad/thing v1.4.5 => good/thing v1.4.5 +) diff --git a/elpa/go-mode-1.6.0/test/testdata/indentation_tests/labels.go b/elpa/go-mode-1.6.0/test/testdata/indentation_tests/labels.go @@ -0,0 +1,20 @@ +package labels + +func main() { + code() +Label: + code() +Label2: + code() +Label3: // Comments! + code() + + for { + Label4: + // code + } + + { + Label5: + } +} diff --git a/elpa/go-mode-1.6.0/test/testdata/indentation_tests/multiline_comment.go b/elpa/go-mode-1.6.0/test/testdata/indentation_tests/multiline_comment.go @@ -0,0 +1,54 @@ +package multilinecomment + +/* Some comment here + with my very own + indentation as it pleases me */ + +func main() { + if true { + // code + } +} + +func _() { + /* foo + * bar + */ + + /* abc + 123 + def + lol + */ + + /* + abc + - def + */ + + /* + hello + there */ + + /* + hello + there */ + + /* + foo + */ + + /* + foo + */ + + /* + foo + */ + + /* foo + asd + asd + asd + */ +} diff --git a/elpa/go-mode-1.6.0/test/testdata/indentation_tests/multiline_if.go b/elpa/go-mode-1.6.0/test/testdata/indentation_tests/multiline_if.go @@ -0,0 +1,152 @@ +package foo + +import ( + "bytes" + "errors" +) + +func _() { + if realLength == -1 && + !chunked(t.TransferEncoding) && + bodyAllowedForStatus(t.StatusCode) { + t.Close = true + } + + if true && + (true || + true && (true || + false) && true) { + true + } + + if true && (true && + true) { + true + } + + if true && + (true && (true || + false) && true) { + true + } + + if true && + foo(true && + true) { + true + } + + if true && + true && (true || + true) { + true + } + + if (true && + true) && + true { + true + } + + if bytes.Contains(out, []byte("-fsanitize")) && + (bytes.Contains(out, []byte("unrecognized")) || + bytes.Contains(out, []byte("unsupported"))) { + return true, errors.New(string(out)) + } + + if true == + false { + return + } + + if true != + false { + return + } + + if foo(1, // hi + // hi + + 2) { // hi + return + } + + if foo( + func() { + }) { + return + } + + if foo == 0 || + !foo.Bar( + "some", + "args") { + return + } + + if true { + break + } else if true { + if true { + break + } + } + + if true { + } else if true || + true { + return + } + + if 1 + + 1 { + X + } + + if 1 + + (1 + + 1) { + X + } + + if 1 + + (1 + + 1) + + 1 { + X + } + + if 1 + + 1 + + 1 + (1 + + 1) { + X + } + + if 1 + + (1 + + 1) + (1 + + 1) { + X + } + + if (Foo{1, + 1}).Bar { + return + } + + if foo || + foo && + foo == + foo+ + foo* + foo { + foo + } + + if foo() || + foo() && + foo() { + foo + } +} diff --git a/elpa/go-mode-1.6.0/test/testdata/indentation_tests/multiline_string.go b/elpa/go-mode-1.6.0/test/testdata/indentation_tests/multiline_string.go @@ -0,0 +1,9 @@ +package multilinestring + +func foo() string { + s := `foo +bar +baz` + + return s +} diff --git a/elpa/go-mode-1.6.0/test/testdata/indentation_tests/multiline_struct.go b/elpa/go-mode-1.6.0/test/testdata/indentation_tests/multiline_struct.go @@ -0,0 +1,63 @@ +func _() { + testCase{char59 + "a." + char63 + "." + char63 + "." + + char63 + ".com", + false} + + foo := Bar{ + { + Index: int(index), + MTU: int(row.Mtu), + DEFAULT: 123, + CASE: 123, + }, + { + Index: int(index), + MTU: int(row.Mtu), + }, + } + + ifi := Interface{ + Index: int(index), + MTU: int(row.Mtu), + Name: name, + HardwareAddr: HardwareAddr(row.PhysAddr[:row.PhysAddrLen]), + Flags: flags} + ift = append(ift, ifi) + + Interface{ + {"230-Anonymous access granted, restrictions apply\n" + + "Read the file README.txt,\n" + + "230 please", + 23, + 230, + "Anonymous access granted, restrictions apply\nRead the file README.txt,\n please"}, + } + + var _ struct { + foo, // super + bar, // important + baz int //comments + } + + Cool(Foo{ + Bar: Cool(Baz{ + Blah: 123, + }), + }) + + Foo{{ + 1, + }, { + 2, + }} + + var Foo = Bar{ + Baz: (&Blah{ + One: 1, + }).Banana, + } + + Foo{ + 1}.Bar( + 1) +} diff --git a/elpa/go-mode-1.6.0/test/testdata/indentation_tests/numeric_literals.go b/elpa/go-mode-1.6.0/test/testdata/indentation_tests/numeric_literals.go @@ -0,0 +1,12 @@ +package main + +func main() { + 1234. + hi() + + -1234. + hi() + + oneTwo3. + hi() +} diff --git a/elpa/go-mode-1.6.0/test/testdata/indentation_tests/return_function_call_struct.go b/elpa/go-mode-1.6.0/test/testdata/indentation_tests/return_function_call_struct.go @@ -0,0 +1,11 @@ +package main + +func main() { + return F( + S{ + 1, + 2, + 3, + }, + ) +} diff --git a/elpa/go-mode-1.6.0/test/testdata/indentation_tests/switch.go b/elpa/go-mode-1.6.0/test/testdata/indentation_tests/switch.go @@ -0,0 +1,76 @@ +package _switch + +func main() { + switch "" { + case "foo": + label: + code() + case "bar": + case "baz": // important comma, + if true { + return + } + case "meow": // some documentation + default: + code() + } + + switch 123 { + case 1, 2, + 3: + case + 1, + 3: + case + // hi + "hi", + "there": + code() + case + /* hi + there */ + "hi", // hi + "there": // there + } + + switch { + // attached + case true: + // body + code() + // could go either way + case true: + // could go either way + case true: + // could go both ways + // could go both ways + case true: + + /* this works too */ + case true: + + /* hi */ + /* this works too */ + case true: + + /* hi + this works too */ + case true: + + // could go either way + case true: + + // could go either way + case true: + + // also works + default: + } + + switch { + case 1: + case foo, + foo, + foo: + } +} diff --git a/elpa/go-mode-1.6.0/test/testdata/indentation_tests/unnamed_method_receiver.go b/elpa/go-mode-1.6.0/test/testdata/indentation_tests/unnamed_method_receiver.go @@ -0,0 +1,8 @@ +package main + +type A int + +func (A) Foo() (A, + A) { + // Code goes here +} diff --git a/elpa/go-mode-1.6.0/test/testdata/movement_tests/functions.go b/elpa/go-mode-1.6.0/test/testdata/movement_tests/functions.go @@ -0,0 +1,59 @@ +// This file can be used to manually test that go-beginning-of-def and +// go-end-of-defun are correct by entering into each function and mark-defun +// (C-M-h). +package main + +type typea int + +func easy(a, b, c int) int { + c += a + c += b + return c +} + +func harder(a chan struct{}) { + close(a) +} + +func harder(a struct { + b struct { + c interface { + Foo() + Bar() + Baz() + } + } +}) interface { + Channer() chan struct{} +} { + return nil +} + +func oneline(a struct{}) (r struct{ a int }) { return r } + +type typeb struct { + a, b, c int +} + +// comment1 breaks end-of-defun by splitting "struct" from "{". (This also +// apparently breaks gofmt, is why this is formatted so weird.) +func comment1(a chan struct /* why? */ { + +}) { + close(a) +} + +func comment2(a struct { + b int // b is sad :{ + c int +}) { + a.b += a.c + a.c += a.b + return +} + +func structWithTag(a chan struct { + v int `{` +}) { + close(a) +}