basex-lsp/webapp/static/clients/codemirror/script.js
2025-11-01 22:53:55 +00:00

235 lines
No EOL
6.8 KiB
JavaScript

const view = new lsp.EditorView({
extensions: lsp.baseExts,
parent: document.getElementById("editor")
});
let doc = "xquery version '3.1';\n(:~ comment:)\nmodule namespace pdfbox='ns';\n";
var client; // https://codemirror.net/docs/ref/#lsp-client
var workspace = {
"file:///some/file.xqm": null
};
function $(id) { return document.getElementById(id) };
// Load saved content from localStorage when the page loads
window.addEventListener('load', () => {
const savedText = localStorage.getItem('code');
if (savedText) doc = savedText;
let svr = localStorage.getItem('lsp');
if (!svr) {
let x = new URL(window.location.href);
x.protocol = "ws";
x.pathname = "ws/lsp"
svr = x.href;
}
$("iServer").value = svr;
formFromStore('fSettings');
view.setState(lsp.EditorState.create({ doc: doc, extensions: lsp.baseExts }));
lsp.updateCompartment(objectFromForm('fSettings'))
connect();
});
// Save content to localStorage when the page is about to unload
window.addEventListener('beforeunload', () => {
const doc = view.state.doc.toString();
localStorage.setItem('code', doc);
localStorage.setItem('lsp', $("iServer").value);
});
$("connect").onclick = e => { e.preventDefault(); connect() };
$("symTrigger").onclick = e => { e.preventDefault(); };
$("symOptions").onclick = e => { e.preventDefault(); };
$("bnNew").onclick = e => {
let name = prompt("New file name?");
if (name === null) return;
alert("TODO")
};
$("search").onclick = e => lsp.openSearchPanel(view);
$("fullscreen").onclick = e => $("editor").requestFullscreen();
$("bnWordAt").onclick = e => {
let pos = view.state.selection.main.head;
let w = view.state.wordAt(pos);
alert("wordAt " + JSON.stringify(w));
};
$("symbols2").onclick = e => {
client.sync();
client.request("textDocument/documentSymbol", { "textDocument": { "uri": $("iFile").value } })
.then(r => {
console.log("symbols", r)
$("symPanel").open = true;
$("symList").setData(r);
});
};
$("cmdList").onclick = e => {
let cmds = lsp.listCommands(view);
let result = "";
[...cmds.keys()].sort().forEach(key => {
result += `<li>${key} ${cmds.get(key).key}</li>`
});
$("popHelpInfo").innerHTML = `<ul>${result}</ul>`
$("popCmds").showPopover()
};
$("symList").addEventListener("itemSelected", e => {
const plugin = lsp.LSPPlugin.get(view)
if (!plugin) return;
const sel = e.detail.range // or selectionRange;
console.log("SYM selection range", sel);
const an = plugin.fromPosition(sel.start)
const hd = plugin.fromPosition(sel.end)
view.dispatch({ selection: { anchor: an, head: hd }, scrollIntoView: true });
});
$("lint").onclick = async e => {
console.log("word", view.state.wordAt(1));
lsp.openLintPanel(view);
};
$("sync").onclick = e => { client.sync(); console.log("XXXsync"); };
// state a state
$("bnSave").onclick = e => { workspace[iFile] = view.state; };
$("bnLoad").onclick = e => { const v = workspace[iFile]; if (v) view.setState(v) };
// select local file
$("bnRead").onclick = e => { $("fileElem").click(); };
$("fileElem").onchange = e => {
let fr = new FileReader();
fr.onload = function () {
alert(fr.result);
}
fr.readAsText(e.target.files[0]);
};
$("format").onclick = e => { console.log("FMT", lsp.formatDocument(view)); };
$("load").onchange = e => {
const url = e.target.value;
if (url.length == 0) return
fetch(url)
.then(response => response.text())
.then(t => {
view.dispatch({
changes: {
from: 0,
to: view.state.doc.length,
insert: t
}
})
//client.sync();
//console.log("SYNC");
});
$("load").value = "";
};
$("tConnect").addEventListener('quiet-change', e => {
e.preventDefault();
$("popConnect").showPopover()
});
$("msgIcon").onclick = e => {
e.preventDefault();
alert("NOT YET")
};
function updateSettings(event) {
event.preventDefault();
console.log("COPTS", lsp.curOpts);
const opts = {
lineWrap: $("lineWrap").checked,
highlightWhitespace: $("highlightWhitespace").checked,
minimap: $("minimap").checked
}
console.log(opts)
lsp.updateCompartment(opts);
$('popSettings').hidePopover();
formToStore("fSettings");
};
$("fSettings").addEventListener("submit", updateSettings);
function connect() {
const server = $("iServer").value;
const file = $("iFile").value;
lsp.simpleWebSocketTransport(server)
.then(transport => {
transport.socket.onclose = (event) => $("tConnect").checked = false;
transport.socket.oneror = (event) => $("msg").innerText = "sock error!";
transport.subscribe(incoming);
client = new lsp.LSPClient({ extensions: lsp.languageServerExtensions() });
client.connect(transport);
$("popConnect").hidePopover();
$("tConnect").checked = true;
const extLsp = client.plugin(file, "xquery");
view.dispatch({
effects: lsp.StateEffect.appendConfig.of(
[lsp.linter(null, { autoPanel: true }), ...extLsp,
lsp.keymap.of([...lsp.formatKeymap])])
})
})
.catch(e => {
console.log(e);
$("tConnect").checked = false;
alert("connection failed: " + server)
});
};
function incoming(msg) {
const rpc = JSON.parse(msg);
log(rpc);
};
function log(rpc) {
console.log("<-", rpc)
if (rpc.id) return;
const when = (new Date()).toISOString();
const msg = { name: rpc.method, detail: when.substring(1 + when.indexOf("T")), kind: 23 /* event */ };
//name,details,kind
$("msgList").setData([msg], true)
};
function formFromStore(name) {
let v = localStorage.getItem(name)
if (!!v) formDeserialize($(name), v);
};
function formToStore(name) {
localStorage.setItem(name, formSerialize($(name)));
};
function objectFromForm(name) {
const data = new FormData($(name));
//https://stackabuse.com/convert-form-data-to-javascript-object/
return Object.fromEntries(data.entries());
}
function formSerialize(form) {
const data = new FormData(form);
//https://stackoverflow.com/a/44033425/1869660
return new URLSearchParams(data).toString();
}
function formDeserialize(form, data) {
const entries = (new URLSearchParams(data)).entries();
for (const [key, val] of entries) {
//http://javascript-coder.com/javascript-form/javascript-form-value.phtml
const input = form.elements[key];
switch (input.type) {
case 'checkbox': input.checked = !!val; break;
default: input.value = val; break;
}
}
}