# Notebook magics


<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->

bAsedPL adds `%apl` and `%%apl` magics to IPython. Both evaluate in
`basedpl.apl`, the workspace that Python code also uses. The magic
renders the captured output and loads the language bar.

The line magic returns a native array or function. The cell magic
displays what the APL prompt would show, including implicit expression
results.

``` python
from fastcore.test import *
from basedpl import Array, apl
from IPython.utils.capture import capture_output
import numpy as np
```

## Rendering output

------------------------------------------------------------------------

### AplOut

``` python
def AplOut(
    *args, **kwargs
):
```

*APL output text, displayed in the SAX2 font where HTML is available.*

------------------------------------------------------------------------

### display_events

``` python
def display_events(
    events
):
```

*Display captured output in order, using MIME bundles for rich results.*

`AplOut` preserves APL spacing. Its HTML representation escapes the
output before putting it in a `<pre>` element.

``` python
out = AplOut('1 < 2')
assert '&lt;' in out._repr_html_()
out
```

<pre class="aplnb_out sax2">1 &lt; 2</pre>

## The magics

The magics evaluate in `basedpl.apl`, so Python calls through `apl`
share their workspace. The language bar and SAX2 stylesheet are loaded
once for each magic instance.

------------------------------------------------------------------------

### APLMagic

``` python
def APLMagic():
```

*IPython APL magics that evaluate in the workspace `basedpl.apl`.*

------------------------------------------------------------------------

### APLMagic.apl

``` python
def apl(
    line, cell:NoneType=None
):
```

*Evaluate a line as a native value or display a cell as an APL session.*

Cell output comes from `apl(code, 'repl')`, which includes implicit
expression results. Line output comes from `apl(code, 'explicit')`,
which captures explicit output and returns native values. A trailing `;`
suppresses all cell display without skipping execution. Output produced
before an error is displayed before the error is raised.

Tab completes user and system names in APL input. Completion reads the
workspace without evaluating anything.

------------------------------------------------------------------------

### APLMagic.complete

``` python
def complete(
    context
):
```

*Complete visible APL names in line and cell magics.*

------------------------------------------------------------------------

### create_magic

``` python
def create_magic(
    shell:NoneType=None
):
```

*Register line and cell magics with `shell`. They evaluate in
`basedpl.apl`.*

The cell magic displays each expression. Assignments remain silent.

``` python
%%apl
m←2 3⍴⍳6
m×10
```

<pre class="aplnb_out sax2">10 20 30
40 50 60</pre>

The line magic returns a native `basedpl.Array`. Use `.np` or `.py` for
conversion. Comments are parsed by bAsedPL, including `⍝` inside quoted
strings.

``` python
z = %apl m ⍝ a matrix
test_is(type(z), Array)
np.testing.assert_array_equal(z.np, [[1, 2, 3], [4, 5, 6]])
test_eq(magic.apl("'a⍝b' ⍝ comment").py, 'a⍝b')
z
```

    1 2 3
    4 5 6

Python values bound through `apl` are visible to the magics. Python
integers stay exact.

``` python
apl(x=np.arange(1, 4))
value = %apl +/x
test_eq(value.py, 6)
test_is(type(value.py), int)
value
```

    6ₓ

bAsedPL’s display commands also work in a cell.

``` python
%%apl
]Display (1 2)'ab'
```

<pre class="aplnb_out sax2">┌→───────────┐
│ ┌→──┐ ┌→─┐ │
│ │1 2│ │ab│ │
│ └~──┘ └──┘ │
└∊───────────┘</pre>

A suppressed cell still updates the workspace.

``` python
%%apl
quiet←7;
```

SVG elements and custom MIME renderers display in cell magics and as
Python values.

``` python
%%apl
circle←•element 'circle'
•svg ('cx':50 ⋄ 'cy':50 ⋄ 'r':30 ⋄ 'fill':'orange') circle ''
```

![](magics_files/figure-commonmark/cell-15-output-1.svg)

## Names and help

Use `]help name` for help and `]help name -source` for source, in either
magic. Leading definition comments supply help. Inspection leaves the
function uncalled.

``` python
%%apl
double←{⍝ Double the argument
⎕←'called' ⋄ ⍵×2}
```

``` python
with capture_output() as cap: magic.apl(']help double')
assert 'Double the argument' in cap.outputs[0].data['text/markdown']
with capture_output() as cap: magic.apl('', ']help double -source')
assert "⎕←'called'" in cap.outputs[0].data['text/markdown']
test_eq(len(cap.outputs), 1)
```

Completion follows the magic’s workspace, including names defined after
registration. Quoted text and comments stay literal.

``` python
from IPython.core.completer import provisionalcompleter
```

``` python
shell = get_ipython()
for text, expected in [('%apl dou', 'double'), ('v = %apl dou', 'double'), ('%%apl\n1+•sr', '•src')]:
    with provisionalcompleter(): matches = list(shell.Completer.completions(text, len(text)))
    assert expected in [m.text for m in matches]
for text in ["%%apl\n'dou", '%%apl\n⍝ dou', 'ordinary_python']:
    with provisionalcompleter(): matches = list(shell.Completer.completions(text, len(text)))
    assert not any(m.type == 'APL name' for m in matches)
```

## Errors and workspace state

Errors retain source locations and leave completed assignments in the
workspace. Incomplete input raises a syntax error and leaves the
workspace unchanged.

``` python
with capture_output() as cap: test_fail(lambda: magic.apl('', '⎕←42 ⋄ 1÷0'), contains='DOMAIN ERROR')
assert '42' in cap.outputs[0].data['text/html']
test_fail(lambda: magic.apl('', '{⍵+1'), contains='SYNTAX ERROR')
test_eq(magic.apl('quiet').py, 9)
```

Set `apl.timeout` to a per-evaluation deadline in seconds. Ctrl-C
interrupts the evaluation. Output captured before cancellation is
displayed. The workspace keeps its names after cancellation.
Native-library calls and individual BigInt operations can delay
cancellation.

## Installation

`%load_ext basedpl.notebooks` registers the magics in the current
kernel. `bapl-install-magic` adds the extension to the default IPython
profile. Registration does not start an APL worker.

------------------------------------------------------------------------

### load_ipython_extension

``` python
def load_ipython_extension(
    ipython
):
```

*Register the APL magics when IPython loads this extension.*

------------------------------------------------------------------------

### create_ipython_config

``` python
def create_ipython_config():
```

*Called by `bapl-install-magic` to install magic*

## Symbol input

The browser input method uses bAsedPL’s symbol names and aliases. It is
active in `%%apl` cells, `%apl` lines and Monaco editors configured for
APL. Matching prefers exact names, then prefixes, then abbreviations. A
unique match is accepted with Tab or a delimiter. Enter accepts it
before the notebook’s usual newline or execution action. Ambiguous
matches remain unchanged and appear in a clickable list.

Strings, comments and pasted text are not expanded. Cursor movement and
Escape cancel automatic expansion. The clickable language bar can still
insert literal glyphs.

Hold **left Alt/Option** to type glyphs using [bAsedPL’s
keyboard](keyboard.qmd): Alt-h `←`, Alt-minus `×`, Alt-equals `÷`,
Alt-Shift-a `⍶`. These chords insert literal glyphs, including inside
APL strings and comments. Right Option keeps its native keyboard
behavior. The glyph map is shared with the bAsedPL REPL.

The JavaScript assertions live in `tests/input.js`. Pytest runs them
through fastcdp’s QUnit runner using bAsedPL’s actual symbol catalogue.
Editor integration checks exercise CodeMirror, Monaco and textarea.
Install the `dev` extra and Chrome for Testing
(`fastcdp-setup --install stable`), then run `pytest -q`. QUnit and the
editor fixtures load from CDNs. Node is not required.
