universal-ctags / citre

A superior code reading & auto-completion tool with pluggable backends.
GNU General Public License v3.0
338 stars 27 forks source link

Does not show variables in completion but functions in C file #181

Open Schievel1 opened 1 month ago

Schievel1 commented 1 month ago

Describe the bug

When in a C file I get completion candidates for functions just fine, but I don't get suggestion for variables.

Here is a small test example. I use the global backend and the mytext variable is not suggested from corfu: grafik on the other hand it completes the function myInBufferFunction: grafik (don't mind the header file, I was just testing around if it has something to do with whether the function is defined in-buffer or in another file - it does not)

Heres my config regarding citre:

(use-package! citre
  :demand t
  :init
  (require 'citre-config) ;; needed for lazy load
  :config
  (setq
   citre-peek-auto-restore-after-jump nil
   citre-global-completion-case-sensitive nil
   citre-project-root-function #'projectile-project-root
   citre-default-create-tags-file-location 'global-cache
   citre-use-project-root-when-creating-tags t)
  (evil-define-key 'normal citre-peek-keymap
    (kbd "]t") #'citre-peek-next-definition
    (kbd "[t") #'citre-peek-prev-definition
    (kbd "]p") #'citre-peek-through
    (kbd "[p") #'citre-peek-through-references
    (kbd "[j") #'citre-peek-jump
    (kbd "ESC") #'citre-peek-abort
    )
  (evil-make-overriding-map citre-peek-keymap 'motion)
  (evil-make-overriding-map citre-peek-keymap 'normal))

To Reproduce

If you are pretty sure about the cause, or you are pretty sure it's a easy fix, or you are pretty sure it can be easily reproduced by developers, you can delete this section. Otherwise, write steps to reproduce, and it should start by $ emacs -Q.

Steps to reproduce:

  1. $ emacs -Q
  2. I can not really reproduce this with emacs -Q. I need to load corfu and compat and citre and whatnot and it does not show me completions at all.

System information

Additional context

I tried with ggtags-mode and it is showing the variables, so it must be a setting or something in citre.

fwiw I seems like the variable tag isn't in the tag file at all:

pascal on Gentoo-WSL ~/.cache/gtags/home/pascal/dev/test
✗ global -c my
myInBufferFunction
myOtherFileFunction

after creating the GTAGS etc. files with ggtags-mode I get:

pascal on Gentoo-WSL ~/.cache/gtags/home/pascal/dev/test
➜ global -c my
myInBufferFunction
myOtherFileFunction
mytext

This is because ggtags can tell global to use ctags as a backend for creating tag files. If I don't do that, is creates a GTAGS file with the same issue. However, creating a tags database with citre or ggtags (without ctags backend) results in

pascal on Gentoo-WSL ~/.cache/gtags/home/pascal/dev/test
➜ global -c my
myInBufferFunction
myOtherFileFunction

pascal on Gentoo-WSL ~/.cache/gtags/home/pascal/dev/test
➜ global -sx mytext
Warning: source file './test.c' is not available.
mytext              3 test.c

So it finds them with -s, which is

       Print other symbol tags.
       Other symbol means the reference to a symbol which has no definition.

Whyever global thinks mytext has no definiton, is citre using the wrong arguments for global?

Schievel1 commented 1 month ago

fwiw the GNU Global project seems to know about this it says here: https://www.gnu.org/software/global/plans.html

Treat variable definitions as a definition tag (GTAGS).

Schievel1 commented 1 month ago

Yup, it works fine when using ctags through global when creating the database. I did this to let it use ctags: (setenv "GTAGSLABEL" "ctags")

I don't know if that is really the right way to go.

AmaiKinono commented 1 month ago

Hi. You could look into the gtags config file gtags.conf. Our document also mentions that.

A gtags "label" is just a set of gtags settings in gtags.conf, including which parser to use for scanning definitions and references. GTAGSLABEL variable specifies which label to use. So yeah, if you like the behavior of bundled ctags label, setting the environment variable is the right thing to do. If you want more, you can customize your own label.

edit: If you look into the code of ggtags-create-tags, it actually modifies GTAGSLABEL to ctags in certain conditions. I think this may not be the best thing to do, as the labels are customizable, there are really no guarantee that certain label has certain behavior.

Schievel1 commented 1 month ago

It is not just a set of settings that is named 'ctags' coincidentally. Global is using the ctags binary with that settings when the label is set to ctags. I had to compile Global with --with-universal-ctags=/usr/bin/exuberant-ctags to tell it were the ctags executable is.

Anyway, if I create the database using citre with GTAGSLABEL unset, what citre is running according to the readme yields:

pascal on Gentoo-WSL ~/.cache/gtags/home/pascal/dev/test
➜ global --color=never --encode-path=' :' --result=grep --literal --reference --symbol -- mytext
Warning: source file './test.c' is not available.
test.c:3:
test.c:11:

So why is it not showing this as completion?

AmaiKinono commented 1 month ago

It is not just a set of settings that is named 'ctags' coincidentally.

It is if you look into it. The default config file on my system is/usr/share/gtags/gtags.conf, the ctags label is defined as:

ctags:\
    :tc=exuberant-ctags:tc=htags:
...
exuberant-ctags|plugin-example|setting to use Exuberant Ctags plug-in parser:\
    :tc=common:\
    :ctagscom=/usr/bin/etags:\
...
    :gtags_parser=Ant\:$ctagslib:\
... gtags parser of other languages are also set to ctagslib. That's why "Global is *using* the ctags binary"

See man gtags.conf to know the syntax. The location of ctags executable can be modified by the ctagscom setting.

➜ global --color=never --encode-path=' :' --result=grep --literal --reference --symbol -- mytext

This is the command Citre use to find references, not definitions or completions. But from the output, a possible guess is the line Warning: source file './test.c' is not available. can not be parsed by Citre, so it runs into an error.

I'd suggest the following test to know exactly where does the problem happen.

  1. Run

    $ global --completion --color=never --encode-path=' :' --result=grep --literal -- my

    to see if global could find the completions.

  2. Eval (citre-global--get-lines "my" 'completion) to see if the global backend actually gets the output of global.

  3. Eval (citre-global-get-tags "my" 'completion (not citre-global-completion-case-sensitive) 'alpha) to see if Citre can parse the output.

  4. Eval (citre-global-get-completions) with the point after a typed "my" to see if the global completion backend works properly.

  5. Eval (citre-get-backend-and-completions) to see if the global backend is actually used.

Schievel1 commented 1 month ago

But from the output, a possible guess is the line Warning: source file './test.c' is not available. can not be parsed by Citre, so it runs into an error.

This is probably because the database files are saved in ~/.cache/gtags and the sources are in a different place. global does not know that. Nevertheless, it outputs the function myInBufferFunction() which is defined in test.c but not the char* mytext which is also defined in test.c.

global --completion --color=never --encode-path=' :' --result=grep --literal -- my

➜ global --completion --color=never --encode-path=' :' --result=grep --literal -- my
myInBufferFunction
myOtherFileFunction

so it does not get them. I continue with the other points regardless.

Eval (citre-global--get-lines "my" 'completion) to see if the global backend actually gets the output of global.

("myInBufferFunction" "myOtherFileFunction")

Eval (citre-global-get-tags "my" 'completion (not citre-global-completion-case-sensitive) 'alpha) to see if Citre can parse the output.

(#s(hash-table size 16 test eq rehash-size 1.5 rehash-threshold 0.8125 data (name "myInBufferFunction")) #s(hash-table size 16 test eq rehash-size 1.5 rehash-threshold 0.8125 data (name "myOtherFileFunction")))

(citre-global-get-completions)

(208 210 (#s(hash-table size 16 test eq rehash-size 1.5 rehash-threshold 0.8125 data (name "myInBufferFunction")) #s(hash-table size 16 test eq rehash-size 1.5 rehash-threshold 0.8125 data (name "myOtherFileFunction"))))

(citre-get-backend-and-completions)

(global 208 210 (#s(hash-table size 16 test eq rehash-size 1.5 rehash-threshold 0.8125 data (name "myInBufferFunction")) #s(hash-table size 16 test eq rehash-size 1.5 rehash-threshold 0.8125 data (name "myOtherFileFunction"))))

But with (setenv "GTAGSLABEL" "ctags") I get

➜ global --completion --color=never --encode-path=' :' --result=grep --literal -- my
myInBufferFunction
myOtherFileFunction
myglobal
mytext

But (without GTAGSLABEL again):

➜ global --completion --color=never --encode-path=' :' --result=grep --literal --symbol -- my
myglobal
mytext

Btw I found this [1] and isn't this the same problem we have here? If so and given that the Global team seems to know about this (Treat variable definitions as a definition tag (GTAGS). in [2]) shouldn't the command be different when global searches for completions? Maybe run once with and once without --symbols then concatenate the results or something?

[1] https://stackoverflow.com/questions/26957779/gnu-global-do-not-see-a-variables [2] https://www.gnu.org/software/global/plans.html

AmaiKinono commented 1 month ago

Btw I found this [1] and isn't this the same problem we have here?

I think yes.

Maybe run once with and once without --symbols then concatenate the results or something?

Yes. I've tested this and it's doable. But although this is a improvement for auto-completion, we still cannot find definitions of such symbols using global. So at the end, a global user may still want to use the ctags plugin parser.