Working on Formating DSL completion
This commit is contained in:
@@ -2114,6 +2114,175 @@ function moveColumn(table, sourceColId, targetColId) {
|
||||
}, ANIMATION_DURATION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize DslEditor with CodeMirror 5
|
||||
*
|
||||
* Features:
|
||||
* - DSL-based autocompletion
|
||||
* - Line numbers
|
||||
* - Readonly support
|
||||
* - Placeholder support
|
||||
* - Textarea synchronization
|
||||
* - Debounced HTMX server update via updateCommandId
|
||||
*
|
||||
* Required CodeMirror addons:
|
||||
* - addon/hint/show-hint.js
|
||||
* - addon/hint/show-hint.css
|
||||
* - addon/display/placeholder.js
|
||||
*
|
||||
* Requires:
|
||||
* - htmx loaded globally
|
||||
*
|
||||
* @param {Object} config
|
||||
*/
|
||||
function initDslEditor(config) {
|
||||
const {
|
||||
elementId,
|
||||
textareaId,
|
||||
lineNumbers,
|
||||
autocompletion,
|
||||
placeholder,
|
||||
readonly,
|
||||
updateCommandId,
|
||||
dsl
|
||||
} = config;
|
||||
|
||||
const wrapper = document.getElementById(elementId);
|
||||
const textarea = document.getElementById(textareaId);
|
||||
const editorContainer = document.getElementById(`cm_${elementId}`);
|
||||
|
||||
if (!wrapper || !textarea || !editorContainer) {
|
||||
console.error(`DslEditor: Missing elements for ${elementId}`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof CodeMirror === "undefined") {
|
||||
console.error("DslEditor: CodeMirror 5 not loaded");
|
||||
return;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------
|
||||
* Build completion list from DSL config
|
||||
* -------------------------------------------------- */
|
||||
|
||||
const completionItems = [];
|
||||
|
||||
if (dsl && dsl.completions) {
|
||||
const pushAll = (items) => {
|
||||
if (!Array.isArray(items)) return;
|
||||
items.forEach(item => completionItems.push(item));
|
||||
};
|
||||
|
||||
pushAll(dsl.completions.keywords);
|
||||
pushAll(dsl.completions.operators);
|
||||
pushAll(dsl.completions.functions);
|
||||
pushAll(dsl.completions.types);
|
||||
pushAll(dsl.completions.literals);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------
|
||||
* DSL autocompletion hint
|
||||
* -------------------------------------------------- */
|
||||
|
||||
function dslHint(cm) {
|
||||
const cursor = cm.getCursor();
|
||||
const line = cm.getLine(cursor.line);
|
||||
const ch = cursor.ch;
|
||||
|
||||
let start = ch;
|
||||
while (start > 0 && /\w/.test(line.charAt(start - 1))) {
|
||||
start--;
|
||||
}
|
||||
|
||||
const word = line.slice(start, ch);
|
||||
|
||||
const matches = completionItems.filter(item =>
|
||||
item.startsWith(word)
|
||||
);
|
||||
|
||||
return {
|
||||
list: matches,
|
||||
from: CodeMirror.Pos(cursor.line, start),
|
||||
to: CodeMirror.Pos(cursor.line, ch)
|
||||
};
|
||||
}
|
||||
|
||||
/* --------------------------------------------------
|
||||
* Create CodeMirror editor
|
||||
* -------------------------------------------------- */
|
||||
|
||||
const editor = CodeMirror(editorContainer, {
|
||||
value: textarea.value || "",
|
||||
lineNumbers: !!lineNumbers,
|
||||
readOnly: !!readonly,
|
||||
placeholder: placeholder || "",
|
||||
extraKeys: autocompletion ? {
|
||||
"Ctrl-Space": "autocomplete"
|
||||
} : {},
|
||||
hintOptions: autocompletion ? {
|
||||
hint: dslHint,
|
||||
completeSingle: false
|
||||
} : undefined
|
||||
});
|
||||
|
||||
/* --------------------------------------------------
|
||||
* Debounced update + HTMX transport
|
||||
* -------------------------------------------------- */
|
||||
|
||||
let debounceTimer = null;
|
||||
const DEBOUNCE_DELAY = 300;
|
||||
|
||||
editor.on("change", function (cm) {
|
||||
const value = cm.getValue();
|
||||
textarea.value = value;
|
||||
|
||||
if (!updateCommandId) return;
|
||||
|
||||
clearTimeout(debounceTimer);
|
||||
debounceTimer = setTimeout(() => {
|
||||
wrapper.dispatchEvent(
|
||||
new CustomEvent("dsl-editor-update", {
|
||||
detail: {
|
||||
commandId: updateCommandId,
|
||||
value: value
|
||||
}
|
||||
})
|
||||
);
|
||||
}, DEBOUNCE_DELAY);
|
||||
});
|
||||
|
||||
/* --------------------------------------------------
|
||||
* HTMX listener (LOCAL to wrapper)
|
||||
* -------------------------------------------------- */
|
||||
|
||||
if (updateCommandId && typeof htmx !== "undefined") {
|
||||
wrapper.addEventListener("dsl-editor-update", function (e) {
|
||||
htmx.ajax("POST", "/myfasthtml/commands", {
|
||||
target: wrapper,
|
||||
swap: "none",
|
||||
values: {
|
||||
c_id: e.detail.commandId,
|
||||
content: e.detail.value
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/* --------------------------------------------------
|
||||
* Public API
|
||||
* -------------------------------------------------- */
|
||||
|
||||
wrapper._dslEditor = {
|
||||
editor: editor,
|
||||
getContent: () => editor.getValue(),
|
||||
setContent: (content) => editor.setValue(content)
|
||||
};
|
||||
|
||||
console.debug(`DslEditor initialized (CM5 + HTMX): ${elementId} with ${dsl?.name || "DSL"}`);
|
||||
}
|
||||
|
||||
|
||||
|
||||
function updateDatagridSelection(datagridId) {
|
||||
const selectionManager = document.getElementById(`tsm_${datagridId}`);
|
||||
if (!selectionManager) return;
|
||||
|
||||
Reference in New Issue
Block a user