feat: query editor auto-completer for tables and columns

This commit is contained in:
Fabio Di Stasio 2020-12-22 22:31:31 +01:00
parent 0014f48079
commit cb1fce6f99
3 changed files with 2396 additions and 10 deletions

View File

@ -7,22 +7,78 @@
<script>
import * as ace from 'ace-builds';
import 'ace-builds/webpack-resolver';
import 'ace-builds/src-noconflict/ext-language_tools';
import '../libs/ext-language_tools';
import Tables from '@/ipc-api/Tables';
export default {
name: 'QueryEditor',
props: {
value: String,
autoFocus: { type: Boolean, default: false }
schema: String,
autoFocus: { type: Boolean, default: false },
workspace: Object
},
data () {
return {
editor: null
editor: null,
fields: [],
baseCompleter: []
};
},
computed: {
tables () {
return this.workspace.structure.filter(schema => schema.name === this.schema)
.reduce((acc, curr) => {
acc.push(...curr.tables);
return acc;
}, []).map(table => {
return {
name: table.name,
comment: table.comment,
type: table.type,
fields: ['TODO']
};
});
},
mode () {
switch (this.workspace.client) {
case 'mysql':
case 'maria':
return 'mysql';
case 'mssql':
return 'sqlserver';
case 'pg':
return 'pgsql';
default:
return 'sql';
}
},
lastWord () {
const words = this.value.split(' ');
return words[words.length - 1];
},
isLastWordATable () {
return /\w+\.\w*/gm.test(this.lastWord);
},
fieldsCompleter () {
return {
getCompletions: (editor, session, pos, prefix, callback) => {
const completions = [];
this.fields.forEach(field => {
completions.push({
value: field,
meta: 'column',
score: 1000
});
});
callback(null, completions);
}
};
}
},
mounted () {
this.editor = ace.edit(this.$refs.editor, {
mode: 'ace/mode/sql',
mode: `ace/mode/${this.mode}`,
theme: 'ace/theme/twilight',
value: this.value,
fontSize: '14px',
@ -35,6 +91,53 @@ export default {
enableLiveAutocompletion: true
});
this.editor.completers.push({
getCompletions: (editor, session, pos, prefix, callback) => {
const completions = [];
this.tables.forEach(table => {
completions.push({
value: table.name,
meta: table.type,
caption: table.comment
});
});
callback(null, completions);
}
});
this.baseCompleter = this.editor.completers;
this.editor.commands.on('afterExec', e => {
if (['insertstring', 'backspace', 'del'].includes(e.command.name)) {
if (this.isLastWordATable || e.args === '.') {
if (e.args !== ' ') {
const table = this.tables.find(t => t.name === this.lastWord.split('.').pop());
if (table) {
const params = {
uid: this.workspace.uid,
schema: this.schema,
table: table.name
};
Tables.getTableColumns(params).then(res => {
if (res.response.length)
this.fields = res.response.map(field => field.name);
this.editor.completers = [this.fieldsCompleter];
this.editor.execCommand('startAutocomplete');
}).catch(console.log);
}
else
this.editor.completers = this.baseCompleter;
}
else
this.editor.completers = this.baseCompleter;
}
else
this.editor.completers = this.baseCompleter;
}
});
this.editor.session.on('change', () => {
const content = this.editor.getValue();
this.$emit('update:value', content);
@ -50,12 +153,30 @@ export default {
</script>
<style lang="scss">
.editor-wrapper {
border-bottom: 1px solid #444;
.editor-wrapper {
border-bottom: 1px solid #444;
.editor {
height: 200px;
width: 100%;
}
.editor {
height: 200px;
width: 100%;
}
}
.ace_.mdi {
display: inline-block;
width: 17px;
}
.ace_dark.ace_editor.ace_autocomplete .ace_marker-layer .ace_active-line {
background-color: #c9561a99;
}
.ace_dark.ace_editor.ace_autocomplete .ace_marker-layer .ace_line-hover {
background-color: #c9571a33;
border: none;
}
.ace_dark.ace_editor.ace_autocomplete .ace_completion-highlight {
color: #e0d00c;
}
</style>

View File

@ -5,6 +5,8 @@
v-if="isSelected"
:auto-focus="true"
:value.sync="query"
:workspace="workspace"
:schema="schema"
/>
<div class="workspace-query-runner-footer">
<div class="workspace-query-buttons">

File diff suppressed because it is too large Load Diff