From db256c91ccd4af7b3556bac215d556bebfa9c895 Mon Sep 17 00:00:00 2001 From: Alan Bernstein Date: Tue, 16 May 2017 22:54:17 -0500 Subject: [PATCH 1/3] Only autocomplete on single match --- webui/assets/main.js | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/webui/assets/main.js b/webui/assets/main.js index 887181338..3969ed650 100644 --- a/webui/assets/main.js +++ b/webui/assets/main.js @@ -85,20 +85,27 @@ class REPL { } var input_word = repl.input.value.substring(word_start, repl.input.selectionEnd) - // check for keyword match and insert - // this just stops at the first match + // check for keyword match and insert if exactly one match + var matches = [] for(var keyword in keywords) { if(keyword.startsWith(input_word)){ - var cursor_pos = repl.input.selectionEnd - var completion = keyword.substring(input_word.length) - var before = repl.input.value.substring(0, cursor_pos) - var after = repl.input.value.substring(cursor_pos) - repl.input.value = before + completion + after - var new_pos = cursor_pos + completion.length - keywords[keyword] - repl.input.setSelectionRange(new_pos, new_pos) - break + matches.push(keyword) } } + if(matches.length > 1) { + // display in some dynamic element + } + + if(matches.length == 1) { + var cursor_pos = repl.input.selectionEnd + var completion = matches[0].substring(input_word.length) + var before = repl.input.value.substring(0, cursor_pos) + var after = repl.input.value.substring(cursor_pos) + repl.input.value = before + completion + after + var new_pos = cursor_pos + completion.length - keywords[matches[0]] + repl.input.setSelectionRange(new_pos, new_pos) + } + } }) repl.button.onclick = function() { From 39e46e2c483e1e669508ac35245263e499e6e575 Mon Sep 17 00:00:00 2001 From: Alan Bernstein Date: Thu, 18 May 2017 18:30:25 -0500 Subject: [PATCH 2/3] Factor out autocompleter --- webui/assets/main.js | 119 ++++++++++++++++++++++++------------------- 1 file changed, 66 insertions(+), 53 deletions(-) diff --git a/webui/assets/main.js b/webui/assets/main.js index 3969ed650..eea168185 100644 --- a/webui/assets/main.js +++ b/webui/assets/main.js @@ -1,8 +1,9 @@ class REPL { - constructor(input, output, button) { + constructor(input, output, button, completer) { this.input = input this.output = output this.button = button + this.completer = completer this.history = [] this.history_index = 0 this.history_buffer = '' @@ -16,21 +17,6 @@ class REPL { UP_ARROW: 38, DOWN_ARROW: 40 } - var keywords = { - // keyword: length of substring that comes after cursor - "SetBit()": 1, - "ClearBit()": 1, - "SetRowAttrs()": 1, - "SetColumnAttrs()": 1, - "Bitmap()": 1, - "Union()": 1, - "Intersect()": 1, - "Difference()": 1, - "Count()": 1, - "Range()": 1, - "TopN()": 1, - "frame=": 0, - } this.input.addEventListener("keydown", function(e) { if (e.keyCode == keys.UP_ARROW) { @@ -70,42 +56,7 @@ class REPL { } if (e.keyCode == keys.TAB) { e.preventDefault() - - // extract word fragment ending at cursor. a word fragment: - // - starts with last nonalpha character before cursor (or beginning of string) - // - ends at cursor - var word_start = repl.input.selectionEnd-1 - while(word_start>0) { - var c = repl.input.value.charCodeAt(word_start) - if(!((c>64 && c<91) || (c>96 && c<123))) { - word_start++ - break - } - word_start-- - } - var input_word = repl.input.value.substring(word_start, repl.input.selectionEnd) - - // check for keyword match and insert if exactly one match - var matches = [] - for(var keyword in keywords) { - if(keyword.startsWith(input_word)){ - matches.push(keyword) - } - } - if(matches.length > 1) { - // display in some dynamic element - } - - if(matches.length == 1) { - var cursor_pos = repl.input.selectionEnd - var completion = matches[0].substring(input_word.length) - var before = repl.input.value.substring(0, cursor_pos) - var after = repl.input.value.substring(cursor_pos) - repl.input.value = before + completion + after - var new_pos = cursor_pos + completion.length - keywords[matches[0]] - repl.input.setSelectionRange(new_pos, new_pos) - } - + repl.completer.complete() } }) repl.button.onclick = function() { @@ -451,11 +402,73 @@ Date.prototype.timeNow = function () { populate_version() + +class Autocompleter { + constructor(input) { + this.input = input + this.keyword_map = this.static_keywords + } + + get static_keywords() { + return { + // keyword: length of substring that comes after cursor + "SetBit()": 1, + "ClearBit()": 1, + "SetRowAttrs()": 1, + "SetColumnAttrs()": 1, + "Bitmap()": 1, + "Union()": 1, + "Intersect()": 1, + "Difference()": 1, + "Count()": 1, + "Range()": 1, + "TopN()": 1, + "frame=": 0, + } + } + + complete() { + var completer = this + // extract word fragment ending at cursor. a word fragment: + // - starts with last nonalpha character before cursor (or beginning of string) + // - ends at cursor + var word_start = completer.input.selectionEnd-1 + while(word_start>0) { + var c = completer.input.value.charCodeAt(word_start) + if(!((c>64 && c<91) || (c>96 && c<123))) { + word_start++ + break + } + word_start-- + } + var input_word = completer.input.value.substring(word_start, completer.input.selectionEnd) + + // check for keyword match and insert if exactly one match + var matches = [] + for(var keyword in this.keyword_map) { + if(keyword.startsWith(input_word)){ + matches.push(keyword) + } + } + + if(matches.length == 1) { + var cursor_pos = completer.input.selectionEnd + var completion = matches[0].substring(input_word.length) + var before = completer.input.value.substring(0, cursor_pos) + var after = completer.input.value.substring(cursor_pos) + completer.input.value = before + completion + after + var new_pos = cursor_pos + completion.length - this.keyword_map[matches[0]] + completer.input.setSelectionRange(new_pos, new_pos) + } + } +} + var input = document.getElementById('query') var output = document.getElementById('outputs') var button = document.getElementById('query-btn') -repl = new REPL(input, output, button) +autocompleter = new Autocompleter(input) +repl = new REPL(input, output, button, autocompleter) repl.populate_index_dropdown() repl.bind_events() From 75c59387f686b288c5775993902cb1f469b632c5 Mon Sep 17 00:00:00 2001 From: Alan Bernstein Date: Thu, 18 May 2017 18:34:23 -0500 Subject: [PATCH 3/3] Skeleton for new autocomplete functionality --- webui/assets/main.js | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/webui/assets/main.js b/webui/assets/main.js index eea168185..db54a3b1e 100644 --- a/webui/assets/main.js +++ b/webui/assets/main.js @@ -404,9 +404,11 @@ populate_version() class Autocompleter { - constructor(input) { + constructor(input, output) { this.input = input + this.output = output this.keyword_map = this.static_keywords + this.init_dynamic_keywords() } get static_keywords() { @@ -450,8 +452,12 @@ class Autocompleter { matches.push(keyword) } } + if(matches.length > 1) { + // completer.output.innerHTML = whatever + } if(matches.length == 1) { + // completer.output.innerHTML = "" var cursor_pos = completer.input.selectionEnd var completion = matches[0].substring(input_word.length) var before = completer.input.value.substring(0, cursor_pos) @@ -461,13 +467,28 @@ class Autocompleter { completer.input.setSelectionRange(new_pos, new_pos) } } + + init_dynamic_keywords() { + // hit /schema, parse indexes, frames, rowlabels, columnlabels, add to list + } + + add_keyword() { + // call when index or frame created in webui + } + + remove_keyword() { + // call when index or frame deleted in webui + // issue: if e.g. multiple indexes have same frame, removing one removes all. + // solution: maintain count. requires more elaborate representation of keywords. + } } var input = document.getElementById('query') var output = document.getElementById('outputs') var button = document.getElementById('query-btn') +var autocomplete_output = document.getElementById('autocomplete-container') -autocompleter = new Autocompleter(input) +autocompleter = new Autocompleter(input, autocomplete_output) repl = new REPL(input, output, button, autocompleter) repl.populate_index_dropdown() repl.bind_events()