ToolJet/frontend/src/Editor/CodeBuilder/utils.js
Arpit 91f4bbd709
[Improvement] Suggestion hints (#3040)
* feature: suggestion hints

* removes unwanted vars
2022-05-11 19:05:09 +05:30

165 lines
4.3 KiB
JavaScript

import _ from 'lodash';
import Fuse from 'fuse.js';
export function getSuggestionKeys(currentState) {
const suggestionList = [];
const map = new Map();
const buildMap = (data, path = '') => {
const keys = Object.keys(data);
keys.forEach((key, index) => {
const value = data[key];
const _type = Object.prototype.toString.call(value).slice(8, -1);
const prevType = map.get(path)?.type;
let newPath = '';
if (path === '') {
newPath = key;
} else if (prevType === 'Array') {
newPath = `${path}[${index}]`;
} else {
newPath = `${path}.${key}`;
}
if (_type === 'Object') {
map.set(newPath, { type: _type });
buildMap(value, newPath);
}
if (_type === 'Array') {
map.set(newPath, { type: _type });
buildMap(value, newPath);
} else {
map.set(newPath, { type: _type });
}
});
};
buildMap(currentState, '');
map.forEach((__, key) => suggestionList.push(key));
return suggestionList;
}
export function generateHints(word, suggestions) {
if (word === '') {
return suggestions;
}
const fuse = new Fuse(suggestions);
return fuse.search(word).map((result) => result.item);
}
export function computeCurrentWord(editor, _cursorPosition, ignoreBraces = false) {
const cursor = editor.getCursor();
const line = cursor.line;
const value = editor.getLine(line);
const sliced = value.slice(0, _cursorPosition);
const splitter = ignoreBraces ? ' ' : '{{';
const split = sliced.split(splitter);
const splittedWord = split.slice(-1).pop();
// Check if the word still has spaces, to avoid replacing entire code
const lastWord = splittedWord.split(' ').slice(-1).pop();
return lastWord;
}
export function makeOverlay(style) {
return {
// eslint-disable-next-line no-unused-vars
token: function (stream, state) {
var ch;
if (stream.match('{{')) {
while ((ch = stream.next()) != null)
if (ch === '}' && stream.next() === '}') {
stream.eat('}');
return style;
}
}
// eslint-disable-next-line no-empty
while (stream.next() != null && !stream.match('{{', false)) {}
return null;
},
};
}
export function onBeforeChange(editor, change, ignoreBraces = false) {
if (!ignoreBraces) {
const cursor = editor.getCursor();
const line = cursor.line;
const ch = cursor.ch;
const value = editor.getLine(line);
const isLastCharacterBrace = value.slice(ch - 1, value.length) === '{';
if (isLastCharacterBrace && change.origin === '+input' && change.text[0] === '{') {
change.text[0] = '{}}';
// editor.setCursor({ line: 0, ch: ch })
}
}
return change;
}
function keystrokeChecker(editor) {
const keyPromise = new Promise((resolve, reject) => {
editor.on('keyup', function (editor, event) {
if (event.key == 'Enter' || event.key == 'Backspace') {
resolve(true);
}
reject(false);
});
});
return keyPromise;
}
export function canShowHint(editor, ignoreBraces = false) {
if (!editor.hasFocus()) return false;
const cursor = editor.getCursor();
const line = cursor.line;
const ch = cursor.ch;
const value = editor.getLine(line);
if (ignoreBraces && value.length > 0) return true;
return value.slice(ch, ch + 2) === '}}';
}
export function handleChange(editor, onChange, suggestions, ignoreBraces = false) {
let state = editor.state.matchHighlighter;
editor.addOverlay((state.overlay = makeOverlay(state.options.style)));
const cursor = editor.getCursor();
const currentWord = computeCurrentWord(editor, cursor.ch, ignoreBraces);
const hints = generateHints(currentWord, suggestions);
const options = {
alignWithWord: false,
completeSingle: false,
hint: function () {
return {
from: { line: cursor.line, ch: cursor.ch - currentWord.length },
to: cursor,
list: hints,
};
},
};
if (canShowHint(editor, ignoreBraces)) {
const keystrokeValue = keystrokeChecker(editor)
.then((res) => {
return res;
})
.catch((err) => {
return err;
});
const keystrokeCaller = async () => {
const returnValue = await keystrokeValue;
if (!returnValue) editor.showHint(options);
};
keystrokeCaller();
}
}