openalgo-script 0.2.0 → 0.5.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 +1198 -0
- package/README.md +127 -40
- package/dist/adapters/charts/driving.d.ts +50 -0
- package/dist/adapters/charts/driving.d.ts.map +1 -0
- package/dist/adapters/charts/driving.js +57 -0
- package/dist/adapters/charts/driving.js.map +1 -0
- package/dist/adapters/charts/run.d.ts +20 -0
- package/dist/adapters/charts/run.d.ts.map +1 -1
- package/dist/adapters/charts/run.js +83 -16
- package/dist/adapters/charts/run.js.map +1 -1
- 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/charts/venue.d.ts +73 -0
- package/dist/adapters/charts/venue.d.ts.map +1 -0
- package/dist/adapters/charts/venue.js +104 -0
- package/dist/adapters/charts/venue.js.map +1 -0
- 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/analysis.d.ts +111 -0
- package/dist/core/accounting/analysis.d.ts.map +1 -0
- package/dist/core/accounting/analysis.js +123 -0
- package/dist/core/accounting/analysis.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 +191 -0
- package/dist/core/accounting/equity.d.ts.map +1 -0
- package/dist/core/accounting/equity.js +167 -0
- package/dist/core/accounting/equity.js.map +1 -0
- package/dist/core/accounting/index.d.ts +46 -0
- package/dist/core/accounting/index.d.ts.map +1 -0
- package/dist/core/accounting/index.js +37 -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 +41 -0
- package/dist/core/accounting/report.d.ts.map +1 -0
- package/dist/core/accounting/report.js +65 -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 +87 -0
- package/dist/core/accounting/statistics.d.ts.map +1 -0
- package/dist/core/accounting/statistics.js +268 -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/case.d.ts +60 -0
- package/dist/core/backtest/case.d.ts.map +1 -0
- package/dist/core/backtest/case.js +319 -0
- package/dist/core/backtest/case.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 +191 -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/deliver.d.ts +93 -0
- package/dist/core/backtest/deliver.d.ts.map +1 -0
- package/dist/core/backtest/deliver.js +94 -0
- package/dist/core/backtest/deliver.js.map +1 -0
- package/dist/core/backtest/drive.d.ts +87 -0
- package/dist/core/backtest/drive.d.ts.map +1 -0
- package/dist/core/backtest/drive.js +312 -0
- package/dist/core/backtest/drive.js.map +1 -0
- package/dist/core/backtest/index.d.ts +58 -0
- package/dist/core/backtest/index.d.ts.map +1 -0
- package/dist/core/backtest/index.js +47 -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 +299 -0
- package/dist/core/backtest/record.d.ts.map +1 -0
- package/dist/core/backtest/record.js +243 -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 +137 -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 +258 -0
- package/dist/core/backtest/simulate.d.ts.map +1 -0
- package/dist/core/backtest/simulate.js +335 -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-orders.js +2 -2
- package/dist/core/check/library-orders.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/canonical.d.ts +22 -8
- package/dist/core/emit/canonical.d.ts.map +1 -1
- package/dist/core/emit/canonical.js +67 -6
- package/dist/core/emit/canonical.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/arithmetic.d.ts +6 -25
- package/dist/core/engine/arithmetic.d.ts.map +1 -1
- package/dist/core/engine/arithmetic.js +48 -3
- package/dist/core/engine/arithmetic.js.map +1 -1
- package/dist/core/engine/index.d.ts +2 -2
- package/dist/core/engine/index.d.ts.map +1 -1
- package/dist/core/engine/index.js +2 -2
- package/dist/core/engine/index.js.map +1 -1
- package/dist/core/engine/library/arrays.d.ts.map +1 -1
- package/dist/core/engine/library/arrays.js +8 -2
- package/dist/core/engine/library/arrays.js.map +1 -1
- package/dist/core/engine/library/code-points.d.ts +40 -0
- package/dist/core/engine/library/code-points.d.ts.map +1 -0
- package/dist/core/engine/library/code-points.js +74 -0
- package/dist/core/engine/library/code-points.js.map +1 -0
- package/dist/core/engine/library/index.d.ts +5 -0
- package/dist/core/engine/library/index.d.ts.map +1 -1
- package/dist/core/engine/library/index.js +5 -0
- package/dist/core/engine/library/index.js.map +1 -1
- package/dist/core/engine/library/text.d.ts.map +1 -1
- package/dist/core/engine/library/text.js +51 -34
- package/dist/core/engine/library/text.js.map +1 -1
- package/dist/core/engine/load.d.ts +35 -1
- package/dist/core/engine/load.d.ts.map +1 -1
- package/dist/core/engine/load.js +63 -0
- package/dist/core/engine/load.js.map +1 -1
- package/dist/core/engine/verify-tables.d.ts.map +1 -1
- package/dist/core/engine/verify-tables.js +41 -0
- package/dist/core/engine/verify-tables.js.map +1 -1
- package/dist/core/index.d.ts +43 -4
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +23 -2
- package/dist/core/index.js.map +1 -1
- package/dist/core/stdlib/index.d.ts +1 -1
- package/dist/core/stdlib/index.d.ts.map +1 -1
- package/dist/core/stdlib/index.js +1 -1
- package/dist/core/stdlib/index.js.map +1 -1
- package/dist/core/stdlib/maths/index.d.ts +1 -1
- package/dist/core/stdlib/maths/index.d.ts.map +1 -1
- package/dist/core/stdlib/maths/index.js +1 -1
- package/dist/core/stdlib/maths/index.js.map +1 -1
- package/dist/core/stdlib/maths/rounding.d.ts +5 -0
- package/dist/core/stdlib/maths/rounding.d.ts.map +1 -1
- package/dist/core/stdlib/maths/rounding.js +19 -1
- package/dist/core/stdlib/maths/rounding.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 +42 -3
- package/spec/README.md +2 -1
- package/spec/errors.json +141 -1
- package/src/adapters/charts/driving.ts +109 -0
- package/src/adapters/charts/run.ts +120 -28
- package/src/adapters/charts/surfaces.ts +17 -0
- package/src/adapters/charts/tables.ts +88 -0
- package/src/adapters/charts/venue.ts +132 -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/analysis.ts +188 -0
- package/src/core/accounting/charges.ts +452 -0
- package/src/core/accounting/equity.ts +314 -0
- package/src/core/accounting/index.ts +51 -0
- package/src/core/accounting/markers.ts +95 -0
- package/src/core/accounting/monthly.ts +137 -0
- package/src/core/accounting/report.ts +94 -0
- package/src/core/accounting/shapes.ts +73 -0
- package/src/core/accounting/statistics.ts +387 -0
- package/src/core/accounting/trades.ts +313 -0
- package/src/core/backtest/case.ts +395 -0
- package/src/core/backtest/compare.ts +246 -0
- package/src/core/backtest/declaration.ts +97 -0
- package/src/core/backtest/deliver.ts +161 -0
- package/src/core/backtest/drive.ts +468 -0
- package/src/core/backtest/index.ts +64 -0
- package/src/core/backtest/range.ts +137 -0
- package/src/core/backtest/record.ts +470 -0
- package/src/core/backtest/replay.ts +168 -0
- package/src/core/backtest/resting.ts +125 -0
- package/src/core/backtest/settings.ts +280 -0
- package/src/core/backtest/simulate.ts +499 -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-orders.ts +2 -2
- package/src/core/check/library-prose.generated.ts +367 -0
- package/src/core/check/surface.ts +34 -0
- package/src/core/emit/canonical.ts +67 -9
- package/src/core/emit/defaults.ts +70 -0
- package/src/core/emit/index.ts +2 -0
- package/src/core/engine/arithmetic.ts +23 -3
- package/src/core/engine/index.ts +2 -2
- package/src/core/engine/library/arrays.ts +8 -2
- package/src/core/engine/library/code-points.ts +73 -0
- package/src/core/engine/library/index.ts +6 -0
- package/src/core/engine/library/text.ts +53 -35
- package/src/core/engine/load.ts +84 -2
- package/src/core/engine/verify-tables.ts +43 -0
- package/src/core/index.ts +100 -2
- package/src/core/stdlib/index.ts +1 -0
- package/src/core/stdlib/maths/index.ts +1 -0
- package/src/core/stdlib/maths/rounding.ts +23 -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,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `hover`: what the thing under the pointer is.
|
|
3
|
+
*
|
|
4
|
+
* Three answers, and the difference between them is what the compiler says the
|
|
5
|
+
* word is rather than anything guessed from its spelling:
|
|
6
|
+
*
|
|
7
|
+
* a library name its signatures from the manifest, the line the
|
|
8
|
+
* specification gives it, the first bar its own row
|
|
9
|
+
* promises a value on, and the channels of a named colour
|
|
10
|
+
* a declared name the type the checker worked out, how it is held between
|
|
11
|
+
* bars, and where in the file it was declared
|
|
12
|
+
* a reserved word that it is one, and nothing else. See below
|
|
13
|
+
*
|
|
14
|
+
* ## What a hover wants and cannot have, said plainly
|
|
15
|
+
*
|
|
16
|
+
* **There is no per-word explanation of a keyword in a machine readable form.**
|
|
17
|
+
* `language.md` 3.4 reserves the words in one block and explains each one in the
|
|
18
|
+
* prose of the section that uses it, which is a page rather than a field. So a
|
|
19
|
+
* hover over `var` says it is a reserved word and stops. The alternative was to
|
|
20
|
+
* type thirty-five sentences into this file, and that is the copy that drifts:
|
|
21
|
+
* it would be right today, the specification would improve next month, and the
|
|
22
|
+
* only copy a writer ever reads would be the stale one. A host that wants more
|
|
23
|
+
* links the word to the specification, which is one page and stays current.
|
|
24
|
+
*
|
|
25
|
+
* **A name with several signatures has one description.** The specification
|
|
26
|
+
* gives every signature its own row, and the map read here is keyed by name, so
|
|
27
|
+
* `close` is described by its bar series row rather than by the order function's.
|
|
28
|
+
* Every signature is still listed: it is the sentence beside them that is the
|
|
29
|
+
* first row's.
|
|
30
|
+
*
|
|
31
|
+
* **A parameter has no description of its own anywhere.** `stdlib.md` describes
|
|
32
|
+
* a call in one line and names its parameters in the signature; only section
|
|
33
|
+
* 13's shared input arguments have a table with a meaning per argument. So
|
|
34
|
+
* `signature` shows a parameter's name, type, default and accepted values, which
|
|
35
|
+
* are facts, and no sentence, which would have to be invented.
|
|
36
|
+
*
|
|
37
|
+
* Each of those is recorded in `spec/editor-narrowings.json` beside what the
|
|
38
|
+
* adapter narrows, so a host reads them in one place rather than discovering
|
|
39
|
+
* them.
|
|
40
|
+
*/
|
|
41
|
+
import {
|
|
42
|
+
endOffset,
|
|
43
|
+
entryFor,
|
|
44
|
+
fillTemplate,
|
|
45
|
+
isNamespace,
|
|
46
|
+
libraryEntries,
|
|
47
|
+
namedColour,
|
|
48
|
+
proseFor,
|
|
49
|
+
typeText,
|
|
50
|
+
} from '../core/index.js';
|
|
51
|
+
import type { Colour, SourceFile, Span, Token } from '../core/index.js';
|
|
52
|
+
import { isReserved } from './kinds.js';
|
|
53
|
+
import { signatureTextOf } from './manifest.js';
|
|
54
|
+
import { readChecked } from './reading.js';
|
|
55
|
+
import { bindingNamed } from './scope.js';
|
|
56
|
+
import { tokenOn } from './site.js';
|
|
57
|
+
|
|
58
|
+
export type HoverKind = 'library' | 'declared' | 'keyword';
|
|
59
|
+
|
|
60
|
+
export interface Hover {
|
|
61
|
+
readonly kind: HoverKind;
|
|
62
|
+
/** The word the pointer is over, which is what a host underlines. */
|
|
63
|
+
readonly span: Span;
|
|
64
|
+
/** The name as written, dotted where the word is half of a namespaced name. */
|
|
65
|
+
readonly name: string;
|
|
66
|
+
/**
|
|
67
|
+
* Every signature the name has, in the order the manifest holds them.
|
|
68
|
+
*
|
|
69
|
+
* More than one where the language gives the name more than one, which
|
|
70
|
+
* `stdlib.md` 2.2 allows: a hover has no arguments to choose between them
|
|
71
|
+
* with, so it shows both rather than picking.
|
|
72
|
+
*/
|
|
73
|
+
readonly signatures: readonly string[];
|
|
74
|
+
/** The line the specification gives it, where it gives one. */
|
|
75
|
+
readonly summary: string | undefined;
|
|
76
|
+
/** The first bar it can produce a value for, as its own row states it. */
|
|
77
|
+
readonly warmup: string | undefined;
|
|
78
|
+
/** The type the checker gave a declared name. */
|
|
79
|
+
readonly type: string | undefined;
|
|
80
|
+
/** Where a declared name was declared, which is what a host jumps to. */
|
|
81
|
+
readonly declaredAt: Span | undefined;
|
|
82
|
+
/** The channels of a named colour, for a swatch. */
|
|
83
|
+
readonly colour: Colour | undefined;
|
|
84
|
+
/** Named in the library and not implemented in this version: OS2020. */
|
|
85
|
+
readonly planned: boolean;
|
|
86
|
+
/** The catalogue's OS2020 message for this name, on a planned name and no other. */
|
|
87
|
+
readonly refusal: string | undefined;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* The name a word is part of, which is the dotted one when it is half of one.
|
|
92
|
+
*
|
|
93
|
+
* Pointing at `box` in `draw.box` is pointing at `draw.box`: the member on its
|
|
94
|
+
* own is an ordinary name the library does not have, and a hover that said so
|
|
95
|
+
* would be wrong about the word under the pointer.
|
|
96
|
+
*/
|
|
97
|
+
function nameAt(
|
|
98
|
+
file: SourceFile,
|
|
99
|
+
tokens: readonly Token[],
|
|
100
|
+
token: Token,
|
|
101
|
+
): { name: string; span: Span } {
|
|
102
|
+
const index = tokens.indexOf(token);
|
|
103
|
+
const dot = tokens[index - 1];
|
|
104
|
+
const namespace = tokens[index - 2];
|
|
105
|
+
if (dot?.kind === '.' && namespace?.kind === 'identifier' && isNamespace(namespace.text)) {
|
|
106
|
+
return {
|
|
107
|
+
name: `${namespace.text}.${token.text}`,
|
|
108
|
+
span: file.spanAt(namespace.span.offset, endOffset(token.span) - namespace.span.offset),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
// A namespace with a member after it is pointed at as the whole name too, so
|
|
112
|
+
// that hovering either half of `draw.box` says the same thing.
|
|
113
|
+
const next = tokens[index + 1];
|
|
114
|
+
const member = tokens[index + 2];
|
|
115
|
+
if (
|
|
116
|
+
isNamespace(token.text) &&
|
|
117
|
+
next?.kind === '.' &&
|
|
118
|
+
member?.kind === 'identifier' &&
|
|
119
|
+
libraryEntries(`${token.text}.${member.text}`).length > 0
|
|
120
|
+
) {
|
|
121
|
+
return {
|
|
122
|
+
name: `${token.text}.${member.text}`,
|
|
123
|
+
span: file.spanAt(token.span.offset, endOffset(member.span) - token.span.offset),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
return { name: file.text.slice(token.span.offset, endOffset(token.span)), span: token.span };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const NOTHING = {
|
|
130
|
+
summary: undefined,
|
|
131
|
+
warmup: undefined,
|
|
132
|
+
type: undefined,
|
|
133
|
+
declaredAt: undefined,
|
|
134
|
+
colour: undefined,
|
|
135
|
+
planned: false,
|
|
136
|
+
refusal: undefined,
|
|
137
|
+
} as const;
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* What the word at an offset is, or nothing where the offset is not on a word.
|
|
141
|
+
*
|
|
142
|
+
* A pointer over a space, a bracket, a number or a string gets nothing: those
|
|
143
|
+
* are not names, and a tooltip that appeared over a comma would be in the way
|
|
144
|
+
* rather than useful. A host that wants a literal's value has it in the text.
|
|
145
|
+
*/
|
|
146
|
+
export function hover(source: string, offset: number): Hover | undefined {
|
|
147
|
+
const { file, tokens, checked } = readChecked(source);
|
|
148
|
+
const token = tokenOn(tokens, offset);
|
|
149
|
+
if (token === undefined) return undefined;
|
|
150
|
+
|
|
151
|
+
if (isReserved(token.kind)) {
|
|
152
|
+
// A reserved word, and every one of them is also a word a script may not
|
|
153
|
+
// declare. What it means is the specification's, and there is no per-word
|
|
154
|
+
// field to read it from, which the file header states and the narrowings
|
|
155
|
+
// record.
|
|
156
|
+
return { kind: 'keyword', span: token.span, name: token.text, signatures: [], ...NOTHING };
|
|
157
|
+
}
|
|
158
|
+
if (token.kind !== 'identifier') return undefined;
|
|
159
|
+
|
|
160
|
+
const { name, span } = nameAt(file, tokens, token);
|
|
161
|
+
|
|
162
|
+
const binding = bindingNamed(checked, name, offset);
|
|
163
|
+
if (binding !== undefined) {
|
|
164
|
+
const persistence = binding.persistence === 'none' ? '' : `${binding.persistence} `;
|
|
165
|
+
return {
|
|
166
|
+
kind: 'declared',
|
|
167
|
+
span,
|
|
168
|
+
name,
|
|
169
|
+
signatures: [`${persistence}${name}: ${typeText(binding.type)}`],
|
|
170
|
+
summary: undefined,
|
|
171
|
+
warmup: undefined,
|
|
172
|
+
type: typeText(binding.type),
|
|
173
|
+
declaredAt: binding.declaredAt,
|
|
174
|
+
colour: undefined,
|
|
175
|
+
planned: false,
|
|
176
|
+
refusal: undefined,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const entries = libraryEntries(name);
|
|
181
|
+
const first = entries[0];
|
|
182
|
+
if (first === undefined) return undefined;
|
|
183
|
+
|
|
184
|
+
const prose = proseFor(name);
|
|
185
|
+
const planned = entries.every((one) => one.planned);
|
|
186
|
+
return {
|
|
187
|
+
kind: 'library',
|
|
188
|
+
span,
|
|
189
|
+
name,
|
|
190
|
+
signatures: entries.map(signatureTextOf),
|
|
191
|
+
summary: prose?.summary,
|
|
192
|
+
warmup: prose?.warmup,
|
|
193
|
+
type: typeText(first.returns),
|
|
194
|
+
declaredAt: undefined,
|
|
195
|
+
colour: namedColour(name),
|
|
196
|
+
planned,
|
|
197
|
+
refusal: planned ? fillTemplate(entryFor('OS2020').message, { name }) : undefined,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenScript, the language intelligence, headless.
|
|
3
|
+
*
|
|
4
|
+
* Six pure functions, text in and data out: highlight, complete, diagnose,
|
|
5
|
+
* hover, signature, format. Three of them are here. No DOM anywhere, no package,
|
|
6
|
+
* no browser global, and nothing on screen: a host supplies the text component,
|
|
7
|
+
* the panel, the apply button, saving and revisions, and keeps its own design
|
|
8
|
+
* system. `scripts/check-layering.mjs` fails the build if this tier so much as
|
|
9
|
+
* mentions a document object, because an editor half that reaches for one is the
|
|
10
|
+
* thing that stops a platform embedding it.
|
|
11
|
+
*
|
|
12
|
+
* **None of it is hand written.** That is the whole reason it is worth shipping
|
|
13
|
+
* rather than leaving to each host, and it is a property of how each function is
|
|
14
|
+
* built rather than an intention:
|
|
15
|
+
*
|
|
16
|
+
* highlight the real lexer, and the language's own tables of words and
|
|
17
|
+
* marks decide what each token is painted as
|
|
18
|
+
* diagnose the whole compiler, and every message and fix comes from the
|
|
19
|
+
* error catalogue the documentation is generated from
|
|
20
|
+
* format the real lexer for the tokens and the real parser for the
|
|
21
|
+
* three questions spacing cannot answer without a tree
|
|
22
|
+
*
|
|
23
|
+
* A hand written version of any of them drifts from the language and nobody
|
|
24
|
+
* notices for a release: a word is added and stays grey, a diagnostic's wording
|
|
25
|
+
* improves in one place. The editor is the compiler wearing a different hat, not
|
|
26
|
+
* a second implementation to keep in step.
|
|
27
|
+
*
|
|
28
|
+
* complete the standard library manifest, the checker's bindings for
|
|
29
|
+
* what the file itself declares, and the error catalogue for
|
|
30
|
+
* what a planned call would be refused with
|
|
31
|
+
* hover the manifest again, and the specification's own tables, read
|
|
32
|
+
* at build time rather than retyped here
|
|
33
|
+
* signature the manifest and the emitter, which between them are where
|
|
34
|
+
* every default the compiler applies lives
|
|
35
|
+
*
|
|
36
|
+
* All six exist, so `openalgo-script/editor` is an entry point in the package's
|
|
37
|
+
* export map. It was not one while three of them were missing, because an entry
|
|
38
|
+
* point that resolves to half a tier fails at a consumer's run time rather than
|
|
39
|
+
* honestly at install.
|
|
40
|
+
*
|
|
41
|
+
* What a host loses by taking the drop-in adapter beside this rather than
|
|
42
|
+
* calling these six itself, and the three places a hover has no sentence to show
|
|
43
|
+
* because none exists in a machine readable form, are recorded together in
|
|
44
|
+
* `spec/editor-narrowings.json`.
|
|
45
|
+
*/
|
|
46
|
+
export type { Diagnostic, Span } from '../core/index.js';
|
|
47
|
+
|
|
48
|
+
export type { HighlightKind } from './kinds.js';
|
|
49
|
+
export type { Highlight } from './scan.js';
|
|
50
|
+
export type { HighlightedLine } from './highlight.js';
|
|
51
|
+
export { highlight, highlightLines } from './highlight.js';
|
|
52
|
+
|
|
53
|
+
export { diagnose } from './diagnose.js';
|
|
54
|
+
|
|
55
|
+
export { format } from './format.js';
|
|
56
|
+
|
|
57
|
+
export type { Completion, CompletionKind } from './complete.js';
|
|
58
|
+
export { complete } from './complete.js';
|
|
59
|
+
|
|
60
|
+
export type { Hover, HoverKind } from './hover.js';
|
|
61
|
+
export { hover } from './hover.js';
|
|
62
|
+
|
|
63
|
+
export type { Parameter } from './manifest.js';
|
|
64
|
+
export type { SignatureHelp } from './signature.js';
|
|
65
|
+
export { signature } from './signature.js';
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a host paints, and where each kind comes from.
|
|
3
|
+
*
|
|
4
|
+
* A highlighter is the one part of a language tool that is always written twice:
|
|
5
|
+
* once in the compiler, where the vocabulary is decided, and once in a theme
|
|
6
|
+
* file, where somebody types the words out again from memory. The second copy is
|
|
7
|
+
* wrong from the day a word is added to the language, and nobody notices, because
|
|
8
|
+
* an unpainted keyword still compiles.
|
|
9
|
+
*
|
|
10
|
+
* So nothing below is a list of words. Every kind is decided by asking the
|
|
11
|
+
* compiler a question it already answers:
|
|
12
|
+
*
|
|
13
|
+
* keyword the token's kind is in the language's reserved word table
|
|
14
|
+
* builtin the standard library manifest has an entry for the name
|
|
15
|
+
* operator the mark is in one of the language's operator tables
|
|
16
|
+
* punctuation a mark the grammar has that no operator table claims
|
|
17
|
+
*
|
|
18
|
+
* A word added to `RESERVED_WORDS`, a function added to the manifest and a mark
|
|
19
|
+
* added to `PUNCTUATORS` are each painted the day they lex, without this file
|
|
20
|
+
* being edited.
|
|
21
|
+
*
|
|
22
|
+
* ## Why `reserved` is a question and not a comparison
|
|
23
|
+
*
|
|
24
|
+
* A token's kind is its own text for a keyword and for a punctuation mark, which
|
|
25
|
+
* is what lets a parser read `token.kind === 'if'`. A consuming platform read
|
|
26
|
+
* that as "a reserved word is a token whose kind equals its text", which is true
|
|
27
|
+
* and is folklore: it is nowhere in the token vocabulary, it silently takes in
|
|
28
|
+
* every punctuation mark as well, and it would stop being true the first time a
|
|
29
|
+
* kind and a spelling came apart. `kindOf` answers the question instead, from
|
|
30
|
+
* the same table the lexer classifies with.
|
|
31
|
+
*/
|
|
32
|
+
import { PUNCTUATORS, RESERVED_WORDS } from '../core/index.js';
|
|
33
|
+
import {
|
|
34
|
+
ARITHMETIC_OPERATORS,
|
|
35
|
+
ASSIGNMENT_OPERATORS,
|
|
36
|
+
COMPARISON_OPERATORS,
|
|
37
|
+
EQUALITY_OPERATORS,
|
|
38
|
+
isLibraryName,
|
|
39
|
+
isNamespace,
|
|
40
|
+
} from '../core/index.js';
|
|
41
|
+
import type { Token, TokenKind } from '../core/index.js';
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* What a span of source is, for the purpose of giving it a colour.
|
|
45
|
+
*
|
|
46
|
+
* Eleven kinds, and the set is closed: a host writes eleven theme rules once and
|
|
47
|
+
* never has to add a twelfth, because a new word in the language arrives as one
|
|
48
|
+
* of these rather than as a kind nobody has a colour for.
|
|
49
|
+
*
|
|
50
|
+
* `whitespace` and `unknown` exist because every character of the file is
|
|
51
|
+
* covered exactly once (see `scan.ts`), so the spaces between tokens have to be
|
|
52
|
+
* something. `unknown` is source the lexer took no token from and that is not a
|
|
53
|
+
* space or a comment: a character the language does not have, a region somebody
|
|
54
|
+
* meant as a block comment, or a line continuation backslash. It does not mean
|
|
55
|
+
* "wrong": `diagnose` says what is wrong, and a highlighter that had a second
|
|
56
|
+
* opinion about it would be a second opinion about the language.
|
|
57
|
+
*/
|
|
58
|
+
export type HighlightKind =
|
|
59
|
+
| 'keyword'
|
|
60
|
+
| 'builtin'
|
|
61
|
+
| 'name'
|
|
62
|
+
| 'number'
|
|
63
|
+
| 'string'
|
|
64
|
+
| 'color'
|
|
65
|
+
| 'comment'
|
|
66
|
+
| 'operator'
|
|
67
|
+
| 'punctuation'
|
|
68
|
+
| 'whitespace'
|
|
69
|
+
| 'unknown';
|
|
70
|
+
|
|
71
|
+
const RESERVED: ReadonlySet<string> = new Set<string>(RESERVED_WORDS);
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* The marks that compute something, as against the marks that group and
|
|
75
|
+
* separate.
|
|
76
|
+
*
|
|
77
|
+
* Built from the language's own operator tables rather than by dividing the
|
|
78
|
+
* punctuation list by hand, so that an operator added to the language is painted
|
|
79
|
+
* as one. The tables of words, `and` and `or` and `not`, are reserved words and
|
|
80
|
+
* are painted as keywords, which is what they are to the lexer.
|
|
81
|
+
*
|
|
82
|
+
* What is left over is punctuation: the brackets, the comma, the dot, and the
|
|
83
|
+
* two marks of the ternary. They group and separate rather than compute, and a
|
|
84
|
+
* theme that wants them dimmer than the arithmetic can have that.
|
|
85
|
+
*/
|
|
86
|
+
const OPERATORS: ReadonlySet<string> = new Set<string>([
|
|
87
|
+
...ARITHMETIC_OPERATORS,
|
|
88
|
+
...COMPARISON_OPERATORS,
|
|
89
|
+
...EQUALITY_OPERATORS,
|
|
90
|
+
...ASSIGNMENT_OPERATORS,
|
|
91
|
+
]);
|
|
92
|
+
|
|
93
|
+
const PUNCTUATION: ReadonlySet<string> = new Set<string>(PUNCTUATORS);
|
|
94
|
+
|
|
95
|
+
/** Whether a token's kind is a reserved word of language.md 3.4. */
|
|
96
|
+
export function isReserved(kind: TokenKind): boolean {
|
|
97
|
+
return RESERVED.has(kind);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Whether a name is one the standard library already has.
|
|
102
|
+
*
|
|
103
|
+
* The manifest is the same index the checker resolves against and the same one
|
|
104
|
+
* the example check reads its globals from, so a function added to the library
|
|
105
|
+
* is painted as a built-in on the day it is added. A namespace is included
|
|
106
|
+
* because `draw` on its own is not a value a script can hold: it is half of a
|
|
107
|
+
* dotted name, and `dotted` below finishes the question for the other half.
|
|
108
|
+
*/
|
|
109
|
+
function isBuiltin(name: string): boolean {
|
|
110
|
+
return isLibraryName(name) || isNamespace(name);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* The member half of a namespaced name, which is only a built-in in company.
|
|
115
|
+
*
|
|
116
|
+
* `box` is an ordinary name, and `draw.box` is a library entry. The manifest is
|
|
117
|
+
* asked about the dotted spelling, so nothing here decides what a namespace
|
|
118
|
+
* holds: `language.md` 15.2 and the manifest do.
|
|
119
|
+
*/
|
|
120
|
+
function dotted(before: readonly Token[], name: string): boolean {
|
|
121
|
+
const dot = before[before.length - 1];
|
|
122
|
+
const namespace = before[before.length - 2];
|
|
123
|
+
if (dot?.kind !== '.' || namespace?.kind !== 'identifier') return false;
|
|
124
|
+
return isNamespace(namespace.text) && isLibraryName(`${namespace.text}.${name}`);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* The kind a token is painted as, given the tokens before it.
|
|
129
|
+
*
|
|
130
|
+
* The tokens before it are needed for exactly one question, the dotted name
|
|
131
|
+
* above. Everything else is decided by the token alone.
|
|
132
|
+
*/
|
|
133
|
+
export function kindOf(token: Token, before: readonly Token[]): HighlightKind {
|
|
134
|
+
switch (token.kind) {
|
|
135
|
+
case 'numberLiteral':
|
|
136
|
+
return 'number';
|
|
137
|
+
case 'stringLiteral':
|
|
138
|
+
return 'string';
|
|
139
|
+
case 'hexColor':
|
|
140
|
+
// The language's own spelling of the type is `color`, so that is the kind
|
|
141
|
+
// a host matches on, whatever the prose around it calls the thing.
|
|
142
|
+
return 'color';
|
|
143
|
+
case 'identifier':
|
|
144
|
+
return isBuiltin(token.text) || dotted(before, token.text) ? 'builtin' : 'name';
|
|
145
|
+
case 'newline':
|
|
146
|
+
case 'indent':
|
|
147
|
+
case 'dedent':
|
|
148
|
+
case 'endOfFile':
|
|
149
|
+
// Layout is the file's shape rather than something to read, and an indent
|
|
150
|
+
// token's span is the leading spaces of its line, which are spaces.
|
|
151
|
+
return 'whitespace';
|
|
152
|
+
default:
|
|
153
|
+
if (isReserved(token.kind)) return 'keyword';
|
|
154
|
+
if (OPERATORS.has(token.kind)) return 'operator';
|
|
155
|
+
return PUNCTUATION.has(token.kind) ? 'punctuation' : 'unknown';
|
|
156
|
+
}
|
|
157
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The canonical layout of a file, line by line.
|
|
3
|
+
*
|
|
4
|
+
* `language.md` 3.13 states the rules; this applies them. The two halves are
|
|
5
|
+
* separate because the specification is what another implementation writes a
|
|
6
|
+
* formatter from, and a rule that lived only here would be a rule only this
|
|
7
|
+
* formatter has.
|
|
8
|
+
*
|
|
9
|
+
* ## What is preserved, and why that is the safe direction
|
|
10
|
+
*
|
|
11
|
+
* The writer's line breaks are kept. A formatter that joined a call spanning
|
|
12
|
+
* four lines into one would be correct and unusable: it would take the layout
|
|
13
|
+
* somebody chose to make a long argument list readable and hand back a line
|
|
14
|
+
* nobody can read. So this decides indentation and spacing, which have one right
|
|
15
|
+
* answer, and leaves the choice of where to break a long statement to the person
|
|
16
|
+
* writing it, which does not.
|
|
17
|
+
*
|
|
18
|
+
* It is also the safe direction. A break that was there before is a break the
|
|
19
|
+
* lexer already accepted, so preserving them cannot turn a legal file into an
|
|
20
|
+
* illegal one, while inventing or removing one can: a statement continues onto
|
|
21
|
+
* the next line only under the three conditions of 3.11, and a formatter that
|
|
22
|
+
* moved a break would have to know them, which is the language's rule
|
|
23
|
+
* implemented twice.
|
|
24
|
+
*
|
|
25
|
+
* ## Comments
|
|
26
|
+
*
|
|
27
|
+
* A comment on a line of its own takes the indentation of the next line that
|
|
28
|
+
* carries a token, so a commented-out statement sits with the block it belongs
|
|
29
|
+
* to. This is safe for a reason worth stating: such a line produces no token at
|
|
30
|
+
* all, so its indentation says nothing about block structure and cannot open or
|
|
31
|
+
* close one (3.10). A comment after code stays where it is, two spaces clear of
|
|
32
|
+
* the code.
|
|
33
|
+
*/
|
|
34
|
+
import type { SourceFile, Token } from '../core/index.js';
|
|
35
|
+
import type { Highlight } from './scan.js';
|
|
36
|
+
import { spaceBetween } from './spacing.js';
|
|
37
|
+
import type { Shapes } from './spacing.js';
|
|
38
|
+
|
|
39
|
+
/** One level of block, `language.md` 3.13. */
|
|
40
|
+
const INDENT = ' ';
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* What a continuation line is indented past the line that began its statement.
|
|
44
|
+
*
|
|
45
|
+
* Eight rather than four so that a continuation can never be read as the body of
|
|
46
|
+
* the header above it. `if crossUp(fast,` with its second half indented four
|
|
47
|
+
* would sit exactly where the block's first statement sits, which is legal and
|
|
48
|
+
* unreadable.
|
|
49
|
+
*/
|
|
50
|
+
const CONTINUATION = ' ';
|
|
51
|
+
|
|
52
|
+
/** Two, so a trailing comment is visibly not part of the code it follows. */
|
|
53
|
+
const BEFORE_COMMENT = ' ';
|
|
54
|
+
|
|
55
|
+
const BACKSLASH = '\\';
|
|
56
|
+
|
|
57
|
+
interface CodeLine {
|
|
58
|
+
readonly tokens: Token[];
|
|
59
|
+
readonly indent: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* The code lines of a file, each with the indentation the canonical layout gives
|
|
64
|
+
* it.
|
|
65
|
+
*
|
|
66
|
+
* Depth comes from the lexer's own indent and dedent tokens rather than from
|
|
67
|
+
* counting spaces, so the blocks this prints are the blocks the compiler read. A
|
|
68
|
+
* line that begins a statement is indented by its depth; a line that continues
|
|
69
|
+
* one is indented past the line that began it.
|
|
70
|
+
*/
|
|
71
|
+
function codeLines(tokens: readonly Token[]): ReadonlyMap<number, CodeLine> {
|
|
72
|
+
const lines = new Map<number, CodeLine>();
|
|
73
|
+
let depth = 0;
|
|
74
|
+
let starting = true;
|
|
75
|
+
|
|
76
|
+
for (const token of tokens) {
|
|
77
|
+
switch (token.kind) {
|
|
78
|
+
case 'indent':
|
|
79
|
+
depth += 1;
|
|
80
|
+
continue;
|
|
81
|
+
case 'dedent':
|
|
82
|
+
depth -= 1;
|
|
83
|
+
continue;
|
|
84
|
+
case 'newline':
|
|
85
|
+
starting = true;
|
|
86
|
+
continue;
|
|
87
|
+
case 'endOfFile':
|
|
88
|
+
continue;
|
|
89
|
+
default:
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
let line = lines.get(token.span.line);
|
|
94
|
+
if (line === undefined) {
|
|
95
|
+
const body = INDENT.repeat(Math.max(depth, 0));
|
|
96
|
+
line = { tokens: [], indent: starting ? body : body + CONTINUATION };
|
|
97
|
+
lines.set(token.span.line, line);
|
|
98
|
+
}
|
|
99
|
+
line.tokens.push(token);
|
|
100
|
+
starting = false;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return lines;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** One code line as text: its indentation, then its tokens with their spacing. */
|
|
107
|
+
function printCode(file: SourceFile, line: CodeLine, shapes: Shapes): string {
|
|
108
|
+
let text = line.indent;
|
|
109
|
+
for (const [at, token] of line.tokens.entries()) {
|
|
110
|
+
const before = line.tokens[at - 1];
|
|
111
|
+
if (before !== undefined && spaceBetween(before, token, shapes)) text += ' ';
|
|
112
|
+
text += file.text.slice(token.span.offset, token.span.offset + token.span.length);
|
|
113
|
+
}
|
|
114
|
+
return text;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** The indentation of the next line that carries a token, for a comment to take. */
|
|
118
|
+
function indentBelow(lines: ReadonlyMap<number, CodeLine>, from: number, last: number): string {
|
|
119
|
+
for (let line = from + 1; line <= last; line += 1) {
|
|
120
|
+
const found = lines.get(line);
|
|
121
|
+
if (found !== undefined) return found.indent;
|
|
122
|
+
}
|
|
123
|
+
return '';
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Blank lines, as the canonical layout has them: never two in a row, never
|
|
128
|
+
* before the first line, and one line ending at the end of the file.
|
|
129
|
+
*
|
|
130
|
+
* A blank line between two paragraphs of a script is the writer's, and it is
|
|
131
|
+
* kept. A run of four is a file that has been edited for a year, and one of them
|
|
132
|
+
* says everything the four said.
|
|
133
|
+
*/
|
|
134
|
+
function joinLines(printed: readonly (string | undefined)[]): string {
|
|
135
|
+
const kept: string[] = [];
|
|
136
|
+
for (const line of printed) {
|
|
137
|
+
if (line !== undefined) {
|
|
138
|
+
kept.push(line);
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
if (kept.length === 0 || kept[kept.length - 1] === '') continue;
|
|
142
|
+
kept.push('');
|
|
143
|
+
}
|
|
144
|
+
while (kept.length > 0 && kept[kept.length - 1] === '') kept.pop();
|
|
145
|
+
return kept.length === 0 ? '' : `${kept.join('\n')}\n`;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* The whole file, laid out.
|
|
150
|
+
*
|
|
151
|
+
* The pieces are the covering scan, which is where the comments come from and
|
|
152
|
+
* where a line continuation backslash is found. A backslash is kept wherever it
|
|
153
|
+
* was written rather than worked out again: whether a statement needs one is the
|
|
154
|
+
* lexer's rule, and the file in front of this one has already been through the
|
|
155
|
+
* lexer with it in place.
|
|
156
|
+
*/
|
|
157
|
+
export function layOut(
|
|
158
|
+
file: SourceFile,
|
|
159
|
+
tokens: readonly Token[],
|
|
160
|
+
pieces: readonly Highlight[],
|
|
161
|
+
shapes: Shapes,
|
|
162
|
+
): string {
|
|
163
|
+
const lines = codeLines(tokens);
|
|
164
|
+
const comments = new Map<number, string>();
|
|
165
|
+
const continued = new Set<number>();
|
|
166
|
+
|
|
167
|
+
for (const piece of pieces) {
|
|
168
|
+
if (piece.kind === 'comment') comments.set(piece.span.line, piece.text.trimEnd());
|
|
169
|
+
else if (piece.kind === 'unknown' && piece.text === BACKSLASH) continued.add(piece.span.line);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const printed: (string | undefined)[] = [];
|
|
173
|
+
for (let line = 1; line <= file.lineCount; line += 1) {
|
|
174
|
+
const code = lines.get(line);
|
|
175
|
+
const comment = comments.get(line);
|
|
176
|
+
|
|
177
|
+
if (code === undefined) {
|
|
178
|
+
printed.push(comment === undefined ? undefined : indentBelow(lines, line, file.lineCount) + comment);
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
let text = printCode(file, code, shapes);
|
|
183
|
+
if (continued.has(line)) text += ` ${BACKSLASH}`;
|
|
184
|
+
if (comment !== undefined) text += BEFORE_COMMENT + comment;
|
|
185
|
+
printed.push(text);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return joinLines(printed);
|
|
189
|
+
}
|