Crscope is a source code browsing tool for Crystal and Ruby. It is a partial reimplementation of cscope, the venerable source code browing tool for C.
Crscope uses different parsing strategies for Crystal and Ruby:
For Crystal, crscope uses the compiler's own parser. It records information about classes, modules, methods, libraries, C functions, method calls, and assignments.
For Ruby, crscope uses uses rough heuristics, making heavy use of regular expressions and the indentation level of lines of code. It records less information than the Crystal parser: classes, modules, methods, and constants.
Like cscope, crscope can also search for files, using a partial match of the term you enter, and can also search using regular expressions.
Crscope records information about method and class names using a qualified
syntax of the form Class1.Class2[...].MethodName, where nested
classes are separated with periods. This is slightly different from
the scoping syntax used in Crystal and Ruby, but it allows for a consistent and
simple naming scheme.
New features
Crscope has several features that are missing in cscope:
Completions: If you press
*while entering a symbol search field, crscope will display a list of possible names, and will also insert as many characters as necessary to give the longest possible match. Completions are not allowed for non-symbol searches.Search fields editing: You can use EMACS-style keys for editing search fields, such as
C-afor beginning of line,C-efor end of line, etc. The entry fields are persistent, and won't be erased if you hit Enter to do a search, which allows you to edit them after a search.Go forward and back in search results. In cscope, you could only go forward in the search results by hitting Space. In crscope, you can also go back by hitting Backspace.
Qualified or unqualified matches. In crscope, you can search for symbol names using an unqualified name, i.e., a name without the namespace qualifications. For example, you can find the
initializefunction in theMyClassclass (and any other class), by simply searching forinitialize. But you can also enable qualified matches, which requires you to enter the fully qualified name, such asMyClass.initialize. Use the keyC-q(Ctrl + Q) to toggle qualified matches.
Curses-based interface
Aside from completions and search field editing (described below), the default curses-based user interface attempts to be close to cscope, though some features are missing.
(Note: in the following descriptions, Ctrl key combinations are
indicated using the notation C-x, where x is a letter key.)
The screen is divided into two sections:
- a large top section that contains the search results.
- a smaller bottom section that contains the search entry fields.
Switch between the two sections using the Tab (C-i) key.
In each section, you can move from one line to another using
the Down, Up, C-n, and C-p keys.
In the search entry fields, you can use EMACS-style editing keys. In symbol
searches, hit the * key to show partial matches in the search results,
or hit Enter to perform a more precise search.
In the search results section, hit the Enter key to run your editor on
the selected file and jump to the selected line number. You can also
type the letter shown on the left column to run the editor on that file and line.
Press C-d at any point to quit.
Press C-c at any point to toggle the "ignore case" flag.
Press C-q at any point to toggle the "qualified match" flag.
Crscope's search types are similar to those in cscope, but differ slightly in some ways.
Find this symbol: Search for a symbol, which may be a library, class, method definition, method call, C function, or the target of an assignment.
Find this method: Search for a method definition.
Find calls by this method: Search for all calls made by a method.
Find calls to this method: Search for all calls to a method. Operator methods like
+are not displayed. Note: some method calls may not be shown; this may be due to compiler optimization or incomplete information about the method in the parse phase. Use the non-regexp search to find all occurrences of the name.Regexp search: Perform a regular expression (
grep -E) search. This is useful for finding all occurrences of a particular symbol that crscope wouldn't find with its "find this symbol" search, such as a parameter name.Non-regexp search: Perform a fixed-string (
grep -F) search. This is useful for finding arbitrary strings, or strings that include regular expression metacharacters like[or?, or symbols that contain the?character.File search: Search for all filenames containing the specified string. This is handy if you can't remember the exact name or full path for a particular file.
Find assignments to this symbol: Search for assignments to a particular symbol.
Line-oriented interface
Crscope has a line-oriented mode whose interface is identical to cscope's, but with
a limited set of search types. Start the line-oriented mode with the -l option:
crscope -l
This mode is used by MicroEMACS, and it could possibly be used by other editors that have cscope integration. This mode implements the following search types:
- 0 - find a partially-qualified or unqualified symbol
- 1 - find the definition of partially-qualified or unqualified method name
- 2 - find methods called by a method
- 3 - find methods calling a method
- 4 - perform a grep -F (non-regexp) search
- 6 - perform a grep -E (regular expression) search
- 7 - file search
- 9 - find assignments to a symbol
Crscope will repreatedly prompt with ">> ", and read a line from standard input. The first character of the line is the search type, as described above. The rest of the line is the string to search.
In response, Crscope will respond with a line containing the number of
matches found, followed the matches, one on each line. For example,
here is a session where I asked cscope to do an inexact search for initialize:
>> 0initialize
cscope: 89 lines
./lib/ncurses/src/ncurses/mouse_event.cr NCurses.initialize 9 def initialize(event : LibNCurses::MEVENT)
./lib/ncurses/src/ncurses/mouse_event.cr NCurses.initialize 15 def initialize(@device_id, @coordinates, @state)
./lib/ncurses/src/ncurses/window.cr NCurses.Window.initialize 12 def initialize(height = nil, width = nil, y = 0, x = 0)
./lib/ncurses/src/ncurses.cr NCurses.Window.initialize 31 def initialize(@window : LibNCurses::Window)
./lib/email/src/email/address.cr EMail.Address.initialize 31 def initialize(mail_address : String, mailbox_name : String? = nil)
./lib/email/src/email/concurrent_sender.cr EMail.ConcurrentSender.initialize 45 def initialize(@config)
./lib/email/src/email/concurrent_sender.cr EMail.ConcurrentSender.initialize 51 def initialize(*args, **named_args)
./lib/email/src/email/header.cr EMail.Header.initialize 70 def initialize(field_name : String)
./lib/email/src/email/header.cr EMail.Header.Date.initialize 183 def initialize
./lib/email/src/email/header.cr EMail.Header.MimeVersion.initialize 218 def initialize(@version : String = "1.0")
./lib/email/src/email/header.cr EMail.Header.ContentType.initialize 232 def initialize(@mime_type : String, @params = Hash(String, String).new)
... [remainder of lines omitted]
Note that the first line says "cscope" instead of "crscope". This is done to ensure compatiblity with editors (such as MicroEMACS) that expect "cscope" in the response..
Each line contains four fields, separated by a space:
- Filename
- Symbol name
- Line number
- Context (the line where the symbol is defined)
Press C-d (Ctrl-D) at the prompt to quit.
Files
Crscope uses two files:
crscope.files: an optional file that contains a list of files to search. If this file doesn't exist, crscope will parse the files you specify on the command line. Unlike cscope, crscope will not automatically search files in the current directory if you don't specify any files on the command line or incrscope.files.crscope.out: the symbol table file that crscope creates to save the result of its file parsing. It is a plain text file that you can edit if necessary. Use the-doption to prevent crscope from rebuilding this file at startup.
Environment Variables
Crscope uses the following environment variables:
EDITORcontains the name of the editor.VISUALcontains the name of the editor ifEDITORis not defined.CRSCOPE_EDITORcontains the format string that crscope uses to construct the editor command line. In this string, use the following format specifiers:%eis replaced by the editor name%lis replaced by the line number%fis replaced by the filename
- If
CRSCOPE_EDITORis not defined, crscope uses the format string "%e +%l %f".
Running crscope
Crscope takes the following options:
-d: Don't rebuildcrscope.out.-l: Run line-oriented interface.-v: Print extremely verbose debug messages.-t tabsize: Specify the size of a tab in source files (default 8). Crscope uses the tabsize to determine indentation, which is crucial to its heuristics for determining class scope.-k: Ignored for cscope compatibility
If you don't specify -l, crscope will start the curses-based interface.
You can specify filenames after any options; crscope will search those files
for symbols. If you don't specify any filenames, crscope will read crscope.files
to get the names of files to read.
Each time you run crscope without the -d option, it will reparse all specified
files, and reconstruct the symbol file crscope.out from scratch.
On the ancient machines of the 80s, this would have been a very expensive operation;
hence, cscope had several ways to optimize this, by only parsing those files
that had changed, and by modifying only the parts of the symbol database for
those changed files. These optimizations are unnecessary on today's fast machines;
crscope should be fast enough even with its brute force strategy.
Build
Build crscope using:
make crscope
Then copy the binary to some place in your PATH.