windy
7 years ago
19 changed files with 3160 additions and 58 deletions
File diff suppressed because one or more lines are too long
@ -0,0 +1,32 @@ |
|||||||
|
/** |
||||||
|
* Created by Windy on 2017/12/15. |
||||||
|
*/ |
||||||
|
Demo.SQLEditor = BI.inherit(BI.Widget, { |
||||||
|
props: { |
||||||
|
baseCls: "demo-editor" |
||||||
|
}, |
||||||
|
render: function () { |
||||||
|
var self = this; |
||||||
|
this.formula = BI.createWidget({ |
||||||
|
type : 'bi.sql_editor', |
||||||
|
width : 300, |
||||||
|
height : 200, |
||||||
|
value : "select * from DEMO_CONTRACT where 合同类型 = '长期协议' and 购买数量 = sum([1,2,3,4])" |
||||||
|
}); |
||||||
|
BI.createWidget({ |
||||||
|
type: "bi.left", |
||||||
|
element: this, |
||||||
|
items: [this.formula, { |
||||||
|
type: "bi.button", |
||||||
|
height: 24, |
||||||
|
handler: function () { |
||||||
|
BI.Msg.alert("", self.formula.getValue()); |
||||||
|
} |
||||||
|
|
||||||
|
}], |
||||||
|
hgap: 20, |
||||||
|
vgap: 20 |
||||||
|
}) |
||||||
|
} |
||||||
|
}); |
||||||
|
BI.shortcut("demo.sql_editor", Demo.SQLEditor); |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,311 @@ |
|||||||
|
// 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) && word.length !== string.length) { |
||||||
|
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], 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); |
||||||
|
var keywordsCount = BI.size(keywords); |
||||||
|
var functions = []; |
||||||
|
var desc = {}; |
||||||
|
var cur = editor.getCursor(); |
||||||
|
var token = editor.getTokenAt(cur); |
||||||
|
BI.each(BI.FormulaCollections, function(idx, formula){ |
||||||
|
if(formula.lastIndexOf(token.string, 0) == 0 && !BI.contains(functions, formula)){ |
||||||
|
functions.push(formula); |
||||||
|
} |
||||||
|
}); |
||||||
|
BI.each(BI.FormulaJson, function(idx, formula){ |
||||||
|
desc[formula.name.toLowerCase()] = formula.def; |
||||||
|
}); |
||||||
|
keywords = BI.concat(BI.keys(keywords), functions); |
||||||
|
identifierQuote = getIdentifierQuote(editor); |
||||||
|
|
||||||
|
if (defaultTableName && !defaultTable) |
||||||
|
defaultTable = findTableByAlias(defaultTableName, editor); |
||||||
|
|
||||||
|
defaultTable = defaultTable || []; |
||||||
|
|
||||||
|
if (defaultTable.columns) |
||||||
|
defaultTable = defaultTable.columns; |
||||||
|
|
||||||
|
var result = []; |
||||||
|
var 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, i) { |
||||||
|
var isKeyword = i < keywordsCount; |
||||||
|
return { |
||||||
|
isKeyword: isKeyword, |
||||||
|
text: w, |
||||||
|
description: desc[w] || "SQL关键字", |
||||||
|
className: isKeyword ? "sql-keyword" : "sql-fr-function", |
||||||
|
render: function (Element, self, data) { |
||||||
|
var label = BI.createWidget({ |
||||||
|
type: "bi.label", |
||||||
|
element: Element, |
||||||
|
text: data.displayText || getText(data) |
||||||
|
}); |
||||||
|
label.setTitle(data.description, { |
||||||
|
container: "body" |
||||||
|
}); |
||||||
|
} |
||||||
|
}; |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
return {list: result, from: Pos(cur.line, start), to: Pos(cur.line, end)}; |
||||||
|
}); |
||||||
|
}); |
@ -0,0 +1,316 @@ |
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: http://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
(function(mod) { |
||||||
|
mod(CodeMirror); |
||||||
|
})(function(CodeMirror) { |
||||||
|
"use strict"; |
||||||
|
|
||||||
|
CodeMirror.defineMode("sql", function(config, parserConfig) { |
||||||
|
"use strict"; |
||||||
|
|
||||||
|
var client = parserConfig.client || {}, |
||||||
|
atoms = parserConfig.atoms || {"false": true, "true": true, "null": true}, |
||||||
|
builtin = parserConfig.builtin || {}, |
||||||
|
keywords = parserConfig.keywords || {}, |
||||||
|
operatorChars = parserConfig.operatorChars || /^[*+\-%<>!=&|~^]/, |
||||||
|
support = parserConfig.support || {}, |
||||||
|
hooks = parserConfig.hooks || {}, |
||||||
|
dateSQL = parserConfig.dateSQL || {"date" : true, "time" : true, "timestamp" : true}, |
||||||
|
functions = parserConfig.functions || {}; |
||||||
|
|
||||||
|
function tokenBase(stream, state) { |
||||||
|
var ch = stream.next(); |
||||||
|
|
||||||
|
// call hooks from the mime type
|
||||||
|
if (hooks[ch]) { |
||||||
|
var result = hooks[ch](stream, state); |
||||||
|
if (result !== false) return result; |
||||||
|
} |
||||||
|
|
||||||
|
if (support.hexNumber && |
||||||
|
((ch == "0" && stream.match(/^[xX][0-9a-fA-F]+/)) |
||||||
|
|| (ch == "x" || ch == "X") && stream.match(/^'[0-9a-fA-F]+'/))) { |
||||||
|
// hex
|
||||||
|
// ref: http://dev.mysql.com/doc/refman/5.5/en/hexadecimal-literals.html
|
||||||
|
return "number"; |
||||||
|
} else if (support.binaryNumber && |
||||||
|
(((ch == "b" || ch == "B") && stream.match(/^'[01]+'/)) |
||||||
|
|| (ch == "0" && stream.match(/^b[01]+/)))) { |
||||||
|
// bitstring
|
||||||
|
// ref: http://dev.mysql.com/doc/refman/5.5/en/bit-field-literals.html
|
||||||
|
return "number"; |
||||||
|
} else if (ch.charCodeAt(0) > 47 && ch.charCodeAt(0) < 58) { |
||||||
|
// numbers
|
||||||
|
// ref: http://dev.mysql.com/doc/refman/5.5/en/number-literals.html
|
||||||
|
stream.match(/^[0-9]*(\.[0-9]+)?([eE][-+]?[0-9]+)?/); |
||||||
|
support.decimallessFloat && stream.match(/^\.(?!\.)/); |
||||||
|
return "number"; |
||||||
|
} else if (ch == "?" && (stream.eatSpace() || stream.eol() || stream.eat(";"))) { |
||||||
|
// placeholders
|
||||||
|
return "variable-3"; |
||||||
|
} else if (ch == "'" || (ch == '"' && support.doubleQuote)) { |
||||||
|
// strings
|
||||||
|
// ref: http://dev.mysql.com/doc/refman/5.5/en/string-literals.html
|
||||||
|
state.tokenize = tokenLiteral(ch); |
||||||
|
return state.tokenize(stream, state); |
||||||
|
} else if ((((support.nCharCast && (ch == "n" || ch == "N")) |
||||||
|
|| (support.charsetCast && ch == "_" && stream.match(/[a-z][a-z0-9]*/i))) |
||||||
|
&& (stream.peek() == "'" || stream.peek() == '"'))) { |
||||||
|
// charset casting: _utf8'str', N'str', n'str'
|
||||||
|
// ref: http://dev.mysql.com/doc/refman/5.5/en/string-literals.html
|
||||||
|
return "keyword"; |
||||||
|
} else if (/^[\(\),\;\[\]]/.test(ch)) { |
||||||
|
// no highlighting
|
||||||
|
return null; |
||||||
|
} else if (support.commentSlashSlash && ch == "/" && stream.eat("/")) { |
||||||
|
// 1-line comment
|
||||||
|
stream.skipToEnd(); |
||||||
|
return "comment"; |
||||||
|
} else if ((support.commentHash && ch == "#") |
||||||
|
|| (ch == "-" && stream.eat("-") && (!support.commentSpaceRequired || stream.eat(" ")))) { |
||||||
|
// 1-line comments
|
||||||
|
// ref: https://kb.askmonty.org/en/comment-syntax/
|
||||||
|
stream.skipToEnd(); |
||||||
|
return "comment"; |
||||||
|
} else if (ch == "/" && stream.eat("*")) { |
||||||
|
// multi-line comments
|
||||||
|
// ref: https://kb.askmonty.org/en/comment-syntax/
|
||||||
|
state.tokenize = tokenComment(1); |
||||||
|
return state.tokenize(stream, state); |
||||||
|
} else if (ch == ".") { |
||||||
|
// .1 for 0.1
|
||||||
|
if (support.zerolessFloat && stream.match(/^(?:\d+(?:e[+-]?\d+)?)/i)) |
||||||
|
return "number"; |
||||||
|
if (stream.match(/^\.+/)) |
||||||
|
return null |
||||||
|
// .table_name (ODBC)
|
||||||
|
// // ref: http://dev.mysql.com/doc/refman/5.6/en/identifier-qualifiers.html
|
||||||
|
if (support.ODBCdotTable && stream.match(/^[\w\d_]+/)) |
||||||
|
return "variable-2"; |
||||||
|
} else if (operatorChars.test(ch)) { |
||||||
|
// operators
|
||||||
|
stream.eatWhile(operatorChars); |
||||||
|
return null; |
||||||
|
} else if (ch == '{' && |
||||||
|
(stream.match(/^( )*(d|D|t|T|ts|TS)( )*'[^']*'( )*}/) || stream.match(/^( )*(d|D|t|T|ts|TS)( )*"[^"]*"( )*}/))) { |
||||||
|
// dates (weird ODBC syntax)
|
||||||
|
// ref: http://dev.mysql.com/doc/refman/5.5/en/date-and-time-literals.html
|
||||||
|
return "number"; |
||||||
|
} else { |
||||||
|
stream.eatWhile(/^[_\w\d]/); |
||||||
|
var word = stream.current().toLowerCase(); |
||||||
|
// dates (standard SQL syntax)
|
||||||
|
// ref: http://dev.mysql.com/doc/refman/5.5/en/date-and-time-literals.html
|
||||||
|
if (dateSQL.hasOwnProperty(word) && (stream.match(/^( )+'[^']*'/) || stream.match(/^( )+"[^"]*"/))) |
||||||
|
return "number"; |
||||||
|
if (atoms.hasOwnProperty(word)) return "atom"; |
||||||
|
if (builtin.hasOwnProperty(word)) return "builtin"; |
||||||
|
if (functions.hasOwnProperty(word) && stream.peek() === '(') return "function"; |
||||||
|
if (keywords.hasOwnProperty(word)) return "keyword"; |
||||||
|
if (client.hasOwnProperty(word)) return "string-2"; |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// 'string', with char specified in quote escaped by '\'
|
||||||
|
function tokenLiteral(quote) { |
||||||
|
return function(stream, state) { |
||||||
|
var escaped = false, ch; |
||||||
|
while ((ch = stream.next()) != null) { |
||||||
|
if (ch == quote && !escaped) { |
||||||
|
state.tokenize = tokenBase; |
||||||
|
break; |
||||||
|
} |
||||||
|
escaped = !escaped && ch == "\\"; |
||||||
|
} |
||||||
|
return "string"; |
||||||
|
}; |
||||||
|
} |
||||||
|
function tokenComment(depth) { |
||||||
|
return function(stream, state) { |
||||||
|
var m = stream.match(/^.*?(\/\*|\*\/)/) |
||||||
|
if (!m) stream.skipToEnd() |
||||||
|
else if (m[1] == "/*") state.tokenize = tokenComment(depth + 1) |
||||||
|
else if (depth > 1) state.tokenize = tokenComment(depth - 1) |
||||||
|
else state.tokenize = tokenBase |
||||||
|
return "comment" |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function pushContext(stream, state, type) { |
||||||
|
state.context = { |
||||||
|
prev: state.context, |
||||||
|
indent: stream.indentation(), |
||||||
|
col: stream.column(), |
||||||
|
type: type |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
function popContext(state) { |
||||||
|
state.indent = state.context.indent; |
||||||
|
state.context = state.context.prev; |
||||||
|
} |
||||||
|
|
||||||
|
return { |
||||||
|
startState: function() { |
||||||
|
return {tokenize: tokenBase, context: null}; |
||||||
|
}, |
||||||
|
|
||||||
|
token: function(stream, state) { |
||||||
|
if (stream.sol()) { |
||||||
|
if (state.context && state.context.align == null) |
||||||
|
state.context.align = false; |
||||||
|
} |
||||||
|
if (state.tokenize == tokenBase && stream.eatSpace()) return null; |
||||||
|
|
||||||
|
var style = state.tokenize(stream, state); |
||||||
|
if (style == "comment") return style; |
||||||
|
|
||||||
|
if (state.context && state.context.align == null) |
||||||
|
state.context.align = true; |
||||||
|
|
||||||
|
var tok = stream.current(); |
||||||
|
if (tok == "(") |
||||||
|
pushContext(stream, state, ")"); |
||||||
|
else if (tok == "[") |
||||||
|
pushContext(stream, state, "]"); |
||||||
|
else if (state.context && state.context.type == tok) |
||||||
|
popContext(state); |
||||||
|
return style; |
||||||
|
}, |
||||||
|
|
||||||
|
indent: function(state, textAfter) { |
||||||
|
var cx = state.context; |
||||||
|
if (!cx) return CodeMirror.Pass; |
||||||
|
var closing = textAfter.charAt(0) == cx.type; |
||||||
|
if (cx.align) return cx.col + (closing ? 0 : 1); |
||||||
|
else return cx.indent + (closing ? 0 : config.indentUnit); |
||||||
|
}, |
||||||
|
|
||||||
|
blockCommentStart: "/*", |
||||||
|
blockCommentEnd: "*/", |
||||||
|
lineComment: support.commentSlashSlash ? "//" : support.commentHash ? "#" : "--" |
||||||
|
}; |
||||||
|
}); |
||||||
|
|
||||||
|
(function() { |
||||||
|
"use strict"; |
||||||
|
|
||||||
|
// `identifier`
|
||||||
|
function hookIdentifier(stream) { |
||||||
|
// MySQL/MariaDB identifiers
|
||||||
|
// ref: http://dev.mysql.com/doc/refman/5.6/en/identifier-qualifiers.html
|
||||||
|
var ch; |
||||||
|
while ((ch = stream.next()) != null) { |
||||||
|
if (ch == "`" && !stream.eat("`")) return "variable-2"; |
||||||
|
} |
||||||
|
stream.backUp(stream.current().length - 1); |
||||||
|
return stream.eatWhile(/\w/) ? "variable-2" : null; |
||||||
|
} |
||||||
|
|
||||||
|
// "identifier"
|
||||||
|
function hookIdentifierDoublequote(stream) { |
||||||
|
// Standard SQL /SQLite identifiers
|
||||||
|
// ref: http://web.archive.org/web/20160813185132/http://savage.net.au/SQL/sql-99.bnf.html#delimited%20identifier
|
||||||
|
// ref: http://sqlite.org/lang_keywords.html
|
||||||
|
var ch; |
||||||
|
while ((ch = stream.next()) != null) { |
||||||
|
if (ch == "\"" && !stream.eat("\"")) return "variable-2"; |
||||||
|
} |
||||||
|
stream.backUp(stream.current().length - 1); |
||||||
|
return stream.eatWhile(/\w/) ? "variable-2" : null; |
||||||
|
} |
||||||
|
|
||||||
|
// variable token
|
||||||
|
function hookVar(stream) { |
||||||
|
// variables
|
||||||
|
// @@prefix.varName @varName
|
||||||
|
// varName can be quoted with ` or ' or "
|
||||||
|
// ref: http://dev.mysql.com/doc/refman/5.5/en/user-variables.html
|
||||||
|
if (stream.eat("@")) { |
||||||
|
stream.match(/^session\./); |
||||||
|
stream.match(/^local\./); |
||||||
|
stream.match(/^global\./); |
||||||
|
} |
||||||
|
|
||||||
|
if (stream.eat("'")) { |
||||||
|
stream.match(/^.*'/); |
||||||
|
return "variable-2"; |
||||||
|
} else if (stream.eat('"')) { |
||||||
|
stream.match(/^.*"/); |
||||||
|
return "variable-2"; |
||||||
|
} else if (stream.eat("`")) { |
||||||
|
stream.match(/^.*`/); |
||||||
|
return "variable-2"; |
||||||
|
} else if (stream.match(/^[0-9a-zA-Z$\.\_]+/)) { |
||||||
|
return "variable-2"; |
||||||
|
} |
||||||
|
return null; |
||||||
|
}; |
||||||
|
|
||||||
|
// short client keyword token
|
||||||
|
function hookClient(stream) { |
||||||
|
// \N means NULL
|
||||||
|
// ref: http://dev.mysql.com/doc/refman/5.5/en/null-values.html
|
||||||
|
if (stream.eat("N")) { |
||||||
|
return "atom"; |
||||||
|
} |
||||||
|
// \g, etc
|
||||||
|
// ref: http://dev.mysql.com/doc/refman/5.5/en/mysql-commands.html
|
||||||
|
return stream.match(/^[a-zA-Z.#!?]/) ? "variable-2" : null; |
||||||
|
} |
||||||
|
|
||||||
|
// these keywords are used by all SQL dialects (however, a mode can still overwrite it)
|
||||||
|
var sqlKeywords = "alter and as asc between by count create delete desc distinct drop from group having in insert into is join like not on or order select set table union update values where limit "; |
||||||
|
|
||||||
|
// turn a space-separated list into an array
|
||||||
|
function set(str) { |
||||||
|
var obj = {}, words = str.split(" "); |
||||||
|
for (var i = 0; i < words.length; ++i) obj[words[i]] = true; |
||||||
|
return obj; |
||||||
|
} |
||||||
|
|
||||||
|
// A generic SQL Mode. It's not a standard, it just try to support what is generally supported
|
||||||
|
CodeMirror.defineMIME("text/x-sql", { |
||||||
|
name: "sql", |
||||||
|
keywords: set(sqlKeywords + "begin"), |
||||||
|
builtin: set("bool boolean bit blob enum long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision real date datetime year unsigned signed decimal numeric"), |
||||||
|
atoms: set("false true null unknown"), |
||||||
|
operatorChars: /^[*+\-%<>!=]/, |
||||||
|
dateSQL: set("date time timestamp"), |
||||||
|
support: set("ODBCdotTable doubleQuote binaryNumber hexNumber"), |
||||||
|
functions: BI.makeObject(BI.FormulaCollections, true) |
||||||
|
}); |
||||||
|
}()); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
/* |
||||||
|
How Properties of Mime Types are used by SQL Mode |
||||||
|
================================================= |
||||||
|
|
||||||
|
keywords: |
||||||
|
A list of keywords you want to be highlighted. |
||||||
|
builtin: |
||||||
|
A list of builtin types you want to be highlighted (if you want types to be of class "builtin" instead of "keyword"). |
||||||
|
operatorChars: |
||||||
|
All characters that must be handled as operators. |
||||||
|
client: |
||||||
|
Commands parsed and executed by the client (not the server). |
||||||
|
support: |
||||||
|
A list of supported syntaxes which are not common, but are supported by more than 1 DBMS. |
||||||
|
* ODBCdotTable: .tableName |
||||||
|
* zerolessFloat: .1 |
||||||
|
* doubleQuote |
||||||
|
* nCharCast: N'string' |
||||||
|
* charsetCast: _utf8'string' |
||||||
|
* commentHash: use # char for comments |
||||||
|
* commentSlashSlash: use // for comments
|
||||||
|
* commentSpaceRequired: require a space after -- for comments |
||||||
|
atoms: |
||||||
|
Keywords that must be highlighted as atoms,. Some DBMS's support more atoms than others: |
||||||
|
UNKNOWN, INFINITY, UNDERFLOW, NaN... |
||||||
|
dateSQL: |
||||||
|
Used for date/time SQL standard syntax, because not all DBMS's support same temporal types. |
||||||
|
*/ |
File diff suppressed because one or more lines are too long
@ -0,0 +1,102 @@ |
|||||||
|
/** |
||||||
|
* 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: "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.sqlHint, { |
||||||
|
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 () { |
||||||
|
return this.editor.getValue(); |
||||||
|
}, |
||||||
|
|
||||||
|
setValue: function (v) { |
||||||
|
this.editor.setValue(v); |
||||||
|
} |
||||||
|
}); |
||||||
|
BI.shortcut("bi.sql_editor", BI.SQLEditor); |
@ -0,0 +1,19 @@ |
|||||||
|
.bi-sql-editor .cm-s-default span[class*="#"] { |
||||||
|
color: #faaa39; |
||||||
|
} |
||||||
|
.bi-sql-editor .cm-s-default .cm-keyword { |
||||||
|
color: #3685f2; |
||||||
|
} |
||||||
|
.bi-sql-editor .cm-s-default .cm-function { |
||||||
|
color: #faaa39; |
||||||
|
} |
||||||
|
.CodeMirror-hints .sql-fr-function { |
||||||
|
color: #faaa39; |
||||||
|
} |
||||||
|
.CodeMirror-hints .sql-keyword { |
||||||
|
color: #3685f2; |
||||||
|
} |
||||||
|
.CodeMirror-hints .CodeMirror-hint-active { |
||||||
|
background: #3f8ce8; |
||||||
|
color: #ffffff; |
||||||
|
} |
@ -0,0 +1,32 @@ |
|||||||
|
@import "../../bibase"; |
||||||
|
|
||||||
|
.bi-sql-editor{ |
||||||
|
|
||||||
|
& .cm-s-default span[class*="#"] { |
||||||
|
color: @color-bi-text-warning; |
||||||
|
} |
||||||
|
|
||||||
|
& .cm-s-default .cm-keyword { |
||||||
|
color: @color-bi-text-highlight; |
||||||
|
} |
||||||
|
|
||||||
|
& .cm-s-default .cm-function { |
||||||
|
color: @color-bi-text-warning; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
.CodeMirror-hints { |
||||||
|
& .sql-fr-function{ |
||||||
|
color: @color-bi-text-warning; |
||||||
|
} |
||||||
|
|
||||||
|
& .sql-keyword{ |
||||||
|
color: @color-bi-text-highlight; |
||||||
|
} |
||||||
|
|
||||||
|
.CodeMirror-hint-active { |
||||||
|
background: @color-bi-background-highlight; |
||||||
|
color: @color-bi-text; |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue