@neo4j-cypher/editor-support
Version:
Core functionality to support Cypher integration into editors
204 lines (201 loc) • 9.21 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.CypherEditorSupport = void 0;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _CompletionTypeResolver = require("./completion/CompletionTypeResolver");
var _AutoCompletion = require("./completion/AutoCompletion");
var CypherTypes = _interopRequireWildcard(require("./lang/CypherTypes"));
var _CypherSyntaxHighlight = require("./highlight/CypherSyntaxHighlight");
var _TreeUtils = require("./util/TreeUtils");
var _PositionConverter = require("./util/PositionConverter");
var _retryOperation = require("./util/retryOperation");
var _parse = require("./util/parse");
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { (0, _defineProperty2.default)(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
class CypherEditorSupport {
constructor(input = "") {
(0, _defineProperty2.default)(this, "schema", {});
(0, _defineProperty2.default)(this, "input", null);
(0, _defineProperty2.default)(this, "positionConverter", new _PositionConverter.PositionConverter(""));
(0, _defineProperty2.default)(this, "parseTree", null);
(0, _defineProperty2.default)(this, "parseErrors", []);
(0, _defineProperty2.default)(this, "referencesProviders", new Map());
(0, _defineProperty2.default)(this, "completion", new _AutoCompletion.AutoCompletion());
(0, _defineProperty2.default)(this, "queriesAndCommands", []);
(0, _defineProperty2.default)(this, "statements", []);
(0, _defineProperty2.default)(this, "listeners", []);
(0, _defineProperty2.default)(this, "version", 0);
(0, _defineProperty2.default)(this, "ensureVersion", (version, delay = 30, times = 5) => (0, _retryOperation.retryOperation)(() => new Promise((resolve, reject) => {
if (version === this.version) {
return resolve();
}
return reject();
}), delay, times));
this.update(input);
}
on(eventName, cb) {
this.listeners[eventName] = Array.isArray(this.listeners[eventName]) ? this.listeners[eventName].concat([cb]) : this.listeners[eventName] = [cb];
}
off(eventName, cb) {
if (!this.listeners[eventName]) return;
const index = this.listeners[eventName].indexOf(cb);
if (index > -1) {
this.listeners[eventName].splice(index, 1);
}
}
trigger(eventName, args = []) {
if (!this.listeners[eventName]) return;
this.listeners[eventName].forEach(cb => cb(...args));
}
update(input = "", version) {
this.trigger("update");
if (input === this.input) {
this.version = version || this.version;
this.trigger("updated", [{
queriesAndCommands: this.queriesAndCommands,
referencesProviders: this.referencesProviders
}]);
return;
}
this.positionConverter = new _PositionConverter.PositionConverter(input);
this.input = input;
// const startTime = new Date();
const {
parseTree,
referencesListener,
errorListener,
referencesProviders
} = (0, _parse.parse)(input);
this.parseTree = parseTree;
// console.log('updated parse tree: ', input, version, 'time: ' + (new Date() - startTime));
this.parseErrors = errorListener.errors;
const {
queriesAndCommands,
statements
} = referencesListener;
this.statements = statements;
this.queriesAndCommands = queriesAndCommands;
this.referencesProviders = referencesProviders;
this.completion.updateReferenceProviders(this.referencesProviders);
this.version = version || this.version;
this.trigger("updated", [{
queriesAndCommands: this.queriesAndCommands,
referencesProviders: this.referencesProviders
}]);
}
setSchema(schema) {
this.schema = schema;
this.completion.updateSchema(this.schema);
}
getElement(line, column) {
const abs = this.positionConverter.toAbsolute(line, column);
function getElement(pt) {
const pos = _TreeUtils.TreeUtils.getPosition(pt);
if (pos != null && (abs < pos.start || abs > pos.stop)) {
return null;
}
const c = pt.getChildCount();
if (c === 0 && pos != null) {
return pt;
}
for (let i = 0; i < c; i += 1) {
const e = getElement(pt.getChild(i));
if (e != null) {
return e;
}
}
return pos != null ? pt : null;
}
return getElement(this.parseTree);
}
getReferences(line, column) {
const e = _TreeUtils.TreeUtils.findAnyParent(this.getElement(line, column), CypherTypes.SYMBOLIC_CONTEXTS);
if (e == null) {
return [];
}
const query = e instanceof CypherTypes.VARIABLE_CONTEXT ? _TreeUtils.TreeUtils.findAnyParent(e, [CypherTypes.QUERY_CONTEXT]) : null;
return this.referencesProviders.get(e).getReferences(e.getText(), query);
}
getCompletionInfo(line, column) {
const element = this.getElementForCompletion(line, column);
const query = _TreeUtils.TreeUtils.findAnyParent(element, [CypherTypes.QUERY_CONTEXT]);
const {
found,
types
} = _CompletionTypeResolver.CompletionTypeResolver.getTypes(element);
return {
element,
query,
found,
types
};
}
getElementForCompletion(line, column) {
const e = this.getElement(line, column);
return _TreeUtils.TreeUtils.findAnyParent(e, CypherTypes.COMPLETION_CANDIDATES) || e;
}
getCompletion(line, column, doFilter = true) {
let info = this.getCompletionInfo(line, column);
// Shift by one symbol back and try again.
if (!info.found && column > 0) {
const prevInfo = this.getCompletionInfo(line, column - 1);
if (prevInfo.found) {
info = prevInfo;
}
}
const {
element,
query,
found,
types
} = info;
const replaceRange = {
from: {
line,
column
},
to: {
line,
column
}
};
let filter = null;
const shouldBeReplaced = _AutoCompletion.AutoCompletion.shouldBeReplaced(element);
if (found && shouldBeReplaced) {
// There are number of situations where we need to be smarter than default behavior
const {
start,
stop
} = _TreeUtils.TreeUtils.getPosition(element);
const smartReplaceRange = _AutoCompletion.AutoCompletion.calculateSmartReplaceRange(element, start, stop);
if (smartReplaceRange) {
replaceRange.from = this.positionConverter.toRelative(smartReplaceRange.start);
replaceRange.to = this.positionConverter.toRelative(smartReplaceRange.stop + 1);
if (smartReplaceRange.filterText) {
filter = smartReplaceRange.filterText;
}
} else {
replaceRange.from = this.positionConverter.toRelative(start);
replaceRange.to = this.positionConverter.toRelative(stop + 1);
}
}
if (filter === null) {
filter = doFilter && found && shouldBeReplaced ? element.getText() : "";
}
return _objectSpread({
items: this.completion.getItems(types, {
filter,
query
})
}, replaceRange);
}
applyHighlighthing(callback) {
_CypherSyntaxHighlight.CypherSyntaxHighlight.process(this.parseTree, callback);
}
}
exports.CypherEditorSupport = CypherEditorSupport;