";
+
+function renderForm() {
+ const r = current();
+ if (!r) {
+ $("form").innerHTML = '
' + (rows.length ? "Select a configuration." : "No GDB server configurations yet. Add one with +: an embedded board through OpenOCD, J-Link, ST-LINK…, or a gdbserver on another machine.") + "
";
+ return;
+ }
+ const emb = r.kind === "embedded";
+ let h = '
' +
+ input("Name", "name", r.name) +
+ '
' +
+ '" +
+ '
" +
+ input("Target", "target", r.target, { list: "targets", placeholder: "the active target (" + (activeTarget || "none") + ")", hint: "The CMake executable that's debugged" + (emb ? " and flashed" : "") + "; it's built first." }) +
+ input("Executable", "program", r.program, { placeholder: "the target's", hint: "Another file instead (an .elf built some other way)." }) +
+ input("Debugger", "debugger", r.debugger, { list: "debuggers", placeholder: defaultDebugger, hint: "A gdb that knows the target's architecture." }) +
+ "";
+ if (emb) {
+ h += sec("GDB server") +
+ '
Common settings to start from; then set your board\'s config files or device.
' +
+ input("GDB server", "server", r.server, { placeholder: "none: it's already running", hint: "Started before gdb connects, and stopped after." }) +
+ input("Server arguments", "serverArgs", r.serverArgs, { area: true, placeholder: "-f interface/stlink.cfg -f target/stm32f4x.cfg" }) +
+ input("Ready when it prints", "serverReady", r.serverReady, { placeholder: "Listening on port 3333", hint: "A regular expression; empty: it gets a moment." }) +
+ input("Server environment", "serverEnvironment", r.serverEnvironment, { placeholder: "A=1;B=2" }) +
+ input("'target remote' args", "remote", r.remote, { placeholder: "localhost:3333" }) +
+ sec("Download") +
+ select("Download executable", "download", r.download, [["always", "Always"], ["updated", "If updated"], ["never", "Never"]], "Flash the program when debugging starts.") +
+ input("Reset command", "resetCommand", r.resetCommand, { placeholder: "monitor reset halt", hint: "Before the download, and again after it." }) +
+ input("Download & Run starts it with", "runCommands", r.runCommands, { area: true, placeholder: "monitor reset run", hint: "gdb commands, one per line, after the download (▶ Run, without a debugger)." });
+ } else {
+ h += sec("Connection") +
+ input("'target remote' args", "remote", r.remote, { placeholder: r.ssh ? "its port: :2345" : "192.168.1.20:2345", hint: r.ssh ? "With SSH, the host is the SSH host's; only the port counts." : "Where gdbserver listens." }) +
+ input("Sysroot", "sysroot", r.sysroot, { placeholder: "none", hint: "The target's root, for its shared libraries." }) +
+ input("Path mappings", "pathMappings", r.pathMappings, { area: true, placeholder: "/remote/src=" + "$" + "{sourceDir}", hint: "Where its sources were built=where they are here, one per line." }) +
+ sec("Upload and start over SSH (optional)") +
+ input("SSH host", "ssh", r.ssh, { placeholder: "user@host", hint: "The program is copied there (scp) and gdbserver started on it; ▶ Run runs it there." }) +
+ input("Remote path", "remotePath", r.remotePath, { placeholder: "/tmp/" }) +
+ input("gdbserver", "gdbserver", r.gdbserver, { placeholder: "gdbserver" }) +
+ input("Program arguments", "args", r.args, { placeholder: "--verbose" });
+ }
+ h += sec("Advanced") +
+ input("gdb commands before connecting", "initCommands", r.initCommands, { area: true, placeholder: "set architecture arm", hint: "One per line." }) +
+ input("gdb commands after connecting", "connectCommands", r.connectCommands, { area: true, placeholder: "monitor arm semihosting enable" }) +
+ "
";
+ $("form").innerHTML = h;
+}
+
+function render() {
+ renderList();
+ renderForm();
+}
+
+$("list").addEventListener("click", (e) => {
+ const li = e.target.closest("li[data-id]");
+ if (!li) return;
+ sel = li.dataset.id;
+ render();
+});
+
+$("form").addEventListener("input", (e) => {
+ const r = current();
+ if (!r) return;
+ if (e.target.id === "preset") {
+ const p = PRESETS[e.target.value];
+ if (!p) return;
+ for (const k of ["server", "serverArgs", "serverReady", "remote", "resetCommand", "runCommands"]) r[k] = p[k];
+ if (/^(Embedded|New)/.test(r.name)) r.name = freeName(p.label);
+ setDirty(true);
+ render();
+ return;
+ }
+ const k = e.target.dataset.k;
+ if (!k) return;
+ r[k] = e.target.type === "checkbox" ? e.target.checked : e.target.value;
+ setDirty(true);
+ if (k === "kind") render();
+ else if (k === "name") renderList();
+ else if (k === "ssh") renderForm();
+});
+
+function addRow(from) {
+ const r = Object.assign({}, EMPTY, from || {}, { id: uid(), origName: undefined });
+ r.name = freeName(from && from.name ? from.name : "Embedded");
+ const at = current() ? rows.indexOf(current()) + 1 : rows.length;
+ rows.splice(at, 0, r);
+ sel = r.id;
+ setDirty(true);
+ render();
+ const name = $("form").querySelector('[data-k="name"]');
+ if (name) { name.focus(); name.select(); }
+}
+
+$("add").onclick = () => addRow();
+$("copy").onclick = () => {
+ const r = current();
+ if (r) addRow({ ...r, rest: { ...r.rest } });
+};
+$("remove").onclick = () => {
+ const r = current();
+ if (!r) return;
+ const i = rows.indexOf(r);
+ rows.splice(i, 1);
+ sel = rows[Math.min(i, rows.length - 1)]?.id ?? null;
+ setDirty(true);
+ render();
+};
+function move(d) {
+ const r = current();
+ const i = rows.indexOf(r);
+ if (!r || i + d < 0 || i + d >= rows.length) return;
+ rows.splice(i, 1);
+ rows.splice(i + d, 0, r);
+ setDirty(true);
+ renderList();
+}
+$("up").onclick = () => move(-1);
+$("down").onclick = () => move(1);
+
+function save(close) {
+ if (!dirty && close) return vscode.postMessage({ type: "cancel" });
+ vscode.postMessage({ type: "save", rows, close });
+}
+$("ok").onclick = () => save(true);
+$("apply").onclick = () => save(false);
+$("cancel").onclick = () => vscode.postMessage({ type: "cancel" });
+
+window.addEventListener("message", (e) => {
+ const m = e.data;
+ if (m.type === "init" || m.type === "saved") {
+ const keep = m.type === "saved" && current() ? current().name.trim() : null;
+ rows = m.rows; active = m.active; defaultDebugger = m.defaultDebugger; activeTarget = m.activeTarget;
+ $("targets").innerHTML = m.targets.map((t) => '