Metadata-Version: 2.4
Name: callscape
Version: 0.1.0
Summary: Point it at a codebase, get a browsable static HTML call-graph map. Built on tree-sitter.
Author-email: Sophie Nguyen <sophie.nguyenthuthuy@gmail.com>
License: MIT
Project-URL: Homepage, https://github.com/sophie-nguyenthuthuy/callscape
Keywords: call-graph,tree-sitter,code-navigation,static-analysis,visualization
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Software Development :: Documentation
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: tree-sitter>=0.23
Requires-Dist: tree-sitter-python>=0.23
Requires-Dist: tree-sitter-javascript>=0.23
Requires-Dist: tree-sitter-go>=0.23
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == "dev"
Dynamic: license-file

# callscape

Point it at a codebase, get a **browsable static HTML call-graph map** — functions,
callers, class/struct definitions, all cross-linked. One output file, no server,
no dependencies at view time. Built on [tree-sitter](https://tree-sitter.github.io/),
so each new language is one self-contained PR.

![callscape browsing its own source](docs/demo.gif)

```bash
pip install callscape          # or: pip install -e ".[dev]" from a checkout
callscape path/to/repo -o map.html
open map.html
```

```
callscape: 12 files · 55 definitions · 174 call edges (163 unresolved)
wrote map.html — open it in a browser
```

## What you get

- **Overview** — stats, per-language file counts, most-connected symbols.
- **Symbol pages** — signature, source, callers, callees; every name is a link.
- **Call-flow graph** — callers → symbol → callees as a clickable SVG.
- **Search** — press `/`, filter by symbol or file path.
- **Single file** — the whole map is one self-contained `.html`. Email it,
  attach it to a PR, drop it on any static host.

Languages today: **Python**, **JavaScript**, **Go**.

## How it works

1. Walk the repo, parse each supported file with its tree-sitter grammar.
2. Each language adapter extracts definitions (functions, methods, classes,
   structs, interfaces) and call sites from the syntax tree.
3. Calls are attributed to their smallest enclosing definition, then resolved
   **by name** across the whole repo.
4. The index is embedded as JSON into a self-contained HTML viewer
   (vanilla JS, no CDN, no telemetry).

### Honest limitations

Resolution is name-based, not type-based. `obj.render()` links to *every*
definition named `render` — dynamic dispatch is approximated, not solved.
Names matching more than 12 definitions are treated as too generic and listed
as unresolved instead of spraying edges. This is a map for orienting yourself
in an unfamiliar codebase, not a precision static analyzer.

## Adding a language (the fun part)

One adapter = one PR. An adapter is a single module in `callscape/languages/`
(~100 lines) exposing four things:

```python
NAME = "ruby"
EXTENSIONS = {".rb"}

def language() -> tree_sitter.Language:
    return Language(tree_sitter_ruby.language())

def extract(root_node, source_bytes) -> tuple[list[RawDef], list[RawCall]]:
    # walk the tree; return definitions (name, kind, byte span, line span,
    # signature, parent) and call sites (bare callee name, byte, line)
    ...
```

Then:

1. Add the grammar package (`tree-sitter-ruby`) to `pyproject.toml` dependencies.
2. List the module in `_ADAPTER_MODULES` in `callscape/languages/__init__.py`.
3. Add a `test_ruby_defs_and_calls` case in `tests/test_languages.py`
   (see the existing Python/JS/Go cases — ~30 lines each).

Enclosure attribution, cross-file resolution, and the viewer are all
language-agnostic — the adapter never touches them. Use
`callscape/languages/go.py` as the template; it is the smallest.

## CLI

```
callscape PATH [-o OUT.html] [--title TITLE] [--exclude GLOB]...
```

`--exclude` globs are relative to the root and repeatable. `node_modules`,
virtualenvs, `vendor`, build output, and hidden directories are skipped
automatically; files over 1.5 MB are ignored.

## Development

```bash
python -m venv .venv && .venv/bin/pip install -e ".[dev]"
.venv/bin/pytest
.venv/bin/callscape . -o demo/self.html --title "callscape (self)"   # dogfood
```

MIT license.
