Skip to content

Commit

Permalink
Merge pull request #1377 from nisargjhaveri/autocomplete
Browse files Browse the repository at this point in the history
Autocomplete table/column names in SQL editors
  • Loading branch information
lem9 committed Oct 15, 2014
2 parents bd25766 + 6cc3ea6 commit 0bea9ef
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 21 deletions.
22 changes: 22 additions & 0 deletions db_sql_autocomplete.php
@@ -0,0 +1,22 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Table/Column autocomplete in SQL editors
*
* @package PhpMyAdmin
*/

require_once 'libraries/common.inc.php';

$db = isset($_POST['db']) ? $_POST['db'] : $GLOBALS['db'];
$sql_autocomplete = array();

if ($db) {
$tableNames = $GLOBALS['dbi']->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));
74 changes: 54 additions & 20 deletions js/codemirror/addon/hint/sql-hint.js
Expand Up @@ -12,6 +12,7 @@
"use strict";

var tables;
var defaultTable;
var keywords;
var CONS = {
QUERY_DIV: ";",
Expand Down Expand Up @@ -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;

addMatches(result, string, columns, function(w) {return "." + w;});
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;

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) {
Expand Down Expand Up @@ -128,30 +166,26 @@

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;
} else {
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();});
}

Expand Down
51 changes: 50 additions & 1 deletion js/functions.js
Expand Up @@ -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
*/
Expand Down Expand Up @@ -1673,20 +1683,59 @@ 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) {
CodeMirror.commands.autocomplete(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.
*/
Expand Down

0 comments on commit 0bea9ef

Please sign in to comment.