Skip to content

Commit

Permalink
feat: support method suggestion
Browse files Browse the repository at this point in the history
feat: add getMethodInfo() in stats api
  • Loading branch information
smelukov committed Aug 24, 2023
1 parent 2d912d0 commit 40267f8
Show file tree
Hide file tree
Showing 7 changed files with 337 additions and 211 deletions.
26 changes: 20 additions & 6 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import walk from './lang/walk.js';
import stringify from './lang/stringify.js';
import compile from './lang/compile.js';
import buildin from './lang/compile-buildin.js';
import methods from './methods.js';
import methods, {methodsInfo, makeMethodInfoFacade, createMethodInfoArg, createMethodInfo} from './methods.js';
import assertions from './assertions.js';
import createStatApi from './stat.js';

Expand Down Expand Up @@ -90,10 +90,12 @@ function createQuery(source, options) {
const statMode = Boolean(options.stat);
const tolerantMode = Boolean(options.tolerant);
const localMethods = options.methods ? { ...methods, ...options.methods } : methods;
const localAssetions = options.assertions ? { ...assertions, ...options.assertions } : assertions;
const localAssertions = options.assertions ? { ...assertions, ...options.assertions } : assertions;
const cache = statMode
? (tolerantMode ? cacheTollerantStat : cacheStrictStat)
: (tolerantMode ? cacheTollerant : cacheStrict);
const methodInfoFacade = makeMethodInfoFacade(methodsInfo, options.methodsInfo);

let fn;

source = String(source);
Expand All @@ -105,20 +107,25 @@ function createQuery(source, options) {
cache.set(source, fn);
}

fn = fn(buildin, localMethods, localAssetions);
fn = fn(buildin, localMethods, localAssertions);

return statMode
? Object.assign((data, context) => createStatApi(source, fn(data, context)), { query: fn })
? Object.assign((data, context) => createStatApi(source, fn(data, context), {
customMethods: {...options.methods},
builtinMethods: {...methods},
getMethodInfo: methodInfoFacade.get
}), {query: fn, setMethodInfo: methodInfoFacade.set})
: fn;
}

function setup(customMethods, customAssertions) {
function setup(customMethods, customAssertions, {methodsInfo}) {
const cacheStrict = new Map();
const cacheStrictStat = new Map();
const cacheTollerant = new Map();
const cacheTollerantStat = new Map();
const localMethods = { ...methods };
const localAssetions = { ...assertions };
const methodInfoFacade = makeMethodInfoFacade(methodsInfo);

for (const [name, fn] of Object.entries(customMethods || {})) {
if (typeof fn === 'string') {
Expand Down Expand Up @@ -169,7 +176,11 @@ function setup(customMethods, customAssertions) {
} else {
const perform = compileFunction(source, statMode, tolerantMode, options.debug)(buildin, localMethods, localAssetions);
fn = statMode
? Object.assign((data, context) => createStatApi(source, perform(data, context)), { query: perform })
? Object.assign((data, context) => createStatApi(source, perform(data, context), {
customMethods: {...customMethods},
builtinMethods: {...methods},
getMethodInfo: methodInfoFacade.get
}), {query: perform, setMethodInfo: methodInfoFacade.set})
: perform;
cache.set(source, fn);
}
Expand All @@ -182,6 +193,9 @@ export default Object.assign(createQuery, {
version,
buildin,
methods,
methodsInfo,
createMethodInfo,
createMethodInfoArg,
assertions,
setup,
syntax: {
Expand Down
5 changes: 5 additions & 0 deletions src/lang/compile.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ export default function compile(ast, tolerant = false, suggestions = null) {

normalizedSuggestRanges.push(range);

if (type === 'path') {
normalizedSuggestRanges.push([start, end, JSON.stringify('customMethods')]);
normalizedSuggestRanges.push([start, end, JSON.stringify('builtinMethods')]);
}

return spName;
}

Expand Down
37 changes: 37 additions & 0 deletions src/methods.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,43 @@ function stableSort(array, cmp) {
.map(item => item.value);
}

export function createMethodInfo(args = [], returns = 'any', options = {}) {
return {
args,
returns,
description: options.description ?? ''
};
}

export function createMethodInfoArg(name, type = 'any', options = {}) {
return {
name,
type,
description: options.description ?? '',
options: {
isOptional: options.isOptional || options.defaultValue !== undefined,
defaultValue: options.defaultValue
}
};
}

export function makeMethodInfoFacade(...methodsInfoList) {
const map = new Map(Object.entries(methodsInfoList.reduce((all, item) => Object.assign(all, item), {})));

function set(name, info) {
map.set(name, info);
}

function get(name) {
return map.get(name) ?? null;
}

return {map, set, get, create: createMethodInfo, createArg: createMethodInfoArg};
}

export const methodsInfo = Object.freeze({
bool: createMethodInfo([createMethodInfoArg('arg')], 'bool')
});

export default Object.freeze({
bool: buildin.bool,
Expand Down
20 changes: 17 additions & 3 deletions src/stat.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ const contextToType = {
'in-value': 'value',
'value-subset': 'value',
'var': 'variable',
'assertion': 'assertion'
'assertion': 'assertion',
'customMethods': 'method',
'builtinMethods': 'method'
};

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

export default (source, { value, stats, assertions }) => ({
export default (source, { value, stats, assertions }, {customMethods, builtinMethods, getMethodInfo}) => ({
get value() {
return value;
},
Expand Down Expand Up @@ -222,6 +224,7 @@ export default (source, { value, stats, assertions }) => ({
}

const { suggestions } = typeSuggestions.get(type);
const methods = new Set();

switch (context) {
case 'assertion':
Expand All @@ -231,10 +234,20 @@ export default (source, { value, stats, assertions }) => ({
}
}
break;
case 'customMethods':
case 'builtinMethods':
for (const method of Object.keys(context === 'customMethods' ? customMethods : builtinMethods)) {
methods.add(method);
}
break;

default:
valuesToSuggestions(context, values, related, suggestions);
}

for (const method of methods) {
suggestions.add(method);
}
}

if (storageType === Set) {
Expand Down Expand Up @@ -275,5 +288,6 @@ export default (source, { value, stats, assertions }) => ({
}

return result.length ? result : null;
}
},
getMethodInfo: getMethodInfo
});
32 changes: 32 additions & 0 deletions test/misc.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,35 @@ describe('query/misc', () => {
});
}
});

describe('method info helpers', () => {
it('createMethodInfo', () => {
assert.deepEqual(query.createMethodInfo([], 'some return type', {description: 'some description'}), {
args: [],
description: 'some description',
returns: 'some return type'
});
});

it('createMethodInfoArg', () => {
assert.deepEqual(query.createMethodInfoArg('arg1', 'some arg type', {description: 'some description'}), {
name: 'arg1',
description: 'some description',
options: {
defaultValue: undefined,
isOptional: false
},
type: 'some arg type'
});

assert.deepEqual(query.createMethodInfoArg('arg1', 'some arg type', {description: 'some description', defaultValue: '123'}), {
name: 'arg1',
description: 'some description',
options: {
defaultValue: '123',
isOptional: true
},
type: 'some arg type'
});
});
});
12 changes: 12 additions & 0 deletions test/stat.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,18 @@ describe('query/stat mode', () => {
}]);
});

describe('getMethodInfo() method', () => {
it('basic', () =>{
const methodsInfo = {
foo: jora.createMethodInfo([])
};
const res = jora('.[]', {...options, methodsInfo})([]);
assert.deepEqual(res.getMethodInfo('foo'), methodsInfo.foo);
assert.deepEqual(res.getMethodInfo('bar'), null);
assert.deepEqual(res.getMethodInfo('bool'), jora.methodsInfo.bool);
});
});

describe('suggestion() method', () => {
it('default behaviour (no options)', () => {
const data = [{ id: 1, foo: 1 }, { id: 2, foo: 42 }];
Expand Down
Loading

0 comments on commit 40267f8

Please sign in to comment.