Giter Club home page Giter Club logo

citre's People

Contributors

amaikinono avatar masatake avatar norris-young avatar tumashu avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

citre's Issues

Insert funtion prototype template and eldoc support

When I want to insert a function call, only the function name is inserted into the buffer without more prototype info, like its parameter list. Is this possible in citre?

Also can citre integrate eldoc?

Accurate inspection on C struct members

Several Citre users talked about this idea to me.

If we are at:

foo.bar|

(| is the cursor), we could:

  1. Find the definitons of foo (we could assume it's a variable, function parameter, or member (maybe more?)).
  2. Find the typeref fields of these definitions. They should be typeref:struct:structName (if there are more than 1 definitons, multiple structName is possible.
  3. Sort the definitions of bar so those with struct:structName scope field are on the top.

I need to learn more on how a C file is tagged:

  • For foo->bar, foo is a pointer. What does its typeref field look like?

Also, some users want struct member completion in this situation:

foo.|

We could do 1 and 2 the same as above, then:

  • Filter all tags and find those with appropriate scope info. This can be slow.
  • Or, assume qualified tags are enabled, then we could look for tags start with structName.. This should be fast.

I'd like to take the latter approach. The infrastructure of Citre needs some mild modify to support it, though.

If we could solve this, the design can be used for other static type languages, too. If we could also find the way of dealing with inheritance, I'd say Citre will have the potential of becoming the best tool for static type languages with class/subtype or even other fancy type systems.

Related works for transferring this repo to the u-ctags organization

@masatake suggests transfer this repo to the universal-ctags organization (here). Since Citre is now close to the 0.1 release, I'd like to do this before we get it into MELPA.

What I'd like to ask is: In the future, I may put donation links (for myself) in README. Is this acceptable for a project under the universal-ctags organization? No problem if it's not suitable, I'm still happy to transfer.

Here's what I need to do (in order):

  • transfer the repo
  • fix the links pointing to this repo in the code
  • finish the TODOs for v0.1 (now the only remaining task is adding more test cases)
  • publish to MELPA
  • add instructions for installing from MELPA
  • make a v0.1 tag
  • add a test for u-ctags to run tests of Citre (see below)

Let the core APIs throw errors on invalid arguments

For now, if the tags file is not found, the error happens on the citre-readtags--get-tags-file-info function, and the error looks like wrong type: stringp or so, which is not very useful.

I just quickly fix it in citre.el (not pushed yet), but we should also let core APIs check the arguments and throw errors when necessary.

Language-specific support framework

I'm working on this topic. It's a complicated one, and I feel it's better to implement basic things for v0.1, and explore further possibilities later (inputs from our users will be important).

Before v0.1

  • Customize filter and sorter expressions for language.
  • See if the elisp sorter is faster than the readtags one. If it is, we'd encourage people to use post-sorter.
    Edit: Sorting in Elisp is slower, and that's largely because of GC. I think it's better to wait for the need of multiple queries (see below) to emerge, than implement post-sorter.

Further possibilities

Customize required fields

For now, we just hard code required/optional fields that are useful to show. For the current UI it works good, but after we have user-controlled filtering, users & language support implementations would ask for extra fields to filter on. I don't have a clear idea on this.

Another usage is: readtags can only filter on language-common fields. Language-specific fields may not be shown to the user, but we need to filter on them. Then it also becomes necessary to add a post-filter that uses elisp function.

Case-insensitive languages

For now, how to filter based on the symbol name is handled by Citre, and the case sensitivity is controlled by an user option. For case-insensitive languages like Fortran, we must override this option.

Another problem is for auto-completion in such languages, the user would expect the result to keep the style of their input. For example, the user inputs fun, a tag named FUNCTION is used to complete it, then it's expected to become function.

A more complicated language is Nim. It's not case-insensitive, but style-insensitive! some_function and someFunction are the same. To support it, we must tell Citre: I'll decide how to filter on the symbol name, and I want to transform the tag name into the style of user input.

Multiple queries

e.g. use filter that depends on the result of former queries, for handling class inheritance. See universal-ctags/ctags#2475 (comment).

This may require post-sorter (sorting in Elisp), since we need to put the result of multiple runs in one list.

citre-peek--make-session: Wrong type argument: stringp, nil

M-x citre-peek reports the error when nothing is at the point.

Backtrace:

Debugger entered--Lisp error: (wrong-type-argument stringp nil)
  substring-no-properties(nil)
  citre-peek--make-session(#<buffer core.c> 156935)
  citre-peek()
  funcall-interactively(citre-peek)
  call-interactively(citre-peek nil nil)
  command-execute(citre-peek)

C: lost information about typeref

In C language, struct tags and type names defined with typedef have different namespaces.

struct kvm;
struct A;
typedef struct A kvm;
struct foo {
  struct
  kvm *bar; /* @ */
};

int bar;

When pressing M-. on bar at "@" line, xref shows:

/tmp/bar.c
6: (member/kvm *@struct:foo) kvm *bar;
9: (variable/int) int bar;

The user may think the "bar" is typed with "kvm*". It is not true; "bar" is typed with "struct kvm*"

Code readers well optimized for C language utilize the difference between "kvm *" and "struct kvm *" as hints.
They expect:

/tmp/bar.c
6: (member/struct:kvm *@struct:foo) kvm *bar;
9: (variable/int) int bar;

or

/tmp/bar.c
6: (member/struct kvm *@struct:foo) kvm *bar;
9: (variable/int) int bar;

ctags provides enough information:

bar	bar.c	/^  kvm *bar;$/;"	m	struct:foo	typeref:struct:kvm *	file:

citre may drop the first element of typeref:.

The case that a client tool should drop the first element is when the first element is "typename".
"typename" is a placeholder. So you can drop it.

Deciding what we want for code reading tools in Citre

Now we have 3 ways of reading code:

  1. citre-jump and xref integration. They are both for linear reading. xref may offer a better experience if a completion framework is not used.

  2. citre-peek. I wrote this for code writing (so you can read the definition without leaving the current buffer), but seems @masatake likes it, so let's discuss the possibility that citre-peek being used in "serious" code reading sessions.

  3. citre-map. This is for long code reading sessions (of several hours/days). When solving a problem, we could lookup a lot of symbols and their definitions, but gradually we found some are important, and others have nothing to do with the problem, so we hide/delete them. Eventually we'll have a small map that leads us to everywhere we care about.

And maybe we want another "git-like" code reading tool, which @masatake has mentioned in our private emails.

For each kind of code reading session, I want Citre to include only one tool that suits it perfectly. I have a lot to say about each of the tools, so I'll split them into different issues and put the links here:

  • citre-peek: #18.
  • citre-map: #19.
  • "git-like" code reading tool: #20.

Jump to C source file from a Makefile

In linux/arch/x86/kernel/apic/Makefile, I gave Emacs "C-u M-.", and "hw_nmi.c".
citre doesn't take me to the C file.

When using readtags directly, I got

$ readtags -e hw_nmi.c
hw_nmi.c	arch/x86/kernel/apic/hw_nmi.c	1;"	kind:F

I extracted the filter expression used in the above process with strace.

(not (or
      (and $extras ((string->regexp "(^|,) ?(anonymous inputFile)(,|$)" :case-fold false) $extras))
      (and $kind ((string->regexp "^(file|F)$" :case-fold false) $kind))
      ))

For me, this filter expression avoiding F kind is too strong.

citre conflicts with tramp

When i use tramp in the dired mode, press the 'v' to open a file, it will be stopped, my friend,
you can have a try. eg:
Tramp: Inserting ‘/ssh:[email protected]:/root/gmssl/gmssl/GmSSL-master/apps/asn1pars.c’...done

My config code as follow:
(use-package citre
:defer t
:init
(require 'citre-config)
(defun prog-mode-citre-bindings()
(local-set-key (kbd "C-c j") 'citre-jump)
(local-set-key (kbd "C-c l") 'citre-jump-back)
(local-set-key (kbd "C-SPC o") 'citre-peek)
(local-set-key (kbd "C-c i") 'custom-citre-completion-at-point)
)
(defun prog-mode-citre-c-bindings()
(local-set-key (kbd "C-SPC o") 'citre-peek)
(local-set-key (kbd "C-SPC i") 'citre-peek-through)
(local-set-key (kbd "C-SPC f") 'citre-peek-chain-forward)
(local-set-key (kbd "C-SPC j") 'citre-peek-chain-backward)
(local-set-key (kbd "C-SPC p") 'citre-peek-next-definition)
(local-set-key (kbd "C-SPC n") 'citre-peek-prev-definition)
(local-set-key (kbd "C-c i") 'custom-citre-completion-at-point)
)
(add-hook 'verilog-mode-hook 'prog-mode-citre-bindings)
(add-hook 'c-mode-hook 'prog-mode-citre-c-bindings)
(setq
;; Set these if readtags/ctags is not in your path.
;; citre-readtags-program "/path/to/readtags"
;; citre-ctags-program "/path/to/ctags"
;; Set this if you use project management plugin like projectile. It's
;; used for things like displaying paths relatively, see its docstring.
citre-project-root-function #'projectile-project-root
;; Set this if you want to always use one location to create a tags file.
citre-default-create-tags-file-location 'global-cache
;; See the "Create tags file" section above to know these options
citre-use-project-root-when-creating-tags t
citre-prompt-language-for-ctags-command t)
)
Please give a advice on this problem! Thank you!

Process output truncated in Windows?

On Windows, I've found sometimes Citre couldn't parse readtags output. I got "Invalid Pattern field" or "Args out of range" Errors.

The reason seems like early output of readtags has been truncated, so the first line we get starts from the middle of a tagline.

Has anyone else encountered this problem?

Is it not reliable to get process output through buffers on Windows?

Turning on citre-mode in dired-mode

u-ctags can record included C header files as reference tags to a tags file
If we can use citre-mode in dired-mode buffers, we can list "which source files includes foo.h" with pressing M-. on "foo.h", a dried entry.

xref-backend-identifier-completion-table, may not using buffer local value for citre-tags

Hello!

Thanks you for a great package, it one of the most important packages for me to make possible to use Emacs for COBOL development!

I have problem thou; I have been experimenting with using file-specific tags because I can have several cobol files in a project and they can have the same variables with the same name, and sections with the same name but they don't have anything to do with each other.

So right now I'm setting citre-tags-files to a buffer-local value, ".tags/NAME-tag", and then I then the output of the ctags command is also modified.

This first seemed to work great, but then I discovered that I did't get any completions when I didn't have any symbol under point when running xref-find-definitions.
From that I can tell it seems that my buffer-local value is not honored when citre-xref--completion-table-cache is created because the :tags-file key is just "path/to/dir/tags".

Is this something that can be fixed, or is there maybe a better way for me to do this?

Then there seems also to be a minor typo in citre-xref--completion-table-cache, :tage-file, instead of :tags-file.

Thanks again for your work!

how to reload citre?

Is there a simple way to reload citre?

My reading session is more than a week.
During the session, citre may be improved.
After doing git pull upstream, what I should do for enjoying the latest result of the development.
I think M-x unload-feature may help me but I'm not sure.

How about introducing M-x citre-reload ?

citre-jump is broken

It can't goto places when there are >1 candidates.

The reason seems to be that completing-read returns the string with no text properties, so the record we attached to the string is lost.

Example:

(get-text-property
 0 'prop
 (completing-read ":" (list (propertize "hi" 'prop t))))
=> nil

It should return t, and I believe it does in Emacs 26. I haven't find information on this behavior.

Top-level documentation?

I just took a look at citre, it seems good! I used it with anjuta-tags to navigate Vala code.

So far, I was able to install it, and get citre-jump to work, also xref-integration. Great!

It would be good to see citre packaged for MELPA, and with some basic top-level documentation in the package description and in a README.md file, to help users get started. Are you planning that?

Thanks for working on citre!

Is citre-map good?

This is a sub-issue of #17.

I actually don't have much to say about this tool. It's designed for long-time and/or non-linear code reading, with its history being a tree (and not a stack), but I don't have much experience doing such reading. What's good about it is that the underlying mechanism is the same as citre-jump, just the UI is different, so I can maintain it without too much effort.

I need @masatake's opinion here: If you think it's useful, I'll keep it; If you don't think it's a good tool for long-time/non-linear code reading, I'll deprecate it.

If we are going to keep it, I'd like to make it possible to keep notes in the map. People could attach a string to each item in the map, and we'll decide what's the best way to show it, either directly in the map, or use an icon to show "here's a note" and people could open it in an overlay.

I once thought about supporting annotating in files but it's better to use specialized packages like annotate.el for it.

Another problem of citre-map is it assumes the code doesn't change. We have citre-code-map-update to update the map, and it needs some manual effort. But I can't think of a way to keep the notes after such updating. Conceptually, if we can diff between the new and old versions, we could tell where are the "updated versions" of the old symbols and definitions, and this should be possible with a VCS, but I still don't have an idea how to do it, and even a VSC can't deal with file renaming 100% reliably.

Any other suggestions on citre-map are welcomed.

Provide post-sorter in core APIs

Sorter expressions in readtags could do simple sorting, and we could use the Emacs built-in sort to further sort it using any compare function.

sort is stable so it won't mess up the order outputted by readtags.

An example is in C, when go to definitions, we should show the tags in current file first, since there may be many static functions in other files with the same name.

Use CI for automatic testing

In #24, I said:

I plan to use https://github.com/purcell/setup-emacs. With it we can test on multiple Emacs versions. An example (from here):

name: CI

on: [push, pull_request]

jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        emacs_version:
          - 25.1
          - 25.3
          - 26.1
          - 26.3
          - snapshot
    steps:
    - uses: purcell/setup-emacs@master
      with:
        version: ${{ matrix.emacs_version }}
    - uses: actions/checkout@v1
    - name: Run tests
      run: 'emacs -Q --batch -L . -f batch-byte-compile *.el'

@masatake said:

I plan to use https://github.com/purcell/setup-emacs. With it we can test on multiple Emacs versions. An example (from here):

Nice.

You may need a way to build readtags at the CI environment.
See https://github.com/universal-ctags/ctags/blob/master/.github/workflows/c-cpp.yml

Please support non-absolute paths

If paths are not absolute, and the TAG_PROC_CWD pseudo-tag is not given (not supported by some tags programs), could citre reasonably assume that the paths are relative to the directory containing the tags file?

This would make citre compatible with automake, which allows automatic generation of tags files, but only with relative paths (when not using universal-ctags).

Rearrange code and add tests

First, I don't plan to work on this soon. But we sure need tests, so discussing it now is not bad ;)

In https://github.com/AmaiKinono/citre/pull/5, @masatake suggested:

I think using an emacs lisp file like citre.el as a target input is not a good idea. Because Emacs provides so much good things for elisp. As the result, we can miss a hidden bug in citre. I think we should choose another language as the target.

https://github.com/universal-ctags/codebase/tree/master/lcopy.d
Here, I listed repositories I used for testing u-ctags.
All of them are large. lsof is not so large. However, it is written in very old style C.

u-ctags itself is not bad target. However, we have to make Units and Tmain excluded when making a tag.


My thought about this:

I think using an emacs lisp file like citre.el as a target input is not a good idea. Because Emacs provides so much good things for elisp. As the result, we can miss a hidden bug in citre.

I'm actually not sure if you are talking about automated test or test "by hand". If we test by hand, sometimes we may use built-in functionalities rather then those offerd by Citre due to habit, and that causes the problem you described. But this won't happen in automated tests.

And, here's the problem: How are automated tests possible for code map? Well, I thought about it. My conclusion is it may actually be easier than testing functionalities in citre.el. Because if you look at those code map commands, most of them don't ask for user inputs, just run it and it will do something. So we could make Emacs open a file, jump to a position, call citre-see-symbol-in-code-map, the run various commands and see if the contents of current line/buffer is as expected. This shouldn't be hard to automate.

These are just some thoughts. For code map, we may need automated tests or not.

think we should choose another language as the target...

If using automated tests, I would like to use some "mini" projects that's suitable for directly including in the Citre repo, so future contributers don't need to clone another repo just to run the tests. In fact, I'm almost sure we will need to do many language specific things in citre.el, so we will need mini projects of different languages for testing.

Entering infinite loop

After doing M-x citre-mode and M-x eldoc-mode, I moved the point in a C buffer.
Then emacs enters an infinite loop.

I inspected a bit about this issue.

the C buffer:

void g (void)
{
        int i;

  1. set the point on <i>nt i.
  2. do M-: (citre--find-function-name-pos-generic)

I had to type C-g to get the control back.

Handle the pattern field properly

I got an error:

progn: Fields not found in tags file: line

I generated the tags file with

cd ~/var/ctags
u-ctags --pseudo-tags=+TAG_PROC_CWD  --fields=+n -n -R main parsers

If I removed -n from the command line and regenerated the tags file. The error was gone.

I think rejecting a tag file with -n option automatically is a simple solution.

How to navigate in a function call chain

First, I've read the documentation but I just don't understand what does <left> and <right> means in the section "Follow a function call chain". I've tried the left and right arrow key on the keyboard but they just move the cursor. Maybe you can clarify that more in the documentation.

Another thing, maybe I should not talk it here, is that I tried Citre with Unreal Engine code and, just like any tagging based code navigation solution, it is almost unpractical. One important problem about these tagging system, is that they do not recognize macros. Consider the following code:

#define SOME_MACRO(...)

struct T
{
    SOME_MACRO(some, annotation)
    int x;
};

This snippet could compile. However, a tagging system could not recognize x as a member variable of struct T. Also, if you try to find definition of SOME_MACRO, the tagging system could not provide the right answer. It can only list all the occurrence of the macro, which includes the #define statement among them.

I guess this is the limitation of the tagging system which makes them not practical to use with heavily macrofied code base.

Question: can project removable?

My understanding is that citre is "tags" centric program.

I wonder why the concept "project" is needed. Is specifying a tags file not enough for making citre working?

To solve the path for source file can be done with _TAG_PROC_CWD pseudo tags:

[yamato@slave]~/var/ctags-github% ./ctags --pseudo-tags=+TAG_PROC_CWD  -R
[yamato@slave]~/var/ctags-github% head tags
!_TAG_FILE_FORMAT	2	/extended format; --format=1 will not append ;" to lines/
!_TAG_FILE_SORTED	1	/0=unsorted, 1=sorted, 2=foldcase/
!_TAG_OUTPUT_FILESEP	slash	/slash or backslash/
!_TAG_OUTPUT_MODE	u-ctags	/u-ctags or e-ctags/
!_TAG_PATTERN_LENGTH_LIMIT	96	/0 for no limit/
!_TAG_PROC_CWD	/home/yamato/var/ctags-github/	//

In this example, all input files are relative to /home/yamato/var/ctags-github/.

If what I write here is correct, I will turn on _TAG_PROC_CWD by default in u-ctags side.

Discussion: better method for escaping quotes in shell commands

In #7, @masatake introduced citre--disable-single-quote-as-terminator and citre--format-shell-command to prevent a certain kind of shell injection. Please read the changes there to know the background of this issue.

I've found a situation that these functions are not enough or not easy to work with: citre--format-shell-command formats all of objects using %s first, but we actually may not use %s for the format string. Here's an actual snippet in citre.el:

(citre--format-shell-command "'%s' -t '%s' -Q '%S' -nel"
                             program tagsfile
                             `(,op ,name-expr ,symbol-expr))

We want to use %S to format the sexp, but before that, it's already formatted using %s by citre--format-shell-command, producing wrong results. See this example:

(format
 "readtags -Q '%S' -l"
 `(prefix? $name ,"citre"))
=> "readtags -Q '(prefix\\? $name \"citre\")' -l"


(citre--format-shell-command
 "readtags -Q '%S' -l"
 `(prefix? $name ,"citre"))
=> "readtags -Q '\"(prefix? $name citre)\"' -l"

(Also notice when formatting using %S, the symbol prefix? becomes prefix\?. This works, obviously because \? is just an escaped ?, but this makes me thinking whether the behavior of using %S to format a whole sexp (which, is actually print it using prin1) is predictable enough for us to rely on)

(edit: for the above problem, I did some read and found the differences between prin1 and princ are: princ does't produce quotes around the formatted part, and doesn't do "quoting", which, according to some other lisp literature, means "escaping" using backslashes. For lists, prin1 doesn't produce extra quotes too, and backslash just means "make the next character stand for itself and don't give it a special meaning". So, we are good using %S, although I suspect %s can also do the work. I'll try it later.)

And I I'm not clear about how to deal with nested quotes. I'm working on reading the pseudo tags, and I wrote this:

(citre--format-shell-command
 "'%s' -t '%s' -Q '(eq? \"%s\" $name)' -D"
 program tagsfile name)

Notice that NAME is formatted using "%s", then it's wrapped again in single quotes. It's clear that single quotes in NAME still need to be escaped, and I think double quotes need to be escaped too, or we won't get the right results. But I'm not sure if other kinds of shell injection can happen in this situation.

And, notice the ,name-expr in the first snippet has the same problem.

I suggest:

  • We get rid of citre--format-shell-command. It's hard to make this function general enough.
  • We also deal with escaping double quotes. Can be a separate function, or we could create a (citre--shell-escape &optional kind), and kind can be 'single, 'double or 'both.
  • When we build any shell command, we decide what kind of escape we want, for each argument, and wrap them in appropriate escape functions.
  • When we write our docs for developer, we explain this whole thing and ask them to pay special attention when dealing with shell commands.

citre conflicts with tramp

When i use tramp in the dired mode, press the 'v' to open a file, it will be stopped, my friend,
you can have a try. eg:
Tramp: Inserting ‘/ssh:[email protected]:/root/gmssl/gmssl/GmSSL-master/apps/asn1pars.c’...done

My config code as follow:
(use-package citre
:defer t
:init
(require 'citre-config)
(defun prog-mode-citre-bindings()
(local-set-key (kbd "C-c j") 'citre-jump)
(local-set-key (kbd "C-c l") 'citre-jump-back)
(local-set-key (kbd "C-SPC o") 'citre-peek)
(local-set-key (kbd "C-c i") 'custom-citre-completion-at-point)
)
(defun prog-mode-citre-c-bindings()
(local-set-key (kbd "C-SPC o") 'citre-peek)
(local-set-key (kbd "C-SPC i") 'citre-peek-through)
(local-set-key (kbd "C-SPC f") 'citre-peek-chain-forward)
(local-set-key (kbd "C-SPC j") 'citre-peek-chain-backward)
(local-set-key (kbd "C-SPC p") 'citre-peek-next-definition)
(local-set-key (kbd "C-SPC n") 'citre-peek-prev-definition)
(local-set-key (kbd "C-c i") 'custom-citre-completion-at-point)
)
(add-hook 'verilog-mode-hook 'prog-mode-citre-bindings)
(add-hook 'c-mode-hook 'prog-mode-citre-c-bindings)
(setq
;; Set these if readtags/ctags is not in your path.
;; citre-readtags-program "/path/to/readtags"
;; citre-ctags-program "/path/to/ctags"
;; Set this if you use project management plugin like projectile. It's
;; used for things like displaying paths relatively, see its docstring.
citre-project-root-function #'projectile-project-root
;; Set this if you want to always use one location to create a tags file.
citre-default-create-tags-file-location 'global-cache
;; See the "Create tags file" section above to know these options
citre-use-project-root-when-creating-tags t
citre-prompt-language-for-ctags-command t)
)
Please give a advice on this problem! Thank you!

Use extensible data type for records, and introduce customizable line parsing

Extend the records

The "records" in Citre need to be extensible, to let the upper tools be able to use all information offered by a tags file. We also want the records to not store unwanted information, since:

  1. For the sake of performance.
  2. If unwanted information needs additional information to be ascertained, we don't need to ask for it, since it may bother the user (see #14, especially the added docs/core-layer.md).

We want the records to be able to hold all fields in a tag entry, plus additional fields defined by Citre. I have 2 in mind now:

  • Absolute path. Now the records actually use absolute paths, but if we want to have all fields offered by the tag entry, unmodified, then we have to make absolute path an additional field. This may need the current working directory information, can be get from the TAG_PROC_CWD pseudo tag, or by asking the user.

  • Full kind. If the tags file uses single-letter field, we can convert them to the full version. For those built-in ones, I guess we could just look at ctags help information and create a table from it; for the user defined ones, we may need the TAG_KIND_DESCRIPTION pseudo tag. I think it's also good to let the user customize the table, based on his/her global ctags config file.

(Let me roast it: Maybe it has a reason in the early days of ctags, but, why using single-letter kinds to "generate smaller tags file", while writing a long "pattern" that only ex-family editors could use, rather than a simple line number?)

Add more additional information

#14 introduced citre--tags-file-info-alist and citre--tags-file-info to deal with additional information needed. Right now it only deals with the current working directory mentioned above, and it's guaranteed to only present when relative paths are indeed used in the tags file. I suggest we extend it into 4 fields:

  • Does the tags file use relative paths?
  • Current working directory when generating the tags file.
  • Does the tags file use single-letter kinds?
  • The table to convert user defined single-letter kinds to the full version.

Customizable line parsing

The basic idea is to let citre--readtags-parse-line to produce records that only contains the information needed. But there's much to consider. My thoughts for now:

  • For some tools, there are "needed fields" and "optional fields". For example, for auto-completion, the tag name is needed, and actually it can work using only the tag names. The tag name is a needed field, and we should throw an error if it doesn't exist (though in this case, it won't happen). If we also have the kind field, then it can show it to the user, maybe can also do some smart things based on it. The kind is an optional field, the tool want it, but when it's missing, we shouldn't throw an error.

  • Sometimes we may also want to do it in another direction, using "excluded fields". Take auto-completion again, it may use many language specific fields, and the number will grow as the tool develops and its user extends it. Then a better way to specify the fields needed maybe: "Don't give me the path, line, pattern and end, give me all other fields, and the tag name must be contained".

Let people extend this without touching Citre (or, readtags.el)

If we want a stable readtags abstraction layer, then this is needed. We need APIs for people to register:

  • Additional fields in records, and how to generate them. I think it's good enough to make all fields offered by the tags file, plus existing extension fields, as raw materials.

  • What's the additional information needed? How to get them?

Then, people can get the defined extension field by citre--readtags-parse-line, and citre--tags-file-info can take care of the additional information.

For the additional fields, I have more to say:

  • Notice that the line field in Citre is not contained in the records, but fetched from the file in realtime. This should also be supported. When we ask for it in citre--readtags-parse-line, it doesn't write that into the records, but write all the needed fields (line and absolute path, in this case).

  • Not all lines have the same fields. If an additional field is defined based on the signature field, then even when it's required on a variable line, we shouldn't throw an error. I think we need a way to declare that an additional field is meaningful only when some fields are presented or have some specific values. Of course, another way is to restrict the "raw material" to only the common fields.

We should do this after all above ideas are implemented.

No completion after dot typed

There is a misleading in the README, though not sure if that is only restricted to me.

It's said that if a dot is typed, the citre then guesses the following symbol should be a struct member and so that completion is limited to member. But when I type, e.g. foo., no completion pops up. Only after continuing to type partial member's name does the completion appear.

And another one. The candidates of a struct are not exactly constrained to THAT struct. Instead, all candidates, as long as they are tagged as member, are listed.

I guess these two phenomena are intrinsic drawbacks from the backend of ctags. It's good at searching with a well defined name at a global scope, but weak at completion or semantics. I mean it cannot find the relationship between two related symbols and tag such attribution.

Is my assumption correct? Do you have any idea about how to make it more completion friendly? Thanks in advacne.

Tips when work with projectile

For some reason, projectile will try to load the entire TAGS every time when one open a file, see here.

Since Citre don't need load the entire TAGS, user of projectile may need remove that hook function from find-file-hooks or modify it.

citre-tags-file-alist is invaild

citre/citre-util.el

Lines 182 to 205 in ed26e10

(defun citre-tags-file-path ()
"Return the canonical path of tags file for current buffer.
This looks up `citre-tags-files' to find the tags file needed,
and throws an user error if no tags file was found."
(if (and citre--tags-file (file-exists-p citre--tags-file))
citre--tags-file
(let ((current-file (or (buffer-file-name) default-directory))
(project (funcall citre-project-root-function)))
(or
(cl-dolist (pair citre-tags-file-alist)
(when (and (or (not (file-name-absolute-p (car pair)))
(not (file-name-absolute-p (cdr pair))))
(null project))
(user-error "Relative path used in `citre-tags-file-alist', \
but project root can't be decided by `citre-project-root-function'"))
(when (file-in-directory-p current-file
(expand-file-name (car pair) project))
(cl-return (setq citre--tags-file
(expand-file-name (cdr pair) project)))))
(cl-dolist (tagsfile citre-tags-files)
(let ((dir (locate-dominating-file current-file tagsfile)))
(when dir
(cl-return (setq citre--tags-file
(concat (expand-file-name dir) tagsfile))))))))))

(let ((current-file (or (buffer-file-name) default-directory)) 
           (project (funcall citre-project-root-function))) 

(null "111")
nil
(null ())
t
(null '(a))
nil
;; null function is empty object t else nil

(null project)
nil
(when (and (or (not (file-name-absolute-p (car pair))) 
                         (not (file-name-absolute-p (cdr pair)))) 
                     (null project))
nil

(file-in-directory-p dir file)
;; current-file  is file
;; citre-tags-file-alist is dict
;; what if (car pair) ?
;; then (cdr pair)
(when (file-in-directory-p current-file 
                                     (expand-file-name (car pair) project)) 
nil
(cl-return (setq citre--tags-file
                            (expand-file-name (cdr pair) project)))))

add citre-mode-map, so user can define key to override global keybindings

  (defvar my/citre-mode-map
    (let ((map (make-sparse-keymap)))
      (define-key map (kbd "M-.") 'citre-jump)
      (define-key map (kbd "M-,") 'citre-jump-back)
      map))
  (require 'citre)
  (require 'citre-config)
  (add-to-list 'minor-mode-map-alist (cons 'citre-mode my/citre-mode-map))

Currently, I hack minor-mode-map-alist to do this, it would be great if this package provide it out of box.

Jump back without `citre-jump-back`

For those who use Citre with other tools (imenu, grep...):

;;; jump back enhancement
(defun my--add-point-to-find-tag-marker-ring (&rest r)
  (xref-push-marker-stack (point-marker)))
(dolist (func '(find-function
                counsel-imenu
                helm-imenu
                projectile-grep
                helm-grep-ag
                counsel-rg
                lsp-ivy-workspace-symbol
                citre-jump))
  (advice-add func :before 'my--add-point-to-find-tag-marker-ring))

You can jump back with any of them using M-,.

freeze when call citre-jump on space character

I use the default setup and citre-jump be used as the backend of xref. When I do M-. (xref-find-definitions) on a whitespace character, Emacs will freeze and not accept any input, until a C-g.

It might good to prompt user input when citre-jump be called on whitespace character, as I can remember this is a quite common behavior for xref backend of etags and GNU global.

Using citre-peek for serious code reading?

This is a sub-issue of #17.

The ideas and implementation

@masatake talks about some ideas in https://github.com/AmaiKinono/citre/issues/8#issuecomment-617524729, and let's discuss them.

Basically what he want is a "peek-back" feature, which means after jumping in a citre-peek session, we use a peek window to display the content where we come from.

If we want this, I'd like to create a citre-peek-back function that peeks the previous node in citre--marker-ring. Then an user option like citre-peek-keep-peek-window, which means to:

  • automatically peek back after citre-jump in a citre-peek session
  • automatically peek forward after citre-jump-back in a citre-peek-back session.

So the peek window always follows (or "escorts" as @masatake puts it) the user.

I also need to create a citre-peek-restore-session that restores the recent session. Since the peek window disappears after C-g, and people unintentionally does that, a restore command is a must for code reading.

What makes it unique?

I'd say I seriously love this idea, but I want to be clear about what kind of code reading sessions it is designed for.

Since this is also for linear code reading, conceptually it's the same as you open 2 windows, and jump to definition/go back in the other window, and cross-read between them. The pros of "escort" I could think of are:

  • Slicker experience. No need to move your eyes between 2 windows, so it feels more like "linear" reading.

The cons are:

  • The peek window takes a lot of space, which may affect reading. It also feels somewhat fragile, but I don't think it's a problem with the restore command.

And here's an interesting observation:

  • You can't search/edit the content in the peek window, but you could just jump to there and start editing. The peek window still shows where you come from for cross-reading, so editing is just as slick (or maybe slicker than) the 2-window approach.

Implementing peek-back and the escort mechanism is not a big deal, but we need to know the essential difference between this and the 2-window approach. We want to clearly tell the user: "use citre-jump/xref when ..., and use peek/escort when ....". I don't know the answer, and that's what I want to bring into discussion.

I don't know if we should expand the business of citre-peek to non-linear code reading. I think that would be unnecessarily complex both in implementation and usage, but if people have good reason for this, we can discuss it.

Better default filtering/sorting behavior

It's meaningful to have good default behavior on filtering/sorting, especially before we invest enough time on language-specific support.

  • Smarter auto case fold. Underscores in the symbol should make it case sensitive.
  • Exclude anonymous/qualified tags (see extras: field).
    Note: I decided to keep qualified tags. They can be useful.
  • Exclude tags that have file scope, and is not in this file.
  • Exclude reference tags for auto-completion.
  • Exclude file tags.

We also want to provide a second-stage filtering mechanism for the user, so they can say "oh I only want tags in this file / in some language". Currently I don't have any idea on the design, and I consider it a task after v0.1.

Better default sorting behavior:

  • For finding definitions, put reference tags below definition tags.

I thought about this:

  • For auto-completion, only keep tags that's in the same language.

But I decided not to do it.

One reason is there are some common multi-language programming patterns, like C/C++ (C headers are considered as C++ file), and HTML/CSS/JavaScript. If we only keep tags that's in the same language, we'll have to immediately offer language support for them.

Another reason is users may have their own sub-parser and its generated tags should be used (see universal-ctags/ctags#2557 (comment), though that's not a very good example). If we only keep tags that's in the same language, we'll have to offer user options for these use cases.

I also thought of sorting them above others, but that's also not a good idea. It's not good when the user wants a symbol in another language, and they go through the candidates from a to z and find it's below that "z".

I'd like to wait till we implement the (manual) second-stage filtering, then pick a language in the results is easy and the user has full control.


Here's a study of $ ctags --list-* outputs. Language-common things that we could make use of are:

  • extras:
    • anonymous: Non-named objects (exclude).
    • fileScope: Tags of file scope (exclude if not in current file).
    • inputFile: File tags (exclude).
    • qualified: Not very useful, but maybe good to keep. I want to exclude qualified tags whose "namespace" is an anonymous tag, but seems it can't be done.
    • reference: Exclude for auto-completion. For finding definitions, we can put them below others and give them a special annotation so people know they are reference tags.
  • fields:
    • extras: For the "extras" info above.
    • access: Access (private/public) of class members. For now I don't have good idea on it.
    • file: Only file tags have this field. Exclude them.
  • kinds:
    • KInds are language-specific, other than the file/F kind for file tags. Exclude them.

company auto completion lag on Windows

I'm using Emacs 27.2 on Windows 10, minimum config to reproduce with chromium repo:

;;; straight.el
(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-vc-git-default-clone-depth 1)
(defalias 'straight! 'straight-use-package)

(straight! 'company)
(straight! '(citre :host github :repo "universal-ctags/citre" :branch "develop"))

(global-company-mode)

(require 'citre)
(require 'citre-config)

19-49-42


previous discussion

Peek window in *xref* buffer

I want the peek window to show the definition for the language object under the point at xref buffer
xref shows only one line pattern for each language object. In some cases, showing only one line is not enough when choosing one of the definitions for the language object.

xref buffer:

/home/jet/var/linux/arch/x86/events/perf_event.h
661: (member) int		apic;
/home/jet/var/linux/arch/x86/include/asm/apic.h
364: (externvar) extern struct apic *apic;
290: (struct) struct apic {
/home/jet/var/linux/arch/x86/include/asm/kvm_host.h
545: (member/kvm_vcpu_arch::kvm_lapic) struct kvm_lapic *apic;    /* kernel irqchip context */
/home/jet/var/linux/arch/x86/kernel/apic/apic_flat_64.c
23: (variable/apic) struct apic *apic __ro_after_init = &apic_flat;
/home/jet/var/linux/arch/x86/kernel/apic/probe_32.c
116: (variable/apic) struct apic *apic __ro_after_init = &apic_default;

If the point is at apic of 290: (struct) struct apic {, I expect the peek window to show the definition of the struct as the first candidate.

Getting an error when passing a symbol name explitly (C-u M-.)

I pressed C-u M-. in a buffer where a "meson.build" file is loaded. The mode of the buffer is "Fundamental".
I gave a name of symbol I was interested in.
I got an error. I took the following backtrace:

Debugger entered--Lisp error: (wrong-type-argument stringp nil)
  file-exists-p(nil)
  (if (file-exists-p tagsfile) nil (error "%s doesn't exist" tagsfile))
  (unless (file-exists-p tagsfile) (error "%s doesn't exist" tagsfile))
  citre-core--tags-file-info(nil)
  (let* ((filter (list 'or)) (match (cond ((memq match '(eq nil)) 'eq) ((eq match 'in-dir) 'prefix) (t nil))) (info (citre-core--tags-file-info tagsfile)) (cwd (gethash 'dir info)) (relative-filename (if (and (gethash 'relative-path-p info) (string-prefix-p cwd filename)) (progn (substring filename (length cwd)))))) (setq filter (cons (citre-core-build-filter 'input filename match) filter)) (if relative-filename (progn (setq filter (cons (citre-core-build-filter 'input relative-filename match) filter)))) (nreverse filter))
  citre-core-filter-input(nil nil)
  (list 'not (citre-core-filter-input file-path tags-file))
  (list 'and (list 'not (citre-core-filter-input file-path tags-file)) (list 'or (citre-core-filter-field-exist 'file) (citre-core-build-filter 'extras "fileScope" 'member)))
  (list 'or (citre-core-build-filter 'extras "anonymous" 'member) (citre-core-filter-kind "file" tags-file) (citre-core-build-filter 'extras "inputFile" 'member) (list 'and (list 'not (citre-core-filter-input file-path tags-file)) (list 'or (citre-core-filter-field-exist 'file) (citre-core-build-filter 'extras "fileScope" 'member))))
  (list 'not (list 'or (citre-core-build-filter 'extras "anonymous" 'member) (citre-core-filter-kind "file" tags-file) (citre-core-build-filter 'extras "inputFile" 'member) (list 'and (list 'not (citre-core-filter-input file-path tags-file)) (list 'or (citre-core-filter-field-exist 'file) (citre-core-build-filter 'extras "fileScope" 'member)))))
  `(not (or ,(citre-core-build-filter 'extras "anonymous" 'member) ,(citre-core-filter-kind "file" tags-file) ,(citre-core-build-filter 'extras "inputFile" 'member) (and (not ,(citre-core-filter-input file-path tags-file)) (or ,(citre-core-filter-field-exist 'file) ,(citre-core-build-filter 'extras "fileScope" 'member)))))
  (let ((tags-file (citre-get-property symbol 'tags-file)) (file-path (citre-get-property symbol 'file-path))) `(not (or ,(citre-core-build-filter 'extras "anonymous" 'member) ,(citre-core-filter-kind "file" tags-file) ,(citre-core-build-filter 'extras "inputFile" 'member) (and (not ,(citre-core-filter-input file-path tags-file)) (or ,(citre-core-filter-field-exist ...) ,(citre-core-build-filter ... "fileScope" ...))))))
  citre-definition-default-filter("softmmu_virtio_ss")
  (or (citre--get-value-in-language-alist :definition-filter symbol) (citre-definition-default-filter symbol))
  (citre-get-records tagsfile symbol 'exact :filter (or (citre--get-value-in-language-alist :definition-filter symbol) (citre-definition-default-filter symbol)) :sorter (or (citre--get-value-in-language-alist :definition-sorter symbol) citre-definition-default-sorter) :require '(name ext-abspath pattern) :optional '(ext-kind-full line typeref))
  (let ((symbol (or symbol (citre-get-symbol))) (tagsfile (or tagsfile (citre-tags-file-path)))) (unless symbol (user-error "No symbol at point")) (citre-get-records tagsfile symbol 'exact :filter (or (citre--get-value-in-language-alist :definition-filter symbol) (citre-definition-default-filter symbol)) :sorter (or (citre--get-value-in-language-alist :definition-sorter symbol) citre-definition-default-sorter) :require '(name ext-abspath pattern) :optional '(ext-kind-full line typeref)))
  citre-get-definitions(nil "softmmu_virtio_ss")
  (mapcar #'citre-xref--make-object (citre-get-definitions nil symbol))
  citre-xref--find-definition("softmmu_virtio_ss")
  (progn (citre-xref--find-definition symbol))
  (closure (t) (_backend symbol) "Define method for xref to find definition of SYMBO..." (progn (citre-xref--find-definition symbol)))(citre "softmmu_virtio_ss")
  apply((closure (t) (_backend symbol) "Define method for xref to find definition of SYMBO..." (progn (citre-xref--find-definition symbol))) citre "softmmu_virtio_ss")
  xref-backend-definitions(citre "softmmu_virtio_ss")
  #f(compiled-function () #<bytecode 0x14560d9>)()
  xref--show-defs-buffer(#f(compiled-function () #<bytecode 0x14560d9>) ((window . #<window 468 on meson.build>) (display-action)))
  xref--show-defs(#f(compiled-function () #<bytecode 0x14560d9>) nil)
  xref--find-definitions("softmmu_virtio_ss" nil)
  xref-find-definitions("softmmu_virtio_ss")
  funcall-interactively(xref-find-definitions "softmmu_virtio_ss")
  call-interactively(xref-find-definitions nil nil)
  command-execute(xref-find-definitions)

I inspected the error.
I pressed C-u before pressing M-. . So xref.el read a symbol from the user. The symbol was passed to
citre-get-definitions.

(defun citre-get-definitions (&optional tagsfile symbol)
  "Get definitions from tags file TAGSFILE of symbol at point.
If SYMBOL is non-nil, use that symbol instead.  If TAGSFILE is
not specified, find it automatically under current project root.

The result is a list of records, with the fields `ext-abspath',
`line' and `kind'."
  (let ((symbol (or symbol (citre-get-symbol)))
...

xref.el passes the symbol to this function. In this case, symbol is not null.
It means citre-get-symbol is not called.
citre-get-symbol attaches two text-props, file-path and tags-file.
When citre-get-symbol is not called, the passed symbol has no such property.
As the result, the code getting the tags file name from the text property doesn't work expectedly.

Using citre-mode from *grep* buffer

When I press \M-. after enabling ctire-mode in grep buffer, I got:

Debugger entered--Lisp error: (wrong-type-argument stringp nil)
  find-file-name-handler(nil file-in-directory-p)
  file-in-directory-p(nil "/home/jet/var/qemu/")
  (and (gethash 'relative-path-p info) (file-in-directory-p filename cwd))
  (if (and (gethash 'relative-path-p info) (file-in-directory-p filename cwd)) (progn (file-relative-name filename cwd)))
  (let* ((filter (list 'or)) (match (cond ((memq match '(eq nil)) 'eq) ((eq match 'in-dir) 'prefix) (t nil))) (info (citre-core-tags-file-info tagsfile)) (cwd (gethash 'dir info)) (relative-filename (if (and (gethash 'relative-path-p info) (file-in-directory-p filename cwd)) (progn (file-relative-name filename cwd))))) (if (eq system-type 'windows-nt) (progn (let* ((v filename)) (aset v 0 (upcase (aref filename 0)))))) (setq filter (cons (citre-core-filter 'input filename match) filter)) (if relative-filename (progn (setq filter (cons (citre-core-filter 'input relative-filename match) filter)))) (nreverse filter))
  citre-core-filter-input(nil "/home/jet/var/qemu/tags")
  (list 'not (citre-core-filter-input file-path tags-file))
  (list 'and (list 'not (citre-core-filter-input file-path tags-file)) (list 'or (citre-core-filter-field-exist 'file) (citre-core-filter 'extras "fileScope" 'csv-contain)))
  (list 'or (citre-core-filter 'extras '("anonymous" "inputFile") 'csv-contain) (citre-core-filter-kind "file" tags-file) (list 'and (list 'not (citre-core-filter-input file-path tags-file)) (list 'or (citre-core-filter-field-exist 'file) (citre-core-filter 'extras "fileScope" 'csv-contain))))
  (list 'not (list 'or (citre-core-filter 'extras '("anonymous" "inputFile") 'csv-contain) (citre-core-filter-kind "file" tags-file) (list 'and (list 'not (citre-core-filter-input file-path tags-file)) (list 'or (citre-core-filter-field-exist 'file) (citre-core-filter 'extras "fileScope" 'csv-contain)))))
  (let ((tags-file (citre-get-property 'tags-file symbol)) (file-path (citre-get-property 'file-path symbol))) (list 'not (list 'or (citre-core-filter 'extras '("anonymous" "inputFile") 'csv-contain) (citre-core-filter-kind "file" tags-file) (list 'and (list 'not (citre-core-filter-input file-path tags-file)) (list 'or (citre-core-filter-field-exist 'file) (citre-core-filter 'extras "fileScope" 'csv-contain))))))
  citre-definition-default-filter(#("bdrv_flush_all" 0 14 (citre-xref-get-at-point t citre-tags-file "/home/jet/var/qemu/tags" citre-file-path nil citre-bounds (471 . 485))))
  (or (citre--get-value-in-language-alist :definition-filter symbol) (citre-definition-default-filter symbol))
  (citre-get-tags tagsfile symbol 'exact :filter (or (citre--get-value-in-language-alist :definition-filter symbol) (citre-definition-default-filter symbol)) :sorter (or (citre--get-value-in-language-alist :definition-sorter symbol) citre-definition-default-sorter) :require '(name ext-abspath pattern) :optional '(ext-kind-full line typeref extras))
  (let ((symbol (or symbol (citre-get-symbol))) (tagsfile (or tagsfile (citre-tags-file-path)))) (if symbol nil (user-error "No symbol at point")) (citre-get-tags tagsfile symbol 'exact :filter (or (citre--get-value-in-language-alist :definition-filter symbol) (citre-definition-default-filter symbol)) :sorter (or (citre--get-value-in-language-alist :definition-sorter symbol) citre-definition-default-sorter) :require '(name ext-abspath pattern) :optional '(ext-kind-full line typeref extras)))
  citre-get-definitions(#("bdrv_flush_all" 0 14 (citre-xref-get-at-point t citre-tags-file "/home/jet/var/qemu/tags" citre-file-path nil citre-bounds (471 . 485))))
  (if (citre-get-property 'xref-get-at-point symbol) (citre-get-definitions symbol) (citre-xref--get-definition-for-completed-symbol symbol))
  (mapcar #'citre-xref--make-object (if (citre-get-property 'xref-get-at-point symbol) (citre-get-definitions symbol) (citre-xref--get-definition-for-completed-symbol symbol)))
  citre-xref--find-definition(#("bdrv_flush_all" 0 14 (citre-xref-get-at-point t citre-tags-file "/home/jet/var/qemu/tags" citre-file-path nil citre-bounds (471 . 485))))
  (progn (citre-xref--find-definition symbol))
  (closure (t) (_backend symbol) "Define method for xref to find definition of SYMBO..." (progn (citre-xref--find-definition symbol)))(citre #("bdrv_flush_all" 0 14 (citre-xref-get-at-point t citre-tags-file "/home/jet/var/qemu/tags" citre-file-path nil citre-bounds (471 . 485))))
  apply((closure (t) (_backend symbol) "Define method for xref to find definition of SYMBO..." (progn (citre-xref--find-definition symbol))) citre #("bdrv_flush_all" 0 14 (citre-xref-get-at-point t citre-tags-file "/home/jet/var/qemu/tags" citre-file-path nil citre-bounds (471 . 485))))
  xref-backend-definitions(citre #("bdrv_flush_all" 0 14 (citre-xref-get-at-point t citre-tags-file "/home/jet/var/qemu/tags" citre-file-path nil citre-bounds (471 . 485))))
  #f(compiled-function () #<bytecode 0x53d5f9>)()
  xref--show-defs-buffer(#f(compiled-function () #<bytecode 0x53d5f9>) ((window . #<window 67 on *grep*>) (display-action)))
  xref--show-defs(#f(compiled-function () #<bytecode 0x53d5f9>) nil)
  xref--find-definitions(#("bdrv_flush_all" 0 14 (citre-xref-get-at-point t citre-tags-file "/home/jet/var/qemu/tags" citre-file-path nil citre-bounds (471 . 485))) nil)
  xref-find-definitions(#("bdrv_flush_all" 0 14 (citre-xref-get-at-point t citre-tags-file "/home/jet/var/qemu/tags" citre-file-path nil citre-bounds (471 . 485))))
  funcall-interactively(xref-find-definitions #("bdrv_flush_all" 0 14 (citre-xref-get-at-point t citre-tags-file "/home/jet/var/qemu/tags" citre-file-path nil citre-bounds (471 . 485))))
  call-interactively(xref-find-definitions nil nil)
  command-execute(xref-find-definitions)

Misc tasks for 0.1 release

User options

  • More faces for people to customize.

Core

  • Change ext-lang field to extra-lang. We want to make it clear that ext-fields are for disambiguity, and extra-fields are merely helpers.

Some ideas of optimization:

  • Get all additional info of tags file in one pass, no matter whether we have ext-fields that need it. This reduces the "cold boot" time by querying readtags less times.
  • When extract the pattern field, don't count unescaped delimiters between each tab. Just see if an unescaped delimiter is before the tab.

Util

  • I think using text properties to attach records on strings is not a good idea, since once it's mixed with other properties, the printed representation becomes really messy, so it's hard to debug. We should change this. One way is to introduce some "private fields" to records.

Docs

  • "Commentary" section in code files, and README.
  • Docs for users.
  • Docs for developers.

Tests

  • Parametrize readtags executable.
  • Update the tests to reflect recent refactoring.
  • Add tests for byte compilation.
  • Add check for long lines, indentation and docstring style.
  • Do all these with a single make command.
  • Setup a CI before we release 0.1.

Thorough tests for the core layer is needed. Here are some memos:

  • Test the case where no tags match.

Style

  • Use file-local variables for line length limit, indentation style, etc..

UI

  • Should we offer the universal function that builds the definition string to show, or let citre-jump and citre-peek build their own ones?
  • Check & make sure the colors looks good on 8-color and monochrome terminals (users may use Emacs on this thing)

Tools

  • Prevent citre-peek from updating contents after every command.

Misc

  • Unify some terms:
    - tags file? tagfile? tagsfile?
    - tag -> tagline
    - record -> tag
    - location -> definition
  • Unify some function names in citre-peek.el.
  • Tweak function names and order of args.
  • TODOs in the code.
  • Autoload.
  • Publish on MELPA.

readtags-error on Windows

I am trying to use ctags on Windows with chromium. At the root directory of chromium, generating tags by ctags --languages=C,C++ -R .

It works indeed, but sometimes code completion by company-capf is laggy, and gives error below:

2021628-224054

I think we may invoke readtags too often here?

My company config:

(setq company-backends '(company-capf
                     company-keywords
                     company-semantic
                     company-dabbrev-code
                     company-files
                     company-dabbrev
                     company-cmake
                     company-clang
                     company-bbdb
                     company-oddmuse))

(setq company-idle-delay 0
          company-echo-delay 0
          company-minimum-prefix-length 3
          company-tooltip-limit 12
          company-tooltip-align-annotations t
          company-require-match nil
          company-transformers nil
          company-global-modes '(not eshell-mode comint-mode erc-mode message-mode help-mode)

citre-see-symbol-in-code-map doesn't work

The target source and generating tags:

[yamato@slave]/tmp% git clone https://github.com/lsof-org/lsof
Cloning into 'lsof'...
remote: Enumerating objects: 86, done.
remote: Counting objects: 100% (86/86), done.
remote: Compressing objects: 100% (63/63), done.
remote: Total 1734 (delta 46), reused 43 (delta 23), pack-reused 1648
Receiving objects: 100% (1734/1734), 4.40 MiB | 3.55 MiB/s, done.
Resolving deltas: 100% (810/810), done.
[yamato@slave]/tmp% cd lsof
[yamato@slave]/tmp/lsof% git log | head -3
commit ab219db05db2691c61d67665a982f2a594656ec5
Merge: df01ed3 50cd320
Author: Masatake YAMATO <[email protected]>
[yamato@slave]/tmp/lsof% u-ctags --fields=Kzn -R
[yamato@slave]/tmp/lsof% u-ctags --version
Universal Ctags 0.0.0(9d345cea), Copyright (C) 2015 Universal Ctags Team
Universal Ctags is derived from Exuberant Ctags.
Exuberant Ctags 5.8, Copyright (C) 1996-2009 Darren Hiebert
  Compiled: Apr 18 2020, 07:32:27
  URL: https://ctags.io/
  Optional compiled features: +wildcards, +regex, +iconv, +option-directory, +xpath, +json, +interactive, +sandbox, +yaml, +packcc

Starting emacs:

[yamato@slave]/tmp/lsof% emacs --version
GNU Emacs 26.1
Copyright (C) 2018 Free Software Foundation, Inc.
GNU Emacs comes with ABSOLUTELY NO WARRANTY.
You may redistribute copies of GNU Emacs
under the terms of the GNU General Public License.
For more information about these matters, see the file named COPYING.
[yamato@slave]/tmp/lsof% emacs -nw -Q

Operations in emacs:

  1. M-x dired and open the directory where citre is cloned.
  2. open citre.el and do M-x eval-buffer
  3. open citre-map.el and do M-x eval-buffer
  4. M-x find-file /tmp/lsof/main.c
  5. M-x citre-mode
  6. M-x toggle-debug-on-error
  7. move to "main" function in main.c buffer
  8. M-x citre-see-symbol-in-code-map
  9. I got an error:
File Edit Options Buffers Tools Debugger Help                                                                                                 
Debugger entered--Lisp error: (wrong-type-argument number-or-marker-p nil)
  >=(2 nil)
  (if (>= 2 pos-depth) (progn (cl-delete nil (mapcar (function (lambda (location) (if (cdr location) nil (car location)))) list))))
  (let* ((pos (alist-get "/home/yamato/var/citre/" citre--code-map-position-alist nil nil (function equal))) (pos-depth (nth 3 pos)) (list (c$
  citre--code-map-refresh(switch-page)
  (let* ((project (or project (citre--project-root))) (map-buf-name (format "*Code map: %s*" (abbreviate-file-name project))) (map-buf-presen$
  citre--open-code-map()
  (let ((sym (thing-at-point 'symbol 'no-properties))) (if sym nil (user-error "No symbol at point")) (if (citre--key-in-alist sym (alist-get$
  citre-see-symbol-in-code-map()
  funcall-interactively(citre-see-symbol-in-code-map)
  call-interactively(citre-see-symbol-in-code-map record nil)
  command-execute(citre-see-symbol-in-code-map record)
  execute-extended-command(nil "citre-see-symbol-in-code-map" nil)
  funcall-interactively(execute-extended-command nil "citre-see-symbol-in-code-map" nil)
  call-interactively(execute-extended-command nil nil)
  command-execute(execute-extended-command)

emacs x windows crashed when calling citre-peek

It seems citre-peek works fine in terminal, while it will cause the Emacs X window crashes.

I'm not sure which information to report about X Emacs, so that I would just copy a minimal output into here right now.

Emacs version: 27.1
OS: Ubuntu 16.04
Emacs output on error:

X protocol error: BadDrawable (invalid Pixmap or Window parameter) on protocol request 55
When compiled with GTK, Emacs cannot recover from X disconnects.
This is a GTK bug: https://gitlab.gnome.org/GNOME/gtk/issues/221
For details, see etc/PROBLEMS.

(emacs:8803): GLib-WARNING **: g_main_context_prepare() called recursively from within a source's check() or prepare() member.

(emacs:8803): GLib-WARNING **: g_main_context_check() called recursively from within a source's check() or prepare() member.
Backtrace:
emacs[0x50d859]
emacs[0x418d21]
emacs[0x418ef5]
emacs[0x418b25]
emacs[0x4c8efe]
emacs[0x4c8f8b]
/usr/lib/x86_64-linux-gnu/libX11.so.6(_XError+0x11d)[0x7f979301aced]
/usr/lib/x86_64-linux-gnu/libX11.so.6(+0x3cb77)[0x7f9793017b77]
/usr/lib/x86_64-linux-gnu/libX11.so.6(+0x3cc35)[0x7f9793017c35]
/usr/lib/x86_64-linux-gnu/libX11.so.6(_XEventsQueued+0x55)[0x7f97930185f5]
/usr/lib/x86_64-linux-gnu/libX11.so.6(XFlush+0x1a)[0x7f9792ff9e1a]
/usr/lib/x86_64-linux-gnu/libX11.so.6(+0x5c7de)[0x7f97930377de]
/usr/lib/x86_64-linux-gnu/libX11.so.6(XDestroyIC+0x12)[0x7f9793025752]
emacs[0x4daf2f]
emacs[0x4d2ea4]
emacs[0x4d347b]
emacs[0x42bc75]
emacs[0x418aed]
emacs[0x4c8efe]
emacs[0x4c8f8b]
/usr/lib/x86_64-linux-gnu/libX11.so.6(_XError+0x11d)[0x7f979301aced]
/usr/lib/x86_64-linux-gnu/libX11.so.6(+0x3cb77)[0x7f9793017b77]
/usr/lib/x86_64-linux-gnu/libX11.so.6(+0x3cc35)[0x7f9793017c35]
/usr/lib/x86_64-linux-gnu/libX11.so.6(_XEventsQueued+0x55)[0x7f97930185f5]
/usr/lib/x86_64-linux-gnu/libX11.so.6(XPending+0x57)[0x7f9793009fd7]
/usr/lib/x86_64-linux-gnu/libgdk-3.so.0(+0x5f7ee)[0x7f97948047ee]
/lib/x86_64-linux-gnu/libglib-2.0.so.0(g_main_context_prepare+0x15d)[0x7f97937809ed]
/lib/x86_64-linux-gnu/libglib-2.0.so.0(+0x4a38b)[0x7f979378138b]
/lib/x86_64-linux-gnu/libglib-2.0.so.0(g_main_context_pending+0x27)[0x7f9793781517]
/usr/lib/x86_64-linux-gnu/libgtk-3.so.0(gtk_events_pending+0xd)[0x7f9794cabced]
emacs[0x4c5e3b]
emacs[0x4fb539]
emacs[0x4fba35]
emacs[0x575533]
emacs[0x575665]
emacs[0x5757de]
emacs[0x5764f5]
emacs[0x4fa56f]
emacs[0x4faa49]
emacs[0x4fb698]
emacs[0x4fe688]
...

[1]  + 8803 abort      emacs

Talk about citre-tree, a "git-like" code reading tool

This is a sub-issue of #17.

This is what @masatake has mentioned in our private mails. The idea is to keep the code reading history as a tree, and we could do git-like operations on it, like changing branch, and maybe rebase. Here's his summary:

What I want may be a "git-for-code-readers". The tool can record the process of code-reading as commits. The chain of commits can be edited and refined. The final version of the chain is something like a proof in mathematics. The tools manages multipe chains as branches.

We both don't have a clear concept of what this tool will be like, or @masatake actually has one and he didn't tell me ;) Anyway, let's call it citre-tree for now.

Maybe citre-map could do the job.

First of all, citre-map already gives us a tree that can be edited. The difference between that and citre-tree is, citre-map draws the tree based on the relationship between the items (file -> symbol -> definition), and not the history of reading, while citre-tree may do the latter. I think they are trying to solve the same problem: citre-map also lets you browse in the codebase broadly, then cut the branches that's not important, and eventually leaves a small map that solves the problem. So, if citre-map does this well, we won't need citre-tree.

Why citre-tree shouldn't be a part of Citre

Even if citre-map is not good, and we need citre-tree, I feel it's not in the scope of this package. Let me explain: I assume in citre-tree, you could use current locations as nodes, and put them into a tree. Since the reading process is non-linear, it doesn't mean locations you jump to later appears deeper in the tree. Actually the relationship between a parent node A and its child B could be:

  • B is the definition of A. When we chasing down a calling tree, we'll have this.

  • B is a reference of A. When we want to see all references of a symbol, we'll have this.

    Notice that we may jump from B to A using "jump to definition", but still want to put B under A. So we don't always jump from the parent node to its child.

  • B is used in the body of A.

  • Other logical relations.

So, the tree is formed in an logical order, where the logic is neither time order, nor you jump from the parent to the child using our tools, but something that's needed to be figured out by the reader himself. If we could agree this, what we need is a tool that allows us to:

  • Collect locations as nodes.
  • Link the nodes into little chains, and eventually into a tree (or, a directed graph since there may also be circles in the graph).
  • Edit the graph.
  • Annotate on the nodes, maybe also on the arrows.

And this actually doesn't has much to do with "jumping to definition/reference" tools. Yes you'll need them, but what they do is just take you from one location to another. You still have to manually create the tree. The tools must not create/modify the tree since they doesn't understand logical relations between the nodes, and only the code reader can figure them out.

If we could agree with this, then I would say it's better to develop it as a new package, so other users could use it with their favorite "jumping to" tools.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.