UNPKG

gethue

Version:

Hue is an Open source SQL Query Editor for Databases/Warehouses

180 lines (169 loc) 6.02 kB
// Licensed to Cloudera, Inc. under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. Cloudera, Inc. licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. import AutocompleteResults from '../sql/autocompleteResults'; import hueDebug from '../utils/hueDebug'; import huePubSub from '../utils/huePubSub'; import sqlParserRepository from '../parse/sql/sqlParserRepository'; class SqlAutocompleter { /** * @param {Object} options * @param {Snippet} options.snippet * @param {string} [options.fixedPrefix] - Optional prefix to always use on parse * @param {string} [options.fixedPostfix] - Optional postfix to always use on parse * @constructor */ constructor(options) { this.snippet = options.snippet; this.editor = options.editor; this.fixedPrefix = options.fixedPrefix || function () { return ''; }; this.fixedPostfix = options.fixedPostfix || function () { return ''; }; this.suggestions = new AutocompleteResults(options); } async parseActiveStatement() { return new Promise((resolve, reject) => { if (this.snippet.positionStatement() && this.snippet.positionStatement().location) { const activeStatementLocation = this.snippet.positionStatement().location; const cursorPosition = this.editor().getCursorPosition(); if ( (activeStatementLocation.first_line - 1 < cursorPosition.row || (activeStatementLocation.first_line - 1 === cursorPosition.row && activeStatementLocation.first_column <= cursorPosition.column)) && (activeStatementLocation.last_line - 1 > cursorPosition.row || (activeStatementLocation.last_line - 1 === cursorPosition.row && activeStatementLocation.last_column >= cursorPosition.column)) ) { const beforeCursor = this.fixedPrefix() + this.editor().session.getTextRange({ start: { row: activeStatementLocation.first_line - 1, column: activeStatementLocation.first_column }, end: cursorPosition }); const afterCursor = this.editor().session.getTextRange({ start: cursorPosition, end: { row: activeStatementLocation.last_line - 1, column: activeStatementLocation.last_column } }) + this.fixedPostfix(); sqlParserRepository .getAutocompleteParser(this.snippet.dialect()) .then(autocompleteParser => { resolve(autocompleteParser.parseSql(beforeCursor, afterCursor)); }) .catch(err => { console.warn(err); reject(err); }); } else { resolve(); } } else { resolve(); } }); } async parseAll() { return new Promise((resolve, reject) => { sqlParserRepository .getAutocompleteParser(this.snippet.dialect()) .then(autocompleteParser => { resolve( autocompleteParser.parseSql( this.editor().getTextBeforeCursor(), this.editor().getTextAfterCursor() ) ); }) .catch(reject); }); } async autocomplete() { let parseResult; try { huePubSub.publish( 'get.active.editor.locations', locations => { // This could happen in case the user is editing at the borders of the statement and the locations haven't // been updated yet, in that case we have to force a location update before parsing if ( this.snippet.ace && this.snippet.ace() && locations && this.snippet.ace().lastChangeTime !== locations.editorChangeTime ) { huePubSub.publish('editor.refresh.statement.locations', this.snippet.id()); } }, this.snippet ); parseResult = await this.parseActiveStatement(); } catch (e) { if (typeof console.warn !== 'undefined') { console.warn(e); } } // In the unlikely case the statement parser fails we fall back to parsing all of it if (!parseResult) { try { parseResult = await this.parseAll(); } catch (e) { if (typeof console.warn !== 'undefined') { console.warn(e); } } } if (!parseResult) { // This prevents Ace from inserting garbled text in case of exception huePubSub.publish('hue.ace.autocompleter.done'); } else { if (typeof hueDebug !== 'undefined' && hueDebug.showParseResult) { // eslint-disable-next-line no-restricted-syntax console.log(parseResult); } try { if (this.lastContextRequest) { this.lastContextRequest.dispose(); } this.lastContextRequest = this.snippet .whenContextSet() .done(() => { this.suggestions.update(parseResult); }) .fail(() => { huePubSub.publish('hue.ace.autocompleter.done'); }); } catch (e) { if (typeof console.warn !== 'undefined') { console.warn(e); } huePubSub.publish('hue.ace.autocompleter.done'); } } } } export default SqlAutocompleter;