@fc-components/monaco-editor 0.2.1 → 0.3.2

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.
@@ -1,11 +1,12 @@
1
- import React, { useRef, useEffect } from 'react';
1
+ import React, { useRef, useEffect, forwardRef, useCallback, useImperativeHandle } from 'react';
2
2
  import MonacoEditor from 'react-monaco-editor';
3
3
  import * as monaco from 'monaco-editor';
4
4
  import { languages, Range, editor, Selection, KeyMod, KeyCode, MarkerSeverity } from 'monaco-editor';
5
5
  import { promLanguageDefinition } from 'monaco-promql';
6
- import { parser, LabelName, UnquotedLabelMatcher, LabelMatchers, VectorSelector, PromQL, Identifier, FunctionCallBody, StringLiteral, BinaryExpr, NumberDurationLiteralInDurationContext, MatrixSelector, GroupingLabels, WithExpr, WithAssignment, AggregateModifier, AggregateExpr, MatchOp, EqlSingle, EqlRegex, Neq, NeqRegex } from '@fc-components/lezer-metricsql';
6
+ import { parser as parser$1, LabelName, UnquotedLabelMatcher, LabelMatchers, VectorSelector, PromQL, Identifier, FunctionCallBody, StringLiteral, BinaryExpr, NumberDurationLiteralInDurationContext, MatrixSelector, GroupingLabels, WithExpr, WithAssignment, AggregateModifier, AggregateExpr, MatchOp, EqlSingle, EqlRegex, Neq, NeqRegex } from '@fc-components/lezer-metricsql';
7
7
  import { v4 } from 'uuid';
8
8
  import { css } from '@emotion/css';
9
+ import { format } from 'sql-formatter';
9
10
 
10
11
  function _arrayLikeToArray(r, a) {
11
12
  (null == a || a > r.length) && (a = r.length);
@@ -1638,7 +1639,7 @@ function getSituation(text, pos) {
1638
1639
  VectorSelector
1639
1640
  LabelMatchers
1640
1641
  */
1641
- var tree = parser.parse(text);
1642
+ var tree = parser$1.parse(text);
1642
1643
  // if the tree contains error, it is very probable that
1643
1644
  // our node is one of those error-nodes.
1644
1645
  // also, if there are errors, the node lezer finds us,
@@ -2308,7 +2309,7 @@ function PromQLEditor(props) {
2308
2309
  var model = editor$1.getModel();
2309
2310
  if (model) {
2310
2311
  var query = model.getValue();
2311
- var errors = validateQuery(query, interpolateString ? interpolateString(query) : query, model.getLinesContent(), parser) || [];
2312
+ var errors = validateQuery(query, interpolateString ? interpolateString(query) : query, model.getLinesContent(), parser$1) || [];
2312
2313
  var markers = errors.map(function (_ref) {
2313
2314
  var error = _ref.error,
2314
2315
  boundary = _objectWithoutPropertiesLoose(_ref, _excluded);
@@ -3659,7 +3660,19 @@ var getSqlCompletionProvider = function getSqlCompletionProvider() {
3659
3660
  };
3660
3661
  };
3661
3662
 
3662
- var _templateObject$2, _templateObject2$2;
3663
+ function formatSql(sql) {
3664
+ try {
3665
+ return format(sql, {
3666
+ tabWidth: 2,
3667
+ keywordCase: 'upper'
3668
+ });
3669
+ } catch (_unused) {
3670
+ // If formatting fails, return the original SQL
3671
+ return sql;
3672
+ }
3673
+ }
3674
+
3675
+ var _templateObject$2, _templateObject2$2, _templateObject3;
3663
3676
  var SQL_LANG_ID = 'sql';
3664
3677
  var SIZE_MAP$2 = {
3665
3678
  small: {
@@ -3687,7 +3700,8 @@ var themeMap$2 = {
3687
3700
  };
3688
3701
  var containerDisabledClassName$2 = /*#__PURE__*/css(_templateObject$2 || (_templateObject$2 = /*#__PURE__*/_taggedTemplateLiteralLoose(["\n .monaco-editor {\n user-select: none;\n pointer-events: none;\n }\n"])));
3689
3702
  var containerReadOnlyClassName$2 = /*#__PURE__*/css(_templateObject2$2 || (_templateObject2$2 = /*#__PURE__*/_taggedTemplateLiteralLoose(["\n .monaco-editor .cursors-layer > .cursor {\n opacity: 0 !important;\n }\n"])));
3690
- function SqlEditor(props) {
3703
+ var formatBtnClassName = /*#__PURE__*/css(_templateObject3 || (_templateObject3 = /*#__PURE__*/_taggedTemplateLiteralLoose(["\n position: absolute;\n top: 4px;\n right: 4px;\n z-index: 10;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n padding: 0;\n border: none;\n background: transparent;\n border-radius: 4px;\n cursor: pointer;\n color: inherit;\n opacity: 0.6;\n transition: opacity 0.15s;\n\n &:hover {\n opacity: 1;\n background: var(--format-btn-hover-bg, rgba(128, 128, 128, 0.15));\n }\n"])));
3704
+ var SqlEditor = /*#__PURE__*/forwardRef(function (props, ref) {
3691
3705
  var id = v4();
3692
3706
  var className = props.className,
3693
3707
  maxHeight = props.maxHeight,
@@ -3699,6 +3713,8 @@ function SqlEditor(props) {
3699
3713
  _props$value = props.value,
3700
3714
  value = _props$value === void 0 ? '' : _props$value,
3701
3715
  placeholder = props.placeholder,
3716
+ _props$enableFormat = props.enableFormat,
3717
+ enableFormat = _props$enableFormat === void 0 ? false : _props$enableFormat,
3702
3718
  _props$enableAutocomp = props.enableAutocomplete,
3703
3719
  enableAutocomplete = _props$enableAutocomp === void 0 ? true : _props$enableAutocomp,
3704
3720
  _props$readOnly = props.readOnly,
@@ -3709,7 +3725,8 @@ function SqlEditor(props) {
3709
3725
  onEnter = props.onEnter,
3710
3726
  onBlur = props.onBlur,
3711
3727
  onFocus = props.onFocus,
3712
- editorDidMount = props.editorDidMount;
3728
+ editorDidMount = props.editorDidMount,
3729
+ renderFormatButton = props.renderFormatButton;
3713
3730
  var containerRef = useRef(null);
3714
3731
  var editorRef = useRef(null);
3715
3732
  var modelRef = useRef(null);
@@ -3823,6 +3840,18 @@ function SqlEditor(props) {
3823
3840
  }, '!suggestWidgetVisible && isEditorFocused' + id);
3824
3841
  editorDidMount == null || editorDidMount(editor$1);
3825
3842
  };
3843
+ var handleFormat = useCallback(function () {
3844
+ var editor = editorRef.current;
3845
+ if (!editor) return;
3846
+ var currentValue = editor.getValue();
3847
+ var formatted = formatSql(currentValue);
3848
+ editor.setValue(formatted);
3849
+ }, []);
3850
+ useImperativeHandle(ref, function () {
3851
+ return {
3852
+ format: handleFormat
3853
+ };
3854
+ });
3826
3855
  var themeValue = themeMap$2[theme];
3827
3856
  return React.createElement("div", {
3828
3857
  className: 'ant-input' + (size ? " " + SIZE_MAP$2[size].className : '') + (disabled ? " ant-input-disabled " + containerDisabledClassName$2 : '') + (readOnly ? " " + containerReadOnlyClassName$2 : '') + (className ? " " + className : ''),
@@ -3830,6 +3859,7 @@ function SqlEditor(props) {
3830
3859
  display: 'block',
3831
3860
  resize: 'vertical',
3832
3861
  overflow: 'auto',
3862
+ position: 'relative',
3833
3863
  minHeight: SIZE_MAP$2[size].minHeight,
3834
3864
  maxHeight: maxHeight
3835
3865
  }
@@ -3879,8 +3909,1961 @@ function SqlEditor(props) {
3879
3909
  alwaysConsumeMouseWheel: false
3880
3910
  }
3881
3911
  }
3912
+ })), enableFormat && (renderFormatButton ? renderFormatButton(handleFormat) : React.createElement("button", {
3913
+ onClick: handleFormat,
3914
+ className: formatBtnClassName,
3915
+ title: 'Format SQL',
3916
+ style: {
3917
+ '--format-btn-hover-bg': theme === 'dark' ? 'rgba(255,255,255,0.2)' : 'rgba(128,128,128,0.15)'
3918
+ }
3919
+ }, React.createElement("svg", {
3920
+ xmlns: 'http://www.w3.org/2000/svg',
3921
+ width: '16',
3922
+ height: '16',
3923
+ viewBox: '0 0 24 24',
3924
+ fill: 'none',
3925
+ stroke: 'currentColor',
3926
+ strokeWidth: '2',
3927
+ strokeLinecap: 'round',
3928
+ strokeLinejoin: 'round'
3929
+ }, React.createElement("path", {
3930
+ d: 'm21.64 3.64-1.28-1.28a1.21 1.21 0 0 0-1.72 0L2.36 18.64a1.21 1.21 0 0 0 0 1.72l1.28 1.28a1.2 1.2 0 0 0 1.72 0L21.64 5.36a1.2 1.2 0 0 0 0-1.72'
3931
+ }), React.createElement("path", {
3932
+ d: 'm14 7 3 3'
3933
+ }), React.createElement("path", {
3934
+ d: 'M5 6v4'
3935
+ }), React.createElement("path", {
3936
+ d: 'M19 14v4'
3937
+ }), React.createElement("path", {
3938
+ d: 'M10 2v2'
3939
+ }), React.createElement("path", {
3940
+ d: 'M7 8H3'
3941
+ }), React.createElement("path", {
3942
+ d: 'M21 16h-4'
3943
+ }), React.createElement("path", {
3944
+ d: 'M11 3H9'
3945
+ })))));
3946
+ });
3947
+
3948
+ var languageConfiguration$3 = {
3949
+ wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\@\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g,
3950
+ comments: {
3951
+ lineComment: '//',
3952
+ blockComment: ['/*', '*/']
3953
+ },
3954
+ brackets: [['{', '}'], ['[', ']'], ['(', ')']],
3955
+ autoClosingPairs: [{
3956
+ open: '{',
3957
+ close: '}'
3958
+ }, {
3959
+ open: '[',
3960
+ close: ']'
3961
+ }, {
3962
+ open: '(',
3963
+ close: ')'
3964
+ }, {
3965
+ open: '"',
3966
+ close: '"'
3967
+ }, {
3968
+ open: "'",
3969
+ close: "'"
3970
+ }, {
3971
+ open: '`',
3972
+ close: '`'
3973
+ }],
3974
+ surroundingPairs: [{
3975
+ open: '{',
3976
+ close: '}'
3977
+ }, {
3978
+ open: '[',
3979
+ close: ']'
3980
+ }, {
3981
+ open: '(',
3982
+ close: ')'
3983
+ }, {
3984
+ open: '"',
3985
+ close: '"'
3986
+ }, {
3987
+ open: "'",
3988
+ close: "'"
3989
+ }, {
3990
+ open: '`',
3991
+ close: '`'
3992
+ }],
3993
+ folding: {
3994
+ offSide: false
3995
+ }
3996
+ };
3997
+ // Expr-lang keywords
3998
+ var keywords$3 = ['let', 'true', 'false', 'nil', 'in', 'not', 'and', 'or', 'if', 'else'];
3999
+ // Expr-lang built-in functions
4000
+ var stringFunctions = ['trim', 'trimPrefix', 'trimSuffix', 'upper', 'lower', 'split', 'splitAfter', 'replace', 'repeat', 'indexOf', 'lastIndexOf', 'hasPrefix', 'hasSuffix', 'contains', 'startsWith', 'endsWith'];
4001
+ var dateFunctions = ['now', 'duration', 'date', 'timezone'];
4002
+ var numberFunctions = ['max', 'min', 'abs', 'ceil', 'floor', 'round'];
4003
+ var arrayFunctions = ['all', 'any', 'one', 'none', 'map', 'filter', 'find', 'findIndex', 'findLast', 'findLastIndex', 'groupBy', 'count', 'concat', 'flatten', 'uniq', 'join', 'reduce', 'sum', 'mean', 'median', 'first', 'last', 'take', 'reverse', 'sort', 'sortBy'];
4004
+ var mapFunctions = ['keys', 'values'];
4005
+ var typeConversionFunctions = ['type', 'int', 'float', 'string', 'toJSON', 'fromJSON', 'toBase64', 'fromBase64', 'toPairs', 'fromPairs'];
4006
+ var miscFunctions = ['len', 'get'];
4007
+ var bitwiseFunctions = ['bitand', 'bitor', 'bitxor', 'bitnand', 'bitnot', 'bitshl', 'bitshr', 'bitushr'];
4008
+ var builtinFunctions = /*#__PURE__*/[].concat(stringFunctions, dateFunctions, numberFunctions, arrayFunctions, mapFunctions, typeConversionFunctions, miscFunctions, bitwiseFunctions);
4009
+ var language$3 = {
4010
+ defaultToken: '',
4011
+ tokenPostfix: '.expr',
4012
+ brackets: [{
4013
+ open: '(',
4014
+ close: ')',
4015
+ token: 'delimiter.parenthesis'
4016
+ }, {
4017
+ open: '{',
4018
+ close: '}',
4019
+ token: 'delimiter.curly'
4020
+ }, {
4021
+ open: '[',
4022
+ close: ']',
4023
+ token: 'delimiter.square'
4024
+ }],
4025
+ keywords: keywords$3,
4026
+ builtinFunctions: builtinFunctions,
4027
+ operators: ['+', '-', '*', '/', '%', '^', '**', '==', '!=', '<', '>', '<=', '>=', '!', '&&', '||', '?:', '??', '.', '?.', 'in', 'matches', '..', '|'],
4028
+ escapes: /\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,2}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,
4029
+ digits: /\d+(_+\d+)*/,
4030
+ hexdigits: /[0-9a-fA-F]+(_+[0-9a-fA-F]+)*/,
4031
+ octdigits: /[0-7]+(_+[0-7]+)*/,
4032
+ bindigits: /[01]+(_+[01]+)*/,
4033
+ tokenizer: {
4034
+ root: [{
4035
+ include: '@whitespace'
4036
+ }, {
4037
+ include: '@comments'
4038
+ }, {
4039
+ include: '@numbers'
4040
+ }, {
4041
+ include: '@strings'
4042
+ }, {
4043
+ include: '@bytes'
4044
+ },
4045
+ // Function calls: identifier followed by '('
4046
+ [/[a-zA-Z_]\w*(?=\s*\()/, {
4047
+ cases: {
4048
+ '@builtinFunctions': 'keyword.function',
4049
+ '@default': 'identifier.function'
4050
+ }
4051
+ }],
4052
+ // Keywords and identifiers
4053
+ [/[a-zA-Z_]\w*/, {
4054
+ cases: {
4055
+ '@keywords': 'keyword',
4056
+ '@default': 'identifier'
4057
+ }
4058
+ }],
4059
+ // Operators
4060
+ [/[?][?:]/, 'operator'], [/[?][.]/, 'operator'], [/[.]{2}/, 'operator'], [/[*]{2}/, 'operator'], [/[|]/, 'operator'], [/[+\-*/%^]/, 'operator'], [/==|!=|<=|>=|<|>/, 'operator'], [/!|&&|\|\|/, 'operator'], [/[=]/, 'operator'], [/[.~]/, 'operator'],
4061
+ // Delimiters
4062
+ [/[,;]/, 'delimiter'], [/[{}()\[\]]/, '@brackets']],
4063
+ whitespace: [[/[ \t\r\n]+/, 'white']],
4064
+ comments: [[/\/\/.*$/, 'comment'], [/\/\*/, {
4065
+ token: 'comment.quote',
4066
+ next: '@comment'
4067
+ }]],
4068
+ comment: [[/[^*/]+/, 'comment'], [/\*\//, {
4069
+ token: 'comment.quote',
4070
+ next: '@pop'
4071
+ }], [/./, 'comment']],
4072
+ numbers: [[/0[xX]@hexdigits/, 'number.hex'], [/0[oO]@octdigits/, 'number.octal'], [/0[bB]@bindigits/, 'number.binary'], [/(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?/, 'number']],
4073
+ strings: [[/"/, {
4074
+ token: 'string.double',
4075
+ next: '@string_double'
4076
+ }], [/'/, {
4077
+ token: 'string',
4078
+ next: '@string_single'
4079
+ }], [/`/, {
4080
+ token: 'string.backtick',
4081
+ next: '@string_backtick'
4082
+ }]],
4083
+ string_double: [[/[^"\\]+/, 'string.double'], [/@escapes/, 'string.escape'], [/\\./, 'string.escape.invalid'], [/"/, {
4084
+ token: 'string.double',
4085
+ next: '@pop'
4086
+ }]],
4087
+ string_single: [[/[^'\\]+/, 'string'], [/@escapes/, 'string.escape'], [/\\./, 'string.escape.invalid'], [/'/, {
4088
+ token: 'string',
4089
+ next: '@pop'
4090
+ }]],
4091
+ string_backtick: [[/[^`]+/, 'string.backtick'], [/`/, {
4092
+ token: 'string.backtick',
4093
+ next: '@pop'
4094
+ }]],
4095
+ bytes: [[/[bB]"/, {
4096
+ token: 'string.bytes',
4097
+ next: '@bytes_double'
4098
+ }], [/[bB]'/, {
4099
+ token: 'string.bytes',
4100
+ next: '@bytes_single'
4101
+ }]],
4102
+ bytes_double: [[/[^"\\]+/, 'string.bytes'], [/\\(?:[abfnrtv\\"]|x[0-9A-Fa-f]{2}|[0-7]{3})/, 'string.escape'], [/\\./, 'string.escape.invalid'], [/"/, {
4103
+ token: 'string.bytes',
4104
+ next: '@pop'
4105
+ }]],
4106
+ bytes_single: [[/[^'\\]+/, 'string.bytes'], [/\\(?:[abfnrtv\\']|x[0-9A-Fa-f]{2}|[0-7]{3})/, 'string.escape'], [/\\./, 'string.escape.invalid'], [/'/, {
4107
+ token: 'string.bytes',
4108
+ next: '@pop'
4109
+ }]]
4110
+ }
4111
+ };
4112
+
4113
+ var EXPR_KEYWORDS = ['let', 'true', 'false', 'nil', 'in', 'not', 'and', 'or', 'if', 'else'];
4114
+ var EXPR_FUNCTIONS = [
4115
+ // String functions
4116
+ {
4117
+ name: 'trim',
4118
+ signature: 'trim(str[, chars])',
4119
+ description: 'Removes white spaces from both ends of a string',
4120
+ category: 'string'
4121
+ }, {
4122
+ name: 'trimPrefix',
4123
+ signature: 'trimPrefix(str, prefix)',
4124
+ description: 'Removes the specified prefix from the string',
4125
+ category: 'string'
4126
+ }, {
4127
+ name: 'trimSuffix',
4128
+ signature: 'trimSuffix(str, suffix)',
4129
+ description: 'Removes the specified suffix from the string',
4130
+ category: 'string'
4131
+ }, {
4132
+ name: 'upper',
4133
+ signature: 'upper(str)',
4134
+ description: 'Converts all characters to uppercase',
4135
+ category: 'string'
4136
+ }, {
4137
+ name: 'lower',
4138
+ signature: 'lower(str)',
4139
+ description: 'Converts all characters to lowercase',
4140
+ category: 'string'
4141
+ }, {
4142
+ name: 'split',
4143
+ signature: 'split(str, delimiter[, n])',
4144
+ description: 'Splits a string at each instance of the delimiter',
4145
+ category: 'string'
4146
+ }, {
4147
+ name: 'splitAfter',
4148
+ signature: 'splitAfter(str, delimiter[, n])',
4149
+ description: 'Splits a string after each instance of the delimiter',
4150
+ category: 'string'
4151
+ }, {
4152
+ name: 'replace',
4153
+ signature: 'replace(str, old, new)',
4154
+ description: 'Replaces all occurrences of old with new',
4155
+ category: 'string'
4156
+ }, {
4157
+ name: 'repeat',
4158
+ signature: 'repeat(str, n)',
4159
+ description: 'Repeats a string n times',
4160
+ category: 'string'
4161
+ }, {
4162
+ name: 'indexOf',
4163
+ signature: 'indexOf(str, substring)',
4164
+ description: 'Returns the index of the first occurrence of substring',
4165
+ category: 'string'
4166
+ }, {
4167
+ name: 'lastIndexOf',
4168
+ signature: 'lastIndexOf(str, substring)',
4169
+ description: 'Returns the index of the last occurrence of substring',
4170
+ category: 'string'
4171
+ }, {
4172
+ name: 'hasPrefix',
4173
+ signature: 'hasPrefix(str, prefix)',
4174
+ description: 'Returns true if string starts with the given prefix',
4175
+ category: 'string'
4176
+ }, {
4177
+ name: 'hasSuffix',
4178
+ signature: 'hasSuffix(str, suffix)',
4179
+ description: 'Returns true if string ends with the given suffix',
4180
+ category: 'string'
4181
+ }, {
4182
+ name: 'contains',
4183
+ signature: 'contains(str, substr)',
4184
+ description: 'Returns true if string contains the given substring',
4185
+ category: 'string'
4186
+ }, {
4187
+ name: 'startsWith',
4188
+ signature: 'startsWith(str, prefix)',
4189
+ description: 'Returns true if string starts with the given prefix',
4190
+ category: 'string'
4191
+ }, {
4192
+ name: 'endsWith',
4193
+ signature: 'endsWith(str, suffix)',
4194
+ description: 'Returns true if string ends with the given suffix',
4195
+ category: 'string'
4196
+ },
4197
+ // Date functions
4198
+ {
4199
+ name: 'now',
4200
+ signature: 'now()',
4201
+ description: 'Returns the current date as a time.Time value',
4202
+ category: 'date'
4203
+ }, {
4204
+ name: 'duration',
4205
+ signature: 'duration(str)',
4206
+ description: 'Returns time.Duration value of the given string',
4207
+ category: 'date'
4208
+ }, {
4209
+ name: 'date',
4210
+ signature: 'date(str[, format[, timezone]])',
4211
+ description: 'Converts the given string into a date representation',
4212
+ category: 'date'
4213
+ }, {
4214
+ name: 'timezone',
4215
+ signature: 'timezone(str)',
4216
+ description: 'Returns the timezone of the given string',
4217
+ category: 'date'
4218
+ },
4219
+ // Number functions
4220
+ {
4221
+ name: 'max',
4222
+ signature: 'max(n1, n2)',
4223
+ description: 'Returns the maximum of two numbers',
4224
+ category: 'number'
4225
+ }, {
4226
+ name: 'min',
4227
+ signature: 'min(n1, n2)',
4228
+ description: 'Returns the minimum of two numbers',
4229
+ category: 'number'
4230
+ }, {
4231
+ name: 'abs',
4232
+ signature: 'abs(n)',
4233
+ description: 'Returns the absolute value of a number',
4234
+ category: 'number'
4235
+ }, {
4236
+ name: 'ceil',
4237
+ signature: 'ceil(n)',
4238
+ description: 'Returns the least integer value greater than or equal to x',
4239
+ category: 'number'
4240
+ }, {
4241
+ name: 'floor',
4242
+ signature: 'floor(n)',
4243
+ description: 'Returns the greatest integer value less than or equal to x',
4244
+ category: 'number'
4245
+ }, {
4246
+ name: 'round',
4247
+ signature: 'round(n)',
4248
+ description: 'Returns the nearest integer, rounding half away from zero',
4249
+ category: 'number'
4250
+ },
4251
+ // Array functions
4252
+ {
4253
+ name: 'all',
4254
+ signature: 'all(array, predicate)',
4255
+ description: 'Returns true if all elements satisfy the predicate',
4256
+ category: 'array'
4257
+ }, {
4258
+ name: 'any',
4259
+ signature: 'any(array, predicate)',
4260
+ description: 'Returns true if any elements satisfy the predicate',
4261
+ category: 'array'
4262
+ }, {
4263
+ name: 'one',
4264
+ signature: 'one(array, predicate)',
4265
+ description: 'Returns true if exactly one element satisfies the predicate',
4266
+ category: 'array'
4267
+ }, {
4268
+ name: 'none',
4269
+ signature: 'none(array, predicate)',
4270
+ description: 'Returns true if no elements satisfy the predicate',
4271
+ category: 'array'
4272
+ }, {
4273
+ name: 'map',
4274
+ signature: 'map(array, predicate)',
4275
+ description: 'Returns new array by applying the predicate to each element',
4276
+ category: 'array'
4277
+ }, {
4278
+ name: 'filter',
4279
+ signature: 'filter(array, predicate)',
4280
+ description: 'Returns new array by filtering elements by predicate',
4281
+ category: 'array'
4282
+ }, {
4283
+ name: 'find',
4284
+ signature: 'find(array, predicate)',
4285
+ description: 'Finds the first element satisfying the predicate',
4286
+ category: 'array'
4287
+ }, {
4288
+ name: 'findIndex',
4289
+ signature: 'findIndex(array, predicate)',
4290
+ description: 'Finds the index of the first element satisfying the predicate',
4291
+ category: 'array'
4292
+ }, {
4293
+ name: 'findLast',
4294
+ signature: 'findLast(array, predicate)',
4295
+ description: 'Finds the last element satisfying the predicate',
4296
+ category: 'array'
4297
+ }, {
4298
+ name: 'findLastIndex',
4299
+ signature: 'findLastIndex(array, predicate)',
4300
+ description: 'Finds the index of the last element satisfying the predicate',
4301
+ category: 'array'
4302
+ }, {
4303
+ name: 'groupBy',
4304
+ signature: 'groupBy(array, predicate)',
4305
+ description: 'Groups elements by the result of the predicate',
4306
+ category: 'array'
4307
+ }, {
4308
+ name: 'count',
4309
+ signature: 'count(array[, predicate])',
4310
+ description: 'Returns the number of elements satisfying the predicate',
4311
+ category: 'array'
4312
+ }, {
4313
+ name: 'concat',
4314
+ signature: 'concat(array1, array2[, ...])',
4315
+ description: 'Concatenates two or more arrays',
4316
+ category: 'array'
4317
+ }, {
4318
+ name: 'flatten',
4319
+ signature: 'flatten(array)',
4320
+ description: 'Flattens an array into one-dimensional array',
4321
+ category: 'array'
4322
+ }, {
4323
+ name: 'uniq',
4324
+ signature: 'uniq(array)',
4325
+ description: 'Removes duplicates from an array',
4326
+ category: 'array'
4327
+ }, {
4328
+ name: 'join',
4329
+ signature: 'join(array[, delimiter])',
4330
+ description: 'Joins an array of strings into a single string',
4331
+ category: 'array'
4332
+ }, {
4333
+ name: 'reduce',
4334
+ signature: 'reduce(array, predicate[, initial])',
4335
+ description: 'Reduces an array to a single value',
4336
+ category: 'array'
4337
+ }, {
4338
+ name: 'sum',
4339
+ signature: 'sum(array[, predicate])',
4340
+ description: 'Returns the sum of all numbers in an array',
4341
+ category: 'array'
4342
+ }, {
4343
+ name: 'mean',
4344
+ signature: 'mean(array)',
4345
+ description: 'Returns the average of all numbers in an array',
4346
+ category: 'array'
4347
+ }, {
4348
+ name: 'median',
4349
+ signature: 'median(array)',
4350
+ description: 'Returns the median of all numbers in an array',
4351
+ category: 'array'
4352
+ }, {
4353
+ name: 'first',
4354
+ signature: 'first(array)',
4355
+ description: 'Returns the first element from an array',
4356
+ category: 'array'
4357
+ }, {
4358
+ name: 'last',
4359
+ signature: 'last(array)',
4360
+ description: 'Returns the last element from an array',
4361
+ category: 'array'
4362
+ }, {
4363
+ name: 'take',
4364
+ signature: 'take(array, n)',
4365
+ description: 'Returns the first n elements from an array',
4366
+ category: 'array'
4367
+ }, {
4368
+ name: 'reverse',
4369
+ signature: 'reverse(array)',
4370
+ description: 'Returns a new reversed copy of the array',
4371
+ category: 'array'
4372
+ }, {
4373
+ name: 'sort',
4374
+ signature: 'sort(array[, order])',
4375
+ description: 'Sorts an array in ascending or descending order',
4376
+ category: 'array'
4377
+ }, {
4378
+ name: 'sortBy',
4379
+ signature: 'sortBy(array[, predicate, order])',
4380
+ description: 'Sorts an array by the result of the predicate',
4381
+ category: 'array'
4382
+ },
4383
+ // Map functions
4384
+ {
4385
+ name: 'keys',
4386
+ signature: 'keys(map)',
4387
+ description: 'Returns an array containing the keys of the map',
4388
+ category: 'map'
4389
+ }, {
4390
+ name: 'values',
4391
+ signature: 'values(map)',
4392
+ description: 'Returns an array containing the values of the map',
4393
+ category: 'map'
4394
+ },
4395
+ // Type conversion functions
4396
+ {
4397
+ name: 'type',
4398
+ signature: 'type(v)',
4399
+ description: 'Returns the type of the given value',
4400
+ category: 'conversion'
4401
+ }, {
4402
+ name: 'int',
4403
+ signature: 'int(v)',
4404
+ description: 'Returns the integer value of a number or string',
4405
+ category: 'conversion'
4406
+ }, {
4407
+ name: 'float',
4408
+ signature: 'float(v)',
4409
+ description: 'Returns the float value of a number or string',
4410
+ category: 'conversion'
4411
+ }, {
4412
+ name: 'string',
4413
+ signature: 'string(v)',
4414
+ description: 'Converts a value to its string representation',
4415
+ category: 'conversion'
4416
+ }, {
4417
+ name: 'toJSON',
4418
+ signature: 'toJSON(v)',
4419
+ description: 'Converts a value to JSON string representation',
4420
+ category: 'conversion'
4421
+ }, {
4422
+ name: 'fromJSON',
4423
+ signature: 'fromJSON(v)',
4424
+ description: 'Parses a JSON string to a value',
4425
+ category: 'conversion'
4426
+ }, {
4427
+ name: 'toBase64',
4428
+ signature: 'toBase64(v)',
4429
+ description: 'Encodes a string into Base64 format',
4430
+ category: 'conversion'
4431
+ }, {
4432
+ name: 'fromBase64',
4433
+ signature: 'fromBase64(v)',
4434
+ description: 'Decodes a Base64 encoded string',
4435
+ category: 'conversion'
4436
+ }, {
4437
+ name: 'toPairs',
4438
+ signature: 'toPairs(map)',
4439
+ description: 'Converts a map to an array of key-value pairs',
4440
+ category: 'conversion'
4441
+ }, {
4442
+ name: 'fromPairs',
4443
+ signature: 'fromPairs(array)',
4444
+ description: 'Converts an array of key-value pairs to a map',
4445
+ category: 'conversion'
4446
+ },
4447
+ // Miscellaneous functions
4448
+ {
4449
+ name: 'len',
4450
+ signature: 'len(v)',
4451
+ description: 'Returns the length of an array, map, or string',
4452
+ category: 'misc'
4453
+ }, {
4454
+ name: 'get',
4455
+ signature: 'get(v, index)',
4456
+ description: 'Retrieves element at the specified index from an array or map',
4457
+ category: 'misc'
4458
+ },
4459
+ // Bitwise functions
4460
+ {
4461
+ name: 'bitand',
4462
+ signature: 'bitand(int, int)',
4463
+ description: 'Returns the bitwise AND of two integers',
4464
+ category: 'bitwise'
4465
+ }, {
4466
+ name: 'bitor',
4467
+ signature: 'bitor(int, int)',
4468
+ description: 'Returns the bitwise OR of two integers',
4469
+ category: 'bitwise'
4470
+ }, {
4471
+ name: 'bitxor',
4472
+ signature: 'bitxor(int, int)',
4473
+ description: 'Returns the bitwise XOR of two integers',
4474
+ category: 'bitwise'
4475
+ }, {
4476
+ name: 'bitnand',
4477
+ signature: 'bitnand(int, int)',
4478
+ description: 'Returns the bitwise AND NOT of two integers',
4479
+ category: 'bitwise'
4480
+ }, {
4481
+ name: 'bitnot',
4482
+ signature: 'bitnot(int)',
4483
+ description: 'Returns the bitwise NOT of an integer',
4484
+ category: 'bitwise'
4485
+ }, {
4486
+ name: 'bitshl',
4487
+ signature: 'bitshl(int, int)',
4488
+ description: 'Returns the Left Shift of an integer',
4489
+ category: 'bitwise'
4490
+ }, {
4491
+ name: 'bitshr',
4492
+ signature: 'bitshr(int, int)',
4493
+ description: 'Returns the Right Shift of an integer',
4494
+ category: 'bitwise'
4495
+ }, {
4496
+ name: 'bitushr',
4497
+ signature: 'bitushr(int, int)',
4498
+ description: 'Returns the unsigned Right Shift of an integer',
4499
+ category: 'bitwise'
4500
+ }];
4501
+ var getExprCompletionProvider = function getExprCompletionProvider() {
4502
+ return {
4503
+ provideCompletionItems: function provideCompletionItems(model, position, _context, _token) {
4504
+ var word = model.getWordUntilPosition(position);
4505
+ var range = new Range(position.lineNumber, word.startColumn, position.lineNumber, word.endColumn);
4506
+ var suggestions = [].concat(EXPR_KEYWORDS.map(function (keyword) {
4507
+ return {
4508
+ label: keyword,
4509
+ kind: languages.CompletionItemKind.Keyword,
4510
+ insertText: keyword,
4511
+ range: range,
4512
+ sortText: '1' + keyword
4513
+ };
4514
+ }), EXPR_FUNCTIONS.map(function (func) {
4515
+ return {
4516
+ label: func.name,
4517
+ kind: languages.CompletionItemKind.Function,
4518
+ insertText: func.name,
4519
+ detail: func.signature,
4520
+ documentation: func.description + (" (" + func.category + ")"),
4521
+ range: range,
4522
+ sortText: '2' + func.name,
4523
+ insertTextRules: languages.CompletionItemInsertTextRule.InsertAsSnippet
4524
+ };
4525
+ }));
4526
+ return {
4527
+ suggestions: suggestions
4528
+ };
4529
+ }
4530
+ };
4531
+ };
4532
+
4533
+ /**
4534
+ * Expr-lang token types and AST definitions
4535
+ */
4536
+ var TokenKind;
4537
+ (function (TokenKind) {
4538
+ TokenKind["Identifier"] = "Identifier";
4539
+ TokenKind["Number"] = "Number";
4540
+ TokenKind["String"] = "String";
4541
+ TokenKind["Operator"] = "Operator";
4542
+ TokenKind["Bracket"] = "Bracket";
4543
+ TokenKind["EOF"] = "EOF";
4544
+ })(TokenKind || (TokenKind = {}));
4545
+ var EOF_TOKEN = {
4546
+ kind: TokenKind.EOF,
4547
+ value: '',
4548
+ start: 0,
4549
+ end: 0,
4550
+ line: 0,
4551
+ column: 0
4552
+ };
4553
+ // Operator precedence (higher = binds tighter)
4554
+ var OPERATOR_PRECEDENCE = {
4555
+ '??': 1,
4556
+ '||': 2,
4557
+ or: 2,
4558
+ '&&': 3,
4559
+ and: 3,
4560
+ '==': 4,
4561
+ '!=': 4,
4562
+ '<': 5,
4563
+ '>': 5,
4564
+ '<=': 5,
4565
+ '>=': 5,
4566
+ "in": 5,
4567
+ matches: 5,
4568
+ contains: 5,
4569
+ startsWith: 5,
4570
+ endsWith: 5,
4571
+ '+': 6,
4572
+ '-': 6,
4573
+ '*': 7,
4574
+ '/': 7,
4575
+ '%': 7,
4576
+ '^': 8,
4577
+ '**': 8
4578
+ };
4579
+ // Unary operators
4580
+ var UNARY_OPERATORS = /*#__PURE__*/new Set(['!', 'not', '-']);
4581
+ // Comparison operators (for chained comparisons)
4582
+ var COMPARISON_OPERATORS = /*#__PURE__*/new Set(['<', '>', '<=', '>=', '==', '!=']);
4583
+
4584
+ // Tokens that should be treated as Operators even if they look like identifiers
4585
+ var OPERATOR_TOKENS = /*#__PURE__*/new Set(['let', 'if', 'else', 'not', 'and', 'or', 'in', 'matches', 'contains', 'startsWith', 'endsWith', 'hasPrefix', 'hasSuffix']);
4586
+ var Lexer = /*#__PURE__*/function () {
4587
+ function Lexer() {
4588
+ this.input = '';
4589
+ this.pos = 0;
4590
+ this.line = 1;
4591
+ this.column = 1;
4592
+ this.startLine = 1;
4593
+ this.startColumn = 1;
4594
+ }
4595
+ var _proto = Lexer.prototype;
4596
+ _proto.reset = function reset(input) {
4597
+ this.input = input;
4598
+ this.pos = 0;
4599
+ this.line = 1;
4600
+ this.column = 1;
4601
+ this.startLine = 1;
4602
+ this.startColumn = 1;
4603
+ };
4604
+ _proto.next = function next() {
4605
+ this.skipWhitespace();
4606
+ this.skipComments();
4607
+ if (this.pos >= this.input.length) {
4608
+ return _extends({}, EOF_TOKEN, {
4609
+ start: this.pos,
4610
+ end: this.pos,
4611
+ line: this.line,
4612
+ column: this.column
4613
+ });
4614
+ }
4615
+ this.startLine = this.line;
4616
+ this.startColumn = this.column;
4617
+ var ch = this.input[this.pos];
4618
+ // String literals
4619
+ if (ch === '"' || ch === "'") {
4620
+ return this.readString(ch);
4621
+ }
4622
+ if (ch === '`') {
4623
+ return this.readBacktickString();
4624
+ }
4625
+ // Byte literals: b"..." or b'...' or B"..." or B'...'
4626
+ if ((ch === 'b' || ch === 'B') && this.pos + 1 < this.input.length) {
4627
+ var _next = this.input[this.pos + 1];
4628
+ if (_next === '"' || _next === "'") {
4629
+ return this.readByteLiteral();
4630
+ }
4631
+ }
4632
+ // Numbers
4633
+ if (this.isDigit(ch) || ch === '.' && this.pos + 1 < this.input.length && this.isDigit(this.input[this.pos + 1])) {
4634
+ return this.readNumber();
4635
+ }
4636
+ // Identifiers and keywords
4637
+ if (this.isIdentStart(ch)) {
4638
+ var token = this.readIdentOrKeyword();
4639
+ return token;
4640
+ }
4641
+ // Operators and brackets
4642
+ return this.readOperatorOrBracket();
4643
+ };
4644
+ _proto.skipWhitespace = function skipWhitespace() {
4645
+ while (this.pos < this.input.length) {
4646
+ var ch = this.input[this.pos];
4647
+ if (ch === ' ' || ch === '\t' || ch === '\r') {
4648
+ this.pos++;
4649
+ this.column++;
4650
+ } else if (ch === '\n') {
4651
+ this.pos++;
4652
+ this.line++;
4653
+ this.column = 1;
4654
+ } else {
4655
+ break;
4656
+ }
4657
+ }
4658
+ };
4659
+ _proto.skipComments = function skipComments() {
4660
+ while (this.pos < this.input.length) {
4661
+ // Line comment //
4662
+ if (this.input[this.pos] === '/' && this.pos + 1 < this.input.length && this.input[this.pos + 1] === '/') {
4663
+ this.pos += 2;
4664
+ this.column += 2;
4665
+ while (this.pos < this.input.length && this.input[this.pos] !== '\n') {
4666
+ this.pos++;
4667
+ this.column++;
4668
+ }
4669
+ this.skipWhitespace();
4670
+ continue;
4671
+ }
4672
+ // Block comment /* */
4673
+ if (this.input[this.pos] === '/' && this.pos + 1 < this.input.length && this.input[this.pos + 1] === '*') {
4674
+ this.pos += 2;
4675
+ this.column += 2;
4676
+ while (this.pos < this.input.length) {
4677
+ if (this.input[this.pos] === '*' && this.pos + 1 < this.input.length && this.input[this.pos + 1] === '/') {
4678
+ this.pos += 2;
4679
+ this.column += 2;
4680
+ break;
4681
+ }
4682
+ if (this.input[this.pos] === '\n') {
4683
+ this.line++;
4684
+ this.column = 1;
4685
+ } else {
4686
+ this.column++;
4687
+ }
4688
+ this.pos++;
4689
+ }
4690
+ this.skipWhitespace();
4691
+ continue;
4692
+ }
4693
+ break;
4694
+ }
4695
+ };
4696
+ _proto.readString = function readString(quote) {
4697
+ var start = this.pos;
4698
+ this.pos++; // skip opening quote
4699
+ this.column++;
4700
+ var value = '';
4701
+ while (this.pos < this.input.length) {
4702
+ var ch = this.input[this.pos];
4703
+ if (ch === '\\') {
4704
+ if (this.pos + 1 >= this.input.length) break;
4705
+ var next = this.input[this.pos + 1];
4706
+ switch (next) {
4707
+ case 'n':
4708
+ value += '\n';
4709
+ break;
4710
+ case 't':
4711
+ value += '\t';
4712
+ break;
4713
+ case 'r':
4714
+ value += '\r';
4715
+ break;
4716
+ case '\\':
4717
+ value += '\\';
4718
+ break;
4719
+ case quote:
4720
+ value += quote;
4721
+ break;
4722
+ case 'x':
4723
+ {
4724
+ var hex = this.input.substr(this.pos + 2, 2);
4725
+ value += String.fromCharCode(parseInt(hex, 16));
4726
+ this.pos += 2;
4727
+ this.column += 2;
4728
+ break;
4729
+ }
4730
+ case 'u':
4731
+ {
4732
+ var unicode = this.input.substr(this.pos + 2, 4);
4733
+ value += String.fromCharCode(parseInt(unicode, 16));
4734
+ this.pos += 2;
4735
+ this.column += 2;
4736
+ break;
4737
+ }
4738
+ default:
4739
+ value += next;
4740
+ }
4741
+ this.pos += 2;
4742
+ this.column += 2;
4743
+ } else if (ch === quote) {
4744
+ this.pos++; // skip closing quote
4745
+ this.column++;
4746
+ return this.makeToken(TokenKind.String, value, start);
4747
+ } else {
4748
+ value += ch;
4749
+ this.pos++;
4750
+ this.column++;
4751
+ if (ch === '\n') {
4752
+ this.line++;
4753
+ this.column = 1;
4754
+ }
4755
+ }
4756
+ }
4757
+ return this.makeToken(TokenKind.String, value, start);
4758
+ };
4759
+ _proto.readBacktickString = function readBacktickString() {
4760
+ var start = this.pos;
4761
+ this.pos++; // skip opening backtick
4762
+ this.column++;
4763
+ var value = '';
4764
+ while (this.pos < this.input.length) {
4765
+ var ch = this.input[this.pos];
4766
+ if (ch === '`') {
4767
+ this.pos++;
4768
+ this.column++;
4769
+ return this.makeToken(TokenKind.String, value, start);
4770
+ }
4771
+ value += ch;
4772
+ this.pos++;
4773
+ if (ch === '\n') {
4774
+ this.line++;
4775
+ this.column = 1;
4776
+ } else {
4777
+ this.column++;
4778
+ }
4779
+ }
4780
+ return this.makeToken(TokenKind.String, value, start);
4781
+ };
4782
+ _proto.readByteLiteral = function readByteLiteral() {
4783
+ // b"..." or b'...' or B"..." or B'...'
4784
+ var start = this.pos;
4785
+ this.pos++; // skip 'b' or 'B'
4786
+ this.column++;
4787
+ var quote = this.input[this.pos];
4788
+ this.pos++; // skip quote
4789
+ this.column++;
4790
+ var value = '';
4791
+ while (this.pos < this.input.length) {
4792
+ var ch = this.input[this.pos];
4793
+ if (ch === '\\') {
4794
+ if (this.pos + 1 >= this.input.length) break;
4795
+ var next = this.input[this.pos + 1];
4796
+ switch (next) {
4797
+ case 'n':
4798
+ value += '\n';
4799
+ break;
4800
+ case 't':
4801
+ value += '\t';
4802
+ break;
4803
+ case 'r':
4804
+ value += '\r';
4805
+ break;
4806
+ case '\\':
4807
+ value += '\\';
4808
+ break;
4809
+ case quote:
4810
+ value += quote;
4811
+ break;
4812
+ case 'x':
4813
+ {
4814
+ var hex = this.input.substr(this.pos + 2, 2);
4815
+ value += String.fromCharCode(parseInt(hex, 16));
4816
+ this.pos += 2;
4817
+ this.column += 2;
4818
+ break;
4819
+ }
4820
+ default:
4821
+ // octal escape \NNN
4822
+ if (this.isDigit(next)) {
4823
+ var octal = this.input.substr(this.pos + 1, 3);
4824
+ value += String.fromCharCode(parseInt(octal, 8));
4825
+ this.pos += 2;
4826
+ this.column += 2;
4827
+ break;
4828
+ }
4829
+ value += next;
4830
+ }
4831
+ this.pos += 2;
4832
+ this.column += 2;
4833
+ } else if (ch === quote) {
4834
+ this.pos++;
4835
+ this.column++;
4836
+ return this.makeToken(TokenKind.String, value, start);
4837
+ } else {
4838
+ value += ch;
4839
+ this.pos++;
4840
+ this.column++;
4841
+ }
4842
+ }
4843
+ return this.makeToken(TokenKind.String, value, start);
4844
+ };
4845
+ _proto.readNumber = function readNumber() {
4846
+ var start = this.pos;
4847
+ var value = '';
4848
+ while (this.pos < this.input.length) {
4849
+ var ch = this.input[this.pos];
4850
+ if (this.isDigit(ch) || ch === '.' || ch === 'e' || ch === 'E' || ch === '+' || ch === '-' || ch === '_' || ch === 'x' || ch === 'X' || ch === 'o' || ch === 'O' || ch === 'b' || ch === 'B' || ch >= 'a' && ch <= 'f' || ch >= 'A' && ch <= 'F') {
4851
+ value += ch;
4852
+ this.pos++;
4853
+ this.column++;
4854
+ } else {
4855
+ break;
4856
+ }
4857
+ }
4858
+ return this.makeToken(TokenKind.Number, value, start);
4859
+ };
4860
+ _proto.readIdentOrKeyword = function readIdentOrKeyword() {
4861
+ var start = this.pos;
4862
+ var value = '';
4863
+ while (this.pos < this.input.length) {
4864
+ var ch = this.input[this.pos];
4865
+ if (this.isIdentPart(ch)) {
4866
+ value += ch;
4867
+ this.pos++;
4868
+ this.column++;
4869
+ } else {
4870
+ break;
4871
+ }
4872
+ }
4873
+ // Keywords that are operators
4874
+ if (OPERATOR_TOKENS.has(value)) {
4875
+ return this.makeToken(TokenKind.Operator, value, start);
4876
+ }
4877
+ return this.makeToken(TokenKind.Identifier, value, start);
4878
+ };
4879
+ _proto.readOperatorOrBracket = function readOperatorOrBracket() {
4880
+ var start = this.pos;
4881
+ var ch = this.input[this.pos];
4882
+ // Brackets
4883
+ if ('()[]{}'.includes(ch)) {
4884
+ this.pos++;
4885
+ this.column++;
4886
+ return this.makeToken(TokenKind.Bracket, ch, start);
4887
+ }
4888
+ // Multi-character operators
4889
+ var twoChar = this.input.substr(this.pos, 2);
4890
+ var threeChar = this.input.substr(this.pos, 3);
4891
+ // 3-character operators: **=
4892
+ // 2-character operators
4893
+ var twoCharOps = ['==', '!=', '<=', '>=', '&&', '||', '??', '?.', '..', '**', '//', '::', '->'];
4894
+ var threeCharOps = ['...', '<<=', '>>='];
4895
+ if (threeCharOps.includes(threeChar)) {
4896
+ this.pos += 3;
4897
+ this.column += 3;
4898
+ return this.makeToken(TokenKind.Operator, threeChar, start);
4899
+ }
4900
+ if (twoCharOps.includes(twoChar)) {
4901
+ this.pos += 2;
4902
+ this.column += 2;
4903
+ return this.makeToken(TokenKind.Operator, twoChar, start);
4904
+ }
4905
+ // Single-character operators and separators
4906
+ var singleCharOps = '+-*/%^=<>!&|?.,;:#@$~';
4907
+ if (singleCharOps.includes(ch)) {
4908
+ this.pos++;
4909
+ this.column++;
4910
+ return this.makeToken(TokenKind.Operator, ch, start);
4911
+ }
4912
+ // Unrecognized character
4913
+ this.pos++;
4914
+ this.column++;
4915
+ return this.makeToken(TokenKind.Operator, ch, start);
4916
+ };
4917
+ _proto.isDigit = function isDigit(ch) {
4918
+ return ch >= '0' && ch <= '9';
4919
+ };
4920
+ _proto.isIdentStart = function isIdentStart(ch) {
4921
+ return ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z' || ch === '_' || ch === '$';
4922
+ };
4923
+ _proto.isIdentPart = function isIdentPart(ch) {
4924
+ return this.isIdentStart(ch) || this.isDigit(ch);
4925
+ };
4926
+ _proto.makeToken = function makeToken(kind, value, start) {
4927
+ return {
4928
+ kind: kind,
4929
+ value: value,
4930
+ start: start,
4931
+ end: this.pos,
4932
+ line: this.startLine,
4933
+ column: this.startColumn
4934
+ };
4935
+ };
4936
+ return Lexer;
4937
+ }();
4938
+
4939
+ var ExprParser = /*#__PURE__*/function () {
4940
+ function ExprParser() {
4941
+ this.current = _extends({}, EOF_TOKEN);
4942
+ this.errors = [];
4943
+ this.lexer = new Lexer();
4944
+ }
4945
+ /**
4946
+ * Parse an expr-lang expression and return any syntax errors.
4947
+ * Returns empty array if the expression is valid.
4948
+ */
4949
+ var _proto = ExprParser.prototype;
4950
+ _proto.parse = function parse(input) {
4951
+ this.errors = [];
4952
+ this.lexer.reset(input);
4953
+ this.advance();
4954
+ if (input.trim().length === 0) {
4955
+ return [];
4956
+ }
4957
+ this.parseSequenceExpression();
4958
+ // Check for unexpected tokens at the end
4959
+ if (this.curKind() !== TokenKind.EOF && this.errors.length === 0) {
4960
+ this.error("unexpected token \"" + this.curVal() + "\"");
4961
+ }
4962
+ return this.errors;
4963
+ };
4964
+ _proto.advance = function advance() {
4965
+ this.current = this.lexer.next();
4966
+ };
4967
+ _proto.curKind = function curKind() {
4968
+ return this.current.kind;
4969
+ };
4970
+ _proto.curVal = function curVal() {
4971
+ return this.current.value;
4972
+ };
4973
+ _proto.expect = function expect(kind, value) {
4974
+ if (this.curKind() === kind && (value === undefined || this.curVal() === value)) {
4975
+ this.advance();
4976
+ return true;
4977
+ }
4978
+ if (value) {
4979
+ this.error("expected \"" + value + "\" but got \"" + this.curVal() + "\"");
4980
+ } else {
4981
+ this.error("unexpected token \"" + this.curVal() + "\"");
4982
+ }
4983
+ return false;
4984
+ };
4985
+ _proto.error = function error(message) {
4986
+ if (this.errors.length === 0) {
4987
+ // Calculate end position — approximate
4988
+ var endLine = this.current.line;
4989
+ var endColumn = this.current.column + this.curVal().length;
4990
+ this.errors.push({
4991
+ message: message,
4992
+ startLine: this.current.line,
4993
+ startColumn: this.current.column,
4994
+ endLine: endLine,
4995
+ endColumn: endColumn
4996
+ });
4997
+ }
4998
+ };
4999
+ _proto.errorAt = function errorAt(token, message) {
5000
+ if (this.errors.length === 0) {
5001
+ var endColumn = token.column + token.value.length;
5002
+ this.errors.push({
5003
+ message: message,
5004
+ startLine: token.line,
5005
+ startColumn: token.column,
5006
+ endLine: token.line,
5007
+ endColumn: endColumn
5008
+ });
5009
+ }
5010
+ }
5011
+ // ========== Parsing Functions ==========
5012
+ /**
5013
+ * parseSequenceExpression parses multiple expressions separated by semicolons.
5014
+ */;
5015
+ _proto.parseSequenceExpression = function parseSequenceExpression() {
5016
+ if (this.errors.length > 0) return;
5017
+ this.parseExpression(0);
5018
+ while (this.curVal() === ';' && this.errors.length === 0) {
5019
+ this.advance();
5020
+ if (this.curKind() === TokenKind.EOF) break;
5021
+ this.parseExpression(0);
5022
+ }
5023
+ }
5024
+ /**
5025
+ * parseExpression uses precedence climbing to parse binary expressions.
5026
+ */;
5027
+ _proto.parseExpression = function parseExpression(precedence) {
5028
+ if (this.errors.length > 0) return;
5029
+ // Handle "let" at precedence 0
5030
+ if (precedence === 0 && this.curVal() === 'let') {
5031
+ this.parseVariableDeclaration();
5032
+ return;
5033
+ }
5034
+ // Handle "if" at precedence 0
5035
+ if (precedence === 0 && this.curVal() === 'if') {
5036
+ this.parseConditionalIf();
5037
+ return;
5038
+ }
5039
+ // Handle unary operators
5040
+ if (UNARY_OPERATORS.has(this.curVal()) && this.curKind() === TokenKind.Operator) {
5041
+ var unaryToken = this.current;
5042
+ this.advance();
5043
+ if (this.curKind() === TokenKind.EOF) {
5044
+ this.errorAt(unaryToken, 'unexpected token EOF');
5045
+ return;
5046
+ }
5047
+ if (this.errors.length > 0) return;
5048
+ this.parsePrimary();
5049
+ if (this.errors.length > 0) return;
5050
+ this.parsePostfixExpression();
5051
+ return;
5052
+ }
5053
+ this.parsePrimary();
5054
+ if (this.errors.length > 0) return;
5055
+ this.parsePostfixExpression();
5056
+ if (this.errors.length > 0) return;
5057
+ // Handle binary operators with precedence climbing
5058
+ while (this.curKind() === TokenKind.Operator && this.errors.length === 0) {
5059
+ var op = this.curVal();
5060
+ var opToken = this.current;
5061
+ // Handle pipe operator |
5062
+ if (op === '|') {
5063
+ this.advance(); // skip |
5064
+ if (this.curKind() === TokenKind.EOF) {
5065
+ this.errorAt(opToken, 'unexpected token EOF');
5066
+ return;
5067
+ }
5068
+ if (this.curKind() === TokenKind.Identifier) {
5069
+ this.advance(); // skip identifier
5070
+ this.parseArguments();
5071
+ if (this.curVal() === ';') break;
5072
+ continue;
5073
+ }
5074
+ this.error("expected identifier after pipe \"|\" but got \"" + this.curVal() + "\"");
5075
+ return;
5076
+ }
5077
+ // Handle semicolon — end of this expression
5078
+ if (op === ';') break;
5079
+ // Check precedence
5080
+ var opPrec = OPERATOR_PRECEDENCE[op];
5081
+ if (opPrec === undefined || opPrec < precedence) break;
5082
+ // Handle "not" prefix for "not in", "not contains", etc.
5083
+ if (op === 'not') {
5084
+ this.advance();
5085
+ var nextOp = this.curVal();
5086
+ var negatedPrec = OPERATOR_PRECEDENCE[nextOp];
5087
+ if (negatedPrec !== undefined && negatedPrec >= precedence) {
5088
+ this.advance();
5089
+ this.parseExpression(negatedPrec + 1);
5090
+ continue;
5091
+ }
5092
+ this.error("unexpected token \"" + nextOp + "\" after \"not\"");
5093
+ return;
5094
+ }
5095
+ // Handle chained comparisons: a < b < c => (a < b) && (b < c)
5096
+ if (COMPARISON_OPERATORS.has(op)) {
5097
+ this.advance();
5098
+ if (this.curKind() === TokenKind.EOF) {
5099
+ this.errorAt(opToken, 'unexpected token EOF');
5100
+ return;
5101
+ }
5102
+ this.parseExpression(opPrec + 1);
5103
+ // Keep parsing chained comparisons
5104
+ while (this.curKind() === TokenKind.Operator && COMPARISON_OPERATORS.has(this.curVal()) && this.errors.length === 0) {
5105
+ var chainOpToken = this.current;
5106
+ this.advance();
5107
+ if (this.curKind() === TokenKind.EOF) {
5108
+ this.errorAt(chainOpToken, 'unexpected token EOF');
5109
+ return;
5110
+ }
5111
+ this.parseExpression((OPERATOR_PRECEDENCE[this.curVal()] || 0) + 1);
5112
+ }
5113
+ continue;
5114
+ }
5115
+ this.advance();
5116
+ if (this.curKind() === TokenKind.EOF) {
5117
+ this.errorAt(opToken, 'unexpected token EOF');
5118
+ return;
5119
+ }
5120
+ // Right-associative operators bind to the right
5121
+ // For simplicity, parse with same precedence for right-assoc
5122
+ this.parseExpression(opPrec + 1);
5123
+ // Handle ternary ? :
5124
+ if (precedence === 0 && this.curVal() === '?') {
5125
+ this.parseConditional();
5126
+ }
5127
+ }
5128
+ // Handle ternary ? : at precedence 0
5129
+ if (precedence === 0 && this.curVal() === '?') {
5130
+ this.parseConditional();
5131
+ }
5132
+ }
5133
+ /**
5134
+ * parsePrimary handles unary operators, parentheses, and the #/. pointer prefix.
5135
+ */;
5136
+ _proto.parsePrimary = function parsePrimary() {
5137
+ if (this.errors.length > 0) return;
5138
+ // Unary operators are handled in parseExpression
5139
+ // Parenthesized expression
5140
+ if (this.curKind() === TokenKind.Bracket && this.curVal() === '(') {
5141
+ this.advance(); // skip (
5142
+ this.parseSequenceExpression();
5143
+ if (!this.expect(TokenKind.Bracket, ')')) {
5144
+ return;
5145
+ }
5146
+ return;
5147
+ }
5148
+ // Handle # or . prefix (in predicates)
5149
+ if ((this.curVal() === '#' || this.curVal() === '.') && this.curKind() === TokenKind.Operator) {
5150
+ this.advance();
5151
+ if (this.curKind() === TokenKind.Identifier) {
5152
+ this.advance();
5153
+ }
5154
+ this.parsePostfixExpression();
5155
+ return;
5156
+ }
5157
+ this.parseSecondary();
5158
+ }
5159
+ /**
5160
+ * parseSecondary handles identifiers, literals, arrays, and maps.
5161
+ */;
5162
+ _proto.parseSecondary = function parseSecondary() {
5163
+ if (this.errors.length > 0) return;
5164
+ var token = this.current;
5165
+ switch (token.kind) {
5166
+ case TokenKind.Identifier:
5167
+ {
5168
+ this.advance();
5169
+ // Check for function call
5170
+ if (this.curKind() === TokenKind.Bracket && this.curVal() === '(') {
5171
+ this.parseArguments();
5172
+ }
5173
+ break;
5174
+ }
5175
+ case TokenKind.Number:
5176
+ {
5177
+ this.advance();
5178
+ break;
5179
+ }
5180
+ case TokenKind.String:
5181
+ {
5182
+ this.advance();
5183
+ break;
5184
+ }
5185
+ case TokenKind.Bracket:
5186
+ {
5187
+ if (token.value === '[') {
5188
+ this.parseArrayExpression();
5189
+ } else if (token.value === '{') {
5190
+ this.parseMapExpression();
5191
+ } else {
5192
+ this.error("unexpected token \"" + token.value + "\"");
5193
+ }
5194
+ break;
5195
+ }
5196
+ default:
5197
+ this.error("unexpected token \"" + token.value + "\"");
5198
+ break;
5199
+ }
5200
+ }
5201
+ /**
5202
+ * parsePostfixExpression handles .member, ?.member, [index], [from:to], and calls after the primary.
5203
+ */;
5204
+ _proto.parsePostfixExpression = function parsePostfixExpression() {
5205
+ while (this.errors.length === 0) {
5206
+ var token = this.current;
5207
+ // .member or ?.member
5208
+ if (token.kind === TokenKind.Operator && (token.value === '.' || token.value === '?.')) {
5209
+ this.advance();
5210
+ // After . or ?., expect an identifier (or operator like "not" that can be a method name)
5211
+ if (this.curKind() === TokenKind.Identifier || this.curKind() === TokenKind.Operator) {
5212
+ this.advance();
5213
+ // Check for method call: obj.method()
5214
+ if (this.curKind() === TokenKind.Bracket && this.curVal() === '(') {
5215
+ this.parseArguments();
5216
+ }
5217
+ } else if (this.curKind() === TokenKind.Bracket && this.curVal() === '[' && token.value === '?.') {
5218
+ // obj?[index] — handle bracket after ?.
5219
+ this.advance();
5220
+ this.parseExpression(0);
5221
+ this.expect(TokenKind.Bracket, ']');
5222
+ } else {
5223
+ this.error("expected property name after \"" + token.value + "\" but got \"" + this.curVal() + "\"");
5224
+ return;
5225
+ }
5226
+ continue;
5227
+ }
5228
+ // [index] or [from:to]
5229
+ if (token.kind === TokenKind.Bracket && token.value === '[') {
5230
+ this.advance();
5231
+ // Check for slice: [:] or [:to]
5232
+ if (this.curKind() === TokenKind.Operator && this.curVal() === ':') {
5233
+ this.advance();
5234
+ if (this.curKind() !== TokenKind.Bracket || this.curVal() !== ']') {
5235
+ this.parseExpression(0);
5236
+ }
5237
+ this.expect(TokenKind.Bracket, ']');
5238
+ } else {
5239
+ // Index expression
5240
+ this.parseExpression(0);
5241
+ // Check for slice: [from:]
5242
+ if (this.curKind() === TokenKind.Operator && this.curVal() === ':') {
5243
+ this.advance();
5244
+ if (this.curKind() !== TokenKind.Bracket || this.curVal() !== ']') {
5245
+ this.parseExpression(0);
5246
+ }
5247
+ }
5248
+ this.expect(TokenKind.Bracket, ']');
5249
+ }
5250
+ continue;
5251
+ }
5252
+ // Function call after member access
5253
+ if (token.kind === TokenKind.Bracket && token.value === '(') {
5254
+ this.parseArguments();
5255
+ continue;
5256
+ }
5257
+ break;
5258
+ }
5259
+ }
5260
+ /**
5261
+ * parseVariableDeclaration parses "let name = value; rest"
5262
+ */;
5263
+ _proto.parseVariableDeclaration = function parseVariableDeclaration() {
5264
+ this.expect(TokenKind.Operator, 'let');
5265
+ if (this.errors.length > 0) return;
5266
+ if (this.curKind() !== TokenKind.Identifier) {
5267
+ this.error("expected variable name after \"let\" but got \"" + this.curVal() + "\"");
5268
+ return;
5269
+ }
5270
+ this.advance(); // skip variable name
5271
+ if (!this.expect(TokenKind.Operator, '=')) return;
5272
+ this.parseExpression(0);
5273
+ if (this.errors.length > 0) return;
5274
+ // Optional semicolon after value
5275
+ if (this.curVal() === ';') {
5276
+ this.advance();
5277
+ if (this.curKind() !== TokenKind.EOF) {
5278
+ this.parseSequenceExpression();
5279
+ }
5280
+ }
5281
+ }
5282
+ /**
5283
+ * parseConditionalIf parses "if expr { expr1 } else { expr2 }" or "if expr { expr1 } else if ..."
5284
+ */;
5285
+ _proto.parseConditionalIf = function parseConditionalIf() {
5286
+ this.advance(); // skip 'if'
5287
+ if (this.errors.length > 0) return;
5288
+ this.parseExpression(0);
5289
+ if (this.errors.length > 0) return;
5290
+ if (!this.expect(TokenKind.Bracket, '{')) return;
5291
+ this.parseSequenceExpression();
5292
+ if (this.errors.length > 0) return;
5293
+ if (!this.expect(TokenKind.Bracket, '}')) return;
5294
+ if (!this.expect(TokenKind.Operator, 'else')) return;
5295
+ // Nested if
5296
+ if (this.curVal() === 'if') {
5297
+ this.parseConditionalIf();
5298
+ return;
5299
+ }
5300
+ if (!this.expect(TokenKind.Bracket, '{')) return;
5301
+ this.parseSequenceExpression();
5302
+ if (this.errors.length > 0) return;
5303
+ this.expect(TokenKind.Bracket, '}');
5304
+ }
5305
+ /**
5306
+ * parseConditional parses "expr ? expr1 : expr2" or "expr ?: expr2"
5307
+ */;
5308
+ _proto.parseConditional = function parseConditional() {
5309
+ if (this.curVal() !== '?') return;
5310
+ this.advance(); // skip ?
5311
+ // Elvis operator ?:
5312
+ if (this.curVal() === ':') {
5313
+ this.advance();
5314
+ this.parseExpression(0);
5315
+ return;
5316
+ }
5317
+ this.parseExpression(0);
5318
+ if (this.errors.length > 0) return;
5319
+ if (!this.expect(TokenKind.Operator, ':')) return;
5320
+ this.parseExpression(0);
5321
+ }
5322
+ /**
5323
+ * parseArguments parses function call arguments: (arg1, arg2, ...)
5324
+ */;
5325
+ _proto.parseArguments = function parseArguments() {
5326
+ if (!this.expect(TokenKind.Bracket, '(')) return;
5327
+ while (this.curKind() !== TokenKind.Bracket || this.curVal() !== ')') {
5328
+ if (this.curKind() === TokenKind.EOF) {
5329
+ this.error('unexpected end of expression, expected ")"');
5330
+ return;
5331
+ }
5332
+ if (this.curVal() === ',') {
5333
+ this.advance();
5334
+ continue;
5335
+ }
5336
+ // Check for predicate { ... } argument
5337
+ if (this.curKind() === TokenKind.Bracket && this.curVal() === '{') {
5338
+ this.advance();
5339
+ this.parseSequenceExpression();
5340
+ this.expect(TokenKind.Bracket, '}');
5341
+ } else {
5342
+ this.parseExpression(0);
5343
+ }
5344
+ if (this.errors.length > 0) return;
5345
+ if (this.curVal() === ',') {
5346
+ this.advance();
5347
+ // Allow trailing comma
5348
+ if (this.curKind() === TokenKind.Bracket && this.curVal() === ')') {
5349
+ break;
5350
+ }
5351
+ }
5352
+ }
5353
+ this.expect(TokenKind.Bracket, ')');
5354
+ }
5355
+ /**
5356
+ * parseArrayExpression parses "[elem1, elem2, ...]"
5357
+ */;
5358
+ _proto.parseArrayExpression = function parseArrayExpression() {
5359
+ this.expect(TokenKind.Bracket, '[');
5360
+ if (this.errors.length > 0) return;
5361
+ if (this.curKind() === TokenKind.Bracket && this.curVal() === ']') {
5362
+ this.advance();
5363
+ return;
5364
+ }
5365
+ while (this.errors.length === 0) {
5366
+ this.parseExpression(0);
5367
+ if (this.errors.length > 0) return;
5368
+ if (this.curKind() === TokenKind.Bracket && this.curVal() === ']') {
5369
+ break;
5370
+ }
5371
+ if (this.curVal() === ',') {
5372
+ this.advance();
5373
+ // Allow trailing comma
5374
+ if (this.curKind() === TokenKind.Bracket && this.curVal() === ']') {
5375
+ break;
5376
+ }
5377
+ continue;
5378
+ }
5379
+ this.error("expected \",\" or \"]\" but got \"" + this.curVal() + "\"");
5380
+ return;
5381
+ }
5382
+ this.expect(TokenKind.Bracket, ']');
5383
+ }
5384
+ /**
5385
+ * parseMapExpression parses "{key: value, ...}"
5386
+ */;
5387
+ _proto.parseMapExpression = function parseMapExpression() {
5388
+ this.expect(TokenKind.Bracket, '{');
5389
+ if (this.errors.length > 0) return;
5390
+ if (this.curKind() === TokenKind.Bracket && this.curVal() === '}') {
5391
+ this.advance();
5392
+ return;
5393
+ }
5394
+ while (this.errors.length === 0) {
5395
+ // Key: identifier, string, number, or parenthesized expression
5396
+ if (this.curKind() === TokenKind.Identifier || this.curKind() === TokenKind.String || this.curKind() === TokenKind.Number) {
5397
+ this.advance();
5398
+ } else if (this.curKind() === TokenKind.Bracket && this.curVal() === '(') {
5399
+ this.advance();
5400
+ this.parseExpression(0);
5401
+ this.expect(TokenKind.Bracket, ')');
5402
+ } else {
5403
+ this.error("map key must be a string, number, identifier, or parenthesized expression, got \"" + this.curVal() + "\"");
5404
+ return;
5405
+ }
5406
+ if (!this.expect(TokenKind.Operator, ':')) return;
5407
+ this.parseExpression(0);
5408
+ if (this.errors.length > 0) return;
5409
+ if (this.curKind() === TokenKind.Bracket && this.curVal() === '}') {
5410
+ break;
5411
+ }
5412
+ if (this.curVal() === ',') {
5413
+ this.advance();
5414
+ if (this.curKind() === TokenKind.Bracket && this.curVal() === '}') {
5415
+ break;
5416
+ }
5417
+ continue;
5418
+ }
5419
+ this.error("expected \",\" or \"}\" but got \"" + this.curVal() + "\"");
5420
+ return;
5421
+ }
5422
+ this.expect(TokenKind.Bracket, '}');
5423
+ };
5424
+ return ExprParser;
5425
+ }();
5426
+
5427
+ var EXPR_LANG_ID = 'expr';
5428
+ var parser = /*#__PURE__*/new ExprParser();
5429
+ /**
5430
+ * Validate an expr-lang expression and return Monaco editor markers.
5431
+ * Uses the recursive descent parser for syntax errors,
5432
+ * plus regex-based checks for structural issues like unbalanced quotes/brackets.
5433
+ */
5434
+ var validateExpr = function validateExpr(expr) {
5435
+ var markers = [];
5436
+ if (!expr || expr.trim().length === 0) {
5437
+ return markers;
5438
+ }
5439
+ // 1. Parser-based syntax validation
5440
+ var parseErrors = parser.parse(expr);
5441
+ for (var _iterator = _createForOfIteratorHelperLoose(parseErrors), _step; !(_step = _iterator()).done;) {
5442
+ var err = _step.value;
5443
+ markers.push({
5444
+ severity: MarkerSeverity.Error,
5445
+ startLineNumber: err.startLine,
5446
+ startColumn: err.startColumn,
5447
+ endLineNumber: err.endLine,
5448
+ endColumn: err.endColumn,
5449
+ message: err.message
5450
+ });
5451
+ }
5452
+ // 2. Line-level structural checks (fallback for issues the parser might miss)
5453
+ var structuralMarkers = validateLineLevel(expr);
5454
+ markers.push.apply(markers, structuralMarkers);
5455
+ return markers;
5456
+ };
5457
+ /**
5458
+ * Basic line-level validation for quotes and bracket balancing.
5459
+ */
5460
+ function validateLineLevel(expr) {
5461
+ var markers = [];
5462
+ var lines = expr.split('\n');
5463
+ var inBlockComment = false;
5464
+ lines.forEach(function (line, index) {
5465
+ var lineNumber = index + 1;
5466
+ // Track block comment state
5467
+ if (!inBlockComment) {
5468
+ var blockCommentStart = line.indexOf('/*');
5469
+ if (blockCommentStart !== -1) {
5470
+ var blockCommentEnd = line.indexOf('*/', blockCommentStart + 2);
5471
+ if (blockCommentEnd === -1) {
5472
+ inBlockComment = true;
5473
+ }
5474
+ }
5475
+ } else {
5476
+ var _blockCommentEnd = line.indexOf('*/');
5477
+ if (_blockCommentEnd !== -1) {
5478
+ inBlockComment = false;
5479
+ }
5480
+ if (inBlockComment || line.trim().startsWith('/*')) {
5481
+ return;
5482
+ }
5483
+ }
5484
+ var trimmedLine = line.trim();
5485
+ if (trimmedLine.startsWith('//') || trimmedLine.startsWith('/*')) {
5486
+ return;
5487
+ }
5488
+ // Check for unclosed quotes
5489
+ var singleQuotes = countQuotesOutsideBlockComments(line);
5490
+ if (singleQuotes % 2 !== 0) {
5491
+ markers.push({
5492
+ severity: MarkerSeverity.Warning,
5493
+ startLineNumber: lineNumber,
5494
+ startColumn: 1,
5495
+ endLineNumber: lineNumber,
5496
+ endColumn: line.length + 1,
5497
+ message: "Unclosed single quote '",
5498
+ source: EXPR_LANG_ID
5499
+ });
5500
+ }
5501
+ var doubleQuotes = countDoubleQuotesOutsideBlockComments(line);
5502
+ if (doubleQuotes % 2 !== 0) {
5503
+ markers.push({
5504
+ severity: MarkerSeverity.Warning,
5505
+ startLineNumber: lineNumber,
5506
+ startColumn: 1,
5507
+ endLineNumber: lineNumber,
5508
+ endColumn: line.length + 1,
5509
+ message: 'Unclosed double quote "',
5510
+ source: EXPR_LANG_ID
5511
+ });
5512
+ }
5513
+ var backticks = (line.match(/`/g) || []).length;
5514
+ if (backticks % 2 !== 0) {
5515
+ markers.push({
5516
+ severity: MarkerSeverity.Warning,
5517
+ startLineNumber: lineNumber,
5518
+ startColumn: 1,
5519
+ endLineNumber: lineNumber,
5520
+ endColumn: line.length + 1,
5521
+ message: 'Unclosed backtick `',
5522
+ source: EXPR_LANG_ID
5523
+ });
5524
+ }
5525
+ // Check for unbalanced brackets
5526
+ var openParens = (line.match(/\(/g) || []).length;
5527
+ var closeParens = (line.match(/\)/g) || []).length;
5528
+ if (openParens > closeParens) {
5529
+ markers.push({
5530
+ severity: MarkerSeverity.Warning,
5531
+ startLineNumber: lineNumber,
5532
+ startColumn: 1,
5533
+ endLineNumber: lineNumber,
5534
+ endColumn: line.length + 1,
5535
+ message: 'Unmatched opening parenthesis',
5536
+ source: EXPR_LANG_ID
5537
+ });
5538
+ } else if (closeParens > openParens) {
5539
+ markers.push({
5540
+ severity: MarkerSeverity.Warning,
5541
+ startLineNumber: lineNumber,
5542
+ startColumn: 1,
5543
+ endLineNumber: lineNumber,
5544
+ endColumn: line.length + 1,
5545
+ message: 'Unmatched closing parenthesis',
5546
+ source: EXPR_LANG_ID
5547
+ });
5548
+ }
5549
+ var openBrackets = (line.match(/\[/g) || []).length;
5550
+ var closeBrackets = (line.match(/\]/g) || []).length;
5551
+ if (openBrackets > closeBrackets) {
5552
+ markers.push({
5553
+ severity: MarkerSeverity.Warning,
5554
+ startLineNumber: lineNumber,
5555
+ startColumn: 1,
5556
+ endLineNumber: lineNumber,
5557
+ endColumn: line.length + 1,
5558
+ message: 'Unmatched opening bracket',
5559
+ source: EXPR_LANG_ID
5560
+ });
5561
+ } else if (closeBrackets > openBrackets) {
5562
+ markers.push({
5563
+ severity: MarkerSeverity.Warning,
5564
+ startLineNumber: lineNumber,
5565
+ startColumn: 1,
5566
+ endLineNumber: lineNumber,
5567
+ endColumn: line.length + 1,
5568
+ message: 'Unmatched closing bracket',
5569
+ source: EXPR_LANG_ID
5570
+ });
5571
+ }
5572
+ var openBraces = (line.match(/\{/g) || []).length;
5573
+ var closeBraces = (line.match(/\}/g) || []).length;
5574
+ if (openBraces > closeBraces) {
5575
+ markers.push({
5576
+ severity: MarkerSeverity.Warning,
5577
+ startLineNumber: lineNumber,
5578
+ startColumn: 1,
5579
+ endLineNumber: lineNumber,
5580
+ endColumn: line.length + 1,
5581
+ message: 'Unmatched opening curly brace',
5582
+ source: EXPR_LANG_ID
5583
+ });
5584
+ } else if (closeBraces > openBraces) {
5585
+ markers.push({
5586
+ severity: MarkerSeverity.Warning,
5587
+ startLineNumber: lineNumber,
5588
+ startColumn: 1,
5589
+ endLineNumber: lineNumber,
5590
+ endColumn: line.length + 1,
5591
+ message: 'Unmatched closing curly brace',
5592
+ source: EXPR_LANG_ID
5593
+ });
5594
+ }
5595
+ });
5596
+ return markers;
5597
+ }
5598
+ function countQuotesOutsideBlockComments(line) {
5599
+ var withoutBlockComments = line.replace(/\/\*[\s\S]*?\*\//g, '');
5600
+ return (withoutBlockComments.match(/'/g) || []).length;
5601
+ }
5602
+ function countDoubleQuotesOutsideBlockComments(line) {
5603
+ var withoutBlockComments = line.replace(/\/\*[\s\S]*?\*\//g, '');
5604
+ return (withoutBlockComments.match(/"/g) || []).length;
5605
+ }
5606
+
5607
+ var _templateObject$3, _templateObject2$3;
5608
+ var EXPR_LANG_ID$1 = 'expr';
5609
+ var SIZE_MAP$3 = {
5610
+ small: {
5611
+ className: 'ant-input-sm',
5612
+ top: 1,
5613
+ bottom: 1,
5614
+ minHeight: 24
5615
+ },
5616
+ middle: {
5617
+ className: 'ant-input-md',
5618
+ top: 1,
5619
+ bottom: 1,
5620
+ minHeight: 32
5621
+ },
5622
+ large: {
5623
+ className: 'ant-input-lg',
5624
+ top: 3,
5625
+ bottom: 2,
5626
+ minHeight: 40
5627
+ }
5628
+ };
5629
+ var themeMap$3 = {
5630
+ light: 'expr-light',
5631
+ dark: 'expr-dark'
5632
+ };
5633
+ var containerDisabledClassName$3 = /*#__PURE__*/css(_templateObject$3 || (_templateObject$3 = /*#__PURE__*/_taggedTemplateLiteralLoose(["\n .monaco-editor {\n user-select: none;\n pointer-events: none;\n }\n"])));
5634
+ var containerReadOnlyClassName$3 = /*#__PURE__*/css(_templateObject2$3 || (_templateObject2$3 = /*#__PURE__*/_taggedTemplateLiteralLoose(["\n .monaco-editor .cursors-layer > .cursor {\n opacity: 0 !important;\n }\n"])));
5635
+ function ExprEditor(props) {
5636
+ var id = v4();
5637
+ var className = props.className,
5638
+ maxHeight = props.maxHeight,
5639
+ fontSize = props.fontSize,
5640
+ _props$size = props.size,
5641
+ size = _props$size === void 0 ? 'middle' : _props$size,
5642
+ _props$theme = props.theme,
5643
+ theme = _props$theme === void 0 ? 'light' : _props$theme,
5644
+ _props$value = props.value,
5645
+ value = _props$value === void 0 ? '' : _props$value,
5646
+ placeholder = props.placeholder,
5647
+ _props$enableAutocomp = props.enableAutocomplete,
5648
+ enableAutocomplete = _props$enableAutocomp === void 0 ? true : _props$enableAutocomp,
5649
+ _props$readOnly = props.readOnly,
5650
+ readOnly = _props$readOnly === void 0 ? false : _props$readOnly,
5651
+ _props$disabled = props.disabled,
5652
+ disabled = _props$disabled === void 0 ? false : _props$disabled,
5653
+ onChange = props.onChange,
5654
+ onEnter = props.onEnter,
5655
+ onBlur = props.onBlur,
5656
+ onFocus = props.onFocus,
5657
+ editorDidMount = props.editorDidMount;
5658
+ var containerRef = useRef(null);
5659
+ var editorRef = useRef(null);
5660
+ var modelRef = useRef(null);
5661
+ var disposablesRef = useRef([]);
5662
+ useEffect(function () {
5663
+ // Register language
5664
+ if (!languages.getLanguages().some(function (lang) {
5665
+ return lang.id === EXPR_LANG_ID$1;
5666
+ })) {
5667
+ languages.register({
5668
+ id: EXPR_LANG_ID$1
5669
+ });
5670
+ languages.setMonarchTokensProvider(EXPR_LANG_ID$1, language$3);
5671
+ languages.setLanguageConfiguration(EXPR_LANG_ID$1, languageConfiguration$3);
5672
+ }
5673
+ // Register completion provider
5674
+ if (enableAutocomplete) {
5675
+ var disposable = languages.registerCompletionItemProvider(EXPR_LANG_ID$1, getExprCompletionProvider());
5676
+ disposablesRef.current.push(disposable);
5677
+ }
5678
+ return function () {
5679
+ disposablesRef.current.forEach(function (disposable) {
5680
+ return disposable.dispose();
5681
+ });
5682
+ disposablesRef.current = [];
5683
+ };
5684
+ }, [enableAutocomplete]);
5685
+ var handleEditorMount = function handleEditorMount(editor$1) {
5686
+ editorRef.current = editor$1;
5687
+ modelRef.current = editor$1.getModel();
5688
+ editor.defineTheme('expr-light', {
5689
+ base: 'vs',
5690
+ inherit: true,
5691
+ rules: [],
5692
+ colors: {
5693
+ 'editor.background': '#00000000',
5694
+ focusBorder: '#00000000'
5695
+ }
5696
+ });
5697
+ editor.defineTheme('expr-dark', {
5698
+ base: 'vs-dark',
5699
+ inherit: true,
5700
+ rules: [],
5701
+ colors: {
5702
+ 'editor.background': '#00000000',
5703
+ focusBorder: '#00000000'
5704
+ }
5705
+ });
5706
+ var isEditorFocused = editor$1.createContextKey('isEditorFocused' + id, false);
5707
+ editor$1.onDidBlurEditorWidget(function () {
5708
+ isEditorFocused.set(false);
5709
+ onBlur == null || onBlur(editor$1.getValue());
5710
+ var position = editor$1.getPosition();
5711
+ if (position) {
5712
+ var newSelection = new Selection(position.lineNumber, position.column, position.lineNumber, position.column);
5713
+ editor$1.setSelection(newSelection);
5714
+ }
5715
+ });
5716
+ editor$1.onDidFocusEditorText(function () {
5717
+ isEditorFocused.set(true);
5718
+ onFocus == null || onFocus(editor$1.getValue());
5719
+ });
5720
+ // Auto-height
5721
+ var updateElementHeight = function updateElementHeight() {
5722
+ var containerDiv = containerRef.current;
5723
+ if (containerDiv !== null) {
5724
+ var pixelHeight = editor$1.getContentHeight();
5725
+ containerDiv.style.minHeight = pixelHeight + "px";
5726
+ containerDiv.style.width = '100%';
5727
+ var pixelWidth = containerDiv.clientWidth;
5728
+ editor$1.layout({
5729
+ width: pixelWidth,
5730
+ height: pixelHeight
5731
+ });
5732
+ }
5733
+ };
5734
+ editor$1.onDidContentSizeChange(updateElementHeight);
5735
+ updateElementHeight();
5736
+ // Disable search box
5737
+ editor.addKeybindingRule({
5738
+ keybinding: KeyMod.CtrlCmd | KeyCode.KeyF,
5739
+ command: null
5740
+ });
5741
+ // Shift+Enter for newline
5742
+ editor$1.addCommand(KeyMod.Shift | KeyCode.Enter, function () {
5743
+ var position = editor$1.getPosition();
5744
+ if (position) {
5745
+ editor$1.executeEdits('shift-enter', [{
5746
+ range: new Range(position.lineNumber, position.column, position.lineNumber, position.column),
5747
+ text: '\n'
5748
+ }]);
5749
+ editor$1.setPosition({
5750
+ lineNumber: position.lineNumber + 1,
5751
+ column: 1
5752
+ });
5753
+ }
5754
+ }, 'isEditorFocused' + id);
5755
+ // Prevent default Enter
5756
+ editor.addKeybindingRule({
5757
+ keybinding: KeyCode.Enter,
5758
+ command: '-',
5759
+ when: '!suggestWidgetVisible'
5760
+ });
5761
+ // Custom Enter handler
5762
+ editor$1.addCommand(KeyCode.Enter, function () {
5763
+ onEnter == null || onEnter(editor$1.getValue());
5764
+ }, '!suggestWidgetVisible && isEditorFocused' + id);
5765
+ // Setup validation on content change using decorations (no marker hover clutter)
5766
+ var model = editor$1.getModel();
5767
+ var errorDecorations = [];
5768
+ if (model) {
5769
+ var updateDecorations = function updateDecorations() {
5770
+ var exprValue = model.getValue();
5771
+ var markers = validateExpr(exprValue);
5772
+ var newDecorations = markers.map(function (m) {
5773
+ return {
5774
+ range: new Range(m.startLineNumber, m.startColumn, m.endLineNumber, m.endColumn),
5775
+ options: {
5776
+ className: 'expr-error-squiggly',
5777
+ hoverMessage: {
5778
+ value: m.message
5779
+ },
5780
+ minimap: {
5781
+ color: '#e51400',
5782
+ position: 1
5783
+ },
5784
+ overviewRuler: {
5785
+ color: '#e51400',
5786
+ position: editor.OverviewRulerLane.Right
5787
+ }
5788
+ }
5789
+ };
5790
+ });
5791
+ errorDecorations = model.deltaDecorations(errorDecorations, newDecorations);
5792
+ };
5793
+ var validateDisposable = model.onDidChangeContent(updateDecorations);
5794
+ disposablesRef.current.push(validateDisposable);
5795
+ // Run initial validation
5796
+ updateDecorations();
5797
+ }
5798
+ // Inject CSS for the red squiggly underline
5799
+ var styleEl = document.createElement('style');
5800
+ styleEl.textContent = "\n .expr-error-squiggly {\n background: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 6 3' preserveAspectRatio='none'%3E%3Cpath d='M0,2.5 L1.5,1 L3,2.5 L4.5,1 L6,2.5' stroke='%23e51400' stroke-width='0.6' fill='none'/%3E%3C/svg%3E\") repeat-x left bottom;\n background-size: 6px 3px;\n padding-bottom: 3px;\n }\n ";
5801
+ document.head.appendChild(styleEl);
5802
+ disposablesRef.current.push({
5803
+ dispose: function dispose() {
5804
+ return styleEl.remove();
5805
+ }
5806
+ });
5807
+ editorDidMount == null || editorDidMount(editor$1);
5808
+ };
5809
+ var handleChange = function handleChange(newValue, _e) {
5810
+ onChange == null || onChange(newValue);
5811
+ };
5812
+ var themeValue = themeMap$3[theme];
5813
+ return React.createElement("div", {
5814
+ className: 'ant-input' + (size ? " " + SIZE_MAP$3[size].className : '') + (disabled ? " ant-input-disabled " + containerDisabledClassName$3 : '') + (readOnly ? " " + containerReadOnlyClassName$3 : '') + (className ? " " + className : ''),
5815
+ style: {
5816
+ display: 'block',
5817
+ resize: 'vertical',
5818
+ overflow: 'auto',
5819
+ minHeight: SIZE_MAP$3[size].minHeight,
5820
+ maxHeight: maxHeight
5821
+ }
5822
+ }, React.createElement("div", {
5823
+ ref: containerRef,
5824
+ style: {
5825
+ height: '100%'
5826
+ }
5827
+ }, React.createElement(MonacoEditor, {
5828
+ language: EXPR_LANG_ID$1,
5829
+ theme: themeValue,
5830
+ value: value,
5831
+ onChange: handleChange,
5832
+ options: {
5833
+ placeholder: placeholder,
5834
+ selectOnLineNumbers: true,
5835
+ fontSize: fontSize || 12,
5836
+ roundedSelection: false,
5837
+ scrollBeyondLastLine: false,
5838
+ readOnly: readOnly || disabled,
5839
+ minimap: {
5840
+ enabled: false
5841
+ },
5842
+ lineNumbers: 'off',
5843
+ lineNumbersMinChars: 0,
5844
+ glyphMargin: false,
5845
+ folding: false,
5846
+ lineDecorationsWidth: 0,
5847
+ overviewRulerLanes: 0,
5848
+ overviewRulerBorder: false,
5849
+ hideCursorInOverviewRuler: true,
5850
+ hover: {
5851
+ enabled: true,
5852
+ delay: 200
5853
+ },
5854
+ fixedOverflowWidgets: true,
5855
+ renderLineHighlight: 'none',
5856
+ renderValidationDecorations: 'on',
5857
+ scrollbar: {
5858
+ vertical: 'hidden',
5859
+ horizontal: 'auto'
5860
+ },
5861
+ automaticLayout: true,
5862
+ wordWrap: 'on'
5863
+ },
5864
+ editorDidMount: handleEditorMount
3882
5865
  })));
3883
5866
  }
3884
5867
 
3885
- export { PromQLEditor as PromQLMonacoEditor, SqlEditor as SqlMonacoEditor, YamlEditor as YamlMonacoEditor };
5868
+ export { ExprEditor as ExprMonacoEditor, PromQLEditor as PromQLMonacoEditor, SqlEditor as SqlMonacoEditor, YamlEditor as YamlMonacoEditor };
3886
5869
  //# sourceMappingURL=monaco-editor.esm.js.map