Skip to content

Commit 04be944

Browse files
committed
feat: support method suggestion
feat: add getMethodInfo() in stats api
1 parent 2d912d0 commit 04be944

File tree

7 files changed

+337
-211
lines changed

7 files changed

+337
-211
lines changed

src/index.js

+20-6
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import walk from './lang/walk.js';
55
import stringify from './lang/stringify.js';
66
import compile from './lang/compile.js';
77
import buildin from './lang/compile-buildin.js';
8-
import methods from './methods.js';
8+
import methods, {methodsInfo, makeMethodInfoFacade, createMethodInfoArg, createMethodInfo} from './methods.js';
99
import assertions from './assertions.js';
1010
import createStatApi from './stat.js';
1111

@@ -90,10 +90,12 @@ function createQuery(source, options) {
9090
const statMode = Boolean(options.stat);
9191
const tolerantMode = Boolean(options.tolerant);
9292
const localMethods = options.methods ? { ...methods, ...options.methods } : methods;
93-
const localAssetions = options.assertions ? { ...assertions, ...options.assertions } : assertions;
93+
const localAssertions = options.assertions ? { ...assertions, ...options.assertions } : assertions;
9494
const cache = statMode
9595
? (tolerantMode ? cacheTollerantStat : cacheStrictStat)
9696
: (tolerantMode ? cacheTollerant : cacheStrict);
97+
const methodInfoFacade = makeMethodInfoFacade(methodsInfo, options?.methodsInfo);
98+
9799
let fn;
98100

99101
source = String(source);
@@ -105,20 +107,25 @@ function createQuery(source, options) {
105107
cache.set(source, fn);
106108
}
107109

108-
fn = fn(buildin, localMethods, localAssetions);
110+
fn = fn(buildin, localMethods, localAssertions);
109111

110112
return statMode
111-
? Object.assign((data, context) => createStatApi(source, fn(data, context)), { query: fn })
113+
? Object.assign((data, context) => createStatApi(source, fn(data, context), {
114+
customMethods: {...options.methods},
115+
builtinMethods: {...methods},
116+
getMethodInfo: methodInfoFacade.get
117+
}), {query: fn, setMethodInfo: methodInfoFacade.set})
112118
: fn;
113119
}
114120

115-
function setup(customMethods, customAssertions) {
121+
function setup(customMethods, customAssertions, {methodsInfo} = {}) {
116122
const cacheStrict = new Map();
117123
const cacheStrictStat = new Map();
118124
const cacheTollerant = new Map();
119125
const cacheTollerantStat = new Map();
120126
const localMethods = { ...methods };
121127
const localAssetions = { ...assertions };
128+
const methodInfoFacade = makeMethodInfoFacade(methodsInfo);
122129

123130
for (const [name, fn] of Object.entries(customMethods || {})) {
124131
if (typeof fn === 'string') {
@@ -169,7 +176,11 @@ function setup(customMethods, customAssertions) {
169176
} else {
170177
const perform = compileFunction(source, statMode, tolerantMode, options.debug)(buildin, localMethods, localAssetions);
171178
fn = statMode
172-
? Object.assign((data, context) => createStatApi(source, perform(data, context)), { query: perform })
179+
? Object.assign((data, context) => createStatApi(source, perform(data, context), {
180+
customMethods: {...customMethods},
181+
builtinMethods: {...methods},
182+
getMethodInfo: methodInfoFacade.get
183+
}), {query: perform, setMethodInfo: methodInfoFacade.set})
173184
: perform;
174185
cache.set(source, fn);
175186
}
@@ -182,6 +193,9 @@ export default Object.assign(createQuery, {
182193
version,
183194
buildin,
184195
methods,
196+
methodsInfo,
197+
createMethodInfo,
198+
createMethodInfoArg,
185199
assertions,
186200
setup,
187201
syntax: {

src/lang/compile.js

+5
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ export default function compile(ast, tolerant = false, suggestions = null) {
4444

4545
normalizedSuggestRanges.push(range);
4646

47+
if (type === 'path') {
48+
normalizedSuggestRanges.push([start, end, JSON.stringify('customMethods')]);
49+
normalizedSuggestRanges.push([start, end, JSON.stringify('builtinMethods')]);
50+
}
51+
4752
return spName;
4853
}
4954

src/methods.js

+37
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,43 @@ function stableSort(array, cmp) {
7272
.map(item => item.value);
7373
}
7474

75+
export function createMethodInfo(args = [], returns = 'any', options = {}) {
76+
return {
77+
args,
78+
returns,
79+
description: options.description ?? ''
80+
};
81+
}
82+
83+
export function createMethodInfoArg(name, type = 'any', options = {}) {
84+
return {
85+
name,
86+
type,
87+
description: options.description ?? '',
88+
options: {
89+
isOptional: options.isOptional || options.defaultValue !== undefined,
90+
defaultValue: options.defaultValue
91+
}
92+
};
93+
}
94+
95+
export function makeMethodInfoFacade(...methodsInfoList) {
96+
const map = new Map(Object.entries(methodsInfoList.reduce((all, item) => Object.assign(all, item), {})));
97+
98+
function set(name, info) {
99+
map.set(name, info);
100+
}
101+
102+
function get(name) {
103+
return map.get(name) ?? null;
104+
}
105+
106+
return {map, set, get, create: createMethodInfo, createArg: createMethodInfoArg};
107+
}
108+
109+
export const methodsInfo = Object.freeze({
110+
bool: createMethodInfo([createMethodInfoArg('arg')], 'bool')
111+
});
75112

76113
export default Object.freeze({
77114
bool: buildin.bool,

src/stat.js

+17-3
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ const contextToType = {
99
'in-value': 'value',
1010
'value-subset': 'value',
1111
'var': 'variable',
12-
'assertion': 'assertion'
12+
'assertion': 'assertion',
13+
'customMethods': 'method',
14+
'builtinMethods': 'method'
1315
};
1416

1517
function addObjectKeysToSet(object, set) {
@@ -174,7 +176,7 @@ function defaultFilterFactory(pattern) {
174176
return value => (typeof value === 'string' ? value : String(value)).toLowerCase().indexOf(pattern) !== -1;
175177
}
176178

177-
export default (source, { value, stats, assertions }) => ({
179+
export default (source, { value, stats, assertions }, {customMethods, builtinMethods, getMethodInfo}) => ({
178180
get value() {
179181
return value;
180182
},
@@ -222,6 +224,7 @@ export default (source, { value, stats, assertions }) => ({
222224
}
223225

224226
const { suggestions } = typeSuggestions.get(type);
227+
const methods = new Set();
225228

226229
switch (context) {
227230
case 'assertion':
@@ -231,10 +234,20 @@ export default (source, { value, stats, assertions }) => ({
231234
}
232235
}
233236
break;
237+
case 'customMethods':
238+
case 'builtinMethods':
239+
for (const method of Object.keys(context === 'customMethods' ? customMethods : builtinMethods)) {
240+
methods.add(method);
241+
}
242+
break;
234243

235244
default:
236245
valuesToSuggestions(context, values, related, suggestions);
237246
}
247+
248+
for (const method of methods) {
249+
suggestions.add(method);
250+
}
238251
}
239252

240253
if (storageType === Set) {
@@ -275,5 +288,6 @@ export default (source, { value, stats, assertions }) => ({
275288
}
276289

277290
return result.length ? result : null;
278-
}
291+
},
292+
getMethodInfo: getMethodInfo
279293
});

test/misc.js

+32
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,35 @@ describe('query/misc', () => {
2121
});
2222
}
2323
});
24+
25+
describe('method info helpers', () => {
26+
it('createMethodInfo', () => {
27+
assert.deepEqual(query.createMethodInfo([], 'some return type', {description: 'some description'}), {
28+
args: [],
29+
description: 'some description',
30+
returns: 'some return type'
31+
});
32+
});
33+
34+
it('createMethodInfoArg', () => {
35+
assert.deepEqual(query.createMethodInfoArg('arg1', 'some arg type', {description: 'some description'}), {
36+
name: 'arg1',
37+
description: 'some description',
38+
options: {
39+
defaultValue: undefined,
40+
isOptional: false
41+
},
42+
type: 'some arg type'
43+
});
44+
45+
assert.deepEqual(query.createMethodInfoArg('arg1', 'some arg type', {description: 'some description', defaultValue: '123'}), {
46+
name: 'arg1',
47+
description: 'some description',
48+
options: {
49+
defaultValue: '123',
50+
isOptional: true
51+
},
52+
type: 'some arg type'
53+
});
54+
});
55+
});

test/stat.js

+12
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,18 @@ describe('query/stat mode', () => {
2929
}]);
3030
});
3131

32+
describe('getMethodInfo() method', () => {
33+
it('basic', () =>{
34+
const methodsInfo = {
35+
foo: jora.createMethodInfo([])
36+
};
37+
const res = jora('.[]', {...options, methodsInfo})([]);
38+
assert.deepEqual(res.getMethodInfo('foo'), methodsInfo.foo);
39+
assert.deepEqual(res.getMethodInfo('bar'), null);
40+
assert.deepEqual(res.getMethodInfo('bool'), jora.methodsInfo.bool);
41+
});
42+
});
43+
3244
describe('suggestion() method', () => {
3345
it('default behaviour (no options)', () => {
3446
const data = [{ id: 1, foo: 1 }, { id: 2, foo: 42 }];

0 commit comments

Comments
 (0)