Feature request. Add imenu support it is trivial.

Bug #1922146 reported by Ergus
6
This bug affects 1 person
Affects Status Importance Assigned to Milestone
global-tags.el
Fix Released
Medium
Felipe Lema

Bug Description

Hi Felipe:

I have been looking in the api you made for this and I think that adding support to imenu throw global could be almost trivial.

global provides the option -f that returns the tags in the current file.

Example: `global -f mysource.c`

```
mystruct 2 main.c struct mystruct{
fun1 6 main.c int fun1(int a)
fun2 11 main.c int fun2(
main 18 main.c int main(int argc, char *argv[])
```

and imenu just needs a function that returns an alist with the special format

```
(index-name index-position function arguments…)
```

so it only needs a function to parse the first output and return something like:

```
'(("fun1" 2 #'global-imenu-action "struct mystruct{")
  ("fun2" 6 #'global-imenu-action "int fun1(int a)")
  ...
  )
```

Where:

```
(defun global-imeny-action (tag line regex)
   (goto-line line)
   (search-forward regex)
   (search-backward regex))
```

The whole documentation for imenu is here:

https://www.gnu.org/software/emacs/manual/html_node/elisp/Imenu.html

Could you consider to add this to this package. Because a different package only for this is IMO useless.

Thanks in advance,
Ergus.

Revision history for this message
Ergus (ergus) wrote :

BTW:

Just to extend this a little bit. The following (untested) code more or less should work.

```
(defun global-imeny-action (tag line regex)
   (goto-line line)
   (search-forward regex)
   (search-backward regex))

(defun my/parse (str)
 (let ((l (split-string str ":" t)))
    (list (car l)
   (string-to-number (nth 1 l))
  #'global-imeny-action
   (nth 2 l))))

(defun my/imenu-create-index-function ()
  (let ((file (buffer-file-name)))
    (when file
      (mapcar #'my/parse
       (with-temp-buffer
  (shell-command (concat "global -f --result \"grep\" " file) t)

  (split-string (buffer-string) "\n" t)
  )))))

(setq imenu-create-index-function
      #'my/imenu-create-index-function)
```

Revision history for this message
Ergus (ergus) wrote :

Or maybe better this if you don't want to use the `--result \"grep\"`:

(defun my/global-imeny-action (tag line regex)
   (goto-line line)
   (search-forward regex)
   (search-backward regex))

(defun myparse (str)
  (when (string-match "\\(^[^ ]+\\)[[:blank:]]+\\([[:digit:]]+\\) [^ ]+[[:blank:]]\\{2,\\}\\(.+\\)" str)
    (list (match-string-no-properties 1 str)
   (string-to-number (match-string-no-properties 2 str))
   #'my/global-imeny-action
   (match-string-no-properties 3 str))))

(defun my/imenu-create-index-function ()
  (let ((file (buffer-file-name)))
    (when file
      (mapcar #'myparse
       (with-temp-buffer
  (shell-command (concat "global -f " file) t)

  (split-string (buffer-string) "\n" t)
  )))))

(setq imenu-create-index-function
      #'my/imenu-create-index-function)

Changed in global-tags.el:
importance: Undecided → Medium
assignee: nobody → Felipe Lema (morenonatural)
status: New → In Progress
Revision history for this message
Felipe Lema (morenonatural) wrote :

I just found out that I needed to manually subscribe myself to my project to get emails

sorry about the delay

Revision history for this message
Felipe Lema (morenonatural) wrote :

uploaded first version.

was split in several commits (forgot to squash)

see https://git.launchpad.net/global-tags.el/commit/?id=7f0b03af69eb935bd451c8f7fd9b1c6cfed548e1

Changed in global-tags.el:
status: In Progress → Fix Released
Revision history for this message
Ergus (ergus) wrote :

Hi Felipe:

I will try the new mode in the next days. Could you please add the ;;;###autoload decorators to the modes at least?

I think that the commands that may need them are: `global-tags-exclusive-backend-mode`, `global-tags-shared-backend-mode` and `global-tags-imenu-mode` and for more complex configurations like mine: `global-tags-xref-backend`, `global-tags-try-project-root`, `global-tags-create-database` and `global-tags-update-database`.

BTW:

(thread-last b-fname
        (global-tags--get-lines 'file)
        (seq-map #'global-tags--file-tag-to-imenu-index))

is the same than:

(seq-map #'global-tags--file-tag-to-imenu-index (global-tags--get-lines 'file b-fname))

Which is simpler to read and for the compiler.

in te same way you can write:

(defun global-tags--get-locations (symbol &optional kind)
  "Get locations according to SYMBOL and KIND.

If KIND is omitted, global will do a \"tag\" search."
  (cl-mapcar #'global-tags--get-location
   (global-tags--get-lines kind 'result "grep" symbol)))

Just a suggestion.

Revision history for this message
Ergus (ergus) wrote :

Hi Felipe:

I just tried the new imenu and it is not working with tramp.

Basically it seems that the command called is:

global: '/ssh:server:/path/file.c' which returns: global: out of the source project.

Revision history for this message
Felipe Lema (morenonatural) wrote :
Revision history for this message
Ergus (ergus) wrote :

Thanks Felipe I will try it now.

BTW I made a bug report to global team in order to support --print0 in --completions.

https://lists.gnu.org/archive/html/bug-global/2021-07/msg00000.html

The current behavior was a BUG and should be fixed now on master.

https://lists.gnu.org/archive/html/bug-global/2021-07/msg00001.html

 I don't really know why you prefer to use --print0 that due to this issue made the code more complex... but in any case it should be fixed.

Revision history for this message
Ergus (ergus) wrote :

Hi Felipe:

After trying the change it seems to be not working yet. (actually all the package is not working with tramp)

Apparently there is a conflict when using async-start and process-file with tramp. I can't debug more because async-start is not easy to debug, but this seems to be the reason why there exists async-start-process. async starts a process and process-file another one, so probably there is the problem.

When I try:

(let ((async-debug t))
 (global-tags--get-lines 'file (file-local-name (buffer-file-name))))

I get:

I see Transmitting sexp {{{'(lambda nil
   (require 'simple)
   (let
       ((tramp-use-ssh-controlmaster-options nil)
 (default-directory "/scp:mn3:/home/user/project")
 (command-return-code))
     (let
  ((command-output-str
    (with-output-to-string
      (setq command-return-code
     (process-file "global" nil
     (list standard-output nil)
     nil "--file" "--print0" "/home/user/project/mysource.cpp")))))
       (cons command-return-code
      (when command-output-str
        (split-string command-output-str "" t))))))
}}}
nil

the process seems to return 127, so for some reason it does not find a file.

But when I eval:

(process-file "global" nil
 (list standard-output nil)
  nil "--file" "--print0" "/home/user/project/mysource.cpp"))

this works perfectly fine.

BTW:

If you allow me to do some comments:

1) async + process-file starts 2 processes one nested to the other and seems to be the source of some bugs... maybe creating a sync and an async version of global-tags--get-lines-future may simplify the code (avoiding to use async-get in all the places and simplifiying debuging). You can actually use the sync function and use a macro to call it in the async version. This will be also more efficient in general.

2)

(apply #'global-tags--get-lines-future
                                    (append
                                     (list
                                      command
                                      nil)
                                     flags))

is the same than just:

(apply #'global-tags--get-lines-future command nil flags)

3) If you dont use the --print0 option then global-tags--line-separator, global-tags--ensured-correct-separator, can be deleted and the separator will be always "\n" so the code will be simpler too.

4) simple.el is one of the most basic files in vanilla and is always installed. Usually it is not needed to require it. But in case you want to do, It will be simpler to do that only once on the beginning, not check on every call of global-tags--get-lines-future. Once a file is loaded, the overhead of (require 'file) is very small but still exists. We use require inside a function only when we use external packages conditionally or we want to condition some code depending of when the package is installed.

Revision history for this message
Felipe Lema (morenonatural) wrote :

will take a look at your comments: it's very clear how and why to apply your suggestions

Since it's a lot to write, you may get results earlier by submitting a patch / pull request (see https://bugs.launchpad.net/global-tags.el/+bug/1844961/comments/4).

To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.