universal-ctags / citre Goto Github PK
View Code? Open in Web Editor NEWA superior code reading & auto-completion tool with pluggable backends.
License: GNU General Public License v3.0
A superior code reading & auto-completion tool with pluggable backends.
License: GNU General Public License v3.0
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?
Several Citre users talked about this idea to me.
If we are at:
foo.bar|
(|
is the cursor), we could:
foo
(we could assume it's a variable, function parameter, or member (maybe more?)).typeref
fields of these definitions. They should be typeref:struct:structName
(if there are more than 1 definitons, multiple structName
is possible.bar
so those with struct:structName
scope field are on the top.I need to learn more on how a C file is tagged:
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:
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.
@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):
It seems that (kbd "M-N")
and (kbd "M-P")
in citre-fetch work one-way; by repeating (kbd "M-N")
and visting the last item in the list displayed by citre-fetch, I cannot go back (visiting to the previous item in the list or the first item n the list).
I would like to go back.
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.
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).
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.
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.
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.
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)
Currently when calling citre-peek on a white space character, it throws an error: "Wrong type argument: stringp, nil".
I would like to see if it's a good idea to bring citre-peek-restore out in this case?
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.
Now we have 3 ways of reading code:
citre-jump
and xref
integration. They are both for linear reading. xref
may offer a better experience if a completion framework is not used.
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.
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:
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.
hi, friend!
How do i avoid this question? When i open a declaration of a variable, emacs will pop up this error. But not all!
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!
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?
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.
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!
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
?
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.
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!
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.
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.
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
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).
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.
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;
<i>nt i
.I had to type C-g to get the control back.
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.
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.
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.
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:
citre--format-shell-command
. It's hard to make this function general enough.(citre--shell-escape &optional kind)
, and kind can be 'single
, 'double
or 'both
.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!
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:
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?)
#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:
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".
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.
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.
Hello,
Would it be possible to provide a command to jump to and peek any tag (say, using completing-read)?
Thanks in advance.
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.
It will improve the visual feedback of citre-peek window.
Lines 182 to 205 in ed26e10
(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)))))
(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.
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-,
.
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.
This is a sub-issue of #17.
@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:
citre-jump
in a citre-peek
sessioncitre-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.
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:
The cons are:
And here's an interesting observation:
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.
It's meaningful to have good default behavior on filtering/sorting, especially before we invest enough time on language-specific support.
extras:
field).file
scope, and is not in this file.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:
I thought about this:
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:
file/F
kind for file tags. Exclude them.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)
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.
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.
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)
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:
make
command.Thorough tests for the core layer is needed. Here are some memos:
citre-jump
and citre-peek
build their own ones?citre-peek
from updating contents after every command.citre-peek.el
.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:
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)
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:
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)
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
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.
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
.
citre-tree
shouldn't be a part of CitreEven 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:
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.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.