/* eslint-disable no-labels */ /* eslint-disable no-return-assign */ /* eslint-disable no-cond-assign */ /* eslint-disable no-prototype-builtins */ /* eslint-disable no-mixed-operators */ /* Got from 'ace-builds/src-noconflict/ext-language_tools' and edited to support icons. I'm not responsible of this crazy code. */ ace.define('ace/snippets', ['require', 'exports', 'module', 'ace/lib/oop', 'ace/lib/event_emitter', 'ace/lib/lang', 'ace/range', 'ace/range_list', 'ace/keyboard/hash_handler', 'ace/tokenizer', 'ace/clipboard', 'ace/lib/dom', 'ace/editor'], function (require, exports, module) { 'use strict'; const oop = require('./lib/oop'); const EventEmitter = require('./lib/event_emitter').EventEmitter; const lang = require('./lib/lang'); const Range = require('./range').Range; const RangeList = require('./range_list').RangeList; const HashHandler = require('./keyboard/hash_handler').HashHandler; const Tokenizer = require('./tokenizer').Tokenizer; const clipboard = require('./clipboard'); const VARIABLES = { CURRENT_WORD: function (editor) { return editor.session.getTextRange(editor.session.getWordRange()); }, SELECTION: function (editor, name, indentation) { const text = editor.session.getTextRange(); if (indentation) return text.replace(/\n\r?([ \t]*\S)/g, '\n' + indentation + '$1'); return text; }, CURRENT_LINE: function (editor) { return editor.session.getLine(editor.getCursorPosition().row); }, PREV_LINE: function (editor) { return editor.session.getLine(editor.getCursorPosition().row - 1); }, LINE_INDEX: function (editor) { return editor.getCursorPosition().row; }, LINE_NUMBER: function (editor) { return editor.getCursorPosition().row + 1; }, SOFT_TABS: function (editor) { return editor.session.getUseSoftTabs() ? 'YES' : 'NO'; }, TAB_SIZE: function (editor) { return editor.session.getTabSize(); }, CLIPBOARD: function (editor) { return clipboard.getText && clipboard.getText(); }, FILENAME: function (editor) { return /[^/\\]*$/.exec(this.FILEPATH(editor))[0]; }, FILENAME_BASE: function (editor) { return /[^/\\]*$/.exec(this.FILEPATH(editor))[0].replace(/\.[^.]*$/, ''); }, DIRECTORY: function (editor) { return this.FILEPATH(editor).replace(/[^/\\]*$/, ''); }, FILEPATH: function (editor) { return '/not implemented.txt'; }, WORKSPACE_NAME: function () { return 'Unknown'; }, FULLNAME: function () { return 'Unknown'; }, BLOCK_COMMENT_START: function (editor) { const mode = editor.session.$mode || {}; return mode.blockComment && mode.blockComment.start || ''; }, BLOCK_COMMENT_END: function (editor) { const mode = editor.session.$mode || {}; return mode.blockComment && mode.blockComment.end || ''; }, LINE_COMMENT: function (editor) { const mode = editor.session.$mode || {}; return mode.lineCommentStart || ''; }, CURRENT_YEAR: date.bind(null, { year: 'numeric' }), CURRENT_YEAR_SHORT: date.bind(null, { year: '2-digit' }), CURRENT_MONTH: date.bind(null, { month: 'numeric' }), CURRENT_MONTH_NAME: date.bind(null, { month: 'long' }), CURRENT_MONTH_NAME_SHORT: date.bind(null, { month: 'short' }), CURRENT_DATE: date.bind(null, { day: '2-digit' }), CURRENT_DAY_NAME: date.bind(null, { weekday: 'long' }), CURRENT_DAY_NAME_SHORT: date.bind(null, { weekday: 'short' }), CURRENT_HOUR: date.bind(null, { hour: '2-digit', hour12: false }), CURRENT_MINUTE: date.bind(null, { minute: '2-digit' }), CURRENT_SECOND: date.bind(null, { second: '2-digit' }) }; VARIABLES.SELECTED_TEXT = VARIABLES.SELECTION; function date (dateFormat) { const str = new Date().toLocaleString('en-us', dateFormat); return str.length === 1 ? '0' + str : str; } const SnippetManager = function () { this.snippetMap = {}; this.snippetNameMap = {}; }; (function () { oop.implement(this, EventEmitter); this.getTokenizer = function () { return SnippetManager.$tokenizer || this.createTokenizer(); }; this.createTokenizer = function () { function TabstopToken (str) { str = str.substr(1); if (/^\d+$/.test(str)) return [{ tabstopId: parseInt(str, 10) }]; return [{ text: str }]; } function escape (ch) { return '(?:[^\\\\' + ch + ']|\\\\.)'; } const formatMatcher = { regex: '/(' + escape('/') + '+)/', onMatch: function (val, state, stack) { const ts = stack[0]; ts.fmtString = true; ts.guard = val.slice(1, -1); ts.flag = ''; return ''; }, next: 'formatString' }; SnippetManager.$tokenizer = new Tokenizer({ start: [ { regex: /\\./, onMatch: function (val, state, stack) { const ch = val[1]; if (ch === '}' && stack.length) val = ch; else if ('`$\\'.indexOf(ch) !== -1) val = ch; return [val]; } }, { regex: /}/, onMatch: function (val, state, stack) { return [stack.length ? stack.shift() : val]; } }, { regex: /\$(?:\d+|\w+)/, onMatch: TabstopToken }, { regex: /\$\{[\dA-Z_a-z]+/, onMatch: function (str, state, stack) { const t = TabstopToken(str.substr(1)); stack.unshift(t[0]); return t; }, next: 'snippetVar' }, { regex: /\n/, token: 'newline', merge: false } ], snippetVar: [ { regex: '\\|' + escape('\\|') + '*\\|', onMatch: function (val, state, stack) { const choices = val.slice(1, -1).replace(/\\[,|\\]|,/g, function (operator) { return operator.length === 2 ? operator[1] : '\x00'; }).split('\x00').map(function (value) { return { value: value }; }); stack[0].choices = choices; return [choices[0]]; }, next: 'start' }, formatMatcher, { regex: '([^:}\\\\]|\\\\.)*:?', token: '', next: 'start' } ], formatString: [ { regex: /:/, onMatch: function (val, state, stack) { if (stack.length && stack[0].expectElse) { stack[0].expectElse = false; stack[0].ifEnd = { elseEnd: stack[0] }; return [stack[0].ifEnd]; } return ':'; } }, { regex: /\\./, onMatch: function (val, state, stack) { const ch = val[1]; if (ch === '}' && stack.length) val = ch; else if ('`$\\'.indexOf(ch) !== -1) val = ch; else if (ch === 'n') val = '\n'; else if (ch === 't') val = '\t'; else if ('ulULE'.indexOf(ch) !== -1) val = { changeCase: ch, local: ch > 'a' }; return [val]; } }, { regex: '/\\w*}', onMatch: function (val, state, stack) { const next = stack.shift(); if (next) next.flag = val.slice(1, -1); this.next = next && next.tabstopId ? 'start' : ''; return [next || val]; }, next: 'start' }, { regex: /\$(?:\d+|\w+)/, onMatch: function (val, state, stack) { return [{ text: val.slice(1) }]; } }, { regex: /\${\w+/, onMatch: function (val, state, stack) { const token = { text: val.slice(2) }; stack.unshift(token); return [token]; }, next: 'formatStringVar' }, { regex: /\n/, token: 'newline', merge: false }, { regex: /}/, onMatch: function (val, state, stack) { const next = stack.shift(); this.next = next && next.tabstopId ? 'start' : ''; return [next || val]; }, next: 'start' } ], formatStringVar: [ { regex: /:\/\w+}/, onMatch: function (val, state, stack) { const ts = stack[0]; ts.formatFunction = val.slice(2, -1); return [stack.shift()]; }, next: 'formatString' }, formatMatcher, { regex: /:[\\?\-+]?/, onMatch: function (val, state, stack) { if (val[1] === '+') stack[0].ifEnd = stack[0]; if (val[1] === '?') stack[0].expectElse = true; }, next: 'formatString' }, { regex: '([^:}\\\\]|\\\\.)*:?', token: '', next: 'formatString' } ] }); return SnippetManager.$tokenizer; }; this.tokenizeTmSnippet = function (str, startState) { return this.getTokenizer().getLineTokens(str, startState).tokens.map(function (x) { return x.value || x; }); }; this.getVariableValue = function (editor, name, indentation) { if (/^\d+$/.test(name)) return (this.variables.__ || {})[name] || ''; if (/^[A-Z]\d+$/.test(name)) return (this.variables[name[0] + '__'] || {})[name.substr(1)] || ''; name = name.replace(/^TM_/, ''); if (!this.variables.hasOwnProperty(name)) return ''; let value = this.variables[name]; if (typeof value === 'function') value = this.variables[name](editor, name, indentation); return value === null ? '' : value; }; this.variables = VARIABLES; this.tmStrFormat = function (str, ch, editor) { if (!ch.fmt) return str; const flag = ch.flag || ''; let re = ch.guard; re = new RegExp(re, flag.replace(/[^gim]/g, '')); const fmtTokens = typeof ch.fmt === 'string' ? this.tokenizeTmSnippet(ch.fmt, 'formatString') : ch.fmt; const _self = this; const formatted = str.replace(re, function () { const oldArgs = _self.variables.__; _self.variables.__ = [].slice.call(arguments); const fmtParts = _self.resolveVariables(fmtTokens, editor); let gChangeCase = 'E'; for (let i = 0; i < fmtParts.length; i++) { const ch = fmtParts[i]; if (typeof ch === 'object') { fmtParts[i] = ''; if (ch.changeCase && ch.local) { const next = fmtParts[i + 1]; if (next && typeof next === 'string') { if (ch.changeCase === 'u') fmtParts[i] = next[0].toUpperCase(); else fmtParts[i] = next[0].toLowerCase(); fmtParts[i + 1] = next.substr(1); } } else if (ch.changeCase) gChangeCase = ch.changeCase; } else if (gChangeCase === 'U') fmtParts[i] = ch.toUpperCase(); else if (gChangeCase === 'L') fmtParts[i] = ch.toLowerCase(); } _self.variables.__ = oldArgs; return fmtParts.join(''); }); return formatted; }; this.tmFormatFunction = function (str, ch, editor) { if (ch.formatFunction === 'upcase') return str.toUpperCase(); if (ch.formatFunction === 'downcase') return str.toLowerCase(); return str; }; this.resolveVariables = function (snippet, editor) { const result = []; let indentation = ''; let afterNewLine = true; for (let i = 0; i < snippet.length; i++) { const ch = snippet[i]; if (typeof ch === 'string') { result.push(ch); if (ch === '\n') { afterNewLine = true; indentation = ''; } else if (afterNewLine) { indentation = /^\t*/.exec(ch)[0]; afterNewLine = /\S/.test(ch); } continue; } if (!ch) continue; afterNewLine = false; if (ch.fmtString) { let j = snippet.indexOf(ch, i + 1); if (j === -1) j = snippet.length; ch.fmt = snippet.slice(i + 1, j); i = j; } if (ch.text) { let value = this.getVariableValue(editor, ch.text, indentation) + ''; if (ch.fmtString) value = this.tmStrFormat(value, ch, editor); if (ch.formatFunction) value = this.tmFormatFunction(value, ch, editor); if (value && !ch.ifEnd) { result.push(value); gotoNext(ch); } else if (!value && ch.ifEnd) gotoNext(ch.ifEnd); } else if (ch.elseEnd) gotoNext(ch.elseEnd); else if (ch.tabstopId !== null) result.push(ch); else if (ch.changeCase !== null) result.push(ch); } function gotoNext (ch) { const i1 = snippet.indexOf(ch, i + 1); if (i1 !== -1) i = i1; } return result; }; this.insertSnippetForSelection = function (editor, snippetText) { const cursor = editor.getCursorPosition(); const line = editor.session.getLine(cursor.row); const tabString = editor.session.getTabString(); let indentString = line.match(/^\s*/)[0]; if (cursor.column < indentString.length) indentString = indentString.slice(0, cursor.column); snippetText = snippetText.replace(/\r/g, ''); let tokens = this.tokenizeTmSnippet(snippetText); tokens = this.resolveVariables(tokens, editor); tokens = tokens.map(function (x) { if (x === '\n') return x + indentString; if (typeof x === 'string') return x.replace(/\t/g, tabString); return x; }); const tabstops = []; tokens.forEach(function (p, i) { if (typeof p !== 'object') return; const id = p.tabstopId; let ts = tabstops[id]; if (!ts) { ts = tabstops[id] = []; ts.index = id; ts.value = ''; ts.parents = {}; } if (ts.indexOf(p) !== -1) return; if (p.choices && !ts.choices) ts.choices = p.choices; ts.push(p); const i1 = tokens.indexOf(p, i + 1); if (i1 === -1) return; const value = tokens.slice(i + 1, i1); const isNested = value.some(function (t) { return typeof t === 'object'; }); if (isNested && !ts.value) ts.value = value; else if (value.length && (!ts.value || typeof ts.value !== 'string')) ts.value = value.join(''); }); tabstops.forEach(function (ts) { ts.length = 0; }); const expanding = {}; function copyValue (val) { const copy = []; for (let i = 0; i < val.length; i++) { let p = val[i]; if (typeof p === 'object') { if (expanding[p.tabstopId]) continue; const j = val.lastIndexOf(p, i - 1); p = copy[j] || { tabstopId: p.tabstopId }; } copy[i] = p; } return copy; } for (let i = 0; i < tokens.length; i++) { const p = tokens[i]; if (typeof p !== 'object') continue; const id = p.tabstopId; const ts = tabstops[id]; const i1 = tokens.indexOf(p, i + 1); if (expanding[id]) { if (expanding[id] === p) { delete expanding[id]; Object.keys(expanding).forEach(function (parentId) { ts.parents[parentId] = true; }); } continue; } expanding[id] = p; let value = ts.value; if (typeof value !== 'string') value = copyValue(value); else if (p.fmt) value = this.tmStrFormat(value, p, editor); tokens.splice.apply(tokens, [i + 1, Math.max(0, i1 - i)].concat(value, p)); if (ts.indexOf(p) === -1) ts.push(p); } let row = 0; let column = 0; let text = ''; tokens.forEach(function (t) { if (typeof t === 'string') { const lines = t.split('\n'); if (lines.length > 1) { column = lines[lines.length - 1].length; row += lines.length - 1; } else column += t.length; text += t; } else if (t) { if (!t.start) t.start = { row: row, column: column }; else t.end = { row: row, column: column }; } }); const range = editor.getSelectionRange(); const end = editor.session.replace(range, text); const tabstopManager = new TabstopManager(editor); const selectionId = editor.inVirtualSelectionMode && editor.selection.index; tabstopManager.addTabstops(tabstops, range.start, end, selectionId); }; this.insertSnippet = function (editor, snippetText) { const self = this; if (editor.inVirtualSelectionMode) return self.insertSnippetForSelection(editor, snippetText); editor.forEachSelection(function () { self.insertSnippetForSelection(editor, snippetText); }, null, { keepOrder: true }); if (editor.tabstopManager) editor.tabstopManager.tabNext(); }; this.$getScope = function (editor) { let scope = editor.session.$mode.$id || ''; scope = scope.split('/').pop(); if (scope === 'html' || scope === 'php') { if (scope === 'php' && !editor.session.$mode.inlinePhp) scope = 'html'; const c = editor.getCursorPosition(); let state = editor.session.getState(c.row); if (typeof state === 'object') state = state[0]; if (state.substring) { if (state.substring(0, 3) === 'js-') scope = 'javascript'; else if (state.substring(0, 4) === 'css-') scope = 'css'; else if (state.substring(0, 4) === 'php-') scope = 'php'; } } return scope; }; this.getActiveScopes = function (editor) { const scope = this.$getScope(editor); const scopes = [scope]; const snippetMap = this.snippetMap; if (snippetMap[scope] && snippetMap[scope].includeScopes) scopes.push.apply(scopes, snippetMap[scope].includeScopes); scopes.push('_'); return scopes; }; this.expandWithTab = function (editor, options) { const self = this; const result = editor.forEachSelection(function () { return self.expandSnippetForSelection(editor, options); }, null, { keepOrder: true }); if (result && editor.tabstopManager) editor.tabstopManager.tabNext(); return result; }; this.expandSnippetForSelection = function (editor, options) { const cursor = editor.getCursorPosition(); const line = editor.session.getLine(cursor.row); const before = line.substring(0, cursor.column); const after = line.substr(cursor.column); const snippetMap = this.snippetMap; let snippet; this.getActiveScopes(editor).some(function (scope) { const snippets = snippetMap[scope]; if (snippets) snippet = this.findMatchingSnippet(snippets, before, after); return !!snippet; }, this); if (!snippet) return false; if (options && options.dryRun) return true; editor.session.doc.removeInLine(cursor.row, cursor.column - snippet.replaceBefore.length, cursor.column + snippet.replaceAfter.length ); this.variables.M__ = snippet.matchBefore; this.variables.T__ = snippet.matchAfter; this.insertSnippetForSelection(editor, snippet.content); this.variables.M__ = this.variables.T__ = null; return true; }; this.findMatchingSnippet = function (snippetList, before, after) { for (let i = snippetList.length; i--;) { const s = snippetList[i]; if (s.startRe && !s.startRe.test(before)) continue; if (s.endRe && !s.endRe.test(after)) continue; if (!s.startRe && !s.endRe) continue; s.matchBefore = s.startRe ? s.startRe.exec(before) : ['']; s.matchAfter = s.endRe ? s.endRe.exec(after) : ['']; s.replaceBefore = s.triggerRe ? s.triggerRe.exec(before)[0] : ''; s.replaceAfter = s.endTriggerRe ? s.endTriggerRe.exec(after)[0] : ''; return s; } }; this.snippetMap = {}; this.snippetNameMap = {}; this.register = function (snippets, scope) { const snippetMap = this.snippetMap; const snippetNameMap = this.snippetNameMap; const self = this; if (!snippets) snippets = []; function wrapRegexp (src) { if (src && !/^\^?\(.*\)\$?$|^\\b$/.test(src)) src = '(?:' + src + ')'; return src || ''; } function guardedRegexp (re, guard, opening) { re = wrapRegexp(re); guard = wrapRegexp(guard); if (opening) { re = guard + re; if (re && re[re.length - 1] !== '$') re = re + '$'; } else { re = re + guard; if (re && re[0] !== '^') re = '^' + re; } return new RegExp(re); } function addSnippet (s) { if (!s.scope) s.scope = scope || '_'; scope = s.scope; if (!snippetMap[scope]) { snippetMap[scope] = []; snippetNameMap[scope] = {}; } const map = snippetNameMap[scope]; if (s.name) { const old = map[s.name]; if (old) self.unregister(old); map[s.name] = s; } snippetMap[scope].push(s); if (s.prefix) s.tabTrigger = s.prefix; if (!s.content && s.body) s.content = Array.isArray(s.body) ? s.body.join('\n') : s.body; if (s.tabTrigger && !s.trigger) { if (!s.guard && /^\w/.test(s.tabTrigger)) s.guard = '\\b'; s.trigger = lang.escapeRegExp(s.tabTrigger); } if (!s.trigger && !s.guard && !s.endTrigger && !s.endGuard) return; s.startRe = guardedRegexp(s.trigger, s.guard, true); s.triggerRe = new RegExp(s.trigger); s.endRe = guardedRegexp(s.endTrigger, s.endGuard, true); s.endTriggerRe = new RegExp(s.endTrigger); } if (Array.isArray(snippets)) snippets.forEach(addSnippet); else { Object.keys(snippets).forEach(function (key) { addSnippet(snippets[key]); }); } this._signal('registerSnippets', { scope: scope }); }; this.unregister = function (snippets, scope) { const snippetMap = this.snippetMap; const snippetNameMap = this.snippetNameMap; function removeSnippet (s) { const nameMap = snippetNameMap[s.scope || scope]; if (nameMap && nameMap[s.name]) { delete nameMap[s.name]; const map = snippetMap[s.scope || scope]; const i = map && map.indexOf(s); if (i >= 0) map.splice(i, 1); } } if (snippets.content) removeSnippet(snippets); else if (Array.isArray(snippets)) snippets.forEach(removeSnippet); }; this.parseSnippetFile = function (str) { str = str.replace(/\r/g, ''); const list = []; let snippet = {}; const re = /^#.*|^({[\s\S]*})\s*$|^(\S+) (.*)$|^((?:\n*\t.*)+)/gm; let m; while (m = re.exec(str)) { if (m[1]) { try { snippet = JSON.parse(m[1]); list.push(snippet); } catch (e) {} } if (m[4]) { snippet.content = m[4].replace(/^\t/gm, ''); list.push(snippet); snippet = {}; } else { const key = m[2]; const val = m[3]; if (key === 'regex') { const guardRe = /\/((?:[^\\/\\]|\\.)*)|$/g; snippet.guard = guardRe.exec(val)[1]; snippet.trigger = guardRe.exec(val)[1]; snippet.endTrigger = guardRe.exec(val)[1]; snippet.endGuard = guardRe.exec(val)[1]; } else if (key === 'snippet') { snippet.tabTrigger = val.match(/^\S*/)[0]; if (!snippet.name) snippet.name = val; } else if (key) snippet[key] = val; } } return list; }; this.getSnippetByName = function (name, editor) { const snippetMap = this.snippetNameMap; let snippet; this.getActiveScopes(editor).some(function (scope) { const snippets = snippetMap[scope]; if (snippets) snippet = snippets[name]; return !!snippet; }, this); return snippet; }; }).call(SnippetManager.prototype); const TabstopManager = function (editor) { if (editor.tabstopManager) return editor.tabstopManager; editor.tabstopManager = this; this.$onChange = this.onChange.bind(this); this.$onChangeSelection = lang.delayedCall(this.onChangeSelection.bind(this)).schedule; this.$onChangeSession = this.onChangeSession.bind(this); this.$onAfterExec = this.onAfterExec.bind(this); this.attach(editor); }; (function () { this.attach = function (editor) { this.index = 0; this.ranges = []; this.tabstops = []; this.$openTabstops = null; this.selectedTabstop = null; this.editor = editor; this.editor.on('change', this.$onChange); this.editor.on('changeSelection', this.$onChangeSelection); this.editor.on('changeSession', this.$onChangeSession); this.editor.commands.on('afterExec', this.$onAfterExec); this.editor.keyBinding.addKeyboardHandler(this.keyboardHandler); }; this.detach = function () { this.tabstops.forEach(this.removeTabstopMarkers, this); this.ranges = null; this.tabstops = null; this.selectedTabstop = null; this.editor.removeListener('change', this.$onChange); this.editor.removeListener('changeSelection', this.$onChangeSelection); this.editor.removeListener('changeSession', this.$onChangeSession); this.editor.commands.removeListener('afterExec', this.$onAfterExec); this.editor.keyBinding.removeKeyboardHandler(this.keyboardHandler); this.editor.tabstopManager = null; this.editor = null; }; this.onChange = function (delta) { const isRemove = delta.action[0] === 'r'; const selectedTabstop = this.selectedTabstop || {}; const parents = selectedTabstop.parents || {}; const tabstops = (this.tabstops || []).slice(); for (let i = 0; i < tabstops.length; i++) { const ts = tabstops[i]; const active = ts === selectedTabstop || parents[ts.index]; ts.rangeList.$bias = active ? 0 : 1; if (delta.action === 'remove' && ts !== selectedTabstop) { const parentActive = ts.parents && ts.parents[selectedTabstop.index]; let startIndex = ts.rangeList.pointIndex(delta.start, parentActive); startIndex = startIndex < 0 ? -startIndex - 1 : startIndex + 1; let endIndex = ts.rangeList.pointIndex(delta.end, parentActive); endIndex = endIndex < 0 ? -endIndex - 1 : endIndex - 1; const toRemove = ts.rangeList.ranges.slice(startIndex, endIndex); for (let j = 0; j < toRemove.length; j++) this.removeRange(toRemove[j]); } ts.rangeList.$onChange(delta); } const session = this.editor.session; if (!this.$inChange && isRemove && session.getLength() === 1 && !session.getValue()) this.detach(); }; this.updateLinkedFields = function () { const ts = this.selectedTabstop; if (!ts || !ts.hasLinkedRanges || !ts.firstNonLinked) return; this.$inChange = true; const session = this.editor.session; const text = session.getTextRange(ts.firstNonLinked); for (let i = 0; i < ts.length; i++) { const range = ts[i]; if (!range.linked) continue; const original = range.original; const fmt = exports.snippetManager.tmStrFormat(text, original, this.editor); session.replace(range, fmt); } this.$inChange = false; }; this.onAfterExec = function (e) { if (e.command && !e.command.readOnly) this.updateLinkedFields(); }; this.onChangeSelection = function () { if (!this.editor) return; const lead = this.editor.selection.lead; const anchor = this.editor.selection.anchor; const isEmpty = this.editor.selection.isEmpty(); for (let i = 0; i < this.ranges.length; i++) { if (this.ranges[i].linked) continue; const containsLead = this.ranges[i].contains(lead.row, lead.column); const containsAnchor = isEmpty || this.ranges[i].contains(anchor.row, anchor.column); if (containsLead && containsAnchor) return; } this.detach(); }; this.onChangeSession = function () { this.detach(); }; this.tabNext = function (dir) { const max = this.tabstops.length; let index = this.index + (dir || 1); index = Math.min(Math.max(index, 1), max); if (index === max) index = 0; this.selectTabstop(index); if (index === 0) this.detach(); }; this.selectTabstop = function (index) { this.$openTabstops = null; let ts = this.tabstops[this.index]; if (ts) this.addTabstopMarkers(ts); this.index = index; ts = this.tabstops[this.index]; if (!ts || !ts.length) return; this.selectedTabstop = ts; const range = ts.firstNonLinked || ts; if (ts.choices) range.cursor = range.start; if (!this.editor.inVirtualSelectionMode) { const sel = this.editor.multiSelect; sel.toSingleRange(range); for (let i = 0; i < ts.length; i++) { if (ts.hasLinkedRanges && ts[i].linked) continue; sel.addRange(ts[i].clone(), true); } } else this.editor.selection.fromOrientedRange(range); this.editor.keyBinding.addKeyboardHandler(this.keyboardHandler); if (this.selectedTabstop && this.selectedTabstop.choices) this.editor.execCommand('startAutocomplete', { matches: this.selectedTabstop.choices }); }; this.addTabstops = function (tabstops, start, end) { const useLink = this.useLink || !this.editor.getOption('enableMultiselect'); if (!this.$openTabstops) this.$openTabstops = []; if (!tabstops[0]) { const p = Range.fromPoints(end, end); moveRelative(p.start, start); moveRelative(p.end, start); tabstops[0] = [p]; tabstops[0].index = 0; } const i = this.index; const arg = [i + 1, 0]; const ranges = this.ranges; tabstops.forEach(function (ts, index) { const dest = this.$openTabstops[index] || ts; for (let i = 0; i < ts.length; i++) { const p = ts[i]; const range = Range.fromPoints(p.start, p.end || p.start); movePoint(range.start, start); movePoint(range.end, start); range.original = p; range.tabstop = dest; ranges.push(range); if (dest !== ts) dest.unshift(range); else dest[i] = range; if (p.fmtString || (dest.firstNonLinked && useLink)) { range.linked = true; dest.hasLinkedRanges = true; } else if (!dest.firstNonLinked) dest.firstNonLinked = range; } if (!dest.firstNonLinked) dest.hasLinkedRanges = false; if (dest === ts) { arg.push(dest); this.$openTabstops[index] = dest; } this.addTabstopMarkers(dest); dest.rangeList = dest.rangeList || new RangeList(); dest.rangeList.$bias = 0; dest.rangeList.addList(dest); }, this); if (arg.length > 2) { if (this.tabstops.length) arg.push(arg.splice(2, 1)[0]); this.tabstops.splice.apply(this.tabstops, arg); } }; this.addTabstopMarkers = function (ts) { const session = this.editor.session; ts.forEach(function (range) { if (!range.markerId) range.markerId = session.addMarker(range, 'ace_snippet-marker', 'text'); }); }; this.removeTabstopMarkers = function (ts) { const session = this.editor.session; ts.forEach(function (range) { session.removeMarker(range.markerId); range.markerId = null; }); }; this.removeRange = function (range) { let i = range.tabstop.indexOf(range); if (i !== -1) range.tabstop.splice(i, 1); i = this.ranges.indexOf(range); if (i !== -1) this.ranges.splice(i, 1); i = range.tabstop.rangeList.ranges.indexOf(range); if (i !== -1) range.tabstop.splice(i, 1); this.editor.session.removeMarker(range.markerId); if (!range.tabstop.length) { i = this.tabstops.indexOf(range.tabstop); if (i !== -1) this.tabstops.splice(i, 1); if (!this.tabstops.length) this.detach(); } }; this.keyboardHandler = new HashHandler(); this.keyboardHandler.bindKeys({ Tab: function (editor) { if (exports.snippetManager && exports.snippetManager.expandWithTab(editor)) return; editor.tabstopManager.tabNext(1); editor.renderer.scrollCursorIntoView(); }, 'Shift-Tab': function (editor) { editor.tabstopManager.tabNext(-1); editor.renderer.scrollCursorIntoView(); }, Esc: function (editor) { editor.tabstopManager.detach(); } }); }).call(TabstopManager.prototype); const movePoint = function (point, diff) { if (point.row === 0) point.column += diff.column; point.row += diff.row; }; const moveRelative = function (point, start) { if (point.row === start.row) point.column -= start.column; point.row -= start.row; }; require('./lib/dom').importCssString(` .ace_snippet-marker { -moz-box-sizing: border-box; box-sizing: border-box; background: rgba(194, 193, 208, 0.09); border: 1px dotted rgba(211, 208, 235, 0.62); position: absolute; }`); exports.snippetManager = new SnippetManager(); const Editor = require('./editor').Editor; (function () { this.insertSnippet = function (content, options) { return exports.snippetManager.insertSnippet(this, content, options); }; this.expandSnippet = function (options) { return exports.snippetManager.expandWithTab(this, options); }; }).call(Editor.prototype); }); ace.define('ace/autocomplete/popup', ['require', 'exports', 'module', 'ace/virtual_renderer', 'ace/editor', 'ace/range', 'ace/lib/event', 'ace/lib/lang', 'ace/lib/dom'], function (require, exports, module) { 'use strict'; const Renderer = require('../virtual_renderer').VirtualRenderer; const Editor = require('../editor').Editor; const Range = require('../range').Range; const event = require('../lib/event'); const lang = require('../lib/lang'); const dom = require('../lib/dom'); const $singleLineEditor = function (el) { const renderer = new Renderer(el); renderer.$maxLines = 4; const editor = new Editor(renderer); editor.setHighlightActiveLine(false); editor.setShowPrintMargin(false); editor.renderer.setShowGutter(false); editor.renderer.setHighlightGutterLine(false); editor.$mouseHandler.$focusTimeout = 0; editor.$highlightTagPending = true; return editor; }; const AcePopup = function (parentNode) { const el = dom.createElement('div'); const popup = new $singleLineEditor(el); if (parentNode) parentNode.appendChild(el); el.style.display = 'none'; popup.renderer.content.style.cursor = 'default'; popup.renderer.setStyle('ace_autocomplete'); popup.setOption('displayIndentGuides', false); popup.setOption('dragDelay', 150); const noop = function () {}; popup.focus = noop; popup.$isFocused = true; popup.renderer.$cursorLayer.restartTimer = noop; popup.renderer.$cursorLayer.element.style.opacity = 0; popup.renderer.$maxLines = 8; popup.renderer.$keepTextAreaAtCursor = false; popup.setHighlightActiveLine(false); popup.session.highlight(''); popup.session.$searchHighlight.clazz = 'ace_highlight-marker'; popup.on('mousedown', function (e) { const pos = e.getDocumentPosition(); popup.selection.moveToPosition(pos); selectionMarker.start.row = selectionMarker.end.row = pos.row; e.stop(); }); let lastMouseEvent; const hoverMarker = new Range(-1, 0, -1, Infinity); const selectionMarker = new Range(-1, 0, -1, Infinity); selectionMarker.id = popup.session.addMarker(selectionMarker, 'ace_active-line', 'fullLine'); popup.setSelectOnHover = function (val) { if (!val) hoverMarker.id = popup.session.addMarker(hoverMarker, 'ace_line-hover', 'fullLine'); else if (hoverMarker.id) { popup.session.removeMarker(hoverMarker.id); hoverMarker.id = null; } }; popup.setSelectOnHover(false); popup.on('mousemove', function (e) { if (!lastMouseEvent) { lastMouseEvent = e; return; } if (lastMouseEvent.x === e.x && lastMouseEvent.y === e.y) return; lastMouseEvent = e; lastMouseEvent.scrollTop = popup.renderer.scrollTop; const row = lastMouseEvent.getDocumentPosition().row; if (hoverMarker.start.row !== row) { if (!hoverMarker.id) popup.setRow(row); setHoverMarker(row); } }); popup.renderer.on('beforeRender', function () { if (lastMouseEvent && hoverMarker.start.row !== -1) { lastMouseEvent.$pos = null; const row = lastMouseEvent.getDocumentPosition().row; if (!hoverMarker.id) popup.setRow(row); setHoverMarker(row, true); } }); popup.renderer.on('afterRender', function () { const row = popup.getRow(); const t = popup.renderer.$textLayer; const selected = t.element.childNodes[row - t.config.firstRow]; if (selected !== t.selectedNode && t.selectedNode) dom.removeCssClass(t.selectedNode, 'ace_selected'); t.selectedNode = selected; if (selected) dom.addCssClass(selected, 'ace_selected'); }); const hideHoverMarker = function () { setHoverMarker(-1); }; const setHoverMarker = function (row, suppressRedraw) { if (row !== hoverMarker.start.row) { hoverMarker.start.row = hoverMarker.end.row = row; if (!suppressRedraw) popup.session._emit('changeBackMarker'); popup._emit('changeHoverMarker'); } }; popup.getHoveredRow = function () { return hoverMarker.start.row; }; event.addListener(popup.container, 'mouseout', hideHoverMarker); popup.on('hide', hideHoverMarker); popup.on('changeSelection', hideHoverMarker); popup.session.doc.getLength = function () { return popup.data.length; }; popup.session.doc.getLine = function (i) { const data = popup.data[i]; if (typeof data === 'string') return data; return (data && data.value) || ''; }; const bgTokenizer = popup.session.bgTokenizer; bgTokenizer.$tokenizeRow = function (row) { let data = popup.data[row]; const tokens = []; if (!data) return tokens; if (typeof data === 'string') data = { value: data }; const caption = data.caption || data.value || data.name; function addToken (value, className) { value && tokens.push({ type: (data.className || '') + (className || ''), value: value }); } // Antares additions function addIconToken (meta) { let iconClass; switch (meta) { case 'table': iconClass = 'mdi-table'; break; case 'column': iconClass = 'mdi-rhombus-split-outline'; break; case 'view': iconClass = 'mdi-table-eye'; break; case 'trigger': iconClass = 'mdi-table-cog'; break; case 'routine': iconClass = 'mdi-sync-circle'; break; case 'keyword': iconClass = 'mdi-cube'; break; case 'snippet': iconClass = 'mdi-code-braces'; break; case 'local': iconClass = 'mdi-alphabetical-variant'; break; default: iconClass = 'mdi-circle'; break; } iconClass && tokens.push({ type: ` mdi ${iconClass}`, value: ' ' }); } // > const lower = caption.toLowerCase(); const filterText = (popup.filterText || '').toLowerCase(); let lastIndex = 0; let lastI = 0; addIconToken(data.meta); for (let i = 0; i <= filterText.length; i++) { if (i !== lastI && (data.matchMask & (1 << i) || i === filterText.length)) { const sub = filterText.slice(lastI, i); lastI = i; const index = lower.indexOf(sub, lastIndex); if (index === -1) continue; addToken(caption.slice(lastIndex, index), ''); lastIndex = index + sub.length; addToken(caption.slice(index, lastIndex), 'completion-highlight'); } } addToken(caption.slice(lastIndex, caption.length), ''); if (data.meta) tokens.push({ type: 'completion-meta', value: data.meta }); if (data.message) tokens.push({ type: 'completion-message', value: data.message }); return tokens; }; bgTokenizer.$updateOnChange = noop; bgTokenizer.start = noop; popup.session.$computeWidth = function () { return this.screenWidth = 0; }; popup.isOpen = false; popup.isTopdown = false; popup.autoSelect = true; popup.filterText = ''; popup.data = []; popup.setData = function (list, filterText) { popup.filterText = filterText || ''; popup.setValue(lang.stringRepeat('\n', list.length), -1); popup.data = list || []; popup.setRow(0); }; popup.getData = function (row) { return popup.data[row]; }; popup.getRow = function () { return selectionMarker.start.row; }; popup.setRow = function (line) { line = Math.max(this.autoSelect ? 0 : -1, Math.min(this.data.length, line)); if (selectionMarker.start.row !== line) { popup.selection.clearSelection(); selectionMarker.start.row = selectionMarker.end.row = line || 0; popup.session._emit('changeBackMarker'); popup.moveCursorTo(line || 0, 0); if (popup.isOpen) popup._signal('select'); } }; popup.on('changeSelection', function () { if (popup.isOpen) popup.setRow(popup.selection.lead.row); popup.renderer.scrollCursorIntoView(); }); popup.hide = function () { this.container.style.display = 'none'; this._signal('hide'); popup.isOpen = false; }; popup.show = function (pos, lineHeight, topdownOnly) { const el = this.container; const screenHeight = window.innerHeight; const screenWidth = window.innerWidth; const renderer = this.renderer; const maxH = renderer.$maxLines * lineHeight * 1.4; let top = pos.top + this.$borderSize; const allowTopdown = top > screenHeight / 2 && !topdownOnly; if (allowTopdown && top + lineHeight + maxH > screenHeight) { renderer.$maxPixelHeight = top - 2 * this.$borderSize; el.style.top = ''; el.style.bottom = screenHeight - top + 'px'; popup.isTopdown = false; } else { top += lineHeight; renderer.$maxPixelHeight = screenHeight - top - 0.2 * lineHeight; el.style.top = top + 'px'; el.style.bottom = ''; popup.isTopdown = true; } el.style.display = ''; let left = pos.left; if (left + el.offsetWidth > screenWidth) left = screenWidth - el.offsetWidth; el.style.left = left + 'px'; this._signal('show'); lastMouseEvent = null; popup.isOpen = true; }; popup.goTo = function (where) { let row = this.getRow(); const max = this.session.getLength() - 1; switch (where) { case 'up': row = row <= 0 ? max : row - 1; break; case 'down': row = row >= max ? -1 : row + 1; break; case 'start': row = 0; break; case 'end': row = max; break; } this.setRow(row); }; popup.getTextLeftOffset = function () { return this.$borderSize + this.renderer.$padding + this.$imageSize; }; popup.$imageSize = 0; popup.$borderSize = 1; return popup; }; dom.importCssString(` .ace_editor.ace_autocomplete .ace_marker-layer .ace_active-line { background-color: #CAD6FA; z-index: 1; } .ace_dark.ace_editor.ace_autocomplete .ace_marker-layer .ace_active-line { background-color: #3a674e; } .ace_editor.ace_autocomplete .ace_line-hover { border: 1px solid #abbffe; margin-top: -1px; background: rgba(233,233,253,0.4); position: absolute; z-index: 2; } .ace_dark.ace_editor.ace_autocomplete .ace_line-hover { border: 1px solid rgba(109, 150, 13, 0.8); background: rgba(58, 103, 78, 0.62); } .ace_completion-meta { opacity: 0.5; margin: 0.9em; } .ace_completion-message { color: blue; } .ace_editor.ace_autocomplete .ace_completion-highlight{ color: #2d69c7; } .ace_dark.ace_editor.ace_autocomplete .ace_completion-highlight{ color: #93ca12; } .ace_editor.ace_autocomplete { width: 300px; z-index: 200000; border: 1px lightgray solid; position: fixed; box-shadow: 2px 3px 5px rgba(0,0,0,.2); line-height: 1.4; background: #fefefe; color: #111; } .ace_dark.ace_editor.ace_autocomplete { border: 1px #484747 solid; box-shadow: 2px 3px 5px rgba(0, 0, 0, 0.51); line-height: 1.4; background: #25282c; color: #c1c1c1; }`, 'autocompletion.css'); exports.AcePopup = AcePopup; exports.$singleLineEditor = $singleLineEditor; }); ace.define('ace/autocomplete/util', ['require', 'exports', 'module'], function (require, exports, module) { 'use strict'; exports.parForEach = function (array, fn, callback) { let completed = 0; const arLength = array.length; if (arLength === 0) callback(); for (let i = 0; i < arLength; i++) { fn(array[i], function (result, err) { completed++; if (completed === arLength) callback(result, err); }); } }; const ID_REGEX = /[a-zA-Z_0-9\\$\-\u00A2-\u2000\u2070-\uFFFF]/; exports.retrievePrecedingIdentifier = function (text, pos, regex) { regex = regex || ID_REGEX; const buf = []; for (let i = pos - 1; i >= 0; i--) { if (regex.test(text[i])) buf.push(text[i]); else break; } return buf.reverse().join(''); }; exports.retrieveFollowingIdentifier = function (text, pos, regex) { regex = regex || ID_REGEX; const buf = []; for (let i = pos; i < text.length; i++) { if (regex.test(text[i])) buf.push(text[i]); else break; } return buf; }; exports.getCompletionPrefix = function (editor) { const pos = editor.getCursorPosition(); const line = editor.session.getLine(pos.row); let prefix; editor.completers.forEach(function (completer) { if (completer.identifierRegexps) { completer.identifierRegexps.forEach(function (identifierRegex) { if (!prefix && identifierRegex) prefix = this.retrievePrecedingIdentifier(line, pos.column, identifierRegex); }.bind(this)); } }.bind(this)); return prefix || this.retrievePrecedingIdentifier(line, pos.column); }; }); ace.define('ace/autocomplete', ['require', 'exports', 'module', 'ace/keyboard/hash_handler', 'ace/autocomplete/popup', 'ace/autocomplete/util', 'ace/lib/lang', 'ace/lib/dom', 'ace/snippets', 'ace/config'], function (require, exports, module) { 'use strict'; const HashHandler = require('./keyboard/hash_handler').HashHandler; const AcePopup = require('./autocomplete/popup').AcePopup; const util = require('./autocomplete/util'); const lang = require('./lib/lang'); const dom = require('./lib/dom'); const snippetManager = require('./snippets').snippetManager; const config = require('./config'); const Autocomplete = function () { this.autoInsert = false; this.autoSelect = true; this.exactMatch = false; this.gatherCompletionsId = 0; this.keyboardHandler = new HashHandler(); this.keyboardHandler.bindKeys(this.commands); this.blurListener = this.blurListener.bind(this); this.changeListener = this.changeListener.bind(this); this.mousedownListener = this.mousedownListener.bind(this); this.mousewheelListener = this.mousewheelListener.bind(this); this.changeTimer = lang.delayedCall(function () { this.updateCompletions(true); }.bind(this)); this.tooltipTimer = lang.delayedCall(this.updateDocTooltip.bind(this), 50); }; (function () { this.$init = function () { this.popup = new AcePopup(document.body || document.documentElement); this.popup.on('click', function (e) { this.insertMatch(); e.stop(); }.bind(this)); this.popup.focus = this.editor.focus.bind(this.editor); this.popup.on('show', this.tooltipTimer.bind(null, null)); this.popup.on('select', this.tooltipTimer.bind(null, null)); this.popup.on('changeHoverMarker', this.tooltipTimer.bind(null, null)); return this.popup; }; this.getPopup = function () { return this.popup || this.$init(); }; this.openPopup = function (editor, prefix, keepPopupPosition) { if (!this.popup) this.$init(); this.popup.autoSelect = this.autoSelect; this.popup.setData(this.completions.filtered, this.completions.filterText); editor.keyBinding.addKeyboardHandler(this.keyboardHandler); const renderer = editor.renderer; this.popup.setRow(this.autoSelect ? 0 : -1); if (!keepPopupPosition) { this.popup.setTheme(editor.getTheme()); this.popup.setFontSize(editor.getFontSize()); const lineHeight = renderer.layerConfig.lineHeight; const pos = renderer.$cursorLayer.getPixelPosition(this.base, true); pos.left -= this.popup.getTextLeftOffset(); const rect = editor.container.getBoundingClientRect(); pos.top += rect.top - renderer.layerConfig.offset; pos.left += rect.left - editor.renderer.scrollLeft; pos.left += renderer.gutterWidth; this.popup.show(pos, lineHeight); } else if (keepPopupPosition && !prefix) this.detach(); this.changeTimer.cancel(); }; this.detach = function () { this.editor.keyBinding.removeKeyboardHandler(this.keyboardHandler); this.editor.off('changeSelection', this.changeListener); this.editor.off('blur', this.blurListener); this.editor.off('mousedown', this.mousedownListener); this.editor.off('mousewheel', this.mousewheelListener); this.changeTimer.cancel(); this.hideDocTooltip(); this.gatherCompletionsId += 1; if (this.popup && this.popup.isOpen) this.popup.hide(); if (this.base) this.base.detach(); this.activated = false; this.completions = this.base = null; }; this.changeListener = function (e) { const cursor = this.editor.selection.lead; if (cursor.row !== this.base.row || cursor.column < this.base.column) this.detach(); if (this.activated) this.changeTimer.schedule(); else this.detach(); }; this.blurListener = function (e) { const el = document.activeElement; const text = this.editor.textInput.getElement(); const fromTooltip = e.relatedTarget && this.tooltipNode && this.tooltipNode.contains(e.relatedTarget); const container = this.popup && this.popup.container; if (el !== text && el.parentNode !== container && !fromTooltip && el !== this.tooltipNode && e.relatedTarget !== text ) this.detach(); }; this.mousedownListener = function (e) { this.detach(); }; this.mousewheelListener = function (e) { this.detach(); }; this.goTo = function (where) { this.popup.goTo(where); }; this.insertMatch = function (data, options) { if (!data) data = this.popup.getData(this.popup.getRow()); if (!data) return false; const completions = this.completions; this.editor.startOperation({ command: { name: 'insertMatch' } }); if (data.completer && data.completer.insertMatch) data.completer.insertMatch(this.editor, data); else { if (completions.filterText) { const ranges = this.editor.selection.getAllRanges(); for (let i = 0, range; range = ranges[i]; i++) { range.start.column -= completions.filterText.length; this.editor.session.remove(range); } } if (data.snippet) snippetManager.insertSnippet(this.editor, data.snippet); else this.editor.execCommand('insertstring', data.value || data); } if (this.completions === completions) this.detach(); this.editor.endOperation(); }; this.commands = { Up: function (editor) { editor.completer.goTo('up'); }, Down: function (editor) { editor.completer.goTo('down'); }, 'Ctrl-Up|Ctrl-Home': function (editor) { editor.completer.goTo('start'); }, 'Ctrl-Down|Ctrl-End': function (editor) { editor.completer.goTo('end'); }, Esc: function (editor) { editor.completer.detach(); }, Return: function (editor) { return editor.completer.insertMatch(); }, 'Shift-Return': function (editor) { editor.completer.insertMatch(null, { deleteSuffix: true }); }, Tab: function (editor) { const result = editor.completer.insertMatch(); if (!result && !editor.tabstopManager) editor.completer.goTo('down'); else return result; }, PageUp: function (editor) { editor.completer.popup.gotoPageUp(); }, PageDown: function (editor) { editor.completer.popup.gotoPageDown(); } }; this.gatherCompletions = function (editor, callback) { const session = editor.getSession(); const pos = editor.getCursorPosition(); const prefix = util.getCompletionPrefix(editor); this.base = session.doc.createAnchor(pos.row, pos.column - prefix.length); this.base.$insertRight = true; let matches = []; let total = editor.completers.length; editor.completers.forEach(function (completer, i) { completer.getCompletions(editor, session, pos, prefix, function (err, results) { if (!err && results) matches = matches.concat(results); callback(null, { prefix: util.getCompletionPrefix(editor), matches: matches, finished: (--total === 0) }); }); }); return true; }; this.showPopup = function (editor, options) { if (this.editor) this.detach(); this.activated = true; this.editor = editor; if (editor.completer !== this) { if (editor.completer) editor.completer.detach(); editor.completer = this; } editor.on('changeSelection', this.changeListener); editor.on('blur', this.blurListener); editor.on('mousedown', this.mousedownListener); editor.on('mousewheel', this.mousewheelListener); this.updateCompletions(false, options); }; this.updateCompletions = function (keepPopupPosition, options) { if (keepPopupPosition && this.base && this.completions) { const pos = this.editor.getCursorPosition(); const prefix = this.editor.session.getTextRange({ start: this.base, end: pos }); if (prefix === this.completions.filterText) return; this.completions.setFilter(prefix); if (!this.completions.filtered.length) return this.detach(); if (this.completions.filtered.length === 1 && this.completions.filtered[0].value === prefix && !this.completions.filtered[0].snippet) return this.detach(); this.openPopup(this.editor, prefix, keepPopupPosition); return; } if (options && options.matches) { const pos = this.editor.getSelectionRange().start; this.base = this.editor.session.doc.createAnchor(pos.row, pos.column); this.base.$insertRight = true; this.completions = new FilteredList(options.matches); return this.openPopup(this.editor, '', keepPopupPosition); } const _id = this.gatherCompletionsId; this.gatherCompletions(this.editor, function (err, results) { if (err) console.log(err); const detachIfFinished = function () { if (!results.finished) return; return this.detach(); }.bind(this); const prefix = results.prefix; const matches = results && results.matches; if (!matches || !matches.length) return detachIfFinished(); if (prefix.indexOf(results.prefix) !== 0 || _id !== this.gatherCompletionsId) return; this.completions = new FilteredList(matches); if (this.exactMatch) this.completions.exactMatch = true; this.completions.setFilter(prefix); const filtered = this.completions.filtered; if (!filtered.length) return detachIfFinished(); if (filtered.length === 1 && filtered[0].value === prefix && !filtered[0].snippet) return detachIfFinished(); if (this.autoInsert && filtered.length === 1 && results.finished) return this.insertMatch(filtered[0]); this.openPopup(this.editor, prefix, keepPopupPosition); }.bind(this)); }; this.cancelContextMenu = function () { this.editor.$mouseHandler.cancelContextMenu(); }; this.updateDocTooltip = function () { const popup = this.popup; const all = popup.data; const selected = all && (all[popup.getHoveredRow()] || all[popup.getRow()]); let doc = null; if (!selected || !this.editor || !this.popup.isOpen) return this.hideDocTooltip(); this.editor.completers.some(function (completer) { if (completer.getDocTooltip) doc = completer.getDocTooltip(selected); return doc; }); if (!doc && typeof selected !== 'string') doc = selected; if (typeof doc === 'string') doc = { docText: doc }; if (!doc || !(doc.docHTML || doc.docText)) return this.hideDocTooltip(); this.showDocTooltip(doc); }; this.showDocTooltip = function (item) { if (!this.tooltipNode) { this.tooltipNode = dom.createElement('div'); this.tooltipNode.className = 'ace_tooltip ace_doc-tooltip'; this.tooltipNode.style.margin = 0; this.tooltipNode.style.pointerEvents = 'auto'; this.tooltipNode.tabIndex = -1; this.tooltipNode.onblur = this.blurListener.bind(this); this.tooltipNode.onclick = this.onTooltipClick.bind(this); } const tooltipNode = this.tooltipNode; if (item.docHTML) tooltipNode.innerHTML = item.docHTML; else if (item.docText) tooltipNode.textContent = item.docText; if (!tooltipNode.parentNode) document.body.appendChild(tooltipNode); const popup = this.popup; const rect = popup.container.getBoundingClientRect(); tooltipNode.style.top = popup.container.style.top; tooltipNode.style.bottom = popup.container.style.bottom; tooltipNode.style.display = 'block'; if (window.innerWidth - rect.right < 320) { if (rect.left < 320) { if (popup.isTopdown) { tooltipNode.style.top = rect.bottom + 'px'; tooltipNode.style.left = rect.left + 'px'; tooltipNode.style.right = ''; tooltipNode.style.bottom = ''; } else { tooltipNode.style.top = popup.container.offsetTop - tooltipNode.offsetHeight + 'px'; tooltipNode.style.left = rect.left + 'px'; tooltipNode.style.right = ''; tooltipNode.style.bottom = ''; } } else { tooltipNode.style.right = window.innerWidth - rect.left + 'px'; tooltipNode.style.left = ''; } } else { tooltipNode.style.left = (rect.right + 1) + 'px'; tooltipNode.style.right = ''; } }; this.hideDocTooltip = function () { this.tooltipTimer.cancel(); if (!this.tooltipNode) return; const el = this.tooltipNode; if (!this.editor.isFocused() && document.activeElement === el) this.editor.focus(); this.tooltipNode = null; if (el.parentNode) el.parentNode.removeChild(el); }; this.onTooltipClick = function (e) { let a = e.target; while (a && a !== this.tooltipNode) { if (a.nodeName === 'A' && a.href) { a.rel = 'noreferrer'; a.target = '_blank'; break; } a = a.parentNode; } }; this.destroy = function () { this.detach(); if (this.popup) { this.popup.destroy(); const el = this.popup.container; if (el && el.parentNode) el.parentNode.removeChild(el); } if (this.editor && this.editor.completer === this) this.editor.completer = null; this.popup = null; }; }).call(Autocomplete.prototype); Autocomplete.for = function (editor) { if (editor.completer) return editor.completer; if (config.get('sharedPopups')) { if (!Autocomplete.$shared) Autocomplete.$sharedInstance = new Autocomplete(); editor.completer = Autocomplete.$sharedInstance; } else { editor.completer = new Autocomplete(); editor.once('destroy', function (e, editor) { editor.completer.destroy(); }); } return editor.completer; }; Autocomplete.startCommand = { name: 'startAutocomplete', exec: function (editor, options) { const completer = Autocomplete.for(editor); completer.autoInsert = false; completer.autoSelect = true; completer.showPopup(editor, options); completer.cancelContextMenu(); }, bindKey: 'Ctrl-Space|Ctrl-Shift-Space|Alt-Space' }; const FilteredList = function (array, filterText) { this.all = array; this.filtered = array; this.filterText = filterText || ''; this.exactMatch = false; }; (function () { this.setFilter = function (str) { let matches; if (str.length > this.filterText && str.lastIndexOf(this.filterText, 0) === 0) matches = this.filtered; else matches = this.all; this.filterText = str; matches = this.filterCompletions(matches, this.filterText); matches = matches.sort(function (a, b) { return b.exactMatch - a.exactMatch || b.$score - a.$score || (a.caption || a.value).localeCompare(b.caption || b.value); }); let prev = null; matches = matches.filter(function (item) { const caption = item.snippet || item.caption || item.value; if (caption === prev) return false; prev = caption; return true; }); this.filtered = matches; }; this.filterCompletions = function (items, needle) { const results = []; const upper = needle.toUpperCase(); const lower = needle.toLowerCase(); loop: for (let i = 0, item; item = items[i]; i++) { const caption = item.caption || item.value || item.snippet; if (!caption) continue; let lastIndex = -1; let matchMask = 0; let penalty = 0; let index, distance; if (this.exactMatch) { if (needle !== caption.substr(0, needle.length)) continue loop; } else { const fullMatchIndex = caption.toLowerCase().indexOf(lower); if (fullMatchIndex > -1) penalty = fullMatchIndex; else { for (let j = 0; j < needle.length; j++) { const i1 = caption.indexOf(lower[j], lastIndex + 1); const i2 = caption.indexOf(upper[j], lastIndex + 1); index = (i1 >= 0) ? ((i2 < 0 || i1 < i2) ? i1 : i2) : i2; if (index < 0) continue loop; distance = index - lastIndex - 1; if (distance > 0) { if (lastIndex === -1) penalty += 10; penalty += distance; matchMask = matchMask | (1 << j); } lastIndex = index; } } } item.matchMask = matchMask; item.exactMatch = penalty ? 0 : 1; item.$score = (item.score || 0) - penalty; results.push(item); } return results; }; }).call(FilteredList.prototype); exports.Autocomplete = Autocomplete; exports.FilteredList = FilteredList; }); ace.define('ace/autocomplete/text_completer', ['require', 'exports', 'module', 'ace/range'], function (require, exports, module) { const Range = require('../range').Range; const splitRegex = /[^a-zA-Z_0-9\\$\-\u00C0-\u1FFF\u2C00-\uD7FF\w]+/; function getWordIndex (doc, pos) { const textBefore = doc.getTextRange(Range.fromPoints({ row: 0, column: 0 }, pos)); return textBefore.split(splitRegex).length - 1; } function wordDistance (doc, pos) { const prefixPos = getWordIndex(doc, pos); const words = doc.getValue().split(splitRegex); const wordScores = Object.create(null); const currentWord = words[prefixPos]; words.forEach(function (word, idx) { if (!word || word === currentWord) return; const distance = Math.abs(prefixPos - idx); const score = words.length - distance; if (wordScores[word]) wordScores[word] = Math.max(score, wordScores[word]); else wordScores[word] = score; }); return wordScores; } exports.getCompletions = function (editor, session, pos, prefix, callback) { const wordScore = wordDistance(session, pos); const wordList = Object.keys(wordScore); callback(null, wordList.map(function (word) { return { caption: word, value: word, score: wordScore[word], meta: 'local' }; })); }; }); ace.define('ace/ext/language_tools', ['require', 'exports', 'module', 'ace/snippets', 'ace/autocomplete', 'ace/config', 'ace/lib/lang', 'ace/autocomplete/util', 'ace/autocomplete/text_completer', 'ace/editor', 'ace/config'], function (require, exports, module) { 'use strict'; const snippetManager = require('../snippets').snippetManager; const Autocomplete = require('../autocomplete').Autocomplete; const config = require('../config'); const lang = require('../lib/lang'); const util = require('../autocomplete/util'); const textCompleter = require('../autocomplete/text_completer'); const keyWordCompleter = { getCompletions: function (editor, session, pos, prefix, callback) { if (session.$mode.completer) return session.$mode.completer.getCompletions(editor, session, pos, prefix, callback); const state = editor.session.getState(pos.row); const completions = session.$mode.getCompletions(state, session, pos, prefix); callback(null, completions); } }; const snippetCompleter = { getCompletions: function (editor, session, pos, prefix, callback) { let scopes = []; const token = session.getTokenAt(pos.row, pos.column); if (token && token.type.match(/(tag-name|tag-open|tag-whitespace|attribute-name|attribute-value)\.xml$/)) scopes.push('html-tag'); else scopes = snippetManager.getActiveScopes(editor); const snippetMap = snippetManager.snippetMap; const completions = []; scopes.forEach(function (scope) { const snippets = snippetMap[scope] || []; for (let i = snippets.length; i--;) { const s = snippets[i]; const caption = s.name || s.tabTrigger; if (!caption) continue; completions.push({ caption: caption, snippet: s.content, meta: s.tabTrigger && !s.name ? s.tabTrigger + '\u21E5 ' : 'snippet', type: 'snippet' }); } }, this); callback(null, completions); }, getDocTooltip: function (item) { if (item.type === 'snippet' && !item.docHTML) { item.docHTML = [ '', lang.escapeHTML(item.caption), '', '