client/app/components/queries/QueryEditor/ace.js (91 lines of code) (raw):

import { isNil, map } from "lodash"; import AceEditor from "react-ace"; import ace from "ace-builds"; import "ace-builds/src-noconflict/ext-language_tools"; import "ace-builds/src-noconflict/mode-json"; import "ace-builds/src-noconflict/mode-python"; import "ace-builds/src-noconflict/mode-sql"; import "ace-builds/src-noconflict/mode-yaml"; import "ace-builds/src-noconflict/theme-textmate"; import "ace-builds/src-noconflict/ext-searchbox"; const langTools = ace.acequire("ace/ext/language_tools"); const snippetsModule = ace.acequire("ace/snippets"); // By default Ace will try to load snippet files for the different modes and fail. // We don't need them, so we use these placeholders until we define our own. function defineDummySnippets(mode) { ace.define(`ace/snippets/${mode}`, ["require", "exports", "module"], (require, exports) => { exports.snippetText = ""; exports.scope = mode; }); } defineDummySnippets("python"); defineDummySnippets("sql"); defineDummySnippets("json"); defineDummySnippets("yaml"); function buildTableColumnKeywords(table) { const keywords = []; table.columns.forEach(column => { keywords.push({ name: `${table.name}.${column.name}`, value: `${table.name}.${column.name}`, score: 100, meta: "Column", }); }); return keywords; } function buildKeywordsFromSchema(schema) { const tableKeywords = []; const columnKeywords = {}; const tableColumnKeywords = {}; schema.forEach(table => { tableKeywords.push({ name: table.name, value: table.name, score: 100, meta: "Table", }); tableColumnKeywords[table.name] = buildTableColumnKeywords(table); table.columns.forEach(c => { columnKeywords[c.name] = "Column"; }); }); return { table: tableKeywords, column: map(columnKeywords, (v, k) => ({ name: k, value: k, score: 50, meta: v, })), tableColumn: tableColumnKeywords, }; } const schemaCompleterKeywords = {}; export function updateSchemaCompleter(editorKey, schema = null) { schemaCompleterKeywords[editorKey] = isNil(schema) ? null : buildKeywordsFromSchema(schema); } langTools.setCompleters([ langTools.snippetCompleter, langTools.keyWordCompleter, langTools.textCompleter, { identifierRegexps: [/[a-zA-Z_0-9.\-\u00A2-\uFFFF]/], getCompletions: (editor, session, pos, prefix, callback) => { const { table, column, tableColumn } = schemaCompleterKeywords[editor.id] || { table: [], column: [], tableColumn: [], }; if (prefix.length === 0 || table.length === 0) { callback(null, []); return; } if (prefix[prefix.length - 1] === ".") { const tableName = prefix.substring(0, prefix.length - 1); callback(null, table.concat(tableColumn[tableName])); return; } callback(null, table.concat(column)); }, }, ]); export { AceEditor, langTools, snippetsModule };