123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298 |
- // CodeMirror, copyright (c) by Marijn Haverbeke and others
- // Distributed under an MIT license: https://codemirror.net/LICENSE
- ;(function (mod) {
- if (typeof exports == 'object' && typeof module == 'object')
- // CommonJS
- mod(require('../../lib/codemirror'), require('../../mode/sql/sql'))
- else if (typeof define == 'function' && define.amd)
- // AMD
- define(['../../lib/codemirror', '../../mode/sql/sql'], mod)
- // Plain browser env
- else mod(CodeMirror)
- })(function (CodeMirror) {
- 'use strict'
- 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) {
- 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 duplicated 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 +
- // duplicate 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]
- }
- if (validRange.start) {
- 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$#]*$/g)) {
- 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 {
- var objectOrClass = function (w, className) {
- if (typeof w === 'object') {
- w.className = className
- } else {
- w = { text: w, className: className }
- }
- return w
- }
- addMatches(result, search, defaultTable, function (w) {
- return objectOrClass(w, 'CodeMirror-hint-table CodeMirror-hint-default-table')
- })
- addMatches(result, search, tables, function (w) {
- return objectOrClass(w, 'CodeMirror-hint-table')
- })
- if (!disableKeywords)
- addMatches(result, search, keywords, function (w) {
- return objectOrClass(w.toUpperCase(), 'CodeMirror-hint-keyword')
- })
- }
- return { list: result, from: Pos(cur.line, start), to: Pos(cur.line, end) }
- })
- })
|