elastic-input 0.3.5 → 0.3.6
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/README.md +1 -0
- package/dist/elastic-input.es.js +202 -152
- package/dist/parser/ast.d.ts +2 -0
- package/dist/types.d.ts +9 -0
- package/dist/utils/formatQuery.d.ts +4 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -487,6 +487,7 @@ formatQuery(query, { maxLineLength: 80, indent: '\t' });
|
|
|
487
487
|
|--------|------|---------|-------------|
|
|
488
488
|
| `maxLineLength` | `number` | `60` | Lines shorter than this stay inline |
|
|
489
489
|
| `indent` | `string` | `' '` (2 spaces) | Indent string per nesting level |
|
|
490
|
+
| `whitespaceOperator` | `string` | — | Replace implicit AND (whitespace) with this operator (e.g. `'AND'`, `'&&'`) |
|
|
490
491
|
|
|
491
492
|
## Requirements
|
|
492
493
|
|
package/dist/elastic-input.es.js
CHANGED
|
@@ -444,6 +444,7 @@ class Parser {
|
|
|
444
444
|
operator: "AND",
|
|
445
445
|
left: result,
|
|
446
446
|
right,
|
|
447
|
+
implicit: true,
|
|
447
448
|
start: result.start,
|
|
448
449
|
end: right.end
|
|
449
450
|
};
|
|
@@ -526,6 +527,7 @@ class Parser {
|
|
|
526
527
|
operator: "AND",
|
|
527
528
|
left,
|
|
528
529
|
right,
|
|
530
|
+
implicit: true,
|
|
529
531
|
start: left.start,
|
|
530
532
|
end: right.end
|
|
531
533
|
};
|
|
@@ -2821,6 +2823,9 @@ function AutocompleteDropdown({
|
|
|
2821
2823
|
if (suggestion.type === "error") {
|
|
2822
2824
|
return /* @__PURE__ */ React.createElement("div", { key: i, className: cx("ei-dropdown-item", "ei-dropdown-item--error", classNames == null ? void 0 : classNames.dropdownItem), style: { ...itemStyle, cursor: "default", opacity: 0.8 } }, /* @__PURE__ */ React.createElement("span", { className: "ei-dropdown-item-label", style: { ...getDropdownItemLabelStyle(), color: mergedColors.error } }, suggestion.label || "Error loading suggestions"));
|
|
2823
2825
|
}
|
|
2826
|
+
if (suggestion.type === "noResults") {
|
|
2827
|
+
return /* @__PURE__ */ React.createElement("div", { key: i, className: cx("ei-dropdown-item", "ei-dropdown-item--no-results", classNames == null ? void 0 : classNames.dropdownItem), style: { ...itemStyle, cursor: "default", opacity: 0.7 } }, suggestion.customContent);
|
|
2828
|
+
}
|
|
2824
2829
|
if (suggestion.type === "loading") {
|
|
2825
2830
|
return /* @__PURE__ */ React.createElement("div", { key: i, className: cx("ei-dropdown-item", "ei-dropdown-item--loading", classNames == null ? void 0 : classNames.dropdownItem), style: { ...itemStyle, cursor: "default", opacity: 0.6, justifyContent: "center" } }, /* @__PURE__ */ React.createElement("span", { className: "ei-dropdown-item-label", style: { ...getDropdownItemLabelStyle(), fontStyle: "italic" } }, suggestion.label || "Searching..."), /* @__PURE__ */ React.createElement("span", { className: "ei-dropdown-spinner", style: { marginLeft: "6px", display: "inline-block", animation: "elastic-input-spin 1s linear infinite", width: "14px", height: "14px", border: "2px solid", borderColor: `${mergedColors.placeholder} transparent ${mergedColors.placeholder} transparent`, borderRadius: "50%" } }));
|
|
2826
2831
|
}
|
|
@@ -3626,6 +3631,155 @@ function dedup(ranges) {
|
|
|
3626
3631
|
}
|
|
3627
3632
|
return result;
|
|
3628
3633
|
}
|
|
3634
|
+
const DEFAULT_MAX_LINE_LENGTH = 60;
|
|
3635
|
+
const DEFAULT_INDENT = " ";
|
|
3636
|
+
function formatQuery(input, options) {
|
|
3637
|
+
const maxLineLength = (options == null ? void 0 : options.maxLineLength) ?? DEFAULT_MAX_LINE_LENGTH;
|
|
3638
|
+
const indent = (options == null ? void 0 : options.indent) ?? DEFAULT_INDENT;
|
|
3639
|
+
const whitespaceOperator = options == null ? void 0 : options.whitespaceOperator;
|
|
3640
|
+
let ast;
|
|
3641
|
+
if (typeof input === "string") {
|
|
3642
|
+
const tokens = new Lexer(input, { savedSearches: true, historySearch: true }).tokenize();
|
|
3643
|
+
const parser = new Parser(tokens);
|
|
3644
|
+
ast = parser.parse();
|
|
3645
|
+
} else {
|
|
3646
|
+
ast = input;
|
|
3647
|
+
}
|
|
3648
|
+
if (!ast) return typeof input === "string" ? input : "";
|
|
3649
|
+
return printNode(ast, 0, maxLineLength, indent, whitespaceOperator);
|
|
3650
|
+
}
|
|
3651
|
+
function resolveOperator(node, whitespaceOperator) {
|
|
3652
|
+
if (node.implicit) return whitespaceOperator ?? "";
|
|
3653
|
+
return node.operator;
|
|
3654
|
+
}
|
|
3655
|
+
function inline(node, whitespaceOperator) {
|
|
3656
|
+
switch (node.type) {
|
|
3657
|
+
case "FieldValue": {
|
|
3658
|
+
const val = node.quoted ? `"${node.value}"` : node.value;
|
|
3659
|
+
const op = node.operator === ":" ? ":" : `:${node.operator}`;
|
|
3660
|
+
let s = `${node.field}${op}${val}`;
|
|
3661
|
+
if (node.fuzzy != null) s += `~${node.fuzzy}`;
|
|
3662
|
+
if (node.proximity != null) s += `~${node.proximity}`;
|
|
3663
|
+
if (node.boost != null) s += `^${node.boost}`;
|
|
3664
|
+
return s;
|
|
3665
|
+
}
|
|
3666
|
+
case "BareTerm": {
|
|
3667
|
+
let s = node.quoted ? `"${node.value}"` : node.value;
|
|
3668
|
+
if (node.fuzzy != null) s += `~${node.fuzzy}`;
|
|
3669
|
+
if (node.proximity != null) s += `~${node.proximity}`;
|
|
3670
|
+
if (node.boost != null) s += `^${node.boost}`;
|
|
3671
|
+
return s;
|
|
3672
|
+
}
|
|
3673
|
+
case "Range": {
|
|
3674
|
+
const lb = node.lowerInclusive ? "[" : "{";
|
|
3675
|
+
const rb = node.upperInclusive ? "]" : "}";
|
|
3676
|
+
const lower = node.lowerQuoted ? `"${node.lower}"` : node.lower;
|
|
3677
|
+
const upper = node.upperQuoted ? `"${node.upper}"` : node.upper;
|
|
3678
|
+
const range = `${lb}${lower} TO ${upper}${rb}`;
|
|
3679
|
+
return node.field ? `${node.field}:${range}` : range;
|
|
3680
|
+
}
|
|
3681
|
+
case "Regex":
|
|
3682
|
+
return `/${node.pattern}/`;
|
|
3683
|
+
case "SavedSearch":
|
|
3684
|
+
return `#${node.name}`;
|
|
3685
|
+
case "HistoryRef":
|
|
3686
|
+
return `!${node.ref}`;
|
|
3687
|
+
case "Not":
|
|
3688
|
+
return `NOT ${inline(node.expression, whitespaceOperator)}`;
|
|
3689
|
+
case "Group": {
|
|
3690
|
+
let s = `(${inline(node.expression, whitespaceOperator)})`;
|
|
3691
|
+
if (node.boost != null) s += `^${node.boost}`;
|
|
3692
|
+
return s;
|
|
3693
|
+
}
|
|
3694
|
+
case "FieldGroup": {
|
|
3695
|
+
let s = `${node.field}:(${inline(node.expression, whitespaceOperator)})`;
|
|
3696
|
+
if (node.boost != null) s += `^${node.boost}`;
|
|
3697
|
+
return s;
|
|
3698
|
+
}
|
|
3699
|
+
case "BooleanExpr": {
|
|
3700
|
+
const op = resolveOperator(node, whitespaceOperator);
|
|
3701
|
+
const sep = op ? ` ${op} ` : " ";
|
|
3702
|
+
return `${inline(node.left, whitespaceOperator)}${sep}${inline(node.right, whitespaceOperator)}`;
|
|
3703
|
+
}
|
|
3704
|
+
case "Error":
|
|
3705
|
+
return node.value;
|
|
3706
|
+
}
|
|
3707
|
+
}
|
|
3708
|
+
function flattenChain(node) {
|
|
3709
|
+
const op = node.operator;
|
|
3710
|
+
const implicit = !!node.implicit;
|
|
3711
|
+
const operands = [];
|
|
3712
|
+
const collect = (n) => {
|
|
3713
|
+
if (n.type === "BooleanExpr" && n.operator === op && !!n.implicit === implicit) {
|
|
3714
|
+
collect(n.left);
|
|
3715
|
+
collect(n.right);
|
|
3716
|
+
} else {
|
|
3717
|
+
operands.push(n);
|
|
3718
|
+
}
|
|
3719
|
+
};
|
|
3720
|
+
collect(node);
|
|
3721
|
+
return { operator: op, implicit, operands };
|
|
3722
|
+
}
|
|
3723
|
+
function containsGroups(node) {
|
|
3724
|
+
if (node.type === "Group" || node.type === "FieldGroup") return true;
|
|
3725
|
+
if (node.type === "BooleanExpr") return containsGroups(node.left) || containsGroups(node.right);
|
|
3726
|
+
if (node.type === "Not") return containsGroups(node.expression);
|
|
3727
|
+
return false;
|
|
3728
|
+
}
|
|
3729
|
+
function shouldBreakGroup(expr, maxLineLength) {
|
|
3730
|
+
const inlined = inline(expr);
|
|
3731
|
+
if (inlined.length > maxLineLength) return true;
|
|
3732
|
+
if (containsGroups(expr)) return true;
|
|
3733
|
+
return false;
|
|
3734
|
+
}
|
|
3735
|
+
function printNode(node, depth, maxLineLength, indent, whitespaceOperator) {
|
|
3736
|
+
const pad = indent.repeat(depth);
|
|
3737
|
+
switch (node.type) {
|
|
3738
|
+
case "BooleanExpr": {
|
|
3739
|
+
const { operator, implicit, operands } = flattenChain(node);
|
|
3740
|
+
const displayOp = implicit ? whitespaceOperator ?? "" : operator;
|
|
3741
|
+
const sep = displayOp ? ` ${displayOp} ` : " ";
|
|
3742
|
+
const inlined = operands.map((o) => inline(o, whitespaceOperator)).join(sep);
|
|
3743
|
+
if (inlined.length <= maxLineLength) {
|
|
3744
|
+
return inlined;
|
|
3745
|
+
}
|
|
3746
|
+
const lines = operands.map((operand, i) => {
|
|
3747
|
+
const printed = printNode(operand, depth, maxLineLength, indent, whitespaceOperator);
|
|
3748
|
+
if (i === 0) return printed;
|
|
3749
|
+
return displayOp ? `${pad}${displayOp} ${printed}` : `${pad}${printed}`;
|
|
3750
|
+
});
|
|
3751
|
+
return lines.join("\n");
|
|
3752
|
+
}
|
|
3753
|
+
case "Group": {
|
|
3754
|
+
if (!shouldBreakGroup(node.expression, maxLineLength)) {
|
|
3755
|
+
let s2 = `(${inline(node.expression, whitespaceOperator)})`;
|
|
3756
|
+
if (node.boost != null) s2 += `^${node.boost}`;
|
|
3757
|
+
return s2;
|
|
3758
|
+
}
|
|
3759
|
+
const inner = printNode(node.expression, depth + 1, maxLineLength, indent, whitespaceOperator);
|
|
3760
|
+
let s = `(
|
|
3761
|
+
${indentLines(inner, depth + 1, indent)}
|
|
3762
|
+
${pad})`;
|
|
3763
|
+
if (node.boost != null) s += `^${node.boost}`;
|
|
3764
|
+
return s;
|
|
3765
|
+
}
|
|
3766
|
+
case "FieldGroup": {
|
|
3767
|
+
let s = `${node.field}:(${inline(node.expression, whitespaceOperator)})`;
|
|
3768
|
+
if (node.boost != null) s += `^${node.boost}`;
|
|
3769
|
+
return s;
|
|
3770
|
+
}
|
|
3771
|
+
case "Not":
|
|
3772
|
+
return `NOT ${printNode(node.expression, depth, maxLineLength, indent, whitespaceOperator)}`;
|
|
3773
|
+
default:
|
|
3774
|
+
return inline(node, whitespaceOperator);
|
|
3775
|
+
}
|
|
3776
|
+
}
|
|
3777
|
+
function indentLines(text, depth, indent) {
|
|
3778
|
+
const pad = indent.repeat(depth);
|
|
3779
|
+
return text.split("\n").map((line) => {
|
|
3780
|
+
return line.startsWith(pad) ? line : pad + line;
|
|
3781
|
+
}).join("\n");
|
|
3782
|
+
}
|
|
3629
3783
|
class UndoStack {
|
|
3630
3784
|
constructor(maxSize = 100) {
|
|
3631
3785
|
__publicField(this, "stack", []);
|
|
@@ -3806,12 +3960,14 @@ function ElasticInput(props) {
|
|
|
3806
3960
|
const renderHistoryItem = dropdownConfig == null ? void 0 : dropdownConfig.renderHistoryItem;
|
|
3807
3961
|
const renderSavedSearchItem = dropdownConfig == null ? void 0 : dropdownConfig.renderSavedSearchItem;
|
|
3808
3962
|
const renderDropdownHeader = dropdownConfig == null ? void 0 : dropdownConfig.renderHeader;
|
|
3963
|
+
const renderNoResults = dropdownConfig == null ? void 0 : dropdownConfig.renderNoResults;
|
|
3809
3964
|
const autoSelect = (dropdownConfig == null ? void 0 : dropdownConfig.autoSelect) ?? false;
|
|
3810
3965
|
const homeEndKeys = (dropdownConfig == null ? void 0 : dropdownConfig.homeEndKeys) ?? false;
|
|
3811
3966
|
const multiline = (featuresConfig == null ? void 0 : featuresConfig.multiline) !== false;
|
|
3812
3967
|
const smartSelectAll = (featuresConfig == null ? void 0 : featuresConfig.smartSelectAll) ?? false;
|
|
3813
3968
|
const expandSelection = (featuresConfig == null ? void 0 : featuresConfig.expandSelection) ?? false;
|
|
3814
3969
|
const wildcardWrap = (featuresConfig == null ? void 0 : featuresConfig.wildcardWrap) ?? false;
|
|
3970
|
+
const enableFormatQuery = (featuresConfig == null ? void 0 : featuresConfig.formatQuery) ?? false;
|
|
3815
3971
|
const lexerOptions = React.useMemo(() => ({
|
|
3816
3972
|
savedSearches: enableSavedSearches,
|
|
3817
3973
|
historySearch: enableHistorySearch
|
|
@@ -4044,6 +4200,24 @@ function ElasticInput(props) {
|
|
|
4044
4200
|
return { ...s, customContent: custom };
|
|
4045
4201
|
});
|
|
4046
4202
|
}, [renderFieldHint]);
|
|
4203
|
+
const tryShowNoResults = React.useCallback((context) => {
|
|
4204
|
+
if (!renderNoResults) return false;
|
|
4205
|
+
const content = renderNoResults({ cursorContext: context, partial: context.partial });
|
|
4206
|
+
if (content == null) return false;
|
|
4207
|
+
const noResultsSuggestion = {
|
|
4208
|
+
text: "",
|
|
4209
|
+
label: "",
|
|
4210
|
+
type: "noResults",
|
|
4211
|
+
customContent: content,
|
|
4212
|
+
replaceStart: 0,
|
|
4213
|
+
replaceEnd: 0
|
|
4214
|
+
};
|
|
4215
|
+
setSuggestions([noResultsSuggestion]);
|
|
4216
|
+
setSelectedSuggestionIndex(-1);
|
|
4217
|
+
setShowDatePicker(false);
|
|
4218
|
+
showDropdownAtPosition(32, 300);
|
|
4219
|
+
return true;
|
|
4220
|
+
}, [renderNoResults]);
|
|
4047
4221
|
const updateSuggestionsFromTokens = React.useCallback((toks, offset) => {
|
|
4048
4222
|
var _a, _b, _c;
|
|
4049
4223
|
const result = engineRef.current.getSuggestions(toks, offset);
|
|
@@ -4138,10 +4312,12 @@ function ElasticInput(props) {
|
|
|
4138
4312
|
setAutocompleteContext(contextType);
|
|
4139
4313
|
showDropdownAtPosition(newSuggestions.length * 32, 300);
|
|
4140
4314
|
} else {
|
|
4141
|
-
setShowDropdown(false);
|
|
4142
|
-
setShowDatePicker(false);
|
|
4143
|
-
setSuggestions([]);
|
|
4144
4315
|
setAutocompleteContext(contextType);
|
|
4316
|
+
if (!tryShowNoResults(result.context)) {
|
|
4317
|
+
setShowDropdown(false);
|
|
4318
|
+
setShowDatePicker(false);
|
|
4319
|
+
setSuggestions([]);
|
|
4320
|
+
}
|
|
4145
4321
|
}
|
|
4146
4322
|
} else {
|
|
4147
4323
|
const token = result.context.token;
|
|
@@ -4237,17 +4413,20 @@ function ElasticInput(props) {
|
|
|
4237
4413
|
showDropdownAtPosition(mapped.length * 32, 300);
|
|
4238
4414
|
} else {
|
|
4239
4415
|
const syncResult = engineRef.current.getSuggestions(stateRef.current.tokens, stateRef.current.cursorOffset);
|
|
4240
|
-
|
|
4241
|
-
syncResult.suggestions.filter((s) => s.type === "hint"),
|
|
4242
|
-
syncResult.context
|
|
4243
|
-
);
|
|
4244
|
-
if (hintSuggestions.length > 0) {
|
|
4245
|
-
setSuggestions(hintSuggestions);
|
|
4246
|
-
setSelectedSuggestionIndex(syncResult.context.partial || autoSelect ? 0 : -1);
|
|
4247
|
-
showDropdownAtPosition(hintSuggestions.length * 32, 300);
|
|
4416
|
+
if (partial && tryShowNoResults(syncResult.context)) {
|
|
4248
4417
|
} else {
|
|
4249
|
-
|
|
4250
|
-
|
|
4418
|
+
const hintSuggestions = applyFieldHint(
|
|
4419
|
+
syncResult.suggestions.filter((s) => s.type === "hint"),
|
|
4420
|
+
syncResult.context
|
|
4421
|
+
);
|
|
4422
|
+
if (hintSuggestions.length > 0) {
|
|
4423
|
+
setSuggestions(hintSuggestions);
|
|
4424
|
+
setSelectedSuggestionIndex(syncResult.context.partial || autoSelect ? 0 : -1);
|
|
4425
|
+
showDropdownAtPosition(hintSuggestions.length * 32, 300);
|
|
4426
|
+
} else if (!tryShowNoResults(syncResult.context)) {
|
|
4427
|
+
setShowDropdown(false);
|
|
4428
|
+
setSuggestions([]);
|
|
4429
|
+
}
|
|
4251
4430
|
}
|
|
4252
4431
|
}
|
|
4253
4432
|
} catch (e) {
|
|
@@ -4272,7 +4451,7 @@ function ElasticInput(props) {
|
|
|
4272
4451
|
}
|
|
4273
4452
|
}, debounceMs);
|
|
4274
4453
|
}
|
|
4275
|
-
}, [fetchSuggestionsProp, savedSearches, searchHistory, suggestDebounceMs, applyFieldHint, computeDropdownPosition, showDropdownAtPosition, dropdownAlignToInput, dropdownOpen, dropdownOpenIsCallback, dropdownMode, showOperators, effectiveMaxSuggestions, loadingDelay, autoSelect]);
|
|
4454
|
+
}, [fetchSuggestionsProp, savedSearches, searchHistory, suggestDebounceMs, applyFieldHint, computeDropdownPosition, showDropdownAtPosition, dropdownAlignToInput, dropdownOpen, dropdownOpenIsCallback, dropdownMode, showOperators, effectiveMaxSuggestions, loadingDelay, autoSelect, tryShowNoResults]);
|
|
4276
4455
|
updateSuggestionsRef.current = updateSuggestionsFromTokens;
|
|
4277
4456
|
const closeDropdown = React.useCallback(() => {
|
|
4278
4457
|
var _a;
|
|
@@ -4636,6 +4815,14 @@ function ElasticInput(props) {
|
|
|
4636
4815
|
if (onKeyDownProp) onKeyDownProp(e);
|
|
4637
4816
|
if (e.defaultPrevented) return;
|
|
4638
4817
|
const s = stateRef.current;
|
|
4818
|
+
if (enableFormatQuery && e.key === "F" && e.altKey && e.shiftKey && !e.ctrlKey && !e.metaKey) {
|
|
4819
|
+
e.preventDefault();
|
|
4820
|
+
const formatted = formatQuery(currentValueRef.current);
|
|
4821
|
+
if (formatted !== currentValueRef.current) {
|
|
4822
|
+
applyNewValue(formatted, formatted.length);
|
|
4823
|
+
}
|
|
4824
|
+
return;
|
|
4825
|
+
}
|
|
4639
4826
|
if (editorRef.current && editorRef.current.childNodes.length > 40) {
|
|
4640
4827
|
const sel = window.getSelection();
|
|
4641
4828
|
const hasSelection = sel != null && !sel.isCollapsed;
|
|
@@ -4932,7 +5119,7 @@ function ElasticInput(props) {
|
|
|
4932
5119
|
if (onSearch) onSearch(currentValueRef.current, s.ast);
|
|
4933
5120
|
return;
|
|
4934
5121
|
}
|
|
4935
|
-
}, [onSearch, closeDropdown, acceptSuggestion, applyNewValue, restoreUndoEntry, multiline, dropdownOpenIsCallback, dropdownMode, updateSuggestionsFromTokens, onKeyDownProp, onTabProp, smartSelectAll, expandSelection, homeEndKeys, getDropdownPageSize]);
|
|
5122
|
+
}, [onSearch, closeDropdown, acceptSuggestion, applyNewValue, restoreUndoEntry, multiline, dropdownOpenIsCallback, dropdownMode, updateSuggestionsFromTokens, onKeyDownProp, onTabProp, smartSelectAll, expandSelection, homeEndKeys, getDropdownPageSize, enableFormatQuery]);
|
|
4936
5123
|
const handleKeyUp = React.useCallback((e) => {
|
|
4937
5124
|
if (keyConsumedByDropdownRef.current) {
|
|
4938
5125
|
keyConsumedByDropdownRef.current = false;
|
|
@@ -5175,143 +5362,6 @@ function walk(node, groupField, out) {
|
|
|
5175
5362
|
break;
|
|
5176
5363
|
}
|
|
5177
5364
|
}
|
|
5178
|
-
const DEFAULT_MAX_LINE_LENGTH = 60;
|
|
5179
|
-
const DEFAULT_INDENT = " ";
|
|
5180
|
-
function formatQuery(input, options) {
|
|
5181
|
-
const maxLineLength = (options == null ? void 0 : options.maxLineLength) ?? DEFAULT_MAX_LINE_LENGTH;
|
|
5182
|
-
const indent = (options == null ? void 0 : options.indent) ?? DEFAULT_INDENT;
|
|
5183
|
-
let ast;
|
|
5184
|
-
if (typeof input === "string") {
|
|
5185
|
-
const tokens = new Lexer(input, { savedSearches: true, historySearch: true }).tokenize();
|
|
5186
|
-
const parser = new Parser(tokens);
|
|
5187
|
-
ast = parser.parse();
|
|
5188
|
-
} else {
|
|
5189
|
-
ast = input;
|
|
5190
|
-
}
|
|
5191
|
-
if (!ast) return typeof input === "string" ? input : "";
|
|
5192
|
-
return printNode(ast, 0, maxLineLength, indent);
|
|
5193
|
-
}
|
|
5194
|
-
function inline(node) {
|
|
5195
|
-
switch (node.type) {
|
|
5196
|
-
case "FieldValue": {
|
|
5197
|
-
const val = node.quoted ? `"${node.value}"` : node.value;
|
|
5198
|
-
const op = node.operator === ":" ? ":" : `:${node.operator}`;
|
|
5199
|
-
let s = `${node.field}${op}${val}`;
|
|
5200
|
-
if (node.fuzzy != null) s += `~${node.fuzzy}`;
|
|
5201
|
-
if (node.proximity != null) s += `~${node.proximity}`;
|
|
5202
|
-
if (node.boost != null) s += `^${node.boost}`;
|
|
5203
|
-
return s;
|
|
5204
|
-
}
|
|
5205
|
-
case "BareTerm": {
|
|
5206
|
-
let s = node.quoted ? `"${node.value}"` : node.value;
|
|
5207
|
-
if (node.fuzzy != null) s += `~${node.fuzzy}`;
|
|
5208
|
-
if (node.proximity != null) s += `~${node.proximity}`;
|
|
5209
|
-
if (node.boost != null) s += `^${node.boost}`;
|
|
5210
|
-
return s;
|
|
5211
|
-
}
|
|
5212
|
-
case "Range": {
|
|
5213
|
-
const lb = node.lowerInclusive ? "[" : "{";
|
|
5214
|
-
const rb = node.upperInclusive ? "]" : "}";
|
|
5215
|
-
const lower = node.lowerQuoted ? `"${node.lower}"` : node.lower;
|
|
5216
|
-
const upper = node.upperQuoted ? `"${node.upper}"` : node.upper;
|
|
5217
|
-
const range = `${lb}${lower} TO ${upper}${rb}`;
|
|
5218
|
-
return node.field ? `${node.field}:${range}` : range;
|
|
5219
|
-
}
|
|
5220
|
-
case "Regex":
|
|
5221
|
-
return `/${node.pattern}/`;
|
|
5222
|
-
case "SavedSearch":
|
|
5223
|
-
return `#${node.name}`;
|
|
5224
|
-
case "HistoryRef":
|
|
5225
|
-
return `!${node.ref}`;
|
|
5226
|
-
case "Not":
|
|
5227
|
-
return `NOT ${inline(node.expression)}`;
|
|
5228
|
-
case "Group": {
|
|
5229
|
-
let s = `(${inline(node.expression)})`;
|
|
5230
|
-
if (node.boost != null) s += `^${node.boost}`;
|
|
5231
|
-
return s;
|
|
5232
|
-
}
|
|
5233
|
-
case "FieldGroup": {
|
|
5234
|
-
let s = `${node.field}:(${inline(node.expression)})`;
|
|
5235
|
-
if (node.boost != null) s += `^${node.boost}`;
|
|
5236
|
-
return s;
|
|
5237
|
-
}
|
|
5238
|
-
case "BooleanExpr":
|
|
5239
|
-
return `${inline(node.left)} ${node.operator} ${inline(node.right)}`;
|
|
5240
|
-
case "Error":
|
|
5241
|
-
return node.value;
|
|
5242
|
-
}
|
|
5243
|
-
}
|
|
5244
|
-
function flattenChain(node) {
|
|
5245
|
-
const op = node.operator;
|
|
5246
|
-
const operands = [];
|
|
5247
|
-
const collect = (n) => {
|
|
5248
|
-
if (n.type === "BooleanExpr" && n.operator === op) {
|
|
5249
|
-
collect(n.left);
|
|
5250
|
-
collect(n.right);
|
|
5251
|
-
} else {
|
|
5252
|
-
operands.push(n);
|
|
5253
|
-
}
|
|
5254
|
-
};
|
|
5255
|
-
collect(node);
|
|
5256
|
-
return { operator: op, operands };
|
|
5257
|
-
}
|
|
5258
|
-
function containsGroups(node) {
|
|
5259
|
-
if (node.type === "Group" || node.type === "FieldGroup") return true;
|
|
5260
|
-
if (node.type === "BooleanExpr") return containsGroups(node.left) || containsGroups(node.right);
|
|
5261
|
-
if (node.type === "Not") return containsGroups(node.expression);
|
|
5262
|
-
return false;
|
|
5263
|
-
}
|
|
5264
|
-
function shouldBreakGroup(expr, maxLineLength) {
|
|
5265
|
-
const inlined = inline(expr);
|
|
5266
|
-
if (inlined.length > maxLineLength) return true;
|
|
5267
|
-
if (containsGroups(expr)) return true;
|
|
5268
|
-
return false;
|
|
5269
|
-
}
|
|
5270
|
-
function printNode(node, depth, maxLineLength, indent) {
|
|
5271
|
-
const pad = indent.repeat(depth);
|
|
5272
|
-
switch (node.type) {
|
|
5273
|
-
case "BooleanExpr": {
|
|
5274
|
-
const { operator, operands } = flattenChain(node);
|
|
5275
|
-
const inlined = operands.map((o) => inline(o)).join(` ${operator} `);
|
|
5276
|
-
if (inlined.length <= maxLineLength) {
|
|
5277
|
-
return inlined;
|
|
5278
|
-
}
|
|
5279
|
-
const lines = operands.map((operand, i) => {
|
|
5280
|
-
const printed = printNode(operand, depth, maxLineLength, indent);
|
|
5281
|
-
return i === 0 ? printed : `${pad}${operator} ${printed}`;
|
|
5282
|
-
});
|
|
5283
|
-
return lines.join("\n");
|
|
5284
|
-
}
|
|
5285
|
-
case "Group": {
|
|
5286
|
-
if (!shouldBreakGroup(node.expression, maxLineLength)) {
|
|
5287
|
-
let s2 = `(${inline(node.expression)})`;
|
|
5288
|
-
if (node.boost != null) s2 += `^${node.boost}`;
|
|
5289
|
-
return s2;
|
|
5290
|
-
}
|
|
5291
|
-
const inner = printNode(node.expression, depth + 1, maxLineLength, indent);
|
|
5292
|
-
let s = `(
|
|
5293
|
-
${indentLines(inner, depth + 1, indent)}
|
|
5294
|
-
${pad})`;
|
|
5295
|
-
if (node.boost != null) s += `^${node.boost}`;
|
|
5296
|
-
return s;
|
|
5297
|
-
}
|
|
5298
|
-
case "FieldGroup": {
|
|
5299
|
-
let s = `${node.field}:(${inline(node.expression)})`;
|
|
5300
|
-
if (node.boost != null) s += `^${node.boost}`;
|
|
5301
|
-
return s;
|
|
5302
|
-
}
|
|
5303
|
-
case "Not":
|
|
5304
|
-
return `NOT ${printNode(node.expression, depth, maxLineLength, indent)}`;
|
|
5305
|
-
default:
|
|
5306
|
-
return inline(node);
|
|
5307
|
-
}
|
|
5308
|
-
}
|
|
5309
|
-
function indentLines(text, depth, indent) {
|
|
5310
|
-
const pad = indent.repeat(depth);
|
|
5311
|
-
return text.split("\n").map((line) => {
|
|
5312
|
-
return line.startsWith(pad) ? line : pad + line;
|
|
5313
|
-
}).join("\n");
|
|
5314
|
-
}
|
|
5315
5365
|
export {
|
|
5316
5366
|
AutocompleteEngine,
|
|
5317
5367
|
DARK_COLORS,
|
package/dist/parser/ast.d.ts
CHANGED
package/dist/types.d.ts
CHANGED
|
@@ -273,6 +273,13 @@ export interface DropdownConfig {
|
|
|
273
273
|
* dropdown is open and an item is already selected. When no item is selected
|
|
274
274
|
* (index = -1), the keys pass through for normal text cursor movement. @default false */
|
|
275
275
|
homeEndKeys?: boolean;
|
|
276
|
+
/** Called when the engine returns zero suggestions. Return a React element to display
|
|
277
|
+
* in the dropdown (e.g. "No results for …"), or null/undefined to hide the dropdown.
|
|
278
|
+
* Not called during async loading (the spinner handles that). */
|
|
279
|
+
renderNoResults?: (context: {
|
|
280
|
+
cursorContext: CursorContext;
|
|
281
|
+
partial: string;
|
|
282
|
+
}) => React.ReactNode | null | undefined;
|
|
276
283
|
}
|
|
277
284
|
/**
|
|
278
285
|
* Feature toggles for optional editing behaviors. All default to false except `multiline`.
|
|
@@ -286,6 +293,8 @@ export interface FeaturesConfig {
|
|
|
286
293
|
expandSelection?: boolean;
|
|
287
294
|
/** Pressing `*` with a single value token selected wraps it in wildcards. @default false */
|
|
288
295
|
wildcardWrap?: boolean;
|
|
296
|
+
/** Enable Alt+Shift+F to pretty-print the query in-place using `formatQuery`. @default false */
|
|
297
|
+
formatQuery?: boolean;
|
|
289
298
|
/** Enable `#name` saved-search syntax and autocomplete. When false, `#` is a regular character. @default false */
|
|
290
299
|
savedSearches?: boolean;
|
|
291
300
|
/** Enable `!query` history-search syntax and autocomplete. When false, `!` is a regular character. @default false */
|
|
@@ -6,6 +6,10 @@ export interface FormatQueryOptions {
|
|
|
6
6
|
maxLineLength?: number;
|
|
7
7
|
/** Indent string for each nesting level. @default ' ' (2 spaces) */
|
|
8
8
|
indent?: string;
|
|
9
|
+
/** When set, replaces implicit AND (whitespace between terms) with this operator
|
|
10
|
+
* string in the output. By default, implicit AND is preserved as whitespace.
|
|
11
|
+
* @example 'AND' — turns `status:active name:john` into `status:active AND name:john` */
|
|
12
|
+
whitespaceOperator?: string;
|
|
9
13
|
}
|
|
10
14
|
/**
|
|
11
15
|
* Pretty-print an Elasticsearch query string.
|