dequel-editor 0.6.1 → 0.8.5

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,172 +1,231 @@
1
- import { syntaxTree as b, LRLanguage as P, indentNodeProp as v, foldNodeProp as I, foldInside as N, HighlightStyle as O, LanguageSupport as V, syntaxHighlighting as w } from "@codemirror/language";
2
- import { Tag as s, tags as o, styleTags as A } from "@lezer/highlight";
3
- import { buildParser as E } from "@lezer/generator";
4
- import { Facet as L, StateEffect as D, StateField as Q } from "@codemirror/state";
5
- const T = E(`
1
+ import { syntaxTree as N, LRLanguage as I, indentNodeProp as v, foldNodeProp as F, foldInside as P, HighlightStyle as A, LanguageSupport as L, syntaxHighlighting as k } from "@codemirror/language";
2
+ import { tags as i, Tag as f, styleTags as E } from "@lezer/highlight";
3
+ import { buildParser as W } from "@lezer/generator";
4
+ import { Facet as B, StateEffect as j, StateField as w } from "@codemirror/state";
5
+ const Q = W(`
6
6
  @top QueryList { Query }
7
- @skip { "
7
+ @skip { " " | " " | "
8
8
  " | Comment }
9
+ @detectDelim
9
10
 
10
11
  Query {
11
- anyCondition ((" " | " ")+ anyCondition)*
12
+ queryElement+
12
13
  }
13
14
 
14
- anyCondition { Condition | ExcludeCondition | IgnoredCondition }
15
- Condition { Field ":" Predicate }
16
- ExcludeCondition { "-" Field ":" Predicate }
17
- IgnoredCondition { "!" Field ":" Predicate }
15
+ queryElement {
16
+ logicalOp | Condition | ExcludeCondition | Group | ObjectBlock
17
+ }
18
+
19
+ logicalOp { And | Or }
20
+
21
+ And { "&&" | andWord }
22
+ Or { "||" | orWord }
23
+ andWord[@name=And] { @specialize<Identifier, "AND"> | @specialize<Identifier, "and"> }
24
+ orWord[@name=Or] { @specialize<Identifier, "OR"> | @specialize<Identifier, "or"> }
25
+
26
+ Group { "(" Query ")" }
27
+
28
+ ObjectBlock { Identifier "{" Query "}" }
29
+
30
+ Condition { field ":" Predicate }
31
+ ExcludeCondition { "-" field ":" Predicate }
18
32
 
19
- Predicate { Regex | Value | Command }
20
- Command { Identifier "(" Argument ("," Argument)* ")" }
21
- Argument { Identifier | Number | String }
22
- Value { Identifier | Number | String }
33
+ field[@name=Field] { Identifier }
34
+
35
+ Predicate {
36
+ NegatedPredicate |
37
+ RangePredicate |
38
+ ComparisonPredicate |
39
+ BracketPredicate |
40
+ ShorthandPredicate |
41
+ Command |
42
+ SimplePredicate
43
+ }
44
+
45
+ NegatedPredicate { Not (ComparisonPredicate | ShorthandPredicate | SimplePredicate) }
46
+ ComparisonPredicate { CompareOp Value }
47
+ RangePredicate { Value RangeSep Value }
48
+ BracketPredicate { "[" Value ("," Value)* "]" }
49
+ ShorthandPredicate { (Contains | StartsWith | EndsWith) Value }
50
+ SimplePredicate { Value }
51
+
52
+ Command { Identifier ~ambig "(" ArgList? ")" }
53
+ ArgList { Argument ("," Argument)* }
54
+ Argument { Identifier | Number | String | DateLiteral | DynamicValue }
55
+
56
+ Value { DynamicValue | DateLiteral | Number | String | Identifier ~ambig }
23
57
 
24
58
  @tokens {
59
+ CompareOp { ">=" | "<=" | ">" | "<" }
60
+ RangeSep { ".." }
61
+
62
+ Contains { "*" }
63
+ StartsWith { "^" }
64
+ EndsWith { "$" }
65
+ Not { "!" }
66
+
67
+ DynamicValue { "@" ("today" | "tomorrow" | "yesterday" | "this-year" | "this-month-start" | "this-month-end" | "this-month") }
68
+ DateLiteral { std.digit std.digit std.digit std.digit "-" std.digit std.digit ("-" std.digit std.digit)? }
69
+
70
+ Number { "-"? std.digit+ ("." std.digit+)? }
71
+ String { '"' !["]* '"' }
72
+
25
73
  ":"
26
- Field { Identifier }
27
- Function { Identifier }
74
+
75
+ @precedence { DynamicValue, DateLiteral, Number, Identifier }
76
+
28
77
  Identifier { $[a-zA-Z_]+ ("." $[a-zA-Z_]+)* }
29
- Number { std.digit+ }
30
- String { '"' !["]* '"' }
31
- Regex { RegexContent (RegexFlag)* }
32
- RegexFlag { std.asciiLetter+ }
33
- RegexContent { "/" (![/] | "\\/")* "/" }
78
+
34
79
  Comment { "#" (![
35
80
  ])* }
81
+
82
+ "(" ")" "{" "}" "[" "]" "," "-" "&&" "||"
36
83
  }
37
84
  `), t = {
38
- Condition: s.define(o.keyword),
39
- Field: s.define(o.propertyName),
40
- Command: o.function(o.variableName),
41
- Predicate: s.define(o.variableName),
42
- Value: s.define(o.attributeValue),
43
- Operator: s.define(o.operator),
44
- Regex: s.define(o.regexp),
45
- RegexContent: s.define(o.regexp)
46
- }, q = L.define(), k = (e, i) => {
47
- for (; i.parent; ) {
48
- if (i.parent.name === e)
49
- return i.parent;
50
- i = i.parent;
85
+ Condition: f.define(i.keyword),
86
+ Field: f.define(i.propertyName),
87
+ Command: i.function(i.variableName),
88
+ Predicate: f.define(i.variableName),
89
+ Value: f.define(i.attributeValue),
90
+ Operator: f.define(i.operator),
91
+ DynamicValue: i.special(i.variableName)
92
+ }, R = B.define(), q = (e, n) => {
93
+ for (; n.parent; ) {
94
+ if (n.parent.name === e)
95
+ return n.parent;
96
+ n = n.parent;
51
97
  }
52
98
  return null;
53
99
  };
54
- async function B(e, i, n) {
55
- let d = i;
56
- for (const c of e) {
57
- const a = d.fields.find((l) => l.label === c);
58
- if (a?.type !== "relationship" || !a.target)
100
+ async function z(e, n, a) {
101
+ let o = n;
102
+ for (const d of e) {
103
+ const r = o.fields[d];
104
+ if (r?.type !== "relationship" || !r.schema)
59
105
  return null;
60
- d = await n.get(a.target);
106
+ o = await a.get(r.schema);
61
107
  }
62
- return d;
108
+ return o;
63
109
  }
64
- const H = D.define(), $ = Q.define({
65
- create: () => ({ fields: [] }),
66
- update: (e, i) => {
67
- for (const n of i.effects)
68
- if (n.is(H))
69
- return n.value;
110
+ const T = j.define(), $ = w.define({
111
+ create: () => ({ fields: {} }),
112
+ update: (e, n) => {
113
+ for (const a of n.effects)
114
+ if (a.is(T))
115
+ return a.value;
70
116
  return e;
71
117
  }
72
- }), z = S.data.of({
118
+ }), H = b.data.of({
73
119
  autocomplete: async (e) => {
74
- const n = b(e.state).resolveInner(e.pos, -1), d = e.state.sliceDoc(n.from, e.pos), c = /[\w.]*$/.exec(d);
75
- if (!c && !e.explicit) return null;
76
- const a = c ? n.from + c.index : e.pos, l = e.state.field($, !1), C = e.state.facet(q)[0]?.schemaCache, h = k("Condition", n), g = h?.getChild("Field"), y = h?.getChild(":");
77
- if (g && y && e.pos > y.to) {
78
- const m = e.state.doc.sliceString(g.from, g.to), f = l?.values?.[m];
79
- if (f?.length) {
80
- const p = f.map((r) => ({
81
- label: r,
120
+ const a = N(e.state).resolveInner(e.pos, -1), o = e.state.sliceDoc(a.from, e.pos), d = /[\w.]*$/.exec(o);
121
+ if (!d && !e.explicit) return null;
122
+ const r = d ? a.from + d.index : e.pos, s = e.state.field($, !1), h = e.state.facet(R)[0]?.schemaCache, y = q("Condition", a), u = y?.getChild("Field"), C = y?.getChild(":");
123
+ if (u && C && e.pos > C.to) {
124
+ const p = e.state.doc.sliceString(u.from, u.to), l = s?.fields[p]?.values;
125
+ if (l?.length) {
126
+ const g = l.map((c) => ({
127
+ label: c,
82
128
  type: "value"
83
129
  }));
84
130
  return {
85
- from: a,
86
- options: p
131
+ from: r,
132
+ options: g
87
133
  };
88
134
  }
89
135
  }
90
- if (n.name === "Field" && l?.fields?.length) {
91
- const m = e.state.doc.sliceString(n.from, e.pos), f = m.split(".");
92
- if (f.length > 1 && C) {
93
- const r = f.slice(0, -1), F = n.from + m.lastIndexOf(".") + 1, x = await B(
94
- r,
95
- l,
96
- C
136
+ if (a.name === "Field" && s && Object.keys(s.fields).length) {
137
+ const p = e.state.doc.sliceString(a.from, e.pos), l = p.split(".");
138
+ if (l.length > 1 && h) {
139
+ const c = l.slice(0, -1), m = a.from + p.lastIndexOf(".") + 1, O = await z(
140
+ c,
141
+ s,
142
+ h
97
143
  );
98
- if (x) {
99
- const R = x.fields.map((u) => ({
100
- label: u.label,
101
- type: u.type,
102
- info: u.info
144
+ if (O) {
145
+ const V = Object.entries(O.fields).map(([D, S]) => ({
146
+ label: D,
147
+ type: S.type,
148
+ info: S.info
103
149
  }));
104
150
  return {
105
- from: F,
106
- options: R
151
+ from: m,
152
+ options: V
107
153
  };
108
154
  }
109
155
  return {
110
- from: F,
156
+ from: m,
111
157
  options: []
112
158
  };
113
159
  }
114
- const p = l.fields.map((r) => ({
115
- label: r.label,
116
- type: r.type,
117
- info: r.info
160
+ const g = Object.entries(s.fields).map(([c, m]) => ({
161
+ label: c,
162
+ type: m.type,
163
+ info: m.info
118
164
  }));
119
165
  return {
120
- from: a,
121
- options: p
166
+ from: r,
167
+ options: g
122
168
  };
123
169
  }
124
- return n.name === "Field" ? {
125
- from: a,
170
+ return a.name === "Field" ? {
171
+ from: r,
126
172
  options: []
127
173
  } : null;
128
174
  }
129
- }), Z = O.define([
175
+ }), G = A.define([
130
176
  { tag: t.Condition, class: "cnd" },
131
177
  { tag: t.Operator, class: "cnd-op" },
132
178
  { tag: t.Value, class: "cnd-val" },
133
179
  { tag: t.Command, class: "cnd-val cnd-cmd" },
134
180
  { tag: t.Field, class: "cnd-fld" },
135
- { tag: t.Regex, class: "cnd-val cnd-rgx cnd-rgx-f" },
136
- { tag: o.string, class: "string" },
137
- { tag: o.lineComment, class: "comment" }
138
- ]), _ = T.configure({
181
+ { tag: t.DynamicValue, class: "cnd-val cnd-dynamic" },
182
+ { tag: i.string, class: "string" },
183
+ { tag: i.number, class: "cnd-val" },
184
+ { tag: i.lineComment, class: "comment" }
185
+ ]), Z = Q.configure({
139
186
  props: [
140
- A({
187
+ E({
188
+ // Condition structure
141
189
  "Condition/:": t.Operator,
142
190
  "ExcludeCondition/:": t.Operator,
143
191
  "Condition/Field/...": t.Field,
144
- "Condition/Predicate/RegexContent!": t.RegexContent,
192
+ Field: t.Field,
193
+ // Command and values
145
194
  "Command!": t.Command,
195
+ "Value!": t.Value,
146
196
  "Separator!": t.Operator,
147
- Regex: t.Regex,
148
- Comment: o.lineComment,
149
- Field: t.Field,
150
- "Value!": t.Value
197
+ // Logical operators
198
+ "And Or": t.Operator,
199
+ // Comparison and range operators
200
+ "CompareOp Contains StartsWith EndsWith Not RangeSep": t.Operator,
201
+ // Special value types
202
+ DynamicValue: t.DynamicValue,
203
+ DateLiteral: i.number,
204
+ // Object blocks
205
+ "ObjectBlock/Identifier": t.Field,
206
+ // Comments
207
+ Comment: i.lineComment
151
208
  }),
152
209
  v.add({
153
- Query: (e) => e.column(e.node.from) + e.unit
210
+ Query: (e) => e.column(e.node.from) + e.unit,
211
+ ObjectBlock: (e) => e.column(e.node.from) + e.unit
154
212
  }),
155
- I.add({
156
- Query: N
213
+ F.add({
214
+ Query: P,
215
+ ObjectBlock: P
157
216
  })
158
217
  ]
159
- }), S = P.define({
160
- parser: _,
218
+ }), b = I.define({
219
+ parser: Z,
161
220
  languageData: {}
162
221
  });
163
- function K() {
164
- return new V(S, [
165
- w(Z),
166
- z
222
+ function X() {
223
+ return new L(b, [
224
+ k(G),
225
+ H
167
226
  ]);
168
227
  }
169
228
  export {
170
- K as DequelLang,
171
- S as dequelParser
229
+ X as DequelLang,
230
+ b as dequelParser
172
231
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dequel-editor",
3
- "version": "0.6.1",
3
+ "version": "0.8.5",
4
4
  "description": "CodeMirror-based editor for Dequel query language",
5
5
  "type": "module",
6
6
  "main": "./dist/dequel-editor.js",
@@ -57,6 +57,7 @@
57
57
  "eslint": "^8.57.1",
58
58
  "gettext-parser": "^8.0.0",
59
59
  "http-proxy-middleware": "^3.0.5",
60
+ "jsdom": "^28.1.0",
60
61
  "tailwindcss": "^4.1.18",
61
62
  "tsx": "^4.19.4",
62
63
  "typescript": "^5.9.3",