dotfiles/vscode/.vscode/extensions/mechatroner.rainbow-csv-3.12.0/rbql_suggest.js
Errol Sancaktar ff17c17e23 vscode
2024-06-14 09:31:58 -06:00

287 lines
12 KiB
JavaScript

let rbql_suggest = {};
( function() {
rbql_suggest.active_suggest_idx = null;
rbql_suggest.suggest_list = []
rbql_suggest.apply_suggest_callback = null;
rbql_suggest.autosuggest_header_vars = [];
rbql_suggest.input_id = null;
rbql_suggest.suggest_list_id = null;
rbql_suggest.suggest_entry_class = null;
rbql_suggest.current_join_table_id = null;
rbql_suggest.fetch_join_header_callback = null;
function js_string_escape_column_name(column_name, quote_char) {
column_name = column_name.replace(/\\/g, '\\\\');
column_name = column_name.replace(/\n/g, '\\n');
column_name = column_name.replace(/\r/g, '\\r');
column_name = column_name.replace(/\t/g, '\\t');
if (quote_char === "'")
return column_name.replace(/'/g, "\\'");
if (quote_char === '"')
return column_name.replace(/"/g, '\\"');
return column_name.replace(/`/g, "\\`");
}
function convert_header_to_rbql_variables(header, table_var_prefix) {
let max_suggest_len = 100; // Suggest UI could become unresponsive if there are too many suggest options to consider
let result = [];
for (let h of header) {
let column_var_options = {orig_column_name: h, table_var_prefix: table_var_prefix};
if (h.match('^[_a-zA-Z][_a-zA-Z0-9]*$') !== null) {
column_var_options.dot_var = `${table_var_prefix}.${h}`;
} else {
column_var_options.dot_var = null;
}
let escaped_column_name = js_string_escape_column_name(h, '"');
column_var_options.double_q_var = `${table_var_prefix}["${escaped_column_name}"]`;
escaped_column_name = js_string_escape_column_name(h, "'");
column_var_options.single_q_var = `${table_var_prefix}['${escaped_column_name}']`;
result.push(column_var_options);
if (result.length > max_suggest_len)
break;
}
return result;
}
function initialize_suggest(input_id, suggest_list_id, suggest_entry_class, apply_suggest_callback, header, fetch_join_header_callback=null) {
if (!header)
header = [];
rbql_suggest.autosuggest_header_vars = convert_header_to_rbql_variables(header, 'a');
rbql_suggest.active_suggest_idx = null;
rbql_suggest.suggest_list = []
rbql_suggest.input_id = input_id;
rbql_suggest.suggest_list_id = suggest_list_id;
rbql_suggest.suggest_entry_class = suggest_entry_class;
rbql_suggest.apply_suggest_callback = apply_suggest_callback;
rbql_suggest.fetch_join_header_callback = fetch_join_header_callback;
}
function hide_suggest(suggest_div) {
if (rbql_suggest.active_suggest_idx !== null) {
suggest_div.style.display = 'none';
rbql_suggest.active_suggest_idx = null;
rbql_suggest.suggest_list = [];
}
}
function apply_suggest(suggest_index) {
try {
let rbql_input = document.getElementById(rbql_suggest.input_id);
rbql_input.value = rbql_suggest.suggest_list[suggest_index][0];
rbql_input.selectionStart = rbql_suggest.suggest_list[suggest_index][1];
rbql_input.selectionEnd = rbql_suggest.suggest_list[suggest_index][1];
rbql_input.focus();
if (rbql_suggest.apply_suggest_callback) {
rbql_suggest.apply_suggest_callback(rbql_suggest.suggest_list[suggest_index][0]);
}
hide_suggest(document.getElementById(rbql_suggest.suggest_list_id));
} catch (e) {
console.error(`Autocomplete error: ${e}`);
}
}
function register_suggest_callback(button_element, suggest_index) {
button_element.addEventListener("click", () => {
apply_suggest(suggest_index);
});
}
function highlight_active_suggest_entry(do_highlight) {
let entry_button = document.getElementById(`rbql_suggest_var_${rbql_suggest.active_suggest_idx}`);
if (!entry_button)
return;
if (do_highlight) {
let active_entry_class = rbql_suggest.suggest_entry_class + '_active';
entry_button.className = [rbql_suggest.suggest_entry_class, active_entry_class].join(' ');
entry_button.scrollIntoView();
} else {
entry_button.className = rbql_suggest.suggest_entry_class;
}
}
function remove_children(root_node) {
while (root_node.firstChild) {
root_node.removeChild(root_node.firstChild);
}
}
function show_suggest(suggest_div, query_before_var, relevant_suggest_list, query_after_cursor) {
let rbql_input = document.getElementById(rbql_suggest.input_id);
let caret_left_shift = 0;
try {
let caret_coordinates = getCaretCoordinates(rbql_input, rbql_input.selectionStart);
caret_left_shift = caret_coordinates.left ? caret_coordinates.left : 0;
} catch (e) {
caret_left_shift = 0;
}
remove_children(suggest_div);
rbql_suggest.active_suggest_idx = 0;
rbql_suggest.suggest_list = [];
for (let i = 0; i < relevant_suggest_list.length; i++) {
let suggest_text = relevant_suggest_list[i];
let entry_button = document.createElement('button');
entry_button.className = rbql_suggest.suggest_entry_class;
entry_button.textContent = suggest_text;
entry_button.setAttribute('id', `rbql_suggest_var_${i}`);
register_suggest_callback(entry_button, i);
suggest_div.appendChild(entry_button);
rbql_suggest.suggest_list.push([query_before_var + suggest_text + query_after_cursor, (query_before_var + suggest_text).length]);
}
highlight_active_suggest_entry(true);
suggest_div.style.display = 'block';
let calculated_height = suggest_div.offsetHeight;
let calculated_width = suggest_div.offsetWidth;
let box = rbql_input.getBoundingClientRect();
suggest_div.style.left = Math.max(0, Math.min(box.left + caret_left_shift, box.right - calculated_width)) + 'px';
suggest_div.style.top = (box.top - calculated_height) + 'px';
}
function switch_active_suggest(direction) {
if (rbql_suggest.active_suggest_idx === null)
return false;
highlight_active_suggest_entry(false);
if (direction == 'up') {
rbql_suggest.active_suggest_idx = (rbql_suggest.active_suggest_idx + rbql_suggest.suggest_list.length - 1) % rbql_suggest.suggest_list.length;
} else {
rbql_suggest.active_suggest_idx = (rbql_suggest.active_suggest_idx + 1) % rbql_suggest.suggest_list.length;
}
highlight_active_suggest_entry(true);
return true;
}
function is_printable_key_code(keycode) {
// Taken from here: https://stackoverflow.com/a/12467610/2898283
return (keycode > 47 && keycode < 58) || keycode == 32 || (keycode > 64 && keycode < 91) || (keycode > 185 && keycode < 193) || (keycode > 218 && keycode < 223);
}
function handle_input_keydown(event) {
// We need this logic to prevent the caret from going to the start of the input field with the default arrow-up keydown handler
try {
if (event.keyCode == 38) {
if (switch_active_suggest('up'))
event.preventDefault();
} else if (event.keyCode == 40) {
if (switch_active_suggest('down'))
event.preventDefault();
} else if (event.keyCode == 39) {
if (rbql_suggest.active_suggest_idx !== null) {
apply_suggest(rbql_suggest.active_suggest_idx);
event.preventDefault();
}
}
} catch (e) {
console.error(`Autocomplete error: ${e}`);
}
}
function variable_has_prefix(full_variable, cursor_var_prefix) {
return full_variable && full_variable.toLowerCase().startsWith(cursor_var_prefix.toLowerCase()) && full_variable != cursor_var_prefix;
}
function get_best_matching_variable(cursor_var_prefix, column_var_options) {
if (cursor_var_prefix.startsWith(column_var_options.table_var_prefix + '.')) {
if (variable_has_prefix(column_var_options.dot_var, cursor_var_prefix))
return column_var_options.dot_var;
if (variable_has_prefix(column_var_options.table_var_prefix + '.' + column_var_options.orig_column_name, cursor_var_prefix))
return column_var_options.single_q_var;
}
if (variable_has_prefix(column_var_options.single_q_var, cursor_var_prefix))
return column_var_options.single_q_var;
if (variable_has_prefix(column_var_options.double_q_var, cursor_var_prefix))
return column_var_options.double_q_var;
return null;
}
function get_join_table_id(query) {
let match = query.match(/ join +([^ ]+)(?: *$| +o$| +on)/i);
if (!match)
return null;
return match[1];
}
function adjust_join_table_header_callback(join_header) {
if (!join_header || !join_header.length) {
rbql_suggest.autosuggest_header_vars = rbql_suggest.autosuggest_header_vars.filter(v => v.table_var_prefix != 'b');
} else {
let join_header_vars = convert_header_to_rbql_variables(join_header, 'b');
rbql_suggest.autosuggest_header_vars = rbql_suggest.autosuggest_header_vars.concat(join_header_vars);
}
}
function handle_input_keyup(event) {
try {
if (event.keyCode == 13) {
if (rbql_suggest.active_suggest_idx !== null)
apply_suggest(rbql_suggest.active_suggest_idx);
return;
}
if (is_printable_key_code(event.keyCode) || event.keyCode == 8 /* Bakspace */) {
// We can't move this into the keydown handler because the characters appear in the input box only after keyUp event.
// Or alternatively we could scan the event.keyCode to find out the next char, but this is additional logic
let rbql_input = document.getElementById(rbql_suggest.input_id);
let current_query = rbql_input.value;
if (rbql_suggest.fetch_join_header_callback !== null) {
let join_table_id = get_join_table_id(current_query);
if (rbql_suggest.current_join_table_id != join_table_id) {
rbql_suggest.current_join_table_id = join_table_id;
if (join_table_id === null) {
adjust_join_table_header_callback([]);
} else {
rbql_suggest.fetch_join_header_callback(join_table_id, adjust_join_table_header_callback)
}
}
}
let suggest_div = document.getElementById(rbql_suggest.suggest_list_id);
hide_suggest(suggest_div);
let cursor_pos = rbql_input.selectionStart;
let query_before_cursor = current_query.substr(0, cursor_pos);
let query_after_cursor = current_query.substr(cursor_pos);
// TODO improve the match - just find last var-looking expression. The problem with this one - it won't match extended suggest like a.arbitrary-var -> a['arbitrary-var']
let last_var_prefix_match = query_before_cursor.match(/(?:[^_a-zA-Z0-9])([ab](?:\.[_a-zA-Z0-9]*|\[[^\]]*))$/);
if (last_var_prefix_match) {
let relevant_suggest_list = [];
let cursor_var_prefix = last_var_prefix_match[1];
let query_before_var = query_before_cursor.substr(0, last_var_prefix_match.index + 1);
for (let column_var_options of rbql_suggest.autosuggest_header_vars) {
let suggested_var = get_best_matching_variable(cursor_var_prefix, column_var_options);
if (suggested_var)
relevant_suggest_list.push(suggested_var);
}
if (relevant_suggest_list.length) {
show_suggest(suggest_div, query_before_var, relevant_suggest_list, query_after_cursor);
}
}
}
} catch (e) {
console.error(`Autocomplete error: ${e}`);
}
}
rbql_suggest.initialize_suggest = initialize_suggest;
rbql_suggest.handle_input_keydown = handle_input_keydown;
rbql_suggest.handle_input_keyup = handle_input_keyup;
rbql_suggest.convert_header_to_rbql_variables = convert_header_to_rbql_variables;
} )();