diff --git a/packages/tinymce-plugins/codeblock/index.js b/packages/tinymce-plugins/codeblock/index.js
index c3c1ff613..cf2636e10 100644
--- a/packages/tinymce-plugins/codeblock/index.js
+++ b/packages/tinymce-plugins/codeblock/index.js
@@ -2,11 +2,13 @@ const {
addPluginToPluginManager,
getCharacterRange,
moveCaretTo,
+ getCurrentLine,
} = require("../utils");
const { createCodeBlock, isCodeBlock, TAGNAME, state } = require("./utils");
const { addCodeBlockToolbar, refreshHighlighting } = require("./toolbar");
-const TAB = " ";
+const TAB = ` `;
+const TAB_LENGTH = 2;
const EMPTY_LINE = "
";
/**
@@ -44,6 +46,7 @@ var toggleCodeBlock = function (editor, api, type) {
} else {
var content = editor.selection.getContent({ format: "text" }); //.replace(/^\n/gm, "");
if (type === "shortcut") content = "
";
+ if (!content) content = "
";
insertCodeBlock(editor, content);
}
};
@@ -81,6 +84,12 @@ function blurCodeBlock(editor, block) {
}
var isCreatingCodeBlock = false;
+/**
+ *
+ * @param {*} api
+ * @param {import("tinymce").Editor} editor
+ * @returns
+ */
var registerHandlers = function (api, editor) {
function onNodeChanged(event) {
api.setActive(event.element.tagName === TAGNAME);
@@ -128,13 +137,34 @@ var registerHandlers = function (api, editor) {
// we have to handle all the logic manually.
function onKeyDown(e) {
const node = state.activeBlock;
- if (!node || e.code !== "Tab") return;
+ if (!node) return;
+ if (e.code === "Tab") {
+ e.preventDefault();
+ handleTab(e, node);
+ } else if (e.ctrlKey && e.code === "KeyA") {
+ e.preventDefault();
+ handleCtrlA(node);
+ } else if (e.code === "Enter") {
+ handleEnter(node);
+ }
+ }
+
+ function handleEnter(node) {
+ const currentLine = getCurrentLine(node);
+ const indent =
+ (currentLine.length - currentLine.trimStart().length) / TAB_LENGTH;
+ editor.insertContent(TAB.repeat(indent));
+ }
+
+ function handleCtrlA(node) {
+ editor.selection.select(node, true);
+ }
+
+ function handleTab(e, node) {
const characterRange = getCharacterRange(node);
if (!characterRange) return;
- e.preventDefault();
-
// Shift + Tab = Deindent on all major platforms
const isDeindent = e.shiftKey;
const text = node.textContent;
@@ -171,7 +201,7 @@ var registerHandlers = function (api, editor) {
moveCaretTo(node, characterRange.start, endIndex);
} else {
// TODO: handle line deindent
- editor.insertContent(TAB);
+ editor.selection.setContent(TAB);
}
} else {
editor.insertContent(TAB);
diff --git a/packages/tinymce-plugins/codeblock/toolbar.js b/packages/tinymce-plugins/codeblock/toolbar.js
index 0dcbd6923..fcdbfeb56 100644
--- a/packages/tinymce-plugins/codeblock/toolbar.js
+++ b/packages/tinymce-plugins/codeblock/toolbar.js
@@ -72,7 +72,7 @@ function changeLanguageSelectLabel(text) {
function parseCodeblockLanguage(node) {
if (!node || node.tagName !== TAGNAME) return;
- const languageAliases = getLanguageFromClassName(node.className).split("-");
+ const languageAliases = getLanguageFromClassList(node).split("-");
if (languageAliases.length <= 1) return;
return hljs.getLanguage(languageAliases[1]);
}
@@ -93,26 +93,33 @@ function applyHighlighting(editor, language) {
const alias = language.aliases[0];
persistSelection(node, () => {
- node.innerHTML = hljs.highlight(node.innerText, {
+ const code = hljs.highlight(node.innerText, {
language: alias,
- }).value;
+ });
+ node.innerHTML = code.value.replace(/\n/gm, "
");
editor.save();
});
- changeCodeblockClassName(node, ` language-${alias} `);
+ changeCodeblockClassName(node, `language-${alias}`);
}
function changeCodeblockClassName(node, className) {
- node.className = node.className.replace(
- getLanguageFromClassName(node.className),
- className
- );
+ const language = getLanguageFromClassList(node);
+ if (!!language)
+ node.classList.replace(getLanguageFromClassList(node), className);
+ else node.classList.add(className);
}
-function getLanguageFromClassName(className) {
- const classes = className.split(" ");
- const languageKey = classes.find((c) => c.startsWith("lang"));
- return languageKey || "";
+/**
+ *
+ * @param {Element} node
+ */
+function getLanguageFromClassList(node) {
+ for (let className of node.classList.values()) {
+ if (className.startsWith("language") || className.startsWith("lang"))
+ return className;
+ }
+ return "";
}
function refreshHighlighting(editor) {
diff --git a/packages/tinymce-plugins/utils.js b/packages/tinymce-plugins/utils.js
index e3bb27ff7..d7746cca4 100644
--- a/packages/tinymce-plugins/utils.js
+++ b/packages/tinymce-plugins/utils.js
@@ -35,6 +35,23 @@ function moveCaretTo(node, index, endIndex) {
rangy.getSelection().restoreCharacterRanges(node, [newCharacterRange]);
}
+function getCurrentLine(node) {
+ const characterRange = getCharacterRange(node);
+ const lines = node.innerText.split("\n");
+
+ let currentLine = "";
+ let prevLength = 0;
+ for (let line of lines) {
+ let length = prevLength + line.length + 1;
+ if (characterRange.start === length) {
+ currentLine = line;
+ break;
+ }
+ prevLength += line.length + 1;
+ }
+ return currentLine;
+}
+
function persistSelection(node, action) {
let saved = rangy.getSelection().saveCharacterRanges(node);
action();
@@ -52,6 +69,7 @@ function addPluginToPluginManager(name, register) {
}
module.exports = {
+ getCurrentLine,
getCharacterRange,
moveCaretTo,
persistSelection,