@trebco/treb 30.1.8 → 30.2.4
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/dist/languages/treb-i18n-da.mjs +1 -0
- package/dist/languages/treb-i18n-de.mjs +1 -0
- package/dist/languages/treb-i18n-es.mjs +1 -0
- package/dist/languages/treb-i18n-fr.mjs +1 -0
- package/dist/languages/treb-i18n-it.mjs +1 -0
- package/dist/languages/treb-i18n-nl.mjs +1 -0
- package/dist/languages/treb-i18n-no.mjs +1 -0
- package/dist/languages/treb-i18n-pl.mjs +1 -0
- package/dist/languages/treb-i18n-pt.mjs +1 -0
- package/dist/languages/treb-i18n-sv.mjs +1 -0
- package/dist/treb-spreadsheet-light.mjs +9 -9
- package/dist/treb-spreadsheet.mjs +9 -9
- package/dist/treb.d.ts +15 -1
- package/esbuild-custom-element.mjs +23 -1
- package/esbuild-utils.mjs +1 -0
- package/package.json +1 -1
- package/treb-calculator/src/expression-calculator.ts +40 -32
- package/treb-embed/src/embedded-spreadsheet.ts +90 -7
- package/treb-embed/src/options.ts +18 -0
- package/treb-embed/src/plugin.ts +31 -0
- package/treb-grid/src/editors/autocomplete_matcher.ts +8 -1
package/dist/treb.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! API v30.
|
|
1
|
+
/*! API v30.2. Copyright 2018-2024 trebco, llc. All rights reserved. LGPL: https://treb.app/license */
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* add our tag to the map
|
|
@@ -282,6 +282,17 @@ export interface EmbeddedSpreadsheetOptions {
|
|
|
282
282
|
* starting in 30.1.0. set to false to disable.
|
|
283
283
|
*/
|
|
284
284
|
spill?: boolean;
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* language. at the moment this controls spreadsheet function names
|
|
288
|
+
* only; the plan is to expand to the rest of the interface over time.
|
|
289
|
+
* should be an ISO 639-1 language code, like "en", "fr" or "sv" (case
|
|
290
|
+
* insensitive). we only support a limited subset of languages at the
|
|
291
|
+
* moment.
|
|
292
|
+
*
|
|
293
|
+
* leave blank or set to "locale" to use the current locale.
|
|
294
|
+
*/
|
|
295
|
+
language?: string;
|
|
285
296
|
}
|
|
286
297
|
|
|
287
298
|
/**
|
|
@@ -393,6 +404,9 @@ export declare class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
393
404
|
*/
|
|
394
405
|
ExternalEditor(config?: Partial<ExternalEditorConfig>): void;
|
|
395
406
|
|
|
407
|
+
/** dynamically load language module */
|
|
408
|
+
LoadLanguage(language?: string): Promise<void>;
|
|
409
|
+
|
|
396
410
|
/**
|
|
397
411
|
* Use this function to batch multiple document changes. Essentially the
|
|
398
412
|
* grid stops broadcasting events for the duration of the function call,
|
|
@@ -5,7 +5,7 @@ import * as esbuild from 'esbuild';
|
|
|
5
5
|
import { SassPlugin, WorkerPlugin, NotifyPlugin, HTMLPlugin } from './esbuild-utils.mjs';
|
|
6
6
|
import { promises as fs } from 'fs';
|
|
7
7
|
|
|
8
|
-
import pkg from './package.json'
|
|
8
|
+
import pkg from './package.json' with { type: 'json' };
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
/**
|
|
@@ -58,6 +58,26 @@ for (let i = 0; i < process.argv.length; i++) {
|
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
/**
|
|
62
|
+
* thanks to
|
|
63
|
+
* https://github.com/evanw/esbuild/issues/3337#issuecomment-2085394950
|
|
64
|
+
*/
|
|
65
|
+
function RewriteIgnoredImports() {
|
|
66
|
+
return {
|
|
67
|
+
name: 'RewriteIgnoredImports',
|
|
68
|
+
setup(build) {
|
|
69
|
+
build.onEnd(async (result) => {
|
|
70
|
+
if (result.outputFiles) {
|
|
71
|
+
for (const file of result.outputFiles) {
|
|
72
|
+
const { path, text } = file;
|
|
73
|
+
await fs.writeFile(path, text.replace(/esbuild-ignore-import:/, ''));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
61
81
|
/** @type esbuild.BuildOptions */
|
|
62
82
|
const build_options = {
|
|
63
83
|
entryPoints: [
|
|
@@ -78,7 +98,9 @@ const build_options = {
|
|
|
78
98
|
'process.env.BUILD_VERSION': `"${pkg.version}"`,
|
|
79
99
|
'process.env.BUILD_NAME': `"${pkg.name}"`,
|
|
80
100
|
},
|
|
101
|
+
write: false,
|
|
81
102
|
plugins: [
|
|
103
|
+
RewriteIgnoredImports(),
|
|
82
104
|
NotifyPlugin(),
|
|
83
105
|
WorkerPlugin(options),
|
|
84
106
|
HTMLPlugin(options),
|
package/esbuild-utils.mjs
CHANGED
package/package.json
CHANGED
|
@@ -733,6 +733,41 @@ export class ExpressionCalculator {
|
|
|
733
733
|
|
|
734
734
|
}
|
|
735
735
|
|
|
736
|
+
/**
|
|
737
|
+
* convert expr to a cell address, possibly calculating the contents.
|
|
738
|
+
* returns single address only (ranges with len > 1 will fail)
|
|
739
|
+
*/
|
|
740
|
+
protected AsReference(expr: ExpressionUnit): ICellAddress|undefined {
|
|
741
|
+
|
|
742
|
+
switch (expr.type) {
|
|
743
|
+
case 'address':
|
|
744
|
+
return expr;
|
|
745
|
+
|
|
746
|
+
case 'range':
|
|
747
|
+
if (expr.start.row === expr.end.row && expr.start.column === expr.end.column) {
|
|
748
|
+
return expr.start;
|
|
749
|
+
}
|
|
750
|
+
break;
|
|
751
|
+
|
|
752
|
+
default:
|
|
753
|
+
{
|
|
754
|
+
const union = this.CalculateExpression(expr as ExtendedExpressionUnit, true);
|
|
755
|
+
if (UnionIsExpressionUnit(union)) {
|
|
756
|
+
if (union.value.type === 'address') {
|
|
757
|
+
return union.value;
|
|
758
|
+
}
|
|
759
|
+
if (union.value.type === 'range' && union.value.start.row === union.value.end.row && union.value.start.column === union.value.end.column) {
|
|
760
|
+
return union.value.start;
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
break;
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
return undefined;
|
|
768
|
+
|
|
769
|
+
}
|
|
770
|
+
|
|
736
771
|
protected BinaryExpression(x: UnitBinary): (expr: UnitBinary) => UnionValue /*UnionOrArray*/ {
|
|
737
772
|
|
|
738
773
|
// we are constructing and caching functions for binary expressions.
|
|
@@ -758,40 +793,13 @@ export class ExpressionCalculator {
|
|
|
758
793
|
if (x.operator === ':') {
|
|
759
794
|
return (expr: UnitBinary) => {
|
|
760
795
|
|
|
761
|
-
const
|
|
762
|
-
const
|
|
763
|
-
|
|
764
|
-
let start: UnitAddress|undefined;
|
|
765
|
-
let end: UnitAddress|undefined;
|
|
766
|
-
|
|
767
|
-
// console.info({expr, left, right});
|
|
768
|
-
|
|
769
|
-
if (UnionIsExpressionUnit(left) && UnionIsExpressionUnit(right)) {
|
|
770
|
-
|
|
771
|
-
if (left.value.type === 'range') {
|
|
772
|
-
if (left.value.start.row === left.value.end.row && left.value.start.column === left.value.end.column) {
|
|
773
|
-
start = left.value.start;
|
|
774
|
-
}
|
|
775
|
-
}
|
|
776
|
-
else if (left.value.type === 'address') {
|
|
777
|
-
start = left.value;
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
if (right.value.type === 'range') {
|
|
781
|
-
if (right.value.start.row === right.value.end.row && right.value.start.column === right.value.end.column) {
|
|
782
|
-
end = right.value.start;
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
else if (right.value.type === 'address') {
|
|
786
|
-
end = right.value;
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
if (start && end) {
|
|
790
|
-
return this.CellFunction4(start, end);
|
|
791
|
-
}
|
|
796
|
+
const start = this.AsReference(expr.left);
|
|
797
|
+
const end = this.AsReference(expr.right);
|
|
792
798
|
|
|
799
|
+
if (start && end) {
|
|
800
|
+
return this.CellFunction4(start, end);
|
|
793
801
|
}
|
|
794
|
-
|
|
802
|
+
|
|
795
803
|
return ExpressionError();
|
|
796
804
|
};
|
|
797
805
|
}
|
|
@@ -170,7 +170,6 @@ export interface LoadDocumentOptions {
|
|
|
170
170
|
source?: LoadSource,
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
-
|
|
174
173
|
/**
|
|
175
174
|
* options for the GetRange method
|
|
176
175
|
*/
|
|
@@ -255,6 +254,15 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
255
254
|
/** @internal */
|
|
256
255
|
public static treb_embedded_script_path = '';
|
|
257
256
|
|
|
257
|
+
/**
|
|
258
|
+
* @internal
|
|
259
|
+
*
|
|
260
|
+
* keep track of modules we've tried to load dynamically, so we don't
|
|
261
|
+
* do it again. not sure if this is strictly necessary but especially if
|
|
262
|
+
* we're logging I don't want to see it again
|
|
263
|
+
*/
|
|
264
|
+
protected static failed_dynamic_modules: string[] = [];
|
|
265
|
+
|
|
258
266
|
/* * @internal */
|
|
259
267
|
// public static enable_engine = false;
|
|
260
268
|
|
|
@@ -646,8 +654,6 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
646
654
|
*/
|
|
647
655
|
constructor(options: EmbeddedSpreadsheetOptions & { model?: EmbeddedSpreadsheet }) {
|
|
648
656
|
|
|
649
|
-
// super();
|
|
650
|
-
|
|
651
657
|
// we renamed this option, default to the new name
|
|
652
658
|
|
|
653
659
|
if (options.storage_key && !options.local_storage) {
|
|
@@ -842,6 +848,24 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
842
848
|
this.grid.headless = true; // FIXME: move into grid options
|
|
843
849
|
}
|
|
844
850
|
|
|
851
|
+
// --- testing dynamic loading ---------------------------------------------
|
|
852
|
+
|
|
853
|
+
this.LoadLanguage(options.language);
|
|
854
|
+
|
|
855
|
+
// --- testing plugins -----------------------------------------------------
|
|
856
|
+
|
|
857
|
+
/*
|
|
858
|
+
// FIXME: when to do this? could it wait? should it be async? (...)
|
|
859
|
+
|
|
860
|
+
if (options.plugins) {
|
|
861
|
+
for (const plugin of options.plugins) {
|
|
862
|
+
plugin.Attach(this);
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
*/
|
|
866
|
+
|
|
867
|
+
// -------------------------------------------------------------------------
|
|
868
|
+
|
|
845
869
|
// we're now gating this on container to support fully headless operation
|
|
846
870
|
|
|
847
871
|
if (container) {
|
|
@@ -1180,7 +1204,7 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
1180
1204
|
|
|
1181
1205
|
let list: FunctionDescriptor[] = this.calculator.SupportedFunctions();
|
|
1182
1206
|
|
|
1183
|
-
if (this.language_model) {
|
|
1207
|
+
if (this.language_model?.functions) {
|
|
1184
1208
|
|
|
1185
1209
|
const map: Record<string, TranslatedFunctionDescriptor> = {};
|
|
1186
1210
|
for (const entry of this.language_model.functions || []) {
|
|
@@ -1188,7 +1212,20 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
1188
1212
|
}
|
|
1189
1213
|
|
|
1190
1214
|
list = list.map(descriptor => {
|
|
1191
|
-
|
|
1215
|
+
const partial = map[descriptor.name.toUpperCase()];
|
|
1216
|
+
|
|
1217
|
+
// FIXME: this is not deep enough if we are going to modify
|
|
1218
|
+
// argument names. we will need to keep other elements of the
|
|
1219
|
+
// argument entries. this is sufficient for now if we're just
|
|
1220
|
+
// setting function names / descriptions.
|
|
1221
|
+
|
|
1222
|
+
if (partial) {
|
|
1223
|
+
return {
|
|
1224
|
+
...descriptor,
|
|
1225
|
+
...partial,
|
|
1226
|
+
};
|
|
1227
|
+
}
|
|
1228
|
+
return descriptor;
|
|
1192
1229
|
});
|
|
1193
1230
|
|
|
1194
1231
|
}
|
|
@@ -1956,6 +1993,47 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
1956
1993
|
|
|
1957
1994
|
// --- public API methods ----------------------------------------------------
|
|
1958
1995
|
|
|
1996
|
+
/** dynamically load language module */
|
|
1997
|
+
public async LoadLanguage(language = '') {
|
|
1998
|
+
|
|
1999
|
+
if (!language || language === 'locale') {
|
|
2000
|
+
const locale = Localization.locale || '';
|
|
2001
|
+
const parts = locale.split(/-/).map(part => part.toLowerCase());
|
|
2002
|
+
language = parts[0];
|
|
2003
|
+
}
|
|
2004
|
+
|
|
2005
|
+
this.SetLanguage(); // clear
|
|
2006
|
+
|
|
2007
|
+
language = language.toLowerCase();
|
|
2008
|
+
|
|
2009
|
+
let mod: { LanguageMap: LanguageModel } | undefined;
|
|
2010
|
+
|
|
2011
|
+
if (language && language !== 'en') {
|
|
2012
|
+
|
|
2013
|
+
// FIXME: even though we now have a dynamic import
|
|
2014
|
+
// working, we still probably want to use a filter
|
|
2015
|
+
// list to avoid unnecessary 404s.
|
|
2016
|
+
|
|
2017
|
+
// regarding the import, this is specially crafted to
|
|
2018
|
+
// work in both esbuild and vite. probably will break
|
|
2019
|
+
// some other bundlers though -- might need some magic
|
|
2020
|
+
// comments
|
|
2021
|
+
|
|
2022
|
+
try {
|
|
2023
|
+
mod = await import(`esbuild-ignore-import:./languages/treb-i18n-${language}.mjs`);
|
|
2024
|
+
}
|
|
2025
|
+
catch (err) {
|
|
2026
|
+
console.error(err);
|
|
2027
|
+
}
|
|
2028
|
+
|
|
2029
|
+
}
|
|
2030
|
+
|
|
2031
|
+
if (mod) {
|
|
2032
|
+
this.SetLanguage(mod.LanguageMap);
|
|
2033
|
+
}
|
|
2034
|
+
|
|
2035
|
+
}
|
|
2036
|
+
|
|
1959
2037
|
/**
|
|
1960
2038
|
* this is not public _yet_
|
|
1961
2039
|
*
|
|
@@ -1973,10 +2051,15 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
1973
2051
|
// create a name map for grid
|
|
1974
2052
|
|
|
1975
2053
|
const map: Record< string, string > = {};
|
|
1976
|
-
|
|
1977
|
-
|
|
2054
|
+
|
|
2055
|
+
if (model.functions) {
|
|
2056
|
+
for (const entry of model.functions || []) {
|
|
2057
|
+
map[entry.base] = entry.name;
|
|
2058
|
+
}
|
|
1978
2059
|
}
|
|
2060
|
+
|
|
1979
2061
|
this.grid.SetLanguageMap(map);
|
|
2062
|
+
|
|
1980
2063
|
}
|
|
1981
2064
|
|
|
1982
2065
|
this.UpdateAC();
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
import type { ICellAddress } from 'treb-base-types';
|
|
23
23
|
import type { TREBDocument } from './types';
|
|
24
24
|
import type { ChartRenderer } from 'treb-charts';
|
|
25
|
+
// import type { TREBPlugin } from './plugin';
|
|
25
26
|
|
|
26
27
|
/**
|
|
27
28
|
* factory type for chart renderer, if you want instances (pass a constructor)
|
|
@@ -327,6 +328,23 @@ export interface EmbeddedSpreadsheetOptions {
|
|
|
327
328
|
*/
|
|
328
329
|
spill?: boolean;
|
|
329
330
|
|
|
331
|
+
/**
|
|
332
|
+
* language. at the moment this controls spreadsheet function names
|
|
333
|
+
* only; the plan is to expand to the rest of the interface over time.
|
|
334
|
+
* should be an ISO 639-1 language code, like "en", "fr" or "sv" (case
|
|
335
|
+
* insensitive). we only support a limited subset of languages at the
|
|
336
|
+
* moment.
|
|
337
|
+
*
|
|
338
|
+
* leave blank or set to "locale" to use the current locale.
|
|
339
|
+
*/
|
|
340
|
+
language?: string;
|
|
341
|
+
|
|
342
|
+
/* *
|
|
343
|
+
* @internal
|
|
344
|
+
* testing plugins
|
|
345
|
+
*/
|
|
346
|
+
// plugins?: TREBPlugin[];
|
|
347
|
+
|
|
330
348
|
}
|
|
331
349
|
|
|
332
350
|
/**
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This file is part of TREB.
|
|
3
|
+
*
|
|
4
|
+
* TREB is free software: you can redistribute it and/or modify it under the
|
|
5
|
+
* terms of the GNU General Public License as published by the Free Software
|
|
6
|
+
* Foundation, either version 3 of the License, or (at your option) any
|
|
7
|
+
* later version.
|
|
8
|
+
*
|
|
9
|
+
* TREB is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
10
|
+
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
11
|
+
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
12
|
+
* details.
|
|
13
|
+
*
|
|
14
|
+
* You should have received a copy of the GNU General Public License along
|
|
15
|
+
* with TREB. If not, see <https://www.gnu.org/licenses/>.
|
|
16
|
+
*
|
|
17
|
+
* Copyright 2022-2024 trebco, llc.
|
|
18
|
+
* info@treb.app
|
|
19
|
+
*
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import type { EmbeddedSpreadsheet } from './embedded-spreadsheet';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @internal
|
|
26
|
+
*
|
|
27
|
+
* testing plugins
|
|
28
|
+
*/
|
|
29
|
+
export interface TREBPlugin {
|
|
30
|
+
Attach: (instance: EmbeddedSpreadsheet) => void;
|
|
31
|
+
}
|
|
@@ -151,6 +151,8 @@ export class AutocompleteMatcher {
|
|
|
151
151
|
return {};
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
+
// console.info(data);
|
|
155
|
+
|
|
154
156
|
let match;
|
|
155
157
|
let result: AutocompleteExecResult = {};
|
|
156
158
|
|
|
@@ -162,7 +164,8 @@ export class AutocompleteMatcher {
|
|
|
162
164
|
// if it's a token, and ends with a legal character
|
|
163
165
|
// UPDATE: adding the negative leading \d to fix entering complex numbers
|
|
164
166
|
|
|
165
|
-
match = data.text.match(/(?:^|[^A-Za-z_\d])([A-Za-z_][\w\d_.]*)\s*$/);
|
|
167
|
+
// match = data.text.match(/(?:^|[^A-Za-z_\d])([A-Za-z_][\w\d_.]*)\s*$/);
|
|
168
|
+
match = data.text.match(/(?:^|[^a-zA-Z\u00C0-\u024F_\d])([a-zA-Z\u00C0-\u024F_][\w\d\u00C0-\u024F_.]*)\s*$/);
|
|
166
169
|
|
|
167
170
|
if (match) {
|
|
168
171
|
const token = match[1];
|
|
@@ -178,6 +181,9 @@ export class AutocompleteMatcher {
|
|
|
178
181
|
};
|
|
179
182
|
|
|
180
183
|
}
|
|
184
|
+
else {
|
|
185
|
+
// console.info("NOP");
|
|
186
|
+
}
|
|
181
187
|
|
|
182
188
|
}
|
|
183
189
|
|
|
@@ -261,6 +267,7 @@ export class AutocompleteMatcher {
|
|
|
261
267
|
if ( (char >= 0x61 && char <= 0x7a) // a-z
|
|
262
268
|
|| (char >= 0x41 && char <= 0x5a) // A-Z
|
|
263
269
|
|| (char >= 0x30 && char <= 0x39) // 0-9
|
|
270
|
+
|| (char >= 0x00C0 && char <= 0x024F) // accented characters
|
|
264
271
|
|| (char === 0x5f) // _
|
|
265
272
|
|| (char === 0x2e)) { // .
|
|
266
273
|
|