240 lines
No EOL
6.9 KiB
JavaScript
240 lines
No EOL
6.9 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?", "untitled.xq");
|
|
if (name === null) return;
|
|
docSwitch("", name);
|
|
};
|
|
|
|
$("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 file = e.target.files[0]
|
|
let fr = new FileReader();
|
|
fr.onload = () => docSwitch(fr.result, file.name);
|
|
fr.readAsText(file);
|
|
};
|
|
|
|
$("format").onclick = e => { console.log("FMT", lsp.formatDocument(view)); };
|
|
|
|
$("popUrl").onsubmit = e => {
|
|
e.preventDefault();
|
|
const f = objectFromForm("popUrl");
|
|
fetch(f.url)
|
|
.then(response => response.text())
|
|
.then(t => {
|
|
docSwitch(t, f.url)
|
|
$("popWeb").open = false;
|
|
})
|
|
.catch(error => {
|
|
alert("CORS?: " + error)
|
|
});
|
|
};
|
|
|
|
|
|
|
|
$("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)
|
|
});
|
|
|
|
};
|
|
|
|
// change active doc
|
|
function docSwitch(text, url) {
|
|
view.dispatch({
|
|
changes: {
|
|
from: 0,
|
|
to: view.state.doc.length,
|
|
insert: text
|
|
}
|
|
})
|
|
$("iFile").value = url;
|
|
};
|
|
|
|
|
|
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;
|
|
}
|
|
}
|
|
} |