├── test_scripts ├── __init__.py ├── traced.py └── gold.py ├── tests ├── future_tests │ ├── __init__.py │ ├── without_future.py │ └── with_future.py ├── __init__.py ├── test_db.py ├── test_tracer.py ├── utils.py ├── test_import_hook.py ├── test_utils.py ├── test_interface.py └── test_birdseye.py ├── MANIFEST.in ├── gulp ├── .gitignore ├── install-deps.sh ├── .eslintrc.json └── gulpfile.js ├── docs ├── _static │ ├── custom.css │ └── img │ │ └── call_to_foo.png ├── index.rst ├── how_it_works.rst ├── limitations.rst ├── quickstart.rst ├── tips.rst ├── configuration.rst ├── conf.py ├── integrations.rst └── contributing.rst ├── birdseye ├── trace_module.py ├── trace_module_deep.py ├── __main__.py ├── static │ ├── favicon.ico │ ├── img │ │ ├── logo.png │ │ ├── handle.png │ │ └── type_icons │ │ │ ├── dict.png │ │ │ ├── int.png │ │ │ ├── list.png │ │ │ ├── set.png │ │ │ ├── str.png │ │ │ ├── float.png │ │ │ ├── object.png │ │ │ ├── tuple.png │ │ │ ├── NoneType.png │ │ │ └── complex.png │ ├── css │ │ ├── proton │ │ │ ├── 30px.png │ │ │ ├── 32px.png │ │ │ ├── throbber.gif │ │ │ └── fonts │ │ │ │ └── titillium │ │ │ │ ├── titilliumweb-bold-webfont.eot │ │ │ │ ├── titilliumweb-bold-webfont.ttf │ │ │ │ ├── titilliumweb-bold-webfont.woff │ │ │ │ ├── titilliumweb-regular-webfont.eot │ │ │ │ ├── titilliumweb-regular-webfont.ttf │ │ │ │ ├── titilliumweb-regular-webfont.woff │ │ │ │ ├── titilliumweb-extralight-webfont.eot │ │ │ │ ├── titilliumweb-extralight-webfont.ttf │ │ │ │ └── titilliumweb-extralight-webfont.woff │ │ ├── hljs.min.css │ │ ├── main.css │ │ └── jquery-ui.min.css │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.ttf │ │ ├── glyphicons-halflings-regular.woff │ │ └── glyphicons-halflings-regular.woff2 │ └── js │ │ ├── libs │ │ └── highlight.pack.js │ │ └── call.js ├── clear_db.py ├── templates │ ├── call_base.html │ ├── ipython_call.html │ ├── info_panel.html │ ├── base.html │ ├── call_core.html │ ├── file.html │ ├── index.html │ ├── function.html │ ├── call.html │ └── ipython_iframe.html ├── __init__.py ├── import_hook.py ├── ipython.py ├── utils.py ├── server.py └── db.py ├── setup.py ├── misc ├── type_icons │ ├── dict.png │ ├── int.png │ ├── list.png │ ├── set.png │ ├── str.png │ ├── float.png │ ├── object.png │ ├── tuple.png │ ├── NoneType.png │ └── complex.png ├── test.sh ├── mypy_filter.py └── mypy_ignore.txt ├── tox.ini ├── .gitignore ├── pyproject.toml ├── make_release.sh ├── .github └── workflows │ └── workflow.yml ├── LICENSE.txt ├── setup.cfg └── README.rst /test_scripts/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/future_tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE.txt 2 | -------------------------------------------------------------------------------- /gulp/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json 3 | -------------------------------------------------------------------------------- /docs/_static/custom.css: -------------------------------------------------------------------------------- 1 | table.field-list { 2 | line-height: 1.5; 3 | } -------------------------------------------------------------------------------- /tests/future_tests/without_future.py: -------------------------------------------------------------------------------- 1 | def foo(): 2 | return 3 / 2 3 | -------------------------------------------------------------------------------- /birdseye/trace_module.py: -------------------------------------------------------------------------------- 1 | from birdseye import eye 2 | 3 | eye.trace_this_module(1) 4 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | if __name__ == "__main__": 4 | setup() 5 | -------------------------------------------------------------------------------- /birdseye/trace_module_deep.py: -------------------------------------------------------------------------------- 1 | from birdseye import eye 2 | 3 | eye.trace_this_module(1, deep=True) 4 | -------------------------------------------------------------------------------- /misc/type_icons/dict.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmojaki/birdseye/HEAD/misc/type_icons/dict.png -------------------------------------------------------------------------------- /misc/type_icons/int.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmojaki/birdseye/HEAD/misc/type_icons/int.png -------------------------------------------------------------------------------- /misc/type_icons/list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmojaki/birdseye/HEAD/misc/type_icons/list.png -------------------------------------------------------------------------------- /misc/type_icons/set.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmojaki/birdseye/HEAD/misc/type_icons/set.png -------------------------------------------------------------------------------- /misc/type_icons/str.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmojaki/birdseye/HEAD/misc/type_icons/str.png -------------------------------------------------------------------------------- /birdseye/__main__.py: -------------------------------------------------------------------------------- 1 | from birdseye.server import main 2 | 3 | if __name__ == '__main__': 4 | main() 5 | -------------------------------------------------------------------------------- /misc/type_icons/float.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmojaki/birdseye/HEAD/misc/type_icons/float.png -------------------------------------------------------------------------------- /misc/type_icons/object.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmojaki/birdseye/HEAD/misc/type_icons/object.png -------------------------------------------------------------------------------- /misc/type_icons/tuple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmojaki/birdseye/HEAD/misc/type_icons/tuple.png -------------------------------------------------------------------------------- /birdseye/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmojaki/birdseye/HEAD/birdseye/static/favicon.ico -------------------------------------------------------------------------------- /birdseye/static/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmojaki/birdseye/HEAD/birdseye/static/img/logo.png -------------------------------------------------------------------------------- /misc/type_icons/NoneType.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmojaki/birdseye/HEAD/misc/type_icons/NoneType.png -------------------------------------------------------------------------------- /misc/type_icons/complex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmojaki/birdseye/HEAD/misc/type_icons/complex.png -------------------------------------------------------------------------------- /birdseye/static/img/handle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmojaki/birdseye/HEAD/birdseye/static/img/handle.png -------------------------------------------------------------------------------- /tests/future_tests/with_future.py: -------------------------------------------------------------------------------- 1 | from __future__ import division 2 | 3 | 4 | def foo(): 5 | return 3 / 2 6 | -------------------------------------------------------------------------------- /docs/_static/img/call_to_foo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmojaki/birdseye/HEAD/docs/_static/img/call_to_foo.png -------------------------------------------------------------------------------- /birdseye/static/css/proton/30px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmojaki/birdseye/HEAD/birdseye/static/css/proton/30px.png -------------------------------------------------------------------------------- /birdseye/static/css/proton/32px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmojaki/birdseye/HEAD/birdseye/static/css/proton/32px.png -------------------------------------------------------------------------------- /birdseye/static/css/proton/throbber.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmojaki/birdseye/HEAD/birdseye/static/css/proton/throbber.gif -------------------------------------------------------------------------------- /birdseye/static/img/type_icons/dict.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmojaki/birdseye/HEAD/birdseye/static/img/type_icons/dict.png -------------------------------------------------------------------------------- /birdseye/static/img/type_icons/int.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmojaki/birdseye/HEAD/birdseye/static/img/type_icons/int.png -------------------------------------------------------------------------------- /birdseye/static/img/type_icons/list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmojaki/birdseye/HEAD/birdseye/static/img/type_icons/list.png -------------------------------------------------------------------------------- /birdseye/static/img/type_icons/set.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmojaki/birdseye/HEAD/birdseye/static/img/type_icons/set.png -------------------------------------------------------------------------------- /birdseye/static/img/type_icons/str.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmojaki/birdseye/HEAD/birdseye/static/img/type_icons/str.png -------------------------------------------------------------------------------- /birdseye/static/img/type_icons/float.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmojaki/birdseye/HEAD/birdseye/static/img/type_icons/float.png -------------------------------------------------------------------------------- /birdseye/static/img/type_icons/object.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmojaki/birdseye/HEAD/birdseye/static/img/type_icons/object.png -------------------------------------------------------------------------------- /birdseye/static/img/type_icons/tuple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmojaki/birdseye/HEAD/birdseye/static/img/type_icons/tuple.png -------------------------------------------------------------------------------- /birdseye/static/img/type_icons/NoneType.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmojaki/birdseye/HEAD/birdseye/static/img/type_icons/NoneType.png -------------------------------------------------------------------------------- /birdseye/static/img/type_icons/complex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmojaki/birdseye/HEAD/birdseye/static/img/type_icons/complex.png -------------------------------------------------------------------------------- /birdseye/clear_db.py: -------------------------------------------------------------------------------- 1 | from birdseye.db import Database 2 | 3 | Database(_skip_version_check=True).clear() 4 | 5 | print('Database cleared!') 6 | -------------------------------------------------------------------------------- /birdseye/static/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmojaki/birdseye/HEAD/birdseye/static/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /birdseye/static/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmojaki/birdseye/HEAD/birdseye/static/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /birdseye/static/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmojaki/birdseye/HEAD/birdseye/static/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /birdseye/static/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmojaki/birdseye/HEAD/birdseye/static/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /gulp/install-deps.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eux 4 | 5 | npm install --global gulp-cli 6 | npm install gulp gulp-eslint 7 | 8 | # Now run `gulp` to lint JS continuously 9 | -------------------------------------------------------------------------------- /birdseye/static/css/proton/fonts/titillium/titilliumweb-bold-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmojaki/birdseye/HEAD/birdseye/static/css/proton/fonts/titillium/titilliumweb-bold-webfont.eot -------------------------------------------------------------------------------- /birdseye/static/css/proton/fonts/titillium/titilliumweb-bold-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmojaki/birdseye/HEAD/birdseye/static/css/proton/fonts/titillium/titilliumweb-bold-webfont.ttf -------------------------------------------------------------------------------- /birdseye/static/css/proton/fonts/titillium/titilliumweb-bold-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmojaki/birdseye/HEAD/birdseye/static/css/proton/fonts/titillium/titilliumweb-bold-webfont.woff -------------------------------------------------------------------------------- /birdseye/static/css/proton/fonts/titillium/titilliumweb-regular-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmojaki/birdseye/HEAD/birdseye/static/css/proton/fonts/titillium/titilliumweb-regular-webfont.eot -------------------------------------------------------------------------------- /birdseye/static/css/proton/fonts/titillium/titilliumweb-regular-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmojaki/birdseye/HEAD/birdseye/static/css/proton/fonts/titillium/titilliumweb-regular-webfont.ttf -------------------------------------------------------------------------------- /birdseye/static/css/proton/fonts/titillium/titilliumweb-regular-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmojaki/birdseye/HEAD/birdseye/static/css/proton/fonts/titillium/titilliumweb-regular-webfont.woff -------------------------------------------------------------------------------- /birdseye/static/css/proton/fonts/titillium/titilliumweb-extralight-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmojaki/birdseye/HEAD/birdseye/static/css/proton/fonts/titillium/titilliumweb-extralight-webfont.eot -------------------------------------------------------------------------------- /birdseye/static/css/proton/fonts/titillium/titilliumweb-extralight-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmojaki/birdseye/HEAD/birdseye/static/css/proton/fonts/titillium/titilliumweb-extralight-webfont.ttf -------------------------------------------------------------------------------- /birdseye/static/css/proton/fonts/titillium/titilliumweb-extralight-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmojaki/birdseye/HEAD/birdseye/static/css/proton/fonts/titillium/titilliumweb-extralight-webfont.woff -------------------------------------------------------------------------------- /gulp/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true 4 | }, 5 | "globals": { 6 | "$": false, 7 | "hljs": false, 8 | "_": false 9 | }, 10 | "extends": "eslint:recommended" 11 | } -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py38,py39,py310,py311,py312,py313 3 | 4 | [testenv] 5 | deps = 6 | .[tests] 7 | 8 | commands = ./misc/test.sh 9 | allowlist_externals = ./misc/test.sh 10 | 11 | passenv = 12 | FIX_TESTS 13 | 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | TODO.txt 3 | __pycache__ 4 | *.egg-info/ 5 | .eggs/ 6 | .tox/ 7 | *.pyc 8 | error_screenshot.png 9 | ghostdriver.log 10 | screen_*.png 11 | .mypy_cache/ 12 | sandbox.py 13 | test.txt 14 | _build/ 15 | build/ 16 | version.py 17 | -------------------------------------------------------------------------------- /birdseye/templates/call_base.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block head %} 3 | 4 | 5 | {% endblock %} 6 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../README.rst 2 | :end-before: inclusion-end-marker 3 | 4 | Contents 5 | -------- 6 | 7 | .. toctree:: 8 | :maxdepth: 2 9 | 10 | quickstart 11 | integrations 12 | tips 13 | configuration 14 | limitations 15 | how_it_works 16 | contributing 17 | -------------------------------------------------------------------------------- /birdseye/templates/ipython_call.html: -------------------------------------------------------------------------------- 1 | {% extends "call_base.html" %} 2 | {% block whole_body %} 3 | {% include "call_core.html" %} 4 | 11 | {% endblock %} -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | from cheap_repr import repr_str, cheap_repr 3 | from birdseye import eye 4 | 5 | path = os.path.join(os.path.expanduser('~'), '.birdseye_test.db') 6 | os.environ.setdefault('BIRDSEYE_DB', 'sqlite:///' + path) 7 | 8 | repr_str.maxparts = 30 9 | cheap_repr.raise_exceptions = True 10 | 11 | eye.num_samples['big']['list'] = 10 12 | -------------------------------------------------------------------------------- /gulp/gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'), 2 | eslint = require('gulp-eslint'); 3 | 4 | gulp.task('lint', function() { 5 | return gulp.src('../birdseye/static/js/call.js') 6 | .pipe(eslint('.eslintrc.json')) 7 | .pipe(eslint.format()) 8 | }); 9 | 10 | gulp.task('default', function () { 11 | return gulp.watch(['../birdseye/static/js/call.js'], ['lint']); 12 | }); 13 | -------------------------------------------------------------------------------- /birdseye/templates/info_panel.html: -------------------------------------------------------------------------------- 1 | {% macro info_panel () -%} 2 |
19 | birdseye
20 | {{ func.html_body | safe }}
4 | Full path: {{ full_path }}
8 | {% endif %} 9 | 10 | {% from 'info_panel.html' import info_panel %} 11 | 12 | {% call info_panel() %} 13 |Click on a function name to view calls to that function.
14 |Click on the icon 15 | to view the most recent call to that function.
16 | {% endcall %} 17 | 18 |Click on a file to view all the functions in that file (some of which may not be listed here).
9 |Click on a function name to view calls to that function.
10 |Click on the icon 11 | to view the most recent call to that function.
12 | {% else %} 13 |You haven't traced any functions! Decorate a function with @eye to get started:
15 | from birdseye import eye 16 | 17 | @eye 18 | def foo():19 |
Read more documentation 20 | here. 21 |
22 | 23 | 24 | 27 | {% endif %} 28 | {% endcall %} 29 | 30 |Full file path: {{ func.file }}
35 | 36 | {% if typ == 'function' %} 37 | {# if there are no calls then this may not be the most recent function object #} 38 | {% if calls %} 39 |Line: {{ func.lineno }}
40 | {% endif %} 41 | 42 || 50 | | Start time | 51 | {% if typ == 'function' %} 52 |Arguments | 53 | {% endif %} 54 |Result | 55 |
|---|---|---|---|
| 61 | {{ call.state_icon }} 62 | | 63 |64 | {{ call.pretty_start_time }} 65 | | 66 | 67 | {% if typ == 'function' %} 68 |
69 | {% if call.arguments_list %}
70 |
|
81 | {% endif %}
82 |
83 | 84 | {{ call.result }} 85 | | 86 |
No calls yet. Is the function still running?
93 | {% endif %} 94 | {% endblock %} -------------------------------------------------------------------------------- /docs/tips.rst: -------------------------------------------------------------------------------- 1 | Tips 2 | ==== 3 | 4 | Debugging an entire file 5 | ------------------------- 6 | 7 | Instead of decorating individual functions with ``@eye``, you may want to debug *all* the functions in a module, or you may want to debug the top-level execution of the module itself without wrapping it in a function. 8 | 9 | To trace every function in the file, as well as the module execution itself, add the line:: 10 | 11 | import birdseye.trace_module_deep 12 | 13 | To trace only the module execution but none of the functions (to reduce the performance impact), leave out the ``_deep``, i.e.:: 14 | 15 | import birdseye.trace_module 16 | 17 | There are some caveats to note: 18 | 19 | #. These import statements must be unindented, not inside a block such as ``if`` or ``try``. 20 | #. If the module being traced is not the module that is being run directly, i.e. it's being imported by another module, then: 21 | #. The module will not be traced in Python 2. 22 | #. ``birdseye`` must be imported somewhere before importing the traced module. 23 | #. The execution of the entire module will be traced, not just the part after the import statement as when the traced module is run directly. 24 | 25 | Debugging functions without importing 26 | ------------------------------------- 27 | 28 | If you're working on a project with many files and you're tired of writing ``from birdseye import eye`` every time you want to debug a function, add code such as this to the entrypoint of your project:: 29 | 30 | from birdseye import eye 31 | 32 | # If you don't need Python 2/3 compatibility, 33 | # just one of these lines will do 34 | try: 35 | import __builtin__ as builtins # Python 2 36 | except ImportError: 37 | import builtins # Python 3 38 | 39 | builtins.eye = eye 40 | # or builtins.Line: {{ func.lineno }}
25 | {% endif %} 26 | 27 |Full file path: {{ func.file }}
28 | 29 | {% if typ == 'function' %} 30 | {% if call.arguments_list %} 31 |Arguments:
32 || {{ k }} | 36 |{{ v }} | 37 |
No arguments
42 | {% endif %} 43 | {% endif %} 44 | 45 |46 | Start time: {{ call.pretty_start_time }} 47 |
48 |Result: 49 | {% if call.success %} 50 | {{ call.result }}
51 | {% else %} 52 | 53 |{{ call.traceback }}
54 | {% endif %}
55 | Code:
56 | 57 | {% from 'info_panel.html' import info_panel %} 58 | 59 || ')); 150 | table.append(header); 151 | var rows = []; 152 | for (i = 0; i < numRows; i++) { 153 | var row; 154 | if (i === meta.row_break) { 155 | row = $(' | ||||
|---|---|---|---|---|
| ') 158 | .text('...') 159 | .css({'text-align': 'center'})); 160 | } 161 | table.append(row); 162 | } 163 | row = $(' | ||||
| ').text(label)); 169 | } 170 | for (i = 0; i < numCols; i++) { 171 | if (i === meta.col_break) { 172 | header.append($(' | ').text('...')); 173 | } 174 | column = val[3 + i]; 175 | header.append($(' | ').text(column[0])); 176 | var values = []; 177 | var isNumeric = true; 178 | var maxDecimals = 1; 179 | for (j = 0; j < numRows; j++) { 180 | value = column[1][4 + j][1][0]; 181 | values.push(value); 182 | isNumeric &= !isNaN(parseFloat(value)) || value.toLowerCase() === 'nan'; 183 | var decimals = value.split(".")[1]; 184 | if (decimals) { 185 | maxDecimals = Math.max(maxDecimals, decimals.length); 186 | } 187 | } 188 | for (j = 0; j < numRows; j++) { 189 | if (i === meta.col_break) { 190 | rows[j].append($(' | ').text('...')); 191 | } 192 | value = values[j]; 193 | if (isNumeric) { 194 | value = parseFloat(value).toFixed(Math.min(maxDecimals, 6)); 195 | } 196 | rows[j].append($(' | ').text(value).toggleClass('numeric', isNumeric));
197 | }
198 | }
199 | return table;
200 | }
201 |
202 | var open_paths = [];
203 |
204 | $('#inspector').jstree({
205 | core: {
206 | themes: {
207 | name: 'proton',
208 | responsive: true
209 | }
210 | },
211 | }).on("hover_node.jstree dehover_node.jstree", function (e, data) {
212 | var $node = data.node.original.$node;
213 | if (!$node) {
214 | return;
215 | }
216 | var hovering = e.type === 'hover_node';
217 | $node.toggleClass('hovering', hovering);
218 | }).on("open_node.jstree", function (e, data) {
219 | var path = data.node.original.path;
220 | if (!_(open_paths).deepContains(path)) {
221 | open_paths.push(path);
222 | }
223 | }).on("close_node.jstree", function (e, data) {
224 | var path = data.node.original.path;
225 | while (_(open_paths).deepContains(path)) {
226 | var index = _.findIndex(open_paths, _.partial(_.isEqual, path));
227 | open_paths.splice(index, 1);
228 | }
229 | });
230 |
231 | $code.find('span[data-index]').each(function () {
232 | var $this = $(this);
233 | var tree_index = this.dataset.index;
234 | var json = JSON.stringify(node_values[tree_index]) || '';
235 | $this.toggleClass('box', tree_index in node_values && !(
236 | // This is a statement/comprehension node that never encounters an exception,
237 | // or has any metadata, so it never has a 'value' worth checking.
238 | ($this.hasClass('stmt') || $this.hasClass('loop')) &&
239 | -1 === json.indexOf('-1') &&
240 | -1 === json.indexOf('inner_call')));
241 | $this.toggleClass(
242 | 'has-inner',
243 | json.indexOf('"inner_calls":["') !== -1);
244 | $this.click(function () {
245 | if ($this.hasClass('hovering')) {
246 | $this.toggleClass('selected');
247 | selected_boxes = _.toggle(selected_boxes, tree_index);
248 | }
249 | render();
250 | });
251 | index_to_node[tree_index] = this;
252 | });
253 |
254 | function render() {
255 |
256 | $('#inspector, #resize-handle').css({display: selected_boxes.length ? 'block' : 'none'});
257 |
258 | var loop_indices = {};
259 |
260 | function findRanges(iters) {
261 | if (!iters) {
262 | return;
263 | }
264 | Object.keys(iters).forEach(function (key) {
265 | var value = iters[key];
266 | loop_indices[key] = _.pluck(value, 'index');
267 | findRanges(value[current_iteration(key)].loops);
268 | });
269 | }
270 |
271 | function current_iteration(i) {
272 | if (!(i in loop_indices)) {
273 | return -1;
274 | }
275 | return Math.min(_current_iteration[i], loop_indices[i].length - 1);
276 | }
277 |
278 | findRanges(loop_iterations);
279 |
280 | function get_value(tree_index) {
281 | var value = node_values[tree_index];
282 | var loops = node_loops[tree_index] || [];
283 | loops.forEach(function (loopIndex) {
284 | if (value) {
285 | value = value[current_iteration(loopIndex)];
286 | }
287 | });
288 | return value;
289 | }
290 |
291 | $code.find('span[data-index]').each(
292 | function () {
293 | var value;
294 | var $this = $(this);
295 | var tree_index = this.dataset.index;
296 | if (tree_index in node_values) {
297 | value = get_value(tree_index);
298 | }
299 | $this.toggleClass('has_value', Boolean(value));
300 | $this.toggleClass('stmt_uncovered', $this.hasClass('stmt') && !value);
301 |
302 | if (value && $this.hasClass('box')) {
303 | $this.on('mouseover mouseout', function (e) {
304 | var hovering = e.type === 'mouseover';
305 | $this.toggleClass('hovering', hovering);
306 | if (hovering) {
307 | if (value[1] === -2) {
308 | value[0] = normal_stmt_value;
309 | }
310 | $('#box_value').text(value[0]);
311 | }
312 | e.stopPropagation();
313 | });
314 | $this.toggleClass('exception_node', value[1] === -1);
315 | $this.toggleClass('value_none', value[1] === 0 || value[1] === -2);
316 | $this.children('a.inner-call').remove();
317 |
318 | var inner_calls = value[2].inner_calls || [];
319 | var place_link = function (inner_call, css) {
320 | var link = $('' +
321 | '' +
322 | '')
323 | .css(css);
324 | $this.append(link);
325 | };
326 | if (inner_calls.length === 1) {
327 | place_link(inner_calls[0], {bottom: '-4px'});
328 | } else if (inner_calls.length >= 2) {
329 | place_link(inner_calls[0], {top: 0});
330 | place_link(inner_calls[inner_calls.length - 1], {bottom: '-4px'});
331 | }
332 | } else {
333 | $this.off('mouseover mouseout');
334 | }
335 | }
336 | );
337 |
338 | var inspector = $('#inspector');
339 | inspector.jstree(true).settings.core.data = selected_boxes.map(function (tree_index) {
340 | var node = index_to_node[tree_index];
341 | var $node = $(node);
342 | var value = get_value(tree_index);
343 | return make_jstree_nodes($node.text(), [tree_index], value, $node);
344 | });
345 | inspector.jstree(true).refresh();
346 |
347 | $('.loop-navigator').remove();
348 |
349 | $code.find('.loop').each(function (_, loop_span) {
350 |
351 | var loopIndex = loop_span.dataset.index;
352 |
353 | var buttonGroup = $(' ', {
354 | class: "btn-group loop-navigator",
355 | role: "group",
356 | }).css({
357 | position: 'absolute',
358 | top: $(loop_span).offset().top - $code.offset().top,
359 | right: '5px',
360 | });
361 |
362 | function mkButton(cls, disabled, html, onclick) {
363 | var attrs = {
364 | type: 'button',
365 | class: 'btn btn-default btn-xs ' + cls,
366 | html: html,
367 | };
368 | if (disabled) {
369 | attrs.disabled = 'disabled';
370 | }
371 | if (cls === 'dropdown-toggle') {
372 | attrs['data-toggle'] = 'dropdown';
373 | }
374 | var button = $(' |