All Projects → redguardtoo → Counsel Etags

redguardtoo / Counsel Etags

Fast, energy-saving, and powerful code navigation solution
  • counsel-etags

[[https://travis-ci.org/redguardtoo/counsel-etags][https://travis-ci.org/redguardtoo/counsel-etags.svg?branch=master]] [[http://melpa.org/#/counsel-etags][file:http://melpa.org/packages/counsel-etags-badge.svg]] [[http://stable.melpa.org/#/counsel-etags][file:http://stable.melpa.org/packages/counsel-etags-badge.svg]]

Fast, energy-saving, and powerful code navigation solution.

It's been tested on Linux/Windows/macOS.

[[file:demo.png]]

  • Table of Content :noexport:TOC:
  • [[#counsel-etags][counsel-etags]]
  • [[#install][Install]]
  • [[#usage][Usage]]
  • [[#tips-optional][Tips (OPTIONAL)]]
    • [[#native-windows-emacs][Native Windows Emacs]]
    • [[#gitignore-and-hgignore-are-respected][".gitignore" and ".hgignore" are respected]]
    • [[#set-up-with-use-package][Set up with use-package]]
    • [[#insert-extra-content-into-tags-file-after-its-updated][Insert extra content into tags file after it's updated]]
    • [[#configuration-file][Configuration file]]
    • [[#ignore-directories-and-files][Ignore directories and files]]
    • [[#dependency-on-emacs-apis-is-minimum][Dependency on Emacs APIs is minimum]]
    • [[#specify-multiple-tags-files][Specify multiple tags files]]
    • [[#auto-update-tags-file][Auto update tags file]]
    • [[#rust-programming-language][Rust programming language]]
    • [[#list-all-tags][List all tags]]
    • [[#two-step-tag-matching-using-regular-expression-and-filter][Two-step tag matching using regular expression and filter]]
    • [[#force-update-current-tags-file][Force update current tags file]]
    • [[#open-recent-tag][Open recent tag]]
    • [[#ctags-setup][Ctags setup]]
    • [[#search-with-exclusion-patterns][Search with exclusion patterns]]
    • [[#grep-program][Grep program]]
    • [[#customize-grep-keyword][Customize grep keyword]]
    • [[#windows][Windows]]
    • [[#ctagsexuberant][~/.ctags.exuberant]]
    • [[#use-ctags-to-generate-imenu-items][Use Ctags to generate Imenu items]]
  • [[#step-by-step-guide][Step by step guide]]
    • [[#step-1-a-toy-c-project][Step 1, a toy C project]]
    • [[#step-2-navigate-code][Step 2, navigate code]]
  • [[#bug-report][Bug Report]]
  • Install Please install =counsel-etags= from [[https://melpa.org/#/counsel-etags][MELPA]].

If [[http://ctags.sourceforge.net/][Exuberant Ctags]] or [[https://ctags.io/][Universal Ctags]] exists, this program works out of box.

Universal Ctags is actively maintained and strongly recommended.

Or else, customize =counsel-etags-update-tags-backend= to create tags file with your own CLI. Please note [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Create-Tags-Table.html#Create-Tags-Table][etags]] bundled with Emacs is not supported anymore.

[[https://github.com/redguardtoo/emacs.d/issues/697#issuecomment-394141015][It's reported]] "Exuberant Ctags" v5.8.5 is buggy.

  • Usage Run =M-x counsel-etags-find-tag-at-point= to navigate code without any setup.

This command will:

  • Find project root folder and scan code automatically
  • Find correct tag automatically
  • If no tag is find, =ripgrep= or =grep= is automatically called

Please note it takes time to parse tags file contains long lines. It's the known issue of Emacs Lisp.

You could run =M-x counsel-etags-scan-code= only once and create tags file [[https://www.emacswiki.org/emacs/BuildTags][in your own way]].

Please read [[#step-by-step-guide][Step by step guide]] for more details.

  • Tips (OPTIONAL) ** Native Windows Emacs The grep program path on Native Windows Emacs uses either forward slash or backward slash. Like "C:/rg.exe" or "C:\\rg.exe".

If grep program path is added to environment variable PATH, you don't need worry about slash problem. ** ".gitignore" and ".hgignore" are respected The variable =counsel-etags-ignore-config-file= specifies the paths of ignore configuration files (".gitignore", ".hgignore", etc).

The path is either absolute or relative to the tags file.

Set =counsel-etags-ignore-config-files= to nil to turn off this feature. ** Set up with [[https://github.com/jwiegley/use-package][use-package]] Please place =add-hook= code inside =:init= section, #+begin_src elisp (use-package counsel-etags :ensure t :bind (("C-]" . counsel-etags-find-tag-at-point)) :init (add-hook 'prog-mode-hook (lambda () (add-hook 'after-save-hook 'counsel-etags-virtual-update-tags 'append 'local))) :config (setq counsel-etags-update-interval 60) (push "build" counsel-etags-ignore-directories)) #+end_src ** Insert extra content into tags file after it's updated =counsel-etags-find-tag-name-function= finds tag name at point. If it returns nil, =find-tag-default= is used. =counsel-etags-word-at-point= returns the word at point.

User could append the extra content into tags file in =counsel-etags-after-update-tags-hook=.

The parameter of hook function is full path of the tags file.

=counsel-etags-tag-line= and =counsel-etags-append-to-tags-file= are helper functions to update tags file in the hook,

Sample code to append native javascript API "addEventListener", "dispatchEvent", "removeEventListener" into tags file, #+begin_src elisp (defun my-update-tags-file (tags-file) "Update TAGS-FILE." (when (memq major-mode '(js-mode typescript-mode js2-mode)) (let ((s3 (mapconcat (lambda (tagname) (counsel-etags-tag-line tagname tagname 0)) '(addEventListener dispatchEvent removeEventListener) ""))) (counsel-etags-append-to-tags-file (list (cons "https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/%s" s3)) tags-file)))) (add-hook 'counsel-etags-after-update-tags-hook 'my-update-tags-file) #+end_src ** Configuration file Path of the configuration file is defined in =counsel-etags-ctags-options-file= whose value is =~/.ctags=.

Exuberant Ctags actually can NOT open configuration file ".ctags" through cli option.

We use Emacs Lisp to load =~/.ctags= to workaround this issue.

Please use file name like =ctags.cnf= instead =.ctags= when customize this variable for Exuberant Ctags.

Universal Ctags does NOT have this problem. ** Ignore directories and files You can set up =counsel-etags-ignore-directories= and =counsel-etags-ignore-filenames=, #+begin_src elisp (with-eval-after-load 'counsel-etags ;; counsel-etags-ignore-directories does NOT support wildcast (push "build_clang" counsel-etags-ignore-directories) (push "build_clang" counsel-etags-ignore-directories) ;; counsel-etags-ignore-filenames supports wildcast (push "TAGS" counsel-etags-ignore-filenames) (push "*.json" counsel-etags-ignore-filenames)) #+end_src ** Dependency on Emacs APIs is minimum I intend to keep this package completely independent.

Many native tag API or variable (=tags-file-name=, =tags-table-list=, =visit-tags-table=, =xref-find-references=, etc) are not used.

** Specify multiple tags files =counsel-etags-extra-tags-files= contains extra tags file to parse.

Sample setup, #+begin_src elisp (setq counsel-etags-extra-tags-files '("/usr/include/TAGS" "/usr/local/include/TAGS")) #+end_src

Files in =counsel-etags-extra-tags-files= should have symbols with absolute path only. ** Auto update tags file #+begin_src elisp ;; Don't ask before rereading the TAGS files if they have changed (setq tags-revert-without-query t) ;; Don't warn when TAGS files are large (setq large-file-warning-threshold nil) ;; Setup auto update now (add-hook 'prog-mode-hook (lambda () (add-hook 'after-save-hook 'counsel-etags-virtual-update-tags 'append 'local))) #+end_src You can change callback =counsel-etags-update-tags-backend= to update tags file using your own solution, #+begin_src elisp (setq counsel-etags-update-tags-backend (lambda (src-dir) (shell-command "/usr/bin/ctags -e -R"))) #+end_src ** Rust programming language Tags file for [[https://www.rust-lang.org/][Rust programming language]] can be generated by [[https://github.com/dan-t/rusty-tags][rusty-tags]].

Run =rusty-tags emacs= in shell to generate tags file. You also need =(setq counsel-etags-tags-file-name "rusty-tags.emacs")=.

The easiest way to set up rusty-tags per project is to create [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Directory-Variables.html][.dir-locals.el]] in project root, #+begin_src elisp ((nil . ((counsel-etags-update-tags-backend . (lambda (src-dir) (shell-command "rusty-tags emacs"))) (counsel-etags-tags-file-name . "rusty-tags.emacs")))) #+end_src ** List all tags =M-x counsel-etags-list-tag= ** Two-step tag matching using regular expression and filter =M-x counsel-etags-find-tag= ** Force update current tags file Run =counsel-etags-update-tags-force=. Tags file in project root should exist before running this command. ** Open recent tag =M-x counsel-etags-recent-tag= ** Ctags setup Google "filetype:ctags site:github.com". Here is [[https://gist.github.com/redguardtoo/b12ddae3b8010a276e9b][my configuration for Exuberant Ctags]].

Please note there is some trivial difference between Exuberant Ctags configuration and Universal Ctags.

If you are using Universal Ctags with my configuration for Exuberant Ctags, run below CLI in shell and fixed all the warning by modifying the =~/.ctags= first, #+begin_src sh ctags --options="$HOME/.ctags" -e -R #+end_src

You may need configure environment variable "HOME" on Windows because Ctags looks for "%HOME%/.ctags" by default. ** Search with exclusion patterns All commands support exclusion patterns from [[https://github.com/abo-abo/swiper][ivy]].

You can filter the candidates with =keyword1 !keyword2 keyword3=. So only candidate containing =keyword1= but neither =keyword2= nor =keyword3= are displayed.

You can press =C-c C-o= or =M-x ivy-occur= to export candidates to a buffer.

In summary, all functionalities from [[https://github.com/abo-abo/swiper][ivy]] are supported. ** Grep program If [[https://github.com/BurntSushi/ripgrep][ripgrep]] is installed, it's used as faster grep program. Or else we fallback to =grep=.

Use =M-x counsel-etags-grep= to grep in project root.

Set =counsel-etags-grep-extra-arguments= to add extra arguments for grep.

Use =M-x counsel-etags-grep-current-directory= to grep current directory.

Use =C-u num M-x counsel-etags-grep-current-directory= to grep NUM level up of current directory. If NUM is nil or 0, current directory is searched.

Grep result is sorted by string distance of current file path and candidate file path. The sorting is enabled in Emacs 27+.

You can set =counsel-etags-sort-grep-result-p= to =nil= to disable sorting. ** Customize grep keyword Users could set =counsel-etags-convert-grep-keyword= to customize grep keyword.

For example, below setup enable =counsel-etags-grep= to search Chinese using [[https://github.com/cute-jumper/pinyinlib.el][pinyinlib]], #+begin_src elisp (unless (featurep 'pinyinlib) (require 'pinyinlib)) (setq counsel-etags-convert-grep-keyword (lambda (keyword) (if (and keyword (> (length keyword) 0)) (pinyinlib-build-regexp-string keyword t) keyword))) #+end_src

Or create a new grep command =my-grep-by-pinyin=, #+begin_src elisp (defun my-grep-by-pinyin () (interactive) (unless (featurep 'pinyinlib) (require 'pinyinlib)) (let* ((counsel-etags-convert-grep-keyword (lambda (keyword) (if (and keyword (> (length keyword) 0)) (pinyinlib-build-regexp-string keyword t) keyword)))) (counsel-etags-grep))) #+end_src ** Windows Installing Cygwin and its package Ctags on any driver is all you need to do. No extra setup is required.

But you could still set up =counsel-etags-find-program=, =counsel-etags-ctags-program=, and =counsel-etags-grep-program= to specify the command line program path. ** /.ctags.exuberant If base configuration file "/.ctags.exuberant" exists, it's used to generate "~/.ctags" automatically.

"/.ctags.exuberant" is in Exuberant Ctags format, but the "/.ctags" could be in Universal Ctags format if Universal Ctags is used.

You can customize =counsel-etags-ctags-options-base= to change the path of base configuration file. ** Use Ctags to generate Imenu items Run =M-x counsel-etags-list-tag-in-current-file= to list tags in current file.

You can also use native imenu command with below setup, #+begin_src elisp (setq imenu-create-index-function 'counsel-etags-imenu-default-create-index-function) #+end_src

Set =counsel-etags-imenu-excluded-names= to exclude imenu items by name.

Set =counsel-etags-imenu-excluded-types to exclude imenu items by type.

  • Step by step guide You need use Linux/Cygwin/MSYS2. It should be similar in macOS but I'm not sure whether the directory =/usr/include= exists.

** Step 1, a toy C project Run below script in Bash shell to create a toy project. #+begin_src bash #!/bin/bash mkdir -p ~/proj1 && cd ~/proj1 cat > .dir-locals.el <<EOF ((nil . ((counsel-etags-project-root . "$PWD") (counsel-etags-extra-tags-files . ("./include/TAGS"))))) EOF cat > hello.c <<EOF include <stdio.h>

void fn() { }

int main() { printf('hello world'); fn(); return 0; } EOF mkdir -p include && cd include && find /usr/include | ctags -e -L - #+end_src

** Step 2, navigate code Open =hello.c= in Emacs (say "YES" if Emacs ask any question), move focus over symbol "fn" or "printf", run =counsel-etags-find-tag-at-point=.

  • Bug Report Report bugs to [[https://github.com/redguardtoo/counsel-etags]].
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].