leoliu / ggtags

Emacs frontend to GNU Global source code tagging system.

Home Page:http://elpa.gnu.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

ggtags causes company-capf to fail when GTAGS doesn't exist

danielrlewis opened this issue · comments

When company-mode and ggtags-mode are both enabled, company will use ggtags as a completion source via its company-capf backend. If no tags file (e.g., GTAGS) exists, ggtags will throw an error, preventing company from offering any completion suggestions.

I can reproduce this with Emacs 27.2 and a minimal ~/.emacs.d directory which contains only the following two files.

early-init.el:

;; stop package.el from being loaded, since we're using straight.el instead
(setq package-enable-at-startup nil)

init.el:

;; package manager to install company and ggtags
(defvar bootstrap-version)
(let ((bootstrap-file
       (expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory))
      (bootstrap-version 5))
  (unless (file-exists-p bootstrap-file)
    (with-current-buffer
        (url-retrieve-synchronously
         "https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el"
         'silent 'inhibit-cookies)
      (goto-char (point-max))
      (eval-print-last-sexp)))
  (load bootstrap-file nil 'nomessage))
(setq straight-use-package-by-default t)
(straight-use-package 'use-package)

;; install & enable company
(use-package company)
(add-hook 'prog-mode-hook (lambda () (company-mode)))

;; install & enable ggtags
(use-package ggtags)
(add-hook 'c-mode-common-hook
          (lambda ()
            (when (derived-mode-p 'c-mode 'c++-mode 'java-mode)
              (ggtags-mode 1))))

;; enable the debugger
(setq debug-on-error t)

With that ~/.emacs.d, launch Emacs and wait for company and ggtags to be installed. Then open a cc-mode file in a directory location where no tags file exists. For example, a C file like the following:

#define FOOBAR 1

int main(void)
{
  /* Type FOO on the next line and then pause: */
  
}

As the comment instructs, type "FOO" on the blank line and pause momentarily. The result is the following backtrace:

Debugger entered--Lisp error: (error "‘global’ non-zero exit: global: GTAGS not found.")
  signal(error ("‘global’ non-zero exit: global: GTAGS not found."))
  error("`%s' non-zero exit: %s" "global" "global: GTAGS not found.")
  ggtags-process-string("global" "-c" "FOO")
  apply(ggtags-process-string "global" ("-c" "FOO"))
  #f(compiled-function (prefix) #<bytecode 0x158d6543cf2d>)("FOO")
  #f(compiled-function (string pred action) #<bytecode 0x158d6543cf55>)("FOO" nil t)
  all-completions("FOO" #f(compiled-function (string pred action) #<bytecode 0x158d6543cf55>) nil)
  completion-pcm--all-completions("" ("FOO" point) #f(compiled-function (string pred action) #<bytecode 0x158d6543cf55>) nil)
  completion-basic-all-completions("FOO" #f(compiled-function (string pred action) #<bytecode 0x158d6543cf55>) nil 3)
  #f(compiled-function (style) #<bytecode 0x158d6523f5d5>)(basic)
  completion--some(#f(compiled-function (style) #<bytecode 0x158d6523f5d5>) (basic partial-completion emacs22))
  completion--nth-completion(2 "FOO" #f(compiled-function (string pred action) #<bytecode 0x158d6543cf55>) nil 3 (metadata))
  completion-all-completions("FOO" #f(compiled-function (string pred action) #<bytecode 0x158d6543cf55>) nil 3 (metadata))
  company-capf--candidates("FOO")
  company-capf(candidates "FOO")
  apply(company-capf (candidates "FOO"))
  company-call-backend-raw(candidates "FOO")
  company--fetch-candidates("FOO")
  company-calculate-candidates("FOO" nil)
  company--begin-new()
  company--perform()
  company-auto-begin()
  company-idle-begin(#<buffer test.c> #<window 3 on test.c> 38 91)
  apply(company-idle-begin (#<buffer test.c> #<window 3 on test.c> 38 91))
  timer-event-handler([t 24865 42380 905511 nil company-idle-begin (#<buffer test.c> #<window 3 on test.c> 38 91) nil 842000])

I don't normally run with debug-on-error enabled. The real problem is that this error being thrown prevents company from providing any completion suggestions from other sources. This can be seen by commenting out the debug-on-error line in init.el, and then repeating the repo procedure. If you open that example C file, type M-x ggtags-mode to turn off ggtags, and then type "FOO" on the blank line, company will offer to complete to "FOOBAR". If you try the same but without disabling ggtags, the "FOOBAR" completion is not offered, presumably because the above error was thrown.

As a workaround, I can generate the tags file, which is what I normally do anyway when seriously working on a project. Or, alternatively, I can disable ggtags-mode (it defaults to enabled in my init.el for certain modes). But it would be nice to be able to make quick edits without bothering to generate tags files or disable ggtags-mode and still have other completion sources available during those edits.

Is it possible for ggtags to avoid throwing an error in this situation?