Tanagram Features Guide
Updated for version 202309-01
When you first launch Tanagram, you’ll see the Welcome view. Here, you’ll see a list of recently-opened projects in the sidebar, and a button to open any project you’d like.
When you open a directory, Tanagram will add a
ProjectConfig.tanagramvizproj file. This file is a macOS package. It currently contains a JSON file containing project-specific settings (more on that below), and, if you’ve defined any custom item types (more on that below), a binary file for those definitions.
Note that Tanagram currently does not persist information about your codebase itself to disk — all the data displayed in the main view is loaded and stored in-memory for now.
Once you open a project, you’ll see the main view. It’ll start out empty; click the refresh button in the top-left to load all the items defined in your codebase. As it’s running, it’ll show a circular progress indicator that’s based on the number of source code files in your directory (not just at the root level; Tanagram does a deep traversal). If the progress indicator appears stuck with ~100% CPU usage, it’s probably chewing over a large source file — in my testing so far, a 23k line/1.2MB source file took about 33 seconds to process on a Xeon W-3235 (although it’s constrained to a single thread). Reload performance is definitely an area for improvement; for now, you can use project settings (more on that below) to filter out really large files (e.g.
vendor/ files or those from
The left sidebar lets you filter codebase items by built-in item types or custom item types (more on that below).
The main list in the middle shows items from your codebase, which you can filter:
- By item type, using the left sidebar;
- By top-level directory, using the scope bar;
- By name, using the search box in the top-right. The Open link will open the file, line, and column at which the symbol is defined in VSCode.
The inspector on the right shows details for the selected item, including:
- Its container (i.e. lexical parent) — a codebase item in which the selected item is nested;
- A preview of the item’s implementation;
- Incoming references — other codebase items that call or refer to the selected item;
- Outgoing references — other codebase items that the selected item calls or refers to.
One of Tanagram’s most powerful features is the references graph. This shows all the items within your codebase as nodes on an interactive graph — unlike a static bitmap, you can click on nodes to see details about them, just like if you’d selected them in the main list view.
When you select a node, it’ll load incoming and outgoing references for that node, if they haven’t been loaded already. You can also click the “Load All References” button in the top-left to load all references. For Swift projects, this may take a long time — in my testing using Tanagram’s own codebase, it takes about 12 minutes(!) to load all references for ~10k lines of Swift across ~50 files (it’s bottle-necked on sourcekit itself). I’ll definitely have to make that faster, but in the meantime, try opening a smaller codebase or subdirectory.
The references graph is powerful because it lets you search for paths between any two items. A path is made up of outgoing references — e.g. there’s a path from
X, which calls
Y, which calls
Z, which calls
B. Reference types aren’t limited to method calls; this example shows a path from the
Foo.b() method to the
Bar.name instance variable.
Paths will show you a snippet of source code around where each reference was found.
Custom Item Type Definition
Another powerful feature is the ability to create custom item type definitions. They allow you to identify types of constructs that exist in your codebase by convention. For example:
You may have API endpoints whose implementations are in an
api/folder and whose filenames end in
You may have a bunch of constants that manually group some IDs that are scattered across the codebase but follow a naming convention — perhaps groups of API methods for your documentation, where the constants are all named
DOCS_SECTION_*; or groups of high/normal/low-priority background jobs, where the constants are all named
- You may have cron jobs scattered in folders across the codebase, and you want a unified list of all of them along with the time they’re scheduled to run.
Custom item type definitions will appear as their own entries in the left sidebar.
Once you’ve filtered the items you’re looking for, you can define custom field matchers, which are regex patterns that let you extract important information from the source of each item. Since Tanagram statically operates over your source code, this can include information in comments. Field matchers will highlight matching content in the source code preview, and a capture group named
value can be used to extract a custom value for the matcher. For now, matchers only support the first matching result in an item’s source code; any subsequent matches won’t be included in the extracted value.
Extracted field values appear in additional columns in the main content list. This allows you to define a custom data model schema for browsing your codebase. For example, the “Owner” example in the screenshot example above shows up as an “Owner” column here:
/usr/local/bin/type-script-language-server. If your binary is somewhere else, you can change the setting here. typescript-language-server also depends on
node to be available in its
$PATH when it runs. By default, Tanagram runs typescript-language-server with
$PATH, implying that you have a
node binary at
/usr/local/bin/node. If your
node binary is in a different directory, you can set that directory here. Click Save to save your changes and restart Tanagram to apply those changes (sorry for the inconvenience).
.d.ts files and anything in
If you open a Swift project, you’ll also see a Project Settings entry at the bottom of the left sidebar. Currently, this just shows an option to open the compilation database wizard:
Swift support comes from SourceKit-LSP, which updates its index/understanding of your source code as a side-effect of running an Xcode build (i.e. it doesn’t update live from the file system). SourceKit-LSP finds its index via a compilation database, which is a JSON list of all the files that got compiled in your project as well as the command used to compile it. If a source file is not listed in the compilation database, Tanagram will still run, but you won’t get any results (i.e. incoming or outgoing references) for code items from that file.
In fact, if you open a Swift project without a compilation database, it’ll start by prompting you to create one:
The compilation database wizard will guide you through 4 steps, culminating in a
compile_commands.json file written to the root directory of your project. You can gitignore this file.
I’ve spent the last few weeks working on live-updating Tanagram’s data when source files change on disk. Here, I’m creating a new property. When I save the file, the new property automatically shows up in Tanagram. Removing it from the source code also tombstones it in the UI. pic.twitter.com/GNNROU04ws— Tanagram (@tanagram_) April 1, 2023
Tanagram has some limited support for automatically updating its list of items as you edit the underlying source code:
- If you define new items in an existing source file, or create a new source file within an opened directory, those new items will get added to Tanagram’s database.
- If you remove an existing item from a source file, or delete a file, those items will be tombstoned and faded out in the UI.
- Renaming, either of files or codebase items, is currently not supported — the old names will be tombstoned and the new names will get added anew. This tweet explains why. This also means that if your editor saves changes by writing to a temporary file and then moving that file to the intended path, those changes may not be properly supported.