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; var extLint; 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() }; $("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 += `
  • ${key} ${cmds.get(key).key}
  • ` }); $("popHelpInfo").innerHTML = `` $("popCmds").showPopover() }; $("symList").addEventListener("itemSelected", e => { const sel = e.detail.selectionRange; console.log("SYM selection range", sel); const an = 17 * e.detail.kind view.dispatch({ selection: { anchor: an, head: an + 34 }, 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; let 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; } } }