rust-analyzer-db 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,85 @@
1
+ {% extends "base.html" %}
2
+ {% block title %}API Surface — Rust Analyzer{% endblock %}
3
+ {% block topbar_title %}Public API Surface{% endblock %}
4
+
5
+ {% block content %}
6
+ <div class="cards" style="grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); margin-bottom:20px">
7
+ {% for kind, items in by_kind.items() %}
8
+ <div class="card">
9
+ <div class="card-label">{{ kind }}</div>
10
+ <div class="card-value">{{ items|length }}</div>
11
+ </div>
12
+ {% endfor %}
13
+ <div class="card">
14
+ <div class="card-label">Total Public</div>
15
+ <div class="card-value">{{ total }}</div>
16
+ </div>
17
+ </div>
18
+
19
+ {% for kind in ['struct', 'enum', 'trait', 'function', 'method', 'const', 'type_alias', 'union'] %}
20
+ {% if kind in by_kind %}
21
+ <div class="panel">
22
+ <div class="panel-header">
23
+ <span class="panel-title"><span class="badge badge-{{ kind }}">{{ kind }}</span> ({{ by_kind[kind]|length }})</span>
24
+ </div>
25
+ <div class="panel-body no-pad">
26
+ <div class="table-wrap" style="max-height:400px;overflow-y:auto">
27
+ <table>
28
+ <thead>
29
+ <tr>
30
+ <th>Name</th>
31
+ <th>Visibility</th>
32
+ <th>Signature</th>
33
+ <th>Location</th>
34
+ <th></th>
35
+ </tr>
36
+ </thead>
37
+ <tbody>
38
+ {% for row in by_kind[kind] %}
39
+ <tr>
40
+ <td class="mono">{{ row.name }}</td>
41
+ <td class="muted">{{ row.visibility or '' }}</td>
42
+ <td class="mono" style="font-size:11px;max-width:400px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">{{ row.signature or '' }}</td>
43
+ <td class="muted mono" style="font-size:11px">{{ row.file_path }}:{{ row.start_line }}</td>
44
+ <td>
45
+ <button class="btn" onclick="showSource({{ row.id }})" title="View source">{'{{ '{' }}</button>
46
+ </td>
47
+ </tr>
48
+ {% endfor %}
49
+ </tbody>
50
+ </table>
51
+ </div>
52
+ </div>
53
+ </div>
54
+ {% endif %}
55
+ {% endfor %}
56
+
57
+ <div class="modal-overlay" id="source-modal" style="display:none" onclick="if(event.target===this)closeModal()">
58
+ <div class="modal">
59
+ <div class="modal-header">
60
+ <h3 id="modal-title">Source</h3>
61
+ <button class="modal-close" onclick="closeModal()">&times;</button>
62
+ </div>
63
+ <div class="modal-body">
64
+ <div id="modal-meta" style="margin-bottom:12px;font-size:12px;color:var(--text-secondary)"></div>
65
+ <pre class="source-viewer" id="modal-source"></pre>
66
+ </div>
67
+ </div>
68
+ </div>
69
+ {% endblock %}
70
+
71
+ {% block scripts %}
72
+ <script>
73
+ async function showSource(id) {
74
+ const resp = await fetch('/api/item/' + id);
75
+ if (!resp.ok) return;
76
+ const item = await resp.json();
77
+ document.getElementById('modal-title').textContent = item.kind + ' ' + item.name;
78
+ document.getElementById('modal-meta').textContent = item.file_path + ':' + item.start_line + '-' + item.end_line;
79
+ document.getElementById('modal-source').textContent = item.source || '(no source)';
80
+ document.getElementById('source-modal').style.display = 'flex';
81
+ }
82
+ function closeModal() { document.getElementById('source-modal').style.display = 'none'; }
83
+ document.addEventListener('keydown', e => { if (e.key === 'Escape') closeModal(); });
84
+ </script>
85
+ {% endblock %}
@@ -0,0 +1,82 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>{% block title %}Rust Analyzer DB{% endblock %}</title>
7
+ <link rel="preconnect" href="https://fonts.googleapis.com">
8
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
9
+ <link rel="stylesheet" href="/static/style.css">
10
+ {% block head %}{% endblock %}
11
+ </head>
12
+ <body>
13
+ <div class="app">
14
+ <!-- Sidebar -->
15
+ <aside class="sidebar" id="sidebar">
16
+ <div class="sidebar-header">
17
+ <div class="sidebar-logo">RA</div>
18
+ <div>
19
+ <div class="sidebar-title">Rust Analyzer</div>
20
+ <div class="sidebar-subtitle">Code Intelligence</div>
21
+ </div>
22
+ </div>
23
+ <nav class="sidebar-nav">
24
+ <div class="nav-section">Overview</div>
25
+ <a href="/" class="nav-link {% if active == 'dashboard' %}active{% endif %}">
26
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/></svg>
27
+ Dashboard
28
+ </a>
29
+
30
+ <div class="nav-section">Analysis</div>
31
+ <a href="/items" class="nav-link {% if active == 'items' %}active{% endif %}">
32
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>
33
+ Items
34
+ </a>
35
+ <a href="/complexity" class="nav-link {% if active == 'complexity' %}active{% endif %}">
36
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 12h-4l-3 9L9 3l-3 9H2"/></svg>
37
+ Complexity
38
+ </a>
39
+ <a href="/graph" class="nav-link {% if active == 'graph' %}active{% endif %}">
40
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="2"/><path d="M16.24 7.76a6 6 0 0 1 0 8.49m-8.48-.01a6 6 0 0 1 0-8.49m11.31-2.82a10 10 0 0 1 0 14.14m-14.14 0a10 10 0 0 1 0-14.14"/></svg>
41
+ Call Graph
42
+ </a>
43
+
44
+ <div class="nav-section">Project</div>
45
+ <a href="/api-surface" class="nav-link {% if active == 'api' %}active{% endif %}">
46
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg>
47
+ API Surface
48
+ </a>
49
+ <a href="/deps" class="nav-link {% if active == 'deps' %}active{% endif %}">
50
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/></svg>
51
+ Dependencies
52
+ </a>
53
+ <a href="/search" class="nav-link {% if active == 'search' %}active{% endif %}">
54
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
55
+ Search
56
+ </a>
57
+ </nav>
58
+ <div class="sidebar-footer">
59
+ rust-analyzer-db v{{ version }}
60
+ </div>
61
+ </aside>
62
+
63
+ <!-- Main -->
64
+ <div class="main">
65
+ <header class="topbar">
66
+ <h1 class="topbar-title">{% block topbar_title %}Dashboard{% endblock %}</h1>
67
+ <div class="topbar-search">
68
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
69
+ <form action="/search" method="get">
70
+ <input type="text" name="q" placeholder="Search items, functions, types..." value="{{ search_query|default('') }}">
71
+ </form>
72
+ </div>
73
+ </header>
74
+ <div class="content">
75
+ {% block content %}{% endblock %}
76
+ </div>
77
+ </div>
78
+ </div>
79
+
80
+ {% block scripts %}{% endblock %}
81
+ </body>
82
+ </html>
@@ -0,0 +1,108 @@
1
+ {% extends "base.html" %}
2
+ {% block title %}Complexity — Rust Analyzer{% endblock %}
3
+ {% block topbar_title %}Complexity Report{% endblock %}
4
+
5
+ {% block content %}
6
+ <div class="controls" style="margin-bottom:16px">
7
+ <form method="get" action="/complexity" class="controls">
8
+ <label style="font-size:13px;color:var(--text-secondary)">Min complexity:</label>
9
+ <input type="number" name="min" value="{{ min_cx }}" min="1" max="100" style="width:80px">
10
+ <button type="submit" class="btn btn-primary">Apply</button>
11
+ </form>
12
+ </div>
13
+
14
+ <div class="cards" style="grid-template-columns: repeat(3, 1fr)">
15
+ <div class="card">
16
+ <div class="card-label">Functions Above Threshold</div>
17
+ <div class="card-value">{{ rows|length }}</div>
18
+ </div>
19
+ <div class="card">
20
+ <div class="card-label">Max Cyclomatic</div>
21
+ <div class="card-value">{{ max_cc }}</div>
22
+ </div>
23
+ <div class="card">
24
+ <div class="card-label">Max Cognitive</div>
25
+ <div class="card-value">{{ max_cog }}</div>
26
+ </div>
27
+ </div>
28
+
29
+ <div class="panel">
30
+ <div class="panel-header">
31
+ <span class="panel-title">Functions with Complexity >= {{ min_cx }}</span>
32
+ </div>
33
+ <div class="panel-body no-pad">
34
+ <div class="table-wrap" style="max-height:600px;overflow-y:auto">
35
+ <table>
36
+ <thead>
37
+ <tr>
38
+ <th>Function</th>
39
+ <th>Kind</th>
40
+ <th>Cyclomatic</th>
41
+ <th>Cognitive</th>
42
+ <th>Nesting</th>
43
+ <th>Branches</th>
44
+ <th>Calls</th>
45
+ <th>LOC</th>
46
+ <th>Location</th>
47
+ <th></th>
48
+ </tr>
49
+ </thead>
50
+ <tbody>
51
+ {% for row in rows %}
52
+ <tr>
53
+ <td class="mono">{{ row.name }}</td>
54
+ <td><span class="badge badge-{{ row.kind }}">{{ row.kind }}</span></td>
55
+ <td class="mono">
56
+ <span class="cx-bar" style="width:{{ [row.cyclomatic_complexity * 4, 100] | min }}px;background:{% if row.cyclomatic_complexity >= 10 %}var(--danger){% elif row.cyclomatic_complexity >= 5 %}var(--warning){% else %}var(--success){% endif %}"></span>
57
+ <span class="{% if row.cyclomatic_complexity >= 10 %}cx-high{% elif row.cyclomatic_complexity >= 5 %}cx-medium{% else %}cx-low{% endif %}">{{ row.cyclomatic_complexity }}</span>
58
+ </td>
59
+ <td class="mono">{{ row.cognitive_complexity }}</td>
60
+ <td class="mono">{{ row.nesting_depth }}</td>
61
+ <td class="mono">{{ row.num_branches }}</td>
62
+ <td class="mono">{{ row.num_function_calls }}</td>
63
+ <td class="mono">{{ row.lines_of_code }}</td>
64
+ <td class="muted mono" style="font-size:11px">{{ row.file_path }}:{{ row.start_line }}</td>
65
+ <td>
66
+ <button class="btn" onclick="showSource({{ row.id }})" title="View source">{'{{ '{' }}</button>
67
+ </td>
68
+ </tr>
69
+ {% endfor %}
70
+ {% if not rows %}
71
+ <tr><td colspan="10" class="muted" style="text-align:center;padding:24px">No functions above threshold</td></tr>
72
+ {% endif %}
73
+ </tbody>
74
+ </table>
75
+ </div>
76
+ </div>
77
+ </div>
78
+
79
+ <!-- Source modal -->
80
+ <div class="modal-overlay" id="source-modal" style="display:none" onclick="if(event.target===this)closeModal()">
81
+ <div class="modal">
82
+ <div class="modal-header">
83
+ <h3 id="modal-title">Source</h3>
84
+ <button class="modal-close" onclick="closeModal()">&times;</button>
85
+ </div>
86
+ <div class="modal-body">
87
+ <div id="modal-meta" style="margin-bottom:12px;font-size:12px;color:var(--text-secondary)"></div>
88
+ <pre class="source-viewer" id="modal-source"></pre>
89
+ </div>
90
+ </div>
91
+ </div>
92
+ {% endblock %}
93
+
94
+ {% block scripts %}
95
+ <script>
96
+ async function showSource(id) {
97
+ const resp = await fetch('/api/item/' + id);
98
+ if (!resp.ok) return;
99
+ const item = await resp.json();
100
+ document.getElementById('modal-title').textContent = item.kind + ' ' + item.name;
101
+ document.getElementById('modal-meta').textContent = item.file_path + ':' + item.start_line + '-' + item.end_line;
102
+ document.getElementById('modal-source').textContent = item.source || '(no source)';
103
+ document.getElementById('source-modal').style.display = 'flex';
104
+ }
105
+ function closeModal() { document.getElementById('source-modal').style.display = 'none'; }
106
+ document.addEventListener('keydown', e => { if (e.key === 'Escape') closeModal(); });
107
+ </script>
108
+ {% endblock %}
@@ -0,0 +1,170 @@
1
+ {% extends "base.html" %}
2
+ {% block title %}Dashboard — Rust Analyzer{% endblock %}
3
+ {% block topbar_title %}Dashboard{% endblock %}
4
+
5
+ {% block content %}
6
+ <div class="cards">
7
+ <div class="card">
8
+ <div class="card-label">Files</div>
9
+ <div class="card-value">{{ n_files }}</div>
10
+ <div class="card-sub">Rust source files scanned</div>
11
+ </div>
12
+ <div class="card">
13
+ <div class="card-label">Total Items</div>
14
+ <div class="card-value">{{ total_items }}</div>
15
+ <div class="card-sub">Extracted code entities</div>
16
+ </div>
17
+ <div class="card">
18
+ <div class="card-label">Call Edges</div>
19
+ <div class="card-value">{{ total_calls }}</div>
20
+ <div class="card-sub">{{ resolved_calls }} resolved statically</div>
21
+ </div>
22
+ <div class="card">
23
+ <div class="card-label">Public API</div>
24
+ <div class="card-value">{{ pub_count }}</div>
25
+ <div class="card-sub">Public items</div>
26
+ </div>
27
+ <div class="card">
28
+ <div class="card-label">Avg Complexity</div>
29
+ <div class="card-value">{{ "%.1f"|format(avg_complexity) }}</div>
30
+ <div class="card-sub">Cyclomatic (functions)</div>
31
+ </div>
32
+ <div class="card">
33
+ <div class="card-label">External Deps</div>
34
+ <div class="card-value">{{ n_extern_crates }}</div>
35
+ <div class="card-sub">extern crate declarations</div>
36
+ </div>
37
+ </div>
38
+
39
+ <div class="chart-grid">
40
+ <div class="chart-box">
41
+ <div class="chart-box-title">Items by Kind</div>
42
+ <canvas id="chart-items-kind" height="220"></canvas>
43
+ </div>
44
+ <div class="chart-box">
45
+ <div class="chart-box-title">Complexity Distribution</div>
46
+ <canvas id="chart-complexity" height="220"></canvas>
47
+ </div>
48
+ <div class="chart-box">
49
+ <div class="chart-box-title">Top Files by LOC</div>
50
+ <canvas id="chart-files" height="220"></canvas>
51
+ </div>
52
+ <div class="chart-box">
53
+ <div class="chart-box-title">Call Resolution</div>
54
+ <canvas id="chart-resolution" height="220"></canvas>
55
+ </div>
56
+ </div>
57
+
58
+ <div class="panel">
59
+ <div class="panel-header">
60
+ <span class="panel-title">Most Complex Functions</span>
61
+ <a href="/complexity" class="btn">View All</a>
62
+ </div>
63
+ <div class="panel-body no-pad">
64
+ <div class="table-wrap">
65
+ <table>
66
+ <thead>
67
+ <tr>
68
+ <th>Function</th>
69
+ <th>Kind</th>
70
+ <th>CC</th>
71
+ <th>Cog</th>
72
+ <th>LOC</th>
73
+ <th>Location</th>
74
+ </tr>
75
+ </thead>
76
+ <tbody>
77
+ {% for row in top_complex %}
78
+ <tr>
79
+ <td class="mono">{{ row.name }}</td>
80
+ <td><span class="badge badge-{{ row.kind }}">{{ row.kind }}</span></td>
81
+ <td class="mono {% if row.cyclomatic_complexity >= 10 %}cx-high{% elif row.cyclomatic_complexity >= 5 %}cx-medium{% else %}cx-low{% endif %}">
82
+ {{ row.cyclomatic_complexity }}
83
+ </td>
84
+ <td class="mono">{{ row.cognitive_complexity }}</td>
85
+ <td class="mono">{{ row.lines_of_code }}</td>
86
+ <td class="muted mono" style="font-size:11px">{{ row.file_path }}:{{ row.start_line }}</td>
87
+ </tr>
88
+ {% endfor %}
89
+ {% if not top_complex %}
90
+ <tr><td colspan="6" class="muted" style="text-align:center;padding:24px">No functions found</td></tr>
91
+ {% endif %}
92
+ </tbody>
93
+ </table>
94
+ </div>
95
+ </div>
96
+ </div>
97
+ {% endblock %}
98
+
99
+ {% block scripts %}
100
+ <script src="https://cdn.jsdelivr.net/npm/chart.js@4"></script>
101
+ <script>
102
+ document.addEventListener('DOMContentLoaded', function() {
103
+ const kindData = {{ kind_labels|tojson }};
104
+ const kindCounts = {{ kind_counts|tojson }};
105
+ const cxLabels = {{ cx_labels|tojson }};
106
+ const cxCounts = {{ cx_counts|tojson }};
107
+ const fileLabels = {{ file_labels|tojson }};
108
+ const fileCounts = {{ file_counts|tojson }};
109
+ const resolvedCount = {{ resolved_calls }};
110
+ const unresolvedCount = {{ total_calls - resolved_calls }};
111
+
112
+ const colors = ['#6366f1','#22c55e','#f59e0b','#ef4444','#ec4899','#a855f7','#38bdf8','#fb923c','#f87171','#86efac'];
113
+
114
+ new Chart(document.getElementById('chart-items-kind'), {
115
+ type: 'doughnut',
116
+ data: {
117
+ labels: kindData,
118
+ datasets: [{ data: kindCounts, backgroundColor: colors, borderWidth: 0 }]
119
+ },
120
+ options: {
121
+ plugins: { legend: { position: 'right', labels: { color: '#8b8fa3', font: { size: 11 }, padding: 8 } } },
122
+ cutout: '60%'
123
+ }
124
+ });
125
+
126
+ new Chart(document.getElementById('chart-complexity'), {
127
+ type: 'bar',
128
+ data: {
129
+ labels: cxLabels,
130
+ datasets: [{ data: cxCounts, backgroundColor: '#6366f1', borderRadius: 4, barPercentage: 0.7 }]
131
+ },
132
+ options: {
133
+ plugins: { legend: { display: false } },
134
+ scales: {
135
+ x: { ticks: { color: '#5a5e72', font: { size: 10 } }, grid: { display: false } },
136
+ y: { ticks: { color: '#5a5e72', font: { size: 10 } }, grid: { color: '#1e2235' } }
137
+ }
138
+ }
139
+ });
140
+
141
+ new Chart(document.getElementById('chart-files'), {
142
+ type: 'bar',
143
+ data: {
144
+ labels: fileLabels,
145
+ datasets: [{ data: fileCounts, backgroundColor: '#3b82f6', borderRadius: 4, barPercentage: 0.7 }]
146
+ },
147
+ options: {
148
+ indexAxis: 'y',
149
+ plugins: { legend: { display: false } },
150
+ scales: {
151
+ x: { ticks: { color: '#5a5e72', font: { size: 10 } }, grid: { color: '#1e2235' } },
152
+ y: { ticks: { color: '#5a5e72', font: { size: 10 } }, grid: { display: false } }
153
+ }
154
+ }
155
+ });
156
+
157
+ new Chart(document.getElementById('chart-resolution'), {
158
+ type: 'doughnut',
159
+ data: {
160
+ labels: ['Resolved', 'Unresolved'],
161
+ datasets: [{ data: [resolvedCount, unresolvedCount], backgroundColor: ['#22c55e', '#5a5e72'], borderWidth: 0 }]
162
+ },
163
+ options: {
164
+ plugins: { legend: { position: 'bottom', labels: { color: '#8b8fa3', font: { size: 11 }, padding: 12 } } },
165
+ cutout: '65%'
166
+ }
167
+ });
168
+ });
169
+ </script>
170
+ {% endblock %}
@@ -0,0 +1,63 @@
1
+ {% extends "base.html" %}
2
+ {% block title %}Dependencies — Rust Analyzer{% endblock %}
3
+ {% block topbar_title %}Dependencies{% endblock %}
4
+
5
+ {% block content %}
6
+ {% if extern_crates %}
7
+ <div class="panel">
8
+ <div class="panel-header">
9
+ <span class="panel-title">External Crates ({{ extern_crates|length }})</span>
10
+ </div>
11
+ <div class="panel-body">
12
+ <div class="dep-grid">
13
+ {% for r in extern_crates %}
14
+ <div class="dep-item">
15
+ <div class="dep-item-name">{{ r.name }}</div>
16
+ {% if r.alias %}<div class="dep-item-count">as {{ r.alias }}</div>{% endif %}
17
+ </div>
18
+ {% endfor %}
19
+ </div>
20
+ </div>
21
+ </div>
22
+ {% endif %}
23
+
24
+ <div class="panel">
25
+ <div class="panel-header">
26
+ <span class="panel-title">Use Declarations ({{ crate_groups|length }} top-level crates)</span>
27
+ <div class="controls">
28
+ <label style="font-size:12px;color:var(--text-secondary)">
29
+ <input type="checkbox" id="show-all" onchange="toggleAll()"> Show all paths
30
+ </label>
31
+ </div>
32
+ </div>
33
+ <div class="panel-body">
34
+ {% for crate_name, paths in crate_groups.items() %}
35
+ <div style="margin-bottom:12px">
36
+ <div style="font-weight:600;color:var(--accent);font-size:13px;margin-bottom:4px">
37
+ {{ crate_name }} <span style="font-weight:400;color:var(--text-muted);font-size:11px">({{ paths|length }} imports)</span>
38
+ </div>
39
+ <ul class="use-tree all-paths" style="display:none">
40
+ {% for p in paths|sort %}
41
+ <li><span class="use-crate">{{ crate_name }}</span><span class="use-sep">::</span>{{ p.split('::')[1:]|join('::') }}</li>
42
+ {% endfor %}
43
+ </ul>
44
+ </div>
45
+ {% endfor %}
46
+ {% if not crate_groups %}
47
+ <div class="empty-state">
48
+ <h3>No use declarations found</h3>
49
+ <p>Run <code>scan</code> first to populate the database.</p>
50
+ </div>
51
+ {% endif %}
52
+ </div>
53
+ </div>
54
+ {% endblock %}
55
+
56
+ {% block scripts %}
57
+ <script>
58
+ function toggleAll() {
59
+ const show = document.getElementById('show-all').checked;
60
+ document.querySelectorAll('.all-paths').forEach(el => el.style.display = show ? 'block' : 'none');
61
+ }
62
+ </script>
63
+ {% endblock %}
@@ -0,0 +1,99 @@
1
+ {% extends "base.html" %}
2
+ {% block title %}Call Graph — Rust Analyzer{% endblock %}
3
+ {% block topbar_title %}Call Graph{% endblock %}
4
+
5
+ {% block head %}
6
+ <script src="/static/vis-network.min.js"></script>
7
+ {% endblock %}
8
+
9
+ {% block content %}
10
+ <div class="controls" style="margin-bottom:16px">
11
+ <form method="get" action="/graph" class="controls" id="graph-form">
12
+ <input type="text" name="root" placeholder="Root function name..." value="{{ root_name }}" style="width:200px">
13
+ <select name="direction">
14
+ <option value="both" {% if direction == 'both' %}selected{% endif %}>Both</option>
15
+ <option value="callees" {% if direction == 'callees' %}selected{% endif %}>Callees</option>
16
+ <option value="callers" {% if direction == 'callers' %}selected{% endif %}>Callers</option>
17
+ </select>
18
+ <label style="font-size:12px;color:var(--text-secondary)">Depth:</label>
19
+ <input type="number" name="depth" value="{{ depth }}" min="1" max="10" style="width:60px">
20
+ <label style="font-size:12px;display:flex;align-items:center;gap:4px;color:var(--text-secondary)">
21
+ <input type="checkbox" name="unresolved" {% if not no_unresolved %}checked{% endif %}> Show external
22
+ </label>
23
+ <button type="submit" class="btn btn-primary">Generate</button>
24
+ </form>
25
+ </div>
26
+
27
+ <div class="panel">
28
+ <div class="panel-header">
29
+ <span class="panel-title">{{ title }}</span>
30
+ <span style="font-size:12px;color:var(--text-muted)">{{ node_count }} nodes, {{ edge_count }} edges</span>
31
+ </div>
32
+ <div class="panel-body no-pad">
33
+ <div id="graph-container"></div>
34
+ </div>
35
+ </div>
36
+
37
+ {% if node_count > 400 %}
38
+ <div style="padding:8px 0;font-size:12px;color:var(--warning)">
39
+ Large graph: try narrowing with a --root function or unchecking "Show external" to reduce clutter.
40
+ </div>
41
+ {% endif %}
42
+ {% endblock %}
43
+
44
+ {% block scripts %}
45
+ <script>
46
+ const nodesData = {{ nodes_json|safe }};
47
+ const edgesData = {{ edges_json|safe }};
48
+
49
+ if (nodesData.length > 0) {
50
+ const big = nodesData.length > 200;
51
+ const nodes = new vis.DataSet(nodesData.map(n => ({
52
+ id: n.id,
53
+ label: n.label || '?',
54
+ color: n.kind === 'function' ? '#6366f1' : n.kind === 'method' ? '#22c55e' : '#5a5e72',
55
+ shape: n.kind === 'external' ? 'ellipse' : 'box',
56
+ font: { color: '#e1e4ed', size: big ? 9 : 12 },
57
+ title: n.kind + ': ' + n.label,
58
+ size: big ? 8 : undefined
59
+ })));
60
+ const edges = new vis.DataSet(edgesData.map(e => ({
61
+ from: e.from, to: e.to,
62
+ color: { color: '#3a3d4e', highlight: '#6366f1' },
63
+ arrows: 'to', smooth: { type: 'dynamic' }
64
+ })));
65
+ const container = document.getElementById('graph-container');
66
+ const network = new vis.Network(container, { nodes, edges }, {
67
+ nodes: { margin: 4, borderWidth: 0 },
68
+ physics: {
69
+ solver: 'barnesHut',
70
+ barnesHut: {
71
+ gravitationalConstant: big ? -3000 : -1200,
72
+ centralGravity: 0.15,
73
+ springLength: big ? 80 : 150,
74
+ springConstant: 0.02,
75
+ damping: 0.3
76
+ },
77
+ stabilization: {
78
+ enabled: true,
79
+ iterations: 500,
80
+ fit: true,
81
+ updateInterval: 25
82
+ }
83
+ },
84
+ interaction: { hover: true, tooltipDelay: 100 }
85
+ });
86
+ network.once('stabilizationIterationsDone', () => {
87
+ network.setOptions({ physics: false });
88
+ network.fit({ animation: { duration: 500, easingFunction: 'easeInOutQuad' } });
89
+ });
90
+ setTimeout(() => {
91
+ network.setOptions({ physics: false });
92
+ network.fit({ animation: { duration: 500, easingFunction: 'easeInOutQuad' } });
93
+ }, 10000);
94
+ } else {
95
+ document.getElementById('graph-container').innerHTML =
96
+ '<div style="display:flex;align-items:center;justify-content:center;height:100%;color:var(--text-muted)">No nodes to display.</div>';
97
+ }
98
+ </script>
99
+ {% endblock %}