DSCRTC/dscrtc.user.js

64 lines
2.7 KiB
JavaScript

// ==UserScript==
// @name Deep Space Communications Relay Tab Completion
// @namespace https://hollymcfarland.com
// @version 2026-08-16
// @description Tab complete known definitions
// @author You
// @match https://dscr.dixonary.co.uk/
// @grant none
// ==/UserScript==
(function() {
'use strict';
document.addEventListener("keydown", (event) => {
// Only listen to tab
if (event.key !== "Tab") return;
// Only listen when message box focused
const inputEl = document.querySelector("#message-input");
if (document.activeElement !== inputEl) return;
// Don't tab out of input box
event.preventDefault();
const input = inputEl.value;
const cursorPos = inputEl.selectionStart;
// I don't even want to _think_ about how to handle tab completion if you have a range selected
if (cursorPos !== inputEl.selectionEnd) return;
// Character behind cursor should be non-whitespace
// Character after cursor should at least not be a letter
// Hopefully this accounts for _most_ reasonable signal definitions
if (!(/\S/.test(input[cursorPos-1]) && (input.length === cursorPos || /[^A-Z]/.test(input[cursorPos])))) return;
// Everything from most recent word boundary to the cursor position should be a reasonable estimate of "current signal"
const currentSignal = /[\w-_]+?$/.exec(input.slice(0, cursorPos).toUpperCase())[0];
const signals = JSON.parse(localStorage.getItem("dict")).map((s) => (s.value));
const matches = signals.filter((s) => (s.startsWith(currentSignal)));
// Give up if current signal isn't a prefix of any signal
if (!matches.length) return;
// Longest common prefix algorithm
// After loop, i contains number of additional characters (after currentSignal.length) that are shared by all matches
matches.sort();
console.log(matches);
let i = 0;
for (; i<matches[0].length; i++) {
const idx = i + currentSignal.length;
const [first, last] = [matches[0][idx], matches[matches.length-1][idx]];
if (first === undefined || last === undefined || first !== last) break;
}
const addedCharacters = matches[0].slice(currentSignal.length, currentSignal.length+i);
// Put new computed value back into text box and reposition cursor to match
inputEl.value = input.slice(0, cursorPos) + addedCharacters + input.slice(cursorPos);
inputEl.selectionStart = cursorPos + i;
inputEl.selectionEnd = cursorPos + i;
});
})();