Browse Source

sql_editor

master
windy 7 years ago
parent
commit
8842578229
  1. 24
      demo/js/base/editor/demo.sql_editor.js
  2. 4
      demo/js/config/base.js
  3. 280
      src/base/formula/codemirror/sql-hint.js
  4. 483
      src/base/formula/codemirror/sql-mode.js
  5. 101
      src/base/formula/editor.sql.js
  6. 9
      src/less/base/sql/sql.less

24
demo/js/base/editor/demo.sql_editor.js

@ -0,0 +1,24 @@
/**
* Created by Windy on 2017/12/15.
*/
Demo.SQLEditor = BI.inherit(BI.Widget, {
props: {
baseCls: "demo-editor"
},
render: function () {
this.formula = BI.createWidget({
type : 'bi.sql_editor',
width : 300,
height : 200,
value : 'select * from DEMO_CONTRACT'
});
BI.createWidget({
type: "bi.left",
element: this,
items: [this.formula],
hgap: 20,
vgap: 20
})
}
});
BI.shortcut("demo.sql_editor", Demo.SQLEditor);

4
demo/js/config/base.js

@ -62,6 +62,10 @@ Demo.BASE_CONFIG = [{
pId: 202, pId: 202,
text: "bi.textarea_editor", text: "bi.textarea_editor",
value: "demo.textarea_editor" value: "demo.textarea_editor"
}, {
pId: 202,
text: "bi.sql_editor",
value: "demo.sql_editor"
}, { }, {
pId: 202, pId: 202,
text: "bi.formula_editor", text: "bi.formula_editor",

280
src/base/formula/codemirror/sql-hint.js

@ -0,0 +1,280 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
(function(mod) {
mod(CodeMirror);
})(function(CodeMirror) {
var tables;
var defaultTable;
var keywords;
var identifierQuote;
var CONS = {
QUERY_DIV: ";",
ALIAS_KEYWORD: "AS"
};
var Pos = CodeMirror.Pos, cmpPos = CodeMirror.cmpPos;
function isArray(val) { return Object.prototype.toString.call(val) == "[object Array]" }
function getKeywords(editor) {
var mode = editor.doc.modeOption;
if (mode === "sql") mode = "text/x-sql";
return CodeMirror.resolveMode(mode).keywords;
}
function getIdentifierQuote(editor) {
var mode = editor.doc.modeOption;
if (mode === "sql") mode = "text/x-sql";
return CodeMirror.resolveMode(mode).identifierQuote || "`";
}
function getText(item) {
return typeof item == "string" ? item : item.text;
}
function wrapTable(name, value) {
if (isArray(value)) value = {columns: value}
if (!value.text) value.text = name
return value
}
function parseTables(input) {
var result = {}
if (isArray(input)) {
for (var i = input.length - 1; i >= 0; i--) {
var item = input[i]
result[getText(item).toUpperCase()] = wrapTable(getText(item), item)
}
} else if (input) {
for (var name in input)
result[name.toUpperCase()] = wrapTable(name, input[name])
}
return result
}
function getTable(name) {
return tables[name.toUpperCase()]
}
function shallowClone(object) {
var result = {};
for (var key in object) if (object.hasOwnProperty(key))
result[key] = object[key];
return result;
}
function match(string, word) {
if (BI.isNotEmptyString(string)) {
var len = string.length;
var sub = getText(word).substr(0, len);
return string.toUpperCase() === sub.toUpperCase();
}
}
function addMatches(result, search, wordlist, formatter) {
if (isArray(wordlist)) {
for (var i = 0; i < wordlist.length; i++)
if (match(search, wordlist[i])) result.push(formatter(wordlist[i]))
} else {
for (var word in wordlist) if (wordlist.hasOwnProperty(word)) {
var val = wordlist[word]
if (!val || val === true)
val = word
else
val = val.displayText ? {text: val.text, displayText: val.displayText} : val.text
if (match(search, val)) result.push(formatter(val))
}
}
}
function cleanName(name) {
// Get rid name from identifierQuote and preceding dot(.)
if (name.charAt(0) == ".") {
name = name.substr(1);
}
// replace doublicated identifierQuotes with single identifierQuotes
// and remove single identifierQuotes
var nameParts = name.split(identifierQuote+identifierQuote);
for (var i = 0; i < nameParts.length; i++)
nameParts[i] = nameParts[i].replace(new RegExp(identifierQuote,"g"), "");
return nameParts.join(identifierQuote);
}
function insertIdentifierQuotes(name) {
var nameParts = getText(name).split(".");
for (var i = 0; i < nameParts.length; i++)
nameParts[i] = identifierQuote +
// doublicate identifierQuotes
nameParts[i].replace(new RegExp(identifierQuote,"g"), identifierQuote+identifierQuote) +
identifierQuote;
var escaped = nameParts.join(".");
if (typeof name == "string") return escaped;
name = shallowClone(name);
name.text = escaped;
return name;
}
function nameCompletion(cur, token, result, editor) {
// Try to complete table, column names and return start position of completion
var useIdentifierQuotes = false;
var nameParts = [];
var start = token.start;
var cont = true;
while (cont) {
cont = (token.string.charAt(0) == ".");
useIdentifierQuotes = useIdentifierQuotes || (token.string.charAt(0) == identifierQuote);
start = token.start;
nameParts.unshift(cleanName(token.string));
token = editor.getTokenAt(Pos(cur.line, token.start));
if (token.string == ".") {
cont = true;
token = editor.getTokenAt(Pos(cur.line, token.start));
}
}
// Try to complete table names
var string = nameParts.join(".");
addMatches(result, string, tables, function(w) {
return useIdentifierQuotes ? insertIdentifierQuotes(w) : w;
});
// Try to complete columns from defaultTable
addMatches(result, string, defaultTable, function(w) {
return useIdentifierQuotes ? insertIdentifierQuotes(w) : w;
});
// Try to complete columns
string = nameParts.pop();
var table = nameParts.join(".");
var alias = false;
var aliasTable = table;
// Check if table is available. If not, find table by Alias
if (!getTable(table)) {
var oldTable = table;
table = findTableByAlias(table, editor);
if (table !== oldTable) alias = true;
}
var columns = getTable(table);
if (columns && columns.columns)
columns = columns.columns;
if (columns) {
addMatches(result, string, columns, function(w) {
var tableInsert = table;
if (alias == true) tableInsert = aliasTable;
if (typeof w == "string") {
w = tableInsert + "." + w;
} else {
w = shallowClone(w);
w.text = tableInsert + "." + w.text;
}
return useIdentifierQuotes ? insertIdentifierQuotes(w) : w;
});
}
return start;
}
function eachWord(lineText, f) {
var words = lineText.split(/\s+/)
for (var i = 0; i < words.length; i++)
if (words[i]) f(words[i].replace(/[,;]/g, ''))
}
function findTableByAlias(alias, editor) {
var doc = editor.doc;
var fullQuery = doc.getValue();
var aliasUpperCase = alias.toUpperCase();
var previousWord = "";
var table = "";
var separator = [];
var validRange = {
start: Pos(0, 0),
end: Pos(editor.lastLine(), editor.getLineHandle(editor.lastLine()).length)
};
//add separator
var indexOfSeparator = fullQuery.indexOf(CONS.QUERY_DIV);
while(indexOfSeparator != -1) {
separator.push(doc.posFromIndex(indexOfSeparator));
indexOfSeparator = fullQuery.indexOf(CONS.QUERY_DIV, indexOfSeparator+1);
}
separator.unshift(Pos(0, 0));
separator.push(Pos(editor.lastLine(), editor.getLineHandle(editor.lastLine()).text.length));
//find valid range
var prevItem = null;
var current = editor.getCursor()
for (var i = 0; i < separator.length; i++) {
if ((prevItem == null || cmpPos(current, prevItem) > 0) && cmpPos(current, separator[i]) <= 0) {
validRange = {start: prevItem, end: separator[i]};
break;
}
prevItem = separator[i];
}
var query = doc.getRange(validRange.start, validRange.end, false);
for (var i = 0; i < query.length; i++) {
var lineText = query[i];
eachWord(lineText, function(word) {
var wordUpperCase = word.toUpperCase();
if (wordUpperCase === aliasUpperCase && getTable(previousWord))
table = previousWord;
if (wordUpperCase !== CONS.ALIAS_KEYWORD)
previousWord = word;
});
if (table) break;
}
return table;
}
CodeMirror.registerHelper("hint", "sql", function(editor, options) {
tables = parseTables(options && options.tables)
var defaultTableName = options && options.defaultTable;
var disableKeywords = options && options.disableKeywords;
defaultTable = defaultTableName && getTable(defaultTableName);
keywords = getKeywords(editor);
identifierQuote = getIdentifierQuote(editor);
if (defaultTableName && !defaultTable)
defaultTable = findTableByAlias(defaultTableName, editor);
defaultTable = defaultTable || [];
if (defaultTable.columns)
defaultTable = defaultTable.columns;
var cur = editor.getCursor();
var result = [];
var token = editor.getTokenAt(cur), start, end, search;
if (token.end > cur.ch) {
token.end = cur.ch;
token.string = token.string.slice(0, cur.ch - token.start);
}
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) == "." || search.charAt(0) == identifierQuote) {
start = nameCompletion(cur, token, result, editor);
} else {
addMatches(result, search, tables, function(w) {return w;});
addMatches(result, search, defaultTable, function(w) {return w;});
if (!disableKeywords)
addMatches(result, search, keywords, function(w) {return w.toUpperCase();});
}
return {list: result, from: Pos(cur.line, start), to: Pos(cur.line, end)};
});
});

483
src/base/formula/codemirror/sql-mode.js

File diff suppressed because one or more lines are too long

101
src/base/formula/editor.sql.js

@ -0,0 +1,101 @@
/**
* Created by Windy on 2017/12/15.
*/
BI.SQLEditor = BI.inherit(BI.Widget, {
_defaultConfig: function () {
return $.extend(BI.CodeEditor.superclass._defaultConfig.apply(), {
baseCls: 'bi-sql-editor',
value: '',
lineHeight: 2,
showHint: true
});
},
_init: function () {
BI.CodeEditor.superclass._init.apply(this, arguments);
var o = this.options, self = this;
this.editor = CodeMirror(this.element[0], {
//mode: {name: 'text/x-sql'},
mode: "text/x-sql",
textWrapping: true,
lineWrapping: true,
lineNumbers: false
});
o.lineHeight === 1 ? this.element.addClass("codemirror-low-line-height") : this.element.addClass("codemirror-high-line-height");
this.editor.on("change", function (cm, change) {
self._checkWaterMark();
if (o.showHint) {
CodeMirror.showHint(cm, CodeMirror.formulaHint, {completeSingle: false});
}
BI.nextTick(function () {
self.fireEvent(BI.FormulaEditor.EVENT_CHANGE)
});
});
this.editor.on("focus", function () {
self._checkWaterMark();
self.fireEvent(BI.FormulaEditor.EVENT_FOCUS);
});
this.editor.on("blur", function () {
self.fireEvent(BI.FormulaEditor.EVENT_BLUR);
});
//水印
this.watermark = BI.createWidget({
type: "bi.label",
text: BI.i18nText("Please_Enter_SQL"),
cls: "bi-water-mark",
whiteSpace: "nowrap",
textAlign: "left"
});
this.watermark.element.bind(
"mousedown", function (e) {
self.insertString("");
self.editor.focus();
e.stopEvent();
}
);
this.watermark.element.bind("click", function (e) {
self.editor.focus();
e.stopEvent();
});
BI.createWidget({
type: "bi.absolute",
element: this,
items: [{
el: this.watermark,
top: 0,
left: 5
}]
});
if (BI.isKey(o.value)) {
BI.nextTick(function () {
self.setValue(o.value);
});
}
},
insertString: function (str) {
this.editor.replaceSelection(str);
this.editor.focus();
},
_checkWaterMark: function () {
var o = this.options;
if (!this.disabledWaterMark && BI.isEmptyString(this.editor.getValue()) && BI.isKey(o.watermark)) {
this.watermark && this.watermark.visible();
} else {
this.watermark && this.watermark.invisible();
}
},
getValue: function () {
},
setValue: function (v) {
}
});
BI.shortcut("bi.sql_editor", BI.SQLEditor);

9
src/less/base/sql/sql.less

@ -0,0 +1,9 @@
@import "../../bibase";
.bi-sql-editor{
& .cm-s-default span[class*="#"] {
color: @color-bi-text-warning;
}
}
Loading…
Cancel
Save