From 6cc3ea6eae4a0cef0987cb40dfe8b2fc20ffe20a Mon Sep 17 00:00:00 2001 From: Nisarg Jhaveri Date: Tue, 14 Oct 2014 23:22:15 +0530 Subject: [PATCH] Autocomplete table/column names in SQL editors - In DB and Table level SQL editors, console - Updated CodeMirror addon `sql-hint.js` to newer version Signed-off-by: Nisarg Jhaveri --- db_sql_autocomplete.php | 22 +++++++++ js/codemirror/addon/hint/sql-hint.js | 72 ++++++++++++++++++++-------- js/functions.js | 51 +++++++++++++++++++- 3 files changed, 125 insertions(+), 20 deletions(-) create mode 100644 db_sql_autocomplete.php diff --git a/db_sql_autocomplete.php b/db_sql_autocomplete.php new file mode 100644 index 0000000000..48cf3d884c --- /dev/null +++ b/db_sql_autocomplete.php @@ -0,0 +1,22 @@ +getTables($db); + foreach ($tableNames as $tableName) { + $sql_autocomplete[$tableName] = $GLOBALS['dbi']->getColumnNames($db, $tableName); + } +} + +$response = PMA_Response::getInstance(); +$response->addJSON("tables", json_encode($sql_autocomplete)); diff --git a/js/codemirror/addon/hint/sql-hint.js b/js/codemirror/addon/hint/sql-hint.js index 20653b5318..c2b511fa2f 100644 --- a/js/codemirror/addon/hint/sql-hint.js +++ b/js/codemirror/addon/hint/sql-hint.js @@ -12,6 +12,7 @@ "use strict"; var tables; + var defaultTable; var keywords; var CONS = { QUERY_DIV: ";", @@ -43,18 +44,55 @@ } } - function columnCompletion(result, editor) { + function nameCompletion(result, editor) { var cur = editor.getCursor(); var token = editor.getTokenAt(cur); + var useBacktick = (token.string.charAt(0) == "`"); var string = token.string.substr(1); - var prevCur = Pos(cur.line, token.start); - var table = editor.getTokenAt(prevCur).string; - if (!tables.hasOwnProperty(table)) - table = findTableByAlias(table, editor); - var columns = tables[table]; - if (!columns) return; + var prevToken = editor.getTokenAt(Pos(cur.line, token.start)); + if (token.string.charAt(0) == "." || prevToken.string == "."){ + //Suggest colunm names + if (prevToken.string == ".") { + var prevToken = editor.getTokenAt(Pos(cur.line, token.start - 1)); + } + var table = prevToken.string; + //Check if backtick is used in table name. If yes, use it for columns too. + var useBacktickTable = false; + if (table.match(/`/g)) { + useBacktickTable = true; + table = table.replace(/`/g, ""); + } + //Check if table is available. If not, find table by Alias + if (!tables.hasOwnProperty(table)) + table = findTableByAlias(table, editor); + var columns = tables[table]; + if (!columns) return; - addMatches(result, string, columns, function(w) {return "." + w;}); + if (useBacktick) { + addMatches(result, string, columns, function(w) {return "`" + w + "`";}); + } + else if(useBacktickTable) { + addMatches(result, string, columns, function(w) {return ".`" + w + "`";}); + } + else { + addMatches(result, string, columns, function(w) {return "." + w;}); + } + } + else { + //Suggest table names or colums in defaultTable + while (token.start && string.charAt(0) == ".") { + token = editor.getTokenAt(Pos(cur.line, token.start - 1)); + string = token.string + string; + } + if (useBacktick) { + addMatches(result, string, tables, function(w) {return "`" + w + "`";}); + addMatches(result, string, defaultTable, function(w) {return "`" + w + "`";}); + } + else { + addMatches(result, string, tables, function(w) {return w;}); + addMatches(result, string, defaultTable, function(w) {return w;}); + } + } } function eachWord(lineText, f) { @@ -128,11 +166,14 @@ CodeMirror.registerHelper("hint", "sql", function(editor, options) { tables = (options && options.tables) || {}; + var defaultTableName = options && options.defaultTable; + defaultTable = (defaultTableName && tables[defaultTableName] || []); keywords = keywords || getKeywords(editor); + var cur = editor.getCursor(); var result = []; var token = editor.getTokenAt(cur), start, end, search; - if (token.string.match(/^[.\w@]\w*$/)) { + if (token.string.match(/^[.`\w@]\w*$/)) { search = token.string; start = token.start; end = token.end; @@ -140,18 +181,11 @@ start = end = cur.ch; search = ""; } - if (search.charAt(0) == ".") { - columnCompletion(result, editor); - if (!result.length) { - while (start && search.charAt(0) == ".") { - token = editor.getTokenAt(Pos(cur.line, token.start - 1)); - start = token.start; - search = token.string + search; - } - addMatches(result, search, tables, function(w) {return w;}); - } + if (search.charAt(0) == "." || search.charAt(0) == "`") { + nameCompletion(result, editor); } else { addMatches(result, search, tables, function(w) {return w;}); + addMatches(result, search, defaultTable, function(w) {return w;}); addMatches(result, search, keywords, function(w) {return w.toUpperCase();}); } diff --git a/js/functions.js b/js/functions.js index 29b8d3a57f..2a16385fcd 100644 --- a/js/functions.js +++ b/js/functions.js @@ -34,6 +34,16 @@ var codemirror_editor = false; */ var codemirror_inline_editor = false; +/** + * @var sql_autocomplete object containing list of columns in each table + */ +var sql_autocomplete = false; + +/** + * @var sql_autocomplete_default_table string containing default table to autocomplete columns + */ +var sql_autocomplete_default_table = ''; + /** * @var chart_activeTimeouts object active timeouts that refresh the charts. When disabling a realtime chart, this can be used to stop the continuous ajax requests */ @@ -1673,13 +1683,44 @@ AJAX.registerOnload('functions.js', function () { * "inputRead" event handler for CodeMirror SQL query editors for autocompletion */ function codemirrorAutocompleteOnInputRead(instance) { + if (!instance.options.hintOptions.tables || !sql_autocomplete){ + if (!sql_autocomplete) { + // Reset after teardown + instance.options.hintOptions.tables = false; + instance.options.hintOptions.defaultTable = ''; + + var href = 'db_sql_autocomplete.php'; + var params = { + 'ajax_request': true, + 'token': PMA_commonParams.get('token'), + 'db': PMA_commonParams.get('db') + }; + $.ajax({ + type: 'POST', + url: href, + data: params, + success: function (data) { + if (data.success) { + sql_autocomplete = $.parseJSON(data.tables); + sql_autocomplete_default_table = PMA_commonParams.get('table'); + instance.options.hintOptions.tables = sql_autocomplete; + instance.options.hintOptions.defaultTable = sql_autocomplete_default_table; + } + } + }); + } + else { + instance.options.hintOptions.tables = sql_autocomplete; + instance.options.hintOptions.defaultTable = sql_autocomplete_default_table; + } + } if (instance.state.completionActive) { return; } var cur = instance.getCursor(); var token = instance.getTokenAt(cur); var string = ''; - if (token.string.match(/^[.\w@]\w*$/)) { + if (token.string.match(/^[.`\w@]\w*$/)) { string = token.string; } if (string.length > 0) { @@ -1687,6 +1728,14 @@ function codemirrorAutocompleteOnInputRead(instance) { } } +/** + * Remove autocomplete information before tearing down a page + */ +AJAX.registerTeardown('functions.js', function () { + sql_autocomplete = false; + sql_autocomplete_default_table = ''; +}); + /** * Binds the CodeMirror to the text area used to inline edit a query. */