openalgo-script 0.2.0 → 0.4.0
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.
- package/CHANGELOG.md +269 -0
- package/README.md +65 -31
- package/dist/adapters/charts/surfaces.d.ts +17 -0
- package/dist/adapters/charts/surfaces.d.ts.map +1 -1
- package/dist/adapters/charts/tables.js +82 -0
- package/dist/adapters/charts/tables.js.map +1 -1
- package/dist/adapters/codemirror/commands.d.ts +14 -0
- package/dist/adapters/codemirror/commands.d.ts.map +1 -0
- package/dist/adapters/codemirror/commands.js +52 -0
- package/dist/adapters/codemirror/commands.js.map +1 -0
- package/dist/adapters/codemirror/completion.d.ts +15 -0
- package/dist/adapters/codemirror/completion.d.ts.map +1 -0
- package/dist/adapters/codemirror/completion.js +64 -0
- package/dist/adapters/codemirror/completion.js.map +1 -0
- package/dist/adapters/codemirror/contract.d.ts +156 -0
- package/dist/adapters/codemirror/contract.d.ts.map +1 -0
- package/dist/adapters/codemirror/contract.js +42 -0
- package/dist/adapters/codemirror/contract.js.map +1 -0
- package/dist/adapters/codemirror/index.d.ts +43 -0
- package/dist/adapters/codemirror/index.d.ts.map +1 -0
- package/dist/adapters/codemirror/index.js +8 -0
- package/dist/adapters/codemirror/index.js.map +1 -0
- package/dist/adapters/codemirror/lint.d.ts +17 -0
- package/dist/adapters/codemirror/lint.d.ts.map +1 -0
- package/dist/adapters/codemirror/lint.js +62 -0
- package/dist/adapters/codemirror/lint.js.map +1 -0
- package/dist/adapters/codemirror/positions.d.ts +16 -0
- package/dist/adapters/codemirror/positions.d.ts.map +1 -0
- package/dist/adapters/codemirror/positions.js +65 -0
- package/dist/adapters/codemirror/positions.js.map +1 -0
- package/dist/adapters/codemirror/stream.d.ts +23 -0
- package/dist/adapters/codemirror/stream.d.ts.map +1 -0
- package/dist/adapters/codemirror/stream.js +70 -0
- package/dist/adapters/codemirror/stream.js.map +1 -0
- package/dist/adapters/codemirror/tokens.d.ts +44 -0
- package/dist/adapters/codemirror/tokens.d.ts.map +1 -0
- package/dist/adapters/codemirror/tokens.js +34 -0
- package/dist/adapters/codemirror/tokens.js.map +1 -0
- package/dist/adapters/codemirror/tooltips.d.ts +35 -0
- package/dist/adapters/codemirror/tooltips.d.ts.map +1 -0
- package/dist/adapters/codemirror/tooltips.js +108 -0
- package/dist/adapters/codemirror/tooltips.js.map +1 -0
- package/dist/core/accounting/charges.d.ts +136 -0
- package/dist/core/accounting/charges.d.ts.map +1 -0
- package/dist/core/accounting/charges.js +362 -0
- package/dist/core/accounting/charges.js.map +1 -0
- package/dist/core/accounting/equity.d.ts +158 -0
- package/dist/core/accounting/equity.d.ts.map +1 -0
- package/dist/core/accounting/equity.js +155 -0
- package/dist/core/accounting/equity.js.map +1 -0
- package/dist/core/accounting/index.d.ts +44 -0
- package/dist/core/accounting/index.d.ts.map +1 -0
- package/dist/core/accounting/index.js +36 -0
- package/dist/core/accounting/index.js.map +1 -0
- package/dist/core/accounting/markers.d.ts +43 -0
- package/dist/core/accounting/markers.d.ts.map +1 -0
- package/dist/core/accounting/markers.js +64 -0
- package/dist/core/accounting/markers.js.map +1 -0
- package/dist/core/accounting/monthly.d.ts +52 -0
- package/dist/core/accounting/monthly.d.ts.map +1 -0
- package/dist/core/accounting/monthly.js +99 -0
- package/dist/core/accounting/monthly.js.map +1 -0
- package/dist/core/accounting/report.d.ts +38 -0
- package/dist/core/accounting/report.d.ts.map +1 -0
- package/dist/core/accounting/report.js +63 -0
- package/dist/core/accounting/report.js.map +1 -0
- package/dist/core/accounting/shapes.d.ts +70 -0
- package/dist/core/accounting/shapes.d.ts.map +1 -0
- package/dist/core/accounting/shapes.js +21 -0
- package/dist/core/accounting/shapes.js.map +1 -0
- package/dist/core/accounting/statistics.d.ts +75 -0
- package/dist/core/accounting/statistics.d.ts.map +1 -0
- package/dist/core/accounting/statistics.js +246 -0
- package/dist/core/accounting/statistics.js.map +1 -0
- package/dist/core/accounting/trades.d.ts +118 -0
- package/dist/core/accounting/trades.d.ts.map +1 -0
- package/dist/core/accounting/trades.js +186 -0
- package/dist/core/accounting/trades.js.map +1 -0
- package/dist/core/backtest/compare.d.ts +38 -0
- package/dist/core/backtest/compare.d.ts.map +1 -0
- package/dist/core/backtest/compare.js +189 -0
- package/dist/core/backtest/compare.js.map +1 -0
- package/dist/core/backtest/declaration.d.ts +52 -0
- package/dist/core/backtest/declaration.d.ts.map +1 -0
- package/dist/core/backtest/declaration.js +48 -0
- package/dist/core/backtest/declaration.js.map +1 -0
- package/dist/core/backtest/drive.d.ts +29 -0
- package/dist/core/backtest/drive.d.ts.map +1 -0
- package/dist/core/backtest/drive.js +262 -0
- package/dist/core/backtest/drive.js.map +1 -0
- package/dist/core/backtest/index.d.ts +46 -0
- package/dist/core/backtest/index.d.ts.map +1 -0
- package/dist/core/backtest/index.js +36 -0
- package/dist/core/backtest/index.js.map +1 -0
- package/dist/core/backtest/range.d.ts +84 -0
- package/dist/core/backtest/range.d.ts.map +1 -0
- package/dist/core/backtest/range.js +90 -0
- package/dist/core/backtest/range.js.map +1 -0
- package/dist/core/backtest/record.d.ts +238 -0
- package/dist/core/backtest/record.d.ts.map +1 -0
- package/dist/core/backtest/record.js +176 -0
- package/dist/core/backtest/record.js.map +1 -0
- package/dist/core/backtest/replay.d.ts +48 -0
- package/dist/core/backtest/replay.d.ts.map +1 -0
- package/dist/core/backtest/replay.js +127 -0
- package/dist/core/backtest/replay.js.map +1 -0
- package/dist/core/backtest/resting.d.ts +62 -0
- package/dist/core/backtest/resting.d.ts.map +1 -0
- package/dist/core/backtest/resting.js +59 -0
- package/dist/core/backtest/resting.js.map +1 -0
- package/dist/core/backtest/settings.d.ts +117 -0
- package/dist/core/backtest/settings.d.ts.map +1 -0
- package/dist/core/backtest/settings.js +207 -0
- package/dist/core/backtest/settings.js.map +1 -0
- package/dist/core/backtest/simulate.d.ts +146 -0
- package/dist/core/backtest/simulate.d.ts.map +1 -0
- package/dist/core/backtest/simulate.js +217 -0
- package/dist/core/backtest/simulate.js.map +1 -0
- package/dist/core/catalogue/catalogue.generated.d.ts +66 -0
- package/dist/core/catalogue/catalogue.generated.d.ts.map +1 -1
- package/dist/core/catalogue/catalogue.generated.js +6 -0
- package/dist/core/catalogue/catalogue.generated.js.map +1 -1
- package/dist/core/catalogue/values.generated.d.ts +24 -0
- package/dist/core/catalogue/values.generated.d.ts.map +1 -1
- package/dist/core/check/index.d.ts +2 -1
- package/dist/core/check/index.d.ts.map +1 -1
- package/dist/core/check/index.js +1 -1
- package/dist/core/check/index.js.map +1 -1
- package/dist/core/check/library-prose.generated.d.ts +16 -0
- package/dist/core/check/library-prose.generated.d.ts.map +1 -0
- package/dist/core/check/library-prose.generated.js +353 -0
- package/dist/core/check/library-prose.generated.js.map +1 -0
- package/dist/core/check/surface.d.ts +24 -0
- package/dist/core/check/surface.d.ts.map +1 -1
- package/dist/core/check/surface.js +29 -0
- package/dist/core/check/surface.js.map +1 -1
- package/dist/core/emit/defaults.d.ts +35 -16
- package/dist/core/emit/defaults.d.ts.map +1 -1
- package/dist/core/emit/defaults.js +66 -0
- package/dist/core/emit/defaults.js.map +1 -1
- package/dist/core/emit/index.d.ts +2 -0
- package/dist/core/emit/index.d.ts.map +1 -1
- package/dist/core/emit/index.js +2 -0
- package/dist/core/emit/index.js.map +1 -1
- package/dist/core/engine/index.d.ts +1 -1
- package/dist/core/engine/index.d.ts.map +1 -1
- package/dist/core/engine/index.js +1 -1
- package/dist/core/engine/index.js.map +1 -1
- package/dist/core/engine/load.d.ts +15 -1
- package/dist/core/engine/load.d.ts.map +1 -1
- package/dist/core/engine/load.js +1 -0
- package/dist/core/engine/load.js.map +1 -1
- package/dist/core/index.d.ts +37 -3
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +17 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/version/version.generated.d.ts +1 -1
- package/dist/core/version/version.generated.js +1 -1
- package/dist/editor/complete.d.ts +40 -0
- package/dist/editor/complete.d.ts.map +1 -0
- package/dist/editor/complete.js +206 -0
- package/dist/editor/complete.js.map +1 -0
- package/dist/editor/diagnose.d.ts +18 -0
- package/dist/editor/diagnose.d.ts.map +1 -0
- package/dist/editor/diagnose.js +70 -0
- package/dist/editor/diagnose.js.map +1 -0
- package/dist/editor/format.d.ts +11 -0
- package/dist/editor/format.d.ts.map +1 -0
- package/dist/editor/format.js +50 -0
- package/dist/editor/format.js.map +1 -0
- package/dist/editor/highlight.d.ts +28 -0
- package/dist/editor/highlight.d.ts.map +1 -0
- package/dist/editor/highlight.js +65 -0
- package/dist/editor/highlight.js.map +1 -0
- package/dist/editor/hover.d.ts +40 -0
- package/dist/editor/hover.d.ts.map +1 -0
- package/dist/editor/hover.js +147 -0
- package/dist/editor/hover.js.map +1 -0
- package/dist/editor/index.d.ts +60 -0
- package/dist/editor/index.d.ts.map +1 -0
- package/dist/editor/index.js +7 -0
- package/dist/editor/index.js.map +1 -0
- package/dist/editor/kinds.d.ts +27 -0
- package/dist/editor/kinds.d.ts.map +1 -0
- package/dist/editor/kinds.js +118 -0
- package/dist/editor/kinds.js.map +1 -0
- package/dist/editor/layout.d.ts +47 -0
- package/dist/editor/layout.d.ts.map +1 -0
- package/dist/editor/layout.js +135 -0
- package/dist/editor/layout.js.map +1 -0
- package/dist/editor/manifest.d.ts +47 -0
- package/dist/editor/manifest.d.ts.map +1 -0
- package/dist/editor/manifest.js +93 -0
- package/dist/editor/manifest.js.map +1 -0
- package/dist/editor/reading.d.ts +41 -0
- package/dist/editor/reading.d.ts.map +1 -0
- package/dist/editor/reading.js +51 -0
- package/dist/editor/reading.js.map +1 -0
- package/dist/editor/scan.d.ts +26 -0
- package/dist/editor/scan.d.ts.map +1 -0
- package/dist/editor/scan.js +139 -0
- package/dist/editor/scan.js.map +1 -0
- package/dist/editor/scope.d.ts +19 -0
- package/dist/editor/scope.d.ts.map +1 -0
- package/dist/editor/scope.js +99 -0
- package/dist/editor/scope.js.map +1 -0
- package/dist/editor/signature.d.ts +38 -0
- package/dist/editor/signature.d.ts.map +1 -0
- package/dist/editor/signature.js +112 -0
- package/dist/editor/signature.js.map +1 -0
- package/dist/editor/site.d.ts +46 -0
- package/dist/editor/site.d.ts.map +1 -0
- package/dist/editor/site.js +184 -0
- package/dist/editor/site.js.map +1 -0
- package/dist/editor/spacing.d.ts +29 -0
- package/dist/editor/spacing.d.ts.map +1 -0
- package/dist/editor/spacing.js +116 -0
- package/dist/editor/spacing.js.map +1 -0
- package/package.json +30 -3
- package/spec/errors.json +140 -0
- package/src/adapters/charts/surfaces.ts +17 -0
- package/src/adapters/charts/tables.ts +88 -0
- package/src/adapters/codemirror/commands.ts +53 -0
- package/src/adapters/codemirror/completion.ts +74 -0
- package/src/adapters/codemirror/contract.ts +156 -0
- package/src/adapters/codemirror/index.ts +66 -0
- package/src/adapters/codemirror/lint.ts +66 -0
- package/src/adapters/codemirror/positions.ts +79 -0
- package/src/adapters/codemirror/stream.ts +87 -0
- package/src/adapters/codemirror/tokens.ts +64 -0
- package/src/adapters/codemirror/tooltips.ts +113 -0
- package/src/core/accounting/charges.ts +452 -0
- package/src/core/accounting/equity.ts +276 -0
- package/src/core/accounting/index.ts +49 -0
- package/src/core/accounting/markers.ts +95 -0
- package/src/core/accounting/monthly.ts +137 -0
- package/src/core/accounting/report.ts +89 -0
- package/src/core/accounting/shapes.ts +73 -0
- package/src/core/accounting/statistics.ts +350 -0
- package/src/core/accounting/trades.ts +313 -0
- package/src/core/backtest/compare.ts +244 -0
- package/src/core/backtest/declaration.ts +97 -0
- package/src/core/backtest/drive.ts +364 -0
- package/src/core/backtest/index.ts +52 -0
- package/src/core/backtest/range.ts +137 -0
- package/src/core/backtest/record.ts +341 -0
- package/src/core/backtest/replay.ts +158 -0
- package/src/core/backtest/resting.ts +125 -0
- package/src/core/backtest/settings.ts +280 -0
- package/src/core/backtest/simulate.ts +304 -0
- package/src/core/catalogue/catalogue.generated.ts +6 -0
- package/src/core/catalogue/values.generated.ts +6 -0
- package/src/core/check/index.ts +3 -0
- package/src/core/check/library-prose.generated.ts +367 -0
- package/src/core/check/surface.ts +34 -0
- package/src/core/emit/defaults.ts +70 -0
- package/src/core/emit/index.ts +2 -0
- package/src/core/engine/index.ts +1 -1
- package/src/core/engine/load.ts +16 -2
- package/src/core/index.ts +85 -1
- package/src/core/version/version.generated.ts +1 -1
- package/src/editor/complete.ts +266 -0
- package/src/editor/diagnose.ts +71 -0
- package/src/editor/format.ts +85 -0
- package/src/editor/highlight.ts +110 -0
- package/src/editor/hover.ts +199 -0
- package/src/editor/index.ts +65 -0
- package/src/editor/kinds.ts +157 -0
- package/src/editor/layout.ts +189 -0
- package/src/editor/manifest.ts +119 -0
- package/src/editor/reading.ts +69 -0
- package/src/editor/scan.ts +162 -0
- package/src/editor/scope.ts +122 -0
- package/src/editor/signature.ts +150 -0
- package/src/editor/site.ts +233 -0
- package/src/editor/spacing.ts +132 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Errors as you type, as the editor's linter.
|
|
3
|
+
*
|
|
4
|
+
* The diagnostics are the compiler's own, whole: `diagnose` runs the lexer, the
|
|
5
|
+
* parser, the checker and the emitter, so what a writer sees beside their line
|
|
6
|
+
* is exactly what a compile produces and nothing new can appear when they press
|
|
7
|
+
* apply. Every message and every fix came from the error catalogue that the
|
|
8
|
+
* documentation site is generated from.
|
|
9
|
+
*
|
|
10
|
+
* Three decisions worth stating, because each one is visible to a trader.
|
|
11
|
+
*
|
|
12
|
+
* **The fix is shown.** A `Diagnostic` carries a message saying what is wrong and
|
|
13
|
+
* a fix saying what to do, and the fix is the half that is worth reading. The
|
|
14
|
+
* editor's panel shows one string, so the two are joined with a line break
|
|
15
|
+
* rather than the fix being dropped.
|
|
16
|
+
*
|
|
17
|
+
* **The code is the source.** The editor shows a diagnostic's source beside its
|
|
18
|
+
* message, and a code is the one part of a diagnostic this project promises never
|
|
19
|
+
* to change, so it is what a trader quotes and what a support page is indexed by.
|
|
20
|
+
*
|
|
21
|
+
* **A zero width span is widened by one character.** The compiler points at a
|
|
22
|
+
* position for a mistake that is an absence, a missing bracket among them, and a
|
|
23
|
+
* squiggle of no width is a squiggle nobody sees.
|
|
24
|
+
*/
|
|
25
|
+
import { endOffset } from '../../core/index.js';
|
|
26
|
+
import type { Diagnostic } from '../../core/index.js';
|
|
27
|
+
import { diagnose } from '../../editor/index.js';
|
|
28
|
+
import type { EditorDiagnostic, EditorView } from './contract.js';
|
|
29
|
+
import { documentOf } from './positions.js';
|
|
30
|
+
|
|
31
|
+
/** What the editor prints beside a message, which is the code and nothing else. */
|
|
32
|
+
function sourceOf(one: Diagnostic): string {
|
|
33
|
+
return one.code;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Every diagnostic of a document, in the editor's own shape. */
|
|
37
|
+
export function diagnosticsFor(raw: string): readonly EditorDiagnostic[] {
|
|
38
|
+
const held = documentOf(raw);
|
|
39
|
+
return diagnose(held.text).map((one) => {
|
|
40
|
+
const from = held.at(one.span.offset);
|
|
41
|
+
const to = held.at(endOffset(one.span));
|
|
42
|
+
return {
|
|
43
|
+
from,
|
|
44
|
+
to: to > from ? to : from + 1,
|
|
45
|
+
severity: one.severity,
|
|
46
|
+
message: `${one.message}\n${one.fix}`,
|
|
47
|
+
source: sourceOf(one),
|
|
48
|
+
};
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The linter a host wires in one line.
|
|
54
|
+
*
|
|
55
|
+
* ```ts
|
|
56
|
+
* linter(openscriptLint)
|
|
57
|
+
* ```
|
|
58
|
+
*
|
|
59
|
+
* It is synchronous because it is fast enough to be: a finished ninety line
|
|
60
|
+
* study compiles in a third of a millisecond and the same file with a bracket
|
|
61
|
+
* left open in about a millisecond, both with a budget in the benchmark suite.
|
|
62
|
+
* The editor's own linter already debounces, so nothing here does it twice.
|
|
63
|
+
*/
|
|
64
|
+
export function openscriptLint(view: EditorView): readonly EditorDiagnostic[] {
|
|
65
|
+
return diagnosticsFor(view.state.doc.toString());
|
|
66
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Offsets, translated between the document the editor holds and the text the
|
|
3
|
+
* compiler read.
|
|
4
|
+
*
|
|
5
|
+
* `language.md` 3.1 normalises a file before anything reads it: a byte order
|
|
6
|
+
* mark is dropped and every CRLF becomes LF. Every span the compiler produces
|
|
7
|
+
* therefore indexes the normalised text, which `docs/integrating/the-editor-half.md`
|
|
8
|
+
* tells a host plainly and expects it to handle.
|
|
9
|
+
*
|
|
10
|
+
* A drop-in cannot expect it. A document written on a machine that ends its
|
|
11
|
+
* lines with two characters is an ordinary document, the editor holds it exactly
|
|
12
|
+
* as it is, and an adapter that handed the editor a span from the normalised
|
|
13
|
+
* text would put every squiggle on a file's fiftieth line fifty characters to
|
|
14
|
+
* the left. That is the kind of defect that is reported as "the underline is in
|
|
15
|
+
* the wrong place" and survives for months.
|
|
16
|
+
*
|
|
17
|
+
* So the two are mapped. The map is built only when the document is not already
|
|
18
|
+
* normalised, which is the common case and costs a comparison; when it is built
|
|
19
|
+
* it is one walk of the text.
|
|
20
|
+
*/
|
|
21
|
+
import { normaliseSource } from '../../core/index.js';
|
|
22
|
+
|
|
23
|
+
const BYTE_ORDER_MARK = 0xfeff;
|
|
24
|
+
const CARRIAGE_RETURN = 13;
|
|
25
|
+
const LINE_FEED = 10;
|
|
26
|
+
|
|
27
|
+
/** The document as the compiler reads it, and where each of its offsets sits. */
|
|
28
|
+
export interface Document {
|
|
29
|
+
/** The normalised text: what `diagnose`, `complete` and the rest are given. */
|
|
30
|
+
readonly text: string;
|
|
31
|
+
/**
|
|
32
|
+
* The offset in the editor's own document that a normalised offset names.
|
|
33
|
+
*
|
|
34
|
+
* The identity where the document is already normalised, which is what a
|
|
35
|
+
* document written on a machine that ends its lines with one character is.
|
|
36
|
+
*/
|
|
37
|
+
at(offset: number): number;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function documentOf(raw: string): Document {
|
|
41
|
+
const text = normaliseSource(raw);
|
|
42
|
+
if (text === raw) return { text, at: (offset) => offset };
|
|
43
|
+
|
|
44
|
+
// One entry per normalised offset, plus the one past its end, so that the end
|
|
45
|
+
// of a span at the end of the file maps as readily as its start.
|
|
46
|
+
const map = new Array<number>(text.length + 1);
|
|
47
|
+
let normalised = 0;
|
|
48
|
+
let held = raw.charCodeAt(0) === BYTE_ORDER_MARK ? 1 : 0;
|
|
49
|
+
|
|
50
|
+
while (normalised <= text.length) {
|
|
51
|
+
map[normalised] = held;
|
|
52
|
+
if (normalised === text.length) break;
|
|
53
|
+
if (raw.charCodeAt(held) === CARRIAGE_RETURN && raw.charCodeAt(held + 1) === LINE_FEED) {
|
|
54
|
+
held += 1;
|
|
55
|
+
}
|
|
56
|
+
held += 1;
|
|
57
|
+
normalised += 1;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
text,
|
|
62
|
+
at: (offset) => map[Math.min(Math.max(offset, 0), text.length)] ?? offset,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** The normalised offset an editor offset names, which is the other direction. */
|
|
67
|
+
export function normalisedOffset(held: Document, raw: number): number {
|
|
68
|
+
// Walked rather than inverted, because the inverse of the map above is only
|
|
69
|
+
// needed at one position per call and building a second array to answer one
|
|
70
|
+
// question would cost a file's length for a comparison's worth of work.
|
|
71
|
+
let low = 0;
|
|
72
|
+
let high = held.text.length;
|
|
73
|
+
while (low < high) {
|
|
74
|
+
const middle = (low + high + 1) >> 1;
|
|
75
|
+
if (held.at(middle) <= raw) low = middle;
|
|
76
|
+
else high = middle - 1;
|
|
77
|
+
}
|
|
78
|
+
return low;
|
|
79
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Highlighting, as the editor's line at a time tokenizer.
|
|
3
|
+
*
|
|
4
|
+
* The editor asks for one line's tokens at a time and remembers nothing between
|
|
5
|
+
* lines except the state this file hands back. The tier's `highlight` takes a
|
|
6
|
+
* whole file. So the line is what is lexed here, by the real lexer, through the
|
|
7
|
+
* same `highlight` a host would call itself.
|
|
8
|
+
*
|
|
9
|
+
* **That is an equivalence rather than an approximation, and it is measured.**
|
|
10
|
+
* `tests/adapters/codemirror/stream.test.ts` puts every script in the repository
|
|
11
|
+
* and the malformed corpus through both routes and compares them piece for
|
|
12
|
+
* piece: 4279 lines of real scripts, and not one of them differs. It holds
|
|
13
|
+
* because nothing in this language crosses a line ending: a string literal is
|
|
14
|
+
* refused at the end of its line, there is no block comment, and the layout
|
|
15
|
+
* tokens that do depend on the lines above are painted as whitespace either way.
|
|
16
|
+
* If the language ever grows something that does cross one, that test fails on
|
|
17
|
+
* the day it is added rather than on the day somebody notices a half painted
|
|
18
|
+
* line, and this becomes a line the narrowings file has to carry.
|
|
19
|
+
*
|
|
20
|
+
* A line is lexed once, not once per token: the pieces are computed when the
|
|
21
|
+
* editor arrives at the start of a line and walked from there. Without that, a
|
|
22
|
+
* line of twenty tokens would be lexed twenty times, which is the shape that
|
|
23
|
+
* makes a tokenizer show up in a frame budget.
|
|
24
|
+
*/
|
|
25
|
+
import { highlight } from '../../editor/index.js';
|
|
26
|
+
import type { Highlight } from '../../editor/index.js';
|
|
27
|
+
import type { EditorStreamParser, EditorStringStream } from './contract.js';
|
|
28
|
+
import { HIGHLIGHT_TOKENS } from './tokens.js';
|
|
29
|
+
|
|
30
|
+
/** What is carried from one call to the next: the line, and its pieces. */
|
|
31
|
+
export interface StreamState {
|
|
32
|
+
/** The line the pieces were computed from, so a changed line is redone. */
|
|
33
|
+
line: string;
|
|
34
|
+
pieces: readonly Highlight[];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** The pieces of one line, computed once and kept until the line changes. */
|
|
38
|
+
function piecesFor(state: StreamState, line: string): readonly Highlight[] {
|
|
39
|
+
if (state.line !== line || state.pieces.length === 0) {
|
|
40
|
+
state.line = line;
|
|
41
|
+
state.pieces = highlight(line);
|
|
42
|
+
}
|
|
43
|
+
return state.pieces;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The tokenizer, which a host turns into a language with one call.
|
|
48
|
+
*
|
|
49
|
+
* ```ts
|
|
50
|
+
* const openscript = StreamLanguage.define(openscriptStream);
|
|
51
|
+
* ```
|
|
52
|
+
*
|
|
53
|
+
* `token` moves the stream to the end of the piece that starts where it is and
|
|
54
|
+
* returns that piece's style. A stream that has somehow been left inside a piece,
|
|
55
|
+
* which the editor does when it re-tokenizes from a column rather than from the
|
|
56
|
+
* start of a line, is moved to the end of the piece containing it, so the
|
|
57
|
+
* tokenizer always advances and the editor cannot be made to loop.
|
|
58
|
+
*/
|
|
59
|
+
export const openscriptStream: EditorStreamParser<StreamState> = {
|
|
60
|
+
name: 'openscript',
|
|
61
|
+
|
|
62
|
+
startState: (): StreamState => ({ line: '', pieces: [] }),
|
|
63
|
+
|
|
64
|
+
copyState: (state: StreamState): StreamState => ({ line: state.line, pieces: state.pieces }),
|
|
65
|
+
|
|
66
|
+
token(stream: EditorStringStream, state: StreamState): string | null {
|
|
67
|
+
if (stream.eol()) return null;
|
|
68
|
+
|
|
69
|
+
const pieces = piecesFor(state, stream.string);
|
|
70
|
+
const at = stream.pos;
|
|
71
|
+
const piece = pieces.find(
|
|
72
|
+
(one) => at >= one.span.offset && at < one.span.offset + one.span.length,
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
if (piece === undefined) {
|
|
76
|
+
// No piece covers the position, which cannot happen for a line the tier
|
|
77
|
+
// has covered and is still answered rather than trusted: the pieces tile
|
|
78
|
+
// the line, so this is a position outside it. Moving to the end leaves the
|
|
79
|
+
// editor with an unstyled remainder instead of an infinite loop.
|
|
80
|
+
stream.pos = stream.string.length;
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
stream.pos = piece.span.offset + piece.span.length;
|
|
85
|
+
return HIGHLIGHT_TOKENS[piece.kind];
|
|
86
|
+
},
|
|
87
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The eleven kinds, as the editor's own highlighting tags.
|
|
3
|
+
*
|
|
4
|
+
* This is the translation an adapter exists to do: the language has eleven
|
|
5
|
+
* kinds, closed, and the editor has a vocabulary of tags its themes are written
|
|
6
|
+
* against. Neither knows about the other, and this file is the only place that
|
|
7
|
+
* knows both.
|
|
8
|
+
*
|
|
9
|
+
* **It is one record rather than a switch on purpose.** A style name is the one
|
|
10
|
+
* thing in this adapter that no type can hold: the editor looks a name up in its
|
|
11
|
+
* own tag table at run time and warns on a console about one it does not know,
|
|
12
|
+
* so a name that is wrong here is invisible to every check in this repository
|
|
13
|
+
* and shows up as an unpainted keyword in somebody's product. A host that
|
|
14
|
+
* disagrees with any of these, or whose theme is written against different
|
|
15
|
+
* names, replaces the row in one line:
|
|
16
|
+
*
|
|
17
|
+
* ```ts
|
|
18
|
+
* const tokens = { ...HIGHLIGHT_TOKENS, unknown: "invalid" };
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* Two of the eleven are deliberately unstyled. `whitespace` has nothing to
|
|
22
|
+
* paint. `unknown` is the honest name for source the lexer took no token from,
|
|
23
|
+
* which is a character the language does not have, a region somebody wrote as a
|
|
24
|
+
* block comment, or a line continuation backslash: the first is a mistake and
|
|
25
|
+
* the last is not, so painting all three as invalid would paint a correct line
|
|
26
|
+
* red. `diagnose` is what says which of them is wrong, and the linter below is
|
|
27
|
+
* how a host shows that.
|
|
28
|
+
*/
|
|
29
|
+
import type { HighlightKind } from '../../editor/index.js';
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* A style name per kind, or null for one the editor should leave alone.
|
|
33
|
+
*
|
|
34
|
+
* `builtin` uses the editor's modifier syntax for a name the environment
|
|
35
|
+
* supplies rather than the file, which is what a library name is.
|
|
36
|
+
*/
|
|
37
|
+
export const HIGHLIGHT_TOKENS: Readonly<Record<HighlightKind, string | null>> = {
|
|
38
|
+
keyword: 'keyword',
|
|
39
|
+
builtin: 'variableName.standard',
|
|
40
|
+
name: 'variableName',
|
|
41
|
+
number: 'number',
|
|
42
|
+
string: 'string',
|
|
43
|
+
color: 'color',
|
|
44
|
+
comment: 'comment',
|
|
45
|
+
operator: 'operator',
|
|
46
|
+
punctuation: 'punctuation',
|
|
47
|
+
whitespace: null,
|
|
48
|
+
unknown: null,
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The editor's name for the icon beside a completion row, per kind.
|
|
53
|
+
*
|
|
54
|
+
* The editor ships icons for a fixed set of names and shows none for a name it
|
|
55
|
+
* does not know, which is a missing icon rather than a missing row.
|
|
56
|
+
*/
|
|
57
|
+
export const COMPLETION_TYPES: Readonly<Record<string, string>> = {
|
|
58
|
+
function: 'function',
|
|
59
|
+
value: 'constant',
|
|
60
|
+
namespace: 'namespace',
|
|
61
|
+
member: 'property',
|
|
62
|
+
variable: 'variable',
|
|
63
|
+
argument: 'property',
|
|
64
|
+
};
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hover and signature, as the editor's tooltips.
|
|
3
|
+
*
|
|
4
|
+
* This is the one part of the adapter that would put something on screen, and it
|
|
5
|
+
* is the one part that does not: the markup is a parameter, and there is no
|
|
6
|
+
* default for it. So nothing in this package, at any tier, names a browser
|
|
7
|
+
* global, and `scripts/check-layering.mjs` fails the build if anything under
|
|
8
|
+
* `src` does. The same files load in a worker, on a server and inside somebody
|
|
9
|
+
* else's application.
|
|
10
|
+
*
|
|
11
|
+
* It costs a host four lines and it is the whole point of the split. Every
|
|
12
|
+
* platform has a design system and none of them wants to fight a styled panel
|
|
13
|
+
* that arrived with a language package; a default renderer here would be that
|
|
14
|
+
* panel, and it would be the one thing in this adapter a host could not replace
|
|
15
|
+
* without replacing the adapter.
|
|
16
|
+
*
|
|
17
|
+
* What is not a parameter is the content. `hoverLines` and `signatureLines`
|
|
18
|
+
* below are what a tooltip says, in order, and every line of it came from the
|
|
19
|
+
* compiler: the signatures from the library manifest, the sentence and the
|
|
20
|
+
* warmup from the specification's own tables, the type from the checker, and the
|
|
21
|
+
* refusal from the error catalogue.
|
|
22
|
+
*
|
|
23
|
+
* ```ts
|
|
24
|
+
* hoverTooltip(openscriptHoverTooltip((held) => ({ dom: yourPanel(hoverLines(held)) })))
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
import { hover, signature } from '../../editor/index.js';
|
|
28
|
+
import type { Hover, SignatureHelp } from '../../editor/index.js';
|
|
29
|
+
import type { EditorState, EditorTooltip, EditorTooltipView, EditorView } from './contract.js';
|
|
30
|
+
import { documentOf, normalisedOffset } from './positions.js';
|
|
31
|
+
|
|
32
|
+
/** What a host supplies to draw a tooltip, which is the whole of the markup. */
|
|
33
|
+
export interface Renderer<Held> {
|
|
34
|
+
(held: Held): EditorTooltipView;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The lines a hover shows, in the order it shows them.
|
|
39
|
+
*
|
|
40
|
+
* Every one of them came from the compiler: the signatures from the library
|
|
41
|
+
* manifest, the sentence and the warmup from the specification's own tables, the
|
|
42
|
+
* type from the checker, and the refusal from the error catalogue. Nothing is
|
|
43
|
+
* written here, which is why this function is a join rather than a template.
|
|
44
|
+
*/
|
|
45
|
+
export function hoverLines(held: Hover): readonly string[] {
|
|
46
|
+
const lines = [...held.signatures];
|
|
47
|
+
if (held.summary !== undefined) lines.push(held.summary);
|
|
48
|
+
if (held.warmup !== undefined) lines.push(`First value: ${held.warmup}`);
|
|
49
|
+
if (held.refusal !== undefined) lines.push(held.refusal);
|
|
50
|
+
if (held.kind === 'keyword') lines.push('A reserved word of the language.');
|
|
51
|
+
return lines;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** The lines a signature tooltip shows, with the active parameter named. */
|
|
55
|
+
export function signatureLines(held: SignatureHelp): readonly string[] {
|
|
56
|
+
const lines = [held.signature];
|
|
57
|
+
const active = held.parameters[held.active];
|
|
58
|
+
if (active !== undefined) {
|
|
59
|
+
const value = active.defaultText === undefined ? '' : ` = ${active.defaultText}`;
|
|
60
|
+
const need = active.required ? 'required' : 'optional';
|
|
61
|
+
lines.push(`${active.name}: ${active.type}${value} (${need})`);
|
|
62
|
+
if (active.values.length > 0) lines.push(`One of: ${active.values.join(', ')}`);
|
|
63
|
+
}
|
|
64
|
+
if (held.summary !== undefined) lines.push(held.summary);
|
|
65
|
+
if (held.refusal !== undefined) lines.push(held.refusal);
|
|
66
|
+
return lines;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* A hover tooltip source, which a host wires in one line.
|
|
71
|
+
*
|
|
72
|
+
* ```ts
|
|
73
|
+
* hoverTooltip(openscriptHoverTooltip(render))
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
export function openscriptHoverTooltip(
|
|
77
|
+
render: Renderer<Hover>,
|
|
78
|
+
): (view: EditorView, pos: number) => EditorTooltip | null {
|
|
79
|
+
return (view, pos) => {
|
|
80
|
+
const held = documentOf(view.state.doc.toString());
|
|
81
|
+
const found = hover(held.text, normalisedOffset(held, pos));
|
|
82
|
+
if (found === undefined) return null;
|
|
83
|
+
return {
|
|
84
|
+
pos: held.at(found.span.offset),
|
|
85
|
+
end: held.at(found.span.offset + found.span.length),
|
|
86
|
+
create: () => render(found),
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* A signature tooltip for wherever the cursor is, or nothing where it is in no
|
|
93
|
+
* call.
|
|
94
|
+
*
|
|
95
|
+
* ```ts
|
|
96
|
+
* showTooltip.compute(["doc", "selection"], openscriptSignatureTooltip(render))
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
99
|
+
export function openscriptSignatureTooltip(
|
|
100
|
+
render: Renderer<SignatureHelp>,
|
|
101
|
+
): (state: EditorState) => EditorTooltip | null {
|
|
102
|
+
return (state) => {
|
|
103
|
+
const held = documentOf(state.doc.toString());
|
|
104
|
+
const at = normalisedOffset(held, state.selection.main.head);
|
|
105
|
+
const found = signature(held.text, at);
|
|
106
|
+
if (found === undefined) return null;
|
|
107
|
+
return {
|
|
108
|
+
pos: state.selection.main.head,
|
|
109
|
+
above: true,
|
|
110
|
+
create: () => render(found),
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
}
|