UNPKG

@neo4j-cypher/editor-support

Version:

Core functionality to support Cypher integration into editors

204 lines (201 loc) 9.21 kB
"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;