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.
Files changed (276) hide show
  1. package/CHANGELOG.md +269 -0
  2. package/README.md +65 -31
  3. package/dist/adapters/charts/surfaces.d.ts +17 -0
  4. package/dist/adapters/charts/surfaces.d.ts.map +1 -1
  5. package/dist/adapters/charts/tables.js +82 -0
  6. package/dist/adapters/charts/tables.js.map +1 -1
  7. package/dist/adapters/codemirror/commands.d.ts +14 -0
  8. package/dist/adapters/codemirror/commands.d.ts.map +1 -0
  9. package/dist/adapters/codemirror/commands.js +52 -0
  10. package/dist/adapters/codemirror/commands.js.map +1 -0
  11. package/dist/adapters/codemirror/completion.d.ts +15 -0
  12. package/dist/adapters/codemirror/completion.d.ts.map +1 -0
  13. package/dist/adapters/codemirror/completion.js +64 -0
  14. package/dist/adapters/codemirror/completion.js.map +1 -0
  15. package/dist/adapters/codemirror/contract.d.ts +156 -0
  16. package/dist/adapters/codemirror/contract.d.ts.map +1 -0
  17. package/dist/adapters/codemirror/contract.js +42 -0
  18. package/dist/adapters/codemirror/contract.js.map +1 -0
  19. package/dist/adapters/codemirror/index.d.ts +43 -0
  20. package/dist/adapters/codemirror/index.d.ts.map +1 -0
  21. package/dist/adapters/codemirror/index.js +8 -0
  22. package/dist/adapters/codemirror/index.js.map +1 -0
  23. package/dist/adapters/codemirror/lint.d.ts +17 -0
  24. package/dist/adapters/codemirror/lint.d.ts.map +1 -0
  25. package/dist/adapters/codemirror/lint.js +62 -0
  26. package/dist/adapters/codemirror/lint.js.map +1 -0
  27. package/dist/adapters/codemirror/positions.d.ts +16 -0
  28. package/dist/adapters/codemirror/positions.d.ts.map +1 -0
  29. package/dist/adapters/codemirror/positions.js +65 -0
  30. package/dist/adapters/codemirror/positions.js.map +1 -0
  31. package/dist/adapters/codemirror/stream.d.ts +23 -0
  32. package/dist/adapters/codemirror/stream.d.ts.map +1 -0
  33. package/dist/adapters/codemirror/stream.js +70 -0
  34. package/dist/adapters/codemirror/stream.js.map +1 -0
  35. package/dist/adapters/codemirror/tokens.d.ts +44 -0
  36. package/dist/adapters/codemirror/tokens.d.ts.map +1 -0
  37. package/dist/adapters/codemirror/tokens.js +34 -0
  38. package/dist/adapters/codemirror/tokens.js.map +1 -0
  39. package/dist/adapters/codemirror/tooltips.d.ts +35 -0
  40. package/dist/adapters/codemirror/tooltips.d.ts.map +1 -0
  41. package/dist/adapters/codemirror/tooltips.js +108 -0
  42. package/dist/adapters/codemirror/tooltips.js.map +1 -0
  43. package/dist/core/accounting/charges.d.ts +136 -0
  44. package/dist/core/accounting/charges.d.ts.map +1 -0
  45. package/dist/core/accounting/charges.js +362 -0
  46. package/dist/core/accounting/charges.js.map +1 -0
  47. package/dist/core/accounting/equity.d.ts +158 -0
  48. package/dist/core/accounting/equity.d.ts.map +1 -0
  49. package/dist/core/accounting/equity.js +155 -0
  50. package/dist/core/accounting/equity.js.map +1 -0
  51. package/dist/core/accounting/index.d.ts +44 -0
  52. package/dist/core/accounting/index.d.ts.map +1 -0
  53. package/dist/core/accounting/index.js +36 -0
  54. package/dist/core/accounting/index.js.map +1 -0
  55. package/dist/core/accounting/markers.d.ts +43 -0
  56. package/dist/core/accounting/markers.d.ts.map +1 -0
  57. package/dist/core/accounting/markers.js +64 -0
  58. package/dist/core/accounting/markers.js.map +1 -0
  59. package/dist/core/accounting/monthly.d.ts +52 -0
  60. package/dist/core/accounting/monthly.d.ts.map +1 -0
  61. package/dist/core/accounting/monthly.js +99 -0
  62. package/dist/core/accounting/monthly.js.map +1 -0
  63. package/dist/core/accounting/report.d.ts +38 -0
  64. package/dist/core/accounting/report.d.ts.map +1 -0
  65. package/dist/core/accounting/report.js +63 -0
  66. package/dist/core/accounting/report.js.map +1 -0
  67. package/dist/core/accounting/shapes.d.ts +70 -0
  68. package/dist/core/accounting/shapes.d.ts.map +1 -0
  69. package/dist/core/accounting/shapes.js +21 -0
  70. package/dist/core/accounting/shapes.js.map +1 -0
  71. package/dist/core/accounting/statistics.d.ts +75 -0
  72. package/dist/core/accounting/statistics.d.ts.map +1 -0
  73. package/dist/core/accounting/statistics.js +246 -0
  74. package/dist/core/accounting/statistics.js.map +1 -0
  75. package/dist/core/accounting/trades.d.ts +118 -0
  76. package/dist/core/accounting/trades.d.ts.map +1 -0
  77. package/dist/core/accounting/trades.js +186 -0
  78. package/dist/core/accounting/trades.js.map +1 -0
  79. package/dist/core/backtest/compare.d.ts +38 -0
  80. package/dist/core/backtest/compare.d.ts.map +1 -0
  81. package/dist/core/backtest/compare.js +189 -0
  82. package/dist/core/backtest/compare.js.map +1 -0
  83. package/dist/core/backtest/declaration.d.ts +52 -0
  84. package/dist/core/backtest/declaration.d.ts.map +1 -0
  85. package/dist/core/backtest/declaration.js +48 -0
  86. package/dist/core/backtest/declaration.js.map +1 -0
  87. package/dist/core/backtest/drive.d.ts +29 -0
  88. package/dist/core/backtest/drive.d.ts.map +1 -0
  89. package/dist/core/backtest/drive.js +262 -0
  90. package/dist/core/backtest/drive.js.map +1 -0
  91. package/dist/core/backtest/index.d.ts +46 -0
  92. package/dist/core/backtest/index.d.ts.map +1 -0
  93. package/dist/core/backtest/index.js +36 -0
  94. package/dist/core/backtest/index.js.map +1 -0
  95. package/dist/core/backtest/range.d.ts +84 -0
  96. package/dist/core/backtest/range.d.ts.map +1 -0
  97. package/dist/core/backtest/range.js +90 -0
  98. package/dist/core/backtest/range.js.map +1 -0
  99. package/dist/core/backtest/record.d.ts +238 -0
  100. package/dist/core/backtest/record.d.ts.map +1 -0
  101. package/dist/core/backtest/record.js +176 -0
  102. package/dist/core/backtest/record.js.map +1 -0
  103. package/dist/core/backtest/replay.d.ts +48 -0
  104. package/dist/core/backtest/replay.d.ts.map +1 -0
  105. package/dist/core/backtest/replay.js +127 -0
  106. package/dist/core/backtest/replay.js.map +1 -0
  107. package/dist/core/backtest/resting.d.ts +62 -0
  108. package/dist/core/backtest/resting.d.ts.map +1 -0
  109. package/dist/core/backtest/resting.js +59 -0
  110. package/dist/core/backtest/resting.js.map +1 -0
  111. package/dist/core/backtest/settings.d.ts +117 -0
  112. package/dist/core/backtest/settings.d.ts.map +1 -0
  113. package/dist/core/backtest/settings.js +207 -0
  114. package/dist/core/backtest/settings.js.map +1 -0
  115. package/dist/core/backtest/simulate.d.ts +146 -0
  116. package/dist/core/backtest/simulate.d.ts.map +1 -0
  117. package/dist/core/backtest/simulate.js +217 -0
  118. package/dist/core/backtest/simulate.js.map +1 -0
  119. package/dist/core/catalogue/catalogue.generated.d.ts +66 -0
  120. package/dist/core/catalogue/catalogue.generated.d.ts.map +1 -1
  121. package/dist/core/catalogue/catalogue.generated.js +6 -0
  122. package/dist/core/catalogue/catalogue.generated.js.map +1 -1
  123. package/dist/core/catalogue/values.generated.d.ts +24 -0
  124. package/dist/core/catalogue/values.generated.d.ts.map +1 -1
  125. package/dist/core/check/index.d.ts +2 -1
  126. package/dist/core/check/index.d.ts.map +1 -1
  127. package/dist/core/check/index.js +1 -1
  128. package/dist/core/check/index.js.map +1 -1
  129. package/dist/core/check/library-prose.generated.d.ts +16 -0
  130. package/dist/core/check/library-prose.generated.d.ts.map +1 -0
  131. package/dist/core/check/library-prose.generated.js +353 -0
  132. package/dist/core/check/library-prose.generated.js.map +1 -0
  133. package/dist/core/check/surface.d.ts +24 -0
  134. package/dist/core/check/surface.d.ts.map +1 -1
  135. package/dist/core/check/surface.js +29 -0
  136. package/dist/core/check/surface.js.map +1 -1
  137. package/dist/core/emit/defaults.d.ts +35 -16
  138. package/dist/core/emit/defaults.d.ts.map +1 -1
  139. package/dist/core/emit/defaults.js +66 -0
  140. package/dist/core/emit/defaults.js.map +1 -1
  141. package/dist/core/emit/index.d.ts +2 -0
  142. package/dist/core/emit/index.d.ts.map +1 -1
  143. package/dist/core/emit/index.js +2 -0
  144. package/dist/core/emit/index.js.map +1 -1
  145. package/dist/core/engine/index.d.ts +1 -1
  146. package/dist/core/engine/index.d.ts.map +1 -1
  147. package/dist/core/engine/index.js +1 -1
  148. package/dist/core/engine/index.js.map +1 -1
  149. package/dist/core/engine/load.d.ts +15 -1
  150. package/dist/core/engine/load.d.ts.map +1 -1
  151. package/dist/core/engine/load.js +1 -0
  152. package/dist/core/engine/load.js.map +1 -1
  153. package/dist/core/index.d.ts +37 -3
  154. package/dist/core/index.d.ts.map +1 -1
  155. package/dist/core/index.js +17 -1
  156. package/dist/core/index.js.map +1 -1
  157. package/dist/core/version/version.generated.d.ts +1 -1
  158. package/dist/core/version/version.generated.js +1 -1
  159. package/dist/editor/complete.d.ts +40 -0
  160. package/dist/editor/complete.d.ts.map +1 -0
  161. package/dist/editor/complete.js +206 -0
  162. package/dist/editor/complete.js.map +1 -0
  163. package/dist/editor/diagnose.d.ts +18 -0
  164. package/dist/editor/diagnose.d.ts.map +1 -0
  165. package/dist/editor/diagnose.js +70 -0
  166. package/dist/editor/diagnose.js.map +1 -0
  167. package/dist/editor/format.d.ts +11 -0
  168. package/dist/editor/format.d.ts.map +1 -0
  169. package/dist/editor/format.js +50 -0
  170. package/dist/editor/format.js.map +1 -0
  171. package/dist/editor/highlight.d.ts +28 -0
  172. package/dist/editor/highlight.d.ts.map +1 -0
  173. package/dist/editor/highlight.js +65 -0
  174. package/dist/editor/highlight.js.map +1 -0
  175. package/dist/editor/hover.d.ts +40 -0
  176. package/dist/editor/hover.d.ts.map +1 -0
  177. package/dist/editor/hover.js +147 -0
  178. package/dist/editor/hover.js.map +1 -0
  179. package/dist/editor/index.d.ts +60 -0
  180. package/dist/editor/index.d.ts.map +1 -0
  181. package/dist/editor/index.js +7 -0
  182. package/dist/editor/index.js.map +1 -0
  183. package/dist/editor/kinds.d.ts +27 -0
  184. package/dist/editor/kinds.d.ts.map +1 -0
  185. package/dist/editor/kinds.js +118 -0
  186. package/dist/editor/kinds.js.map +1 -0
  187. package/dist/editor/layout.d.ts +47 -0
  188. package/dist/editor/layout.d.ts.map +1 -0
  189. package/dist/editor/layout.js +135 -0
  190. package/dist/editor/layout.js.map +1 -0
  191. package/dist/editor/manifest.d.ts +47 -0
  192. package/dist/editor/manifest.d.ts.map +1 -0
  193. package/dist/editor/manifest.js +93 -0
  194. package/dist/editor/manifest.js.map +1 -0
  195. package/dist/editor/reading.d.ts +41 -0
  196. package/dist/editor/reading.d.ts.map +1 -0
  197. package/dist/editor/reading.js +51 -0
  198. package/dist/editor/reading.js.map +1 -0
  199. package/dist/editor/scan.d.ts +26 -0
  200. package/dist/editor/scan.d.ts.map +1 -0
  201. package/dist/editor/scan.js +139 -0
  202. package/dist/editor/scan.js.map +1 -0
  203. package/dist/editor/scope.d.ts +19 -0
  204. package/dist/editor/scope.d.ts.map +1 -0
  205. package/dist/editor/scope.js +99 -0
  206. package/dist/editor/scope.js.map +1 -0
  207. package/dist/editor/signature.d.ts +38 -0
  208. package/dist/editor/signature.d.ts.map +1 -0
  209. package/dist/editor/signature.js +112 -0
  210. package/dist/editor/signature.js.map +1 -0
  211. package/dist/editor/site.d.ts +46 -0
  212. package/dist/editor/site.d.ts.map +1 -0
  213. package/dist/editor/site.js +184 -0
  214. package/dist/editor/site.js.map +1 -0
  215. package/dist/editor/spacing.d.ts +29 -0
  216. package/dist/editor/spacing.d.ts.map +1 -0
  217. package/dist/editor/spacing.js +116 -0
  218. package/dist/editor/spacing.js.map +1 -0
  219. package/package.json +30 -3
  220. package/spec/errors.json +140 -0
  221. package/src/adapters/charts/surfaces.ts +17 -0
  222. package/src/adapters/charts/tables.ts +88 -0
  223. package/src/adapters/codemirror/commands.ts +53 -0
  224. package/src/adapters/codemirror/completion.ts +74 -0
  225. package/src/adapters/codemirror/contract.ts +156 -0
  226. package/src/adapters/codemirror/index.ts +66 -0
  227. package/src/adapters/codemirror/lint.ts +66 -0
  228. package/src/adapters/codemirror/positions.ts +79 -0
  229. package/src/adapters/codemirror/stream.ts +87 -0
  230. package/src/adapters/codemirror/tokens.ts +64 -0
  231. package/src/adapters/codemirror/tooltips.ts +113 -0
  232. package/src/core/accounting/charges.ts +452 -0
  233. package/src/core/accounting/equity.ts +276 -0
  234. package/src/core/accounting/index.ts +49 -0
  235. package/src/core/accounting/markers.ts +95 -0
  236. package/src/core/accounting/monthly.ts +137 -0
  237. package/src/core/accounting/report.ts +89 -0
  238. package/src/core/accounting/shapes.ts +73 -0
  239. package/src/core/accounting/statistics.ts +350 -0
  240. package/src/core/accounting/trades.ts +313 -0
  241. package/src/core/backtest/compare.ts +244 -0
  242. package/src/core/backtest/declaration.ts +97 -0
  243. package/src/core/backtest/drive.ts +364 -0
  244. package/src/core/backtest/index.ts +52 -0
  245. package/src/core/backtest/range.ts +137 -0
  246. package/src/core/backtest/record.ts +341 -0
  247. package/src/core/backtest/replay.ts +158 -0
  248. package/src/core/backtest/resting.ts +125 -0
  249. package/src/core/backtest/settings.ts +280 -0
  250. package/src/core/backtest/simulate.ts +304 -0
  251. package/src/core/catalogue/catalogue.generated.ts +6 -0
  252. package/src/core/catalogue/values.generated.ts +6 -0
  253. package/src/core/check/index.ts +3 -0
  254. package/src/core/check/library-prose.generated.ts +367 -0
  255. package/src/core/check/surface.ts +34 -0
  256. package/src/core/emit/defaults.ts +70 -0
  257. package/src/core/emit/index.ts +2 -0
  258. package/src/core/engine/index.ts +1 -1
  259. package/src/core/engine/load.ts +16 -2
  260. package/src/core/index.ts +85 -1
  261. package/src/core/version/version.generated.ts +1 -1
  262. package/src/editor/complete.ts +266 -0
  263. package/src/editor/diagnose.ts +71 -0
  264. package/src/editor/format.ts +85 -0
  265. package/src/editor/highlight.ts +110 -0
  266. package/src/editor/hover.ts +199 -0
  267. package/src/editor/index.ts +65 -0
  268. package/src/editor/kinds.ts +157 -0
  269. package/src/editor/layout.ts +189 -0
  270. package/src/editor/manifest.ts +119 -0
  271. package/src/editor/reading.ts +69 -0
  272. package/src/editor/scan.ts +162 -0
  273. package/src/editor/scope.ts +122 -0
  274. package/src/editor/signature.ts +150 -0
  275. package/src/editor/site.ts +233 -0
  276. 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
+ }