diff --git a/app/src/protyle/toolbar/index.ts b/app/src/protyle/toolbar/index.ts
index 592833c5b70..9e77676ee5f 100644
--- a/app/src/protyle/toolbar/index.ts
+++ b/app/src/protyle/toolbar/index.ts
@@ -1236,10 +1236,19 @@ export class Toolbar {
hideElements(["hint"], protyle);
window.siyuan.menus.menu.remove();
this.range = getEditorRange(nodeElement);
- let html = `
${window.siyuan.languages.clear}
`;
+
+ this.subElement.style.width = "";
+ this.subElement.style.padding = "";
+ this.subElement.innerHTML = ``;
+ const listElement = this.subElement.lastElementChild.lastElementChild as HTMLElement;
+
+ let html = `${window.siyuan.languages.clear}
`;
let hljsLanguages = Constants.ALIAS_CODE_LANGUAGES.concat(window.hljs?.listLanguages() ?? []).sort();
- const eventDetail = {languages: hljsLanguages};
+ const eventDetail = {languages: hljsLanguages, type: "init", listElement};
if (protyle.app && protyle.app.plugins) {
protyle.app.plugins.forEach((plugin: any) => {
plugin.eventBus.emit("code-language-update", eventDetail);
@@ -1247,18 +1256,13 @@ export class Toolbar {
}
hljsLanguages = eventDetail.languages;
- hljsLanguages.forEach((item, index) => {
- html += `${item}
`;
+ hljsLanguages.forEach((item) => {
+ html += `${item}
`;
});
- this.subElement.style.width = "";
- this.subElement.style.padding = "";
- this.subElement.innerHTML = ``;
+ listElement.innerHTML = html;
+ listElement.firstElementChild.nextElementSibling.classList.add("b3-list-item--focus");
- const listElement = this.subElement.lastElementChild.lastElementChild as HTMLElement;
const inputElement = this.subElement.querySelector("input");
inputElement.addEventListener("keydown", (event: KeyboardEvent) => {
event.stopPropagation();
@@ -1269,7 +1273,6 @@ export class Toolbar {
if (event.key === "Enter") {
this.updateLanguage(languageElements, protyle, this.subElement.querySelector(".b3-list-item--focus").textContent);
event.preventDefault();
- event.stopPropagation();
return;
}
if (event.key === "Escape") {
@@ -1277,55 +1280,75 @@ export class Toolbar {
focusByRange(this.range);
}
});
+
+ const highlightText = (text: string, search: string) => {
+ // 转义正则特殊字符
+ const escapedSearch = search.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
+ // 创建不区分大小写的正则表达式
+ const regex = new RegExp(escapedSearch, "gi");
+ // 替换匹配内容并保留原始大小写
+ return text.replace(regex, match =>
+ `${match}`
+ );
+ };
+
inputElement.addEventListener("input", (event) => {
- const lowerCaseValue = inputElement.value.toLowerCase();
- const matchLanguages = hljsLanguages.filter(item => item.includes(lowerCaseValue));
- let html = "";
- // sort
- let matchInput = false;
- if (lowerCaseValue) {
- matchLanguages.sort((a, b) => {
- if (a.startsWith(lowerCaseValue) && b.startsWith(lowerCaseValue)) {
- if (a.length < b.length) {
- return -1;
- } else if (a.length === b.length) {
- return 0;
- } else {
- return 1;
- }
- } else if (a.startsWith(lowerCaseValue)) {
- return -1;
- } else if (b.startsWith(lowerCaseValue)) {
- return 1;
- } else {
- return 0;
- }
+ const value = inputElement.value.trim();
+ let matchLanguages;
+ let html = `${window.siyuan.languages.clear}
`;
+ let isMatchLanguages = false;
+ // Sort
+ if (value) {
+ const lowerCaseValue = value.toLowerCase();
+ matchLanguages = hljsLanguages.filter(
+ item => item.toLowerCase().includes(lowerCaseValue)
+ ).sort((a, b) => {
+ // 不区分大小写
+ const aStartsWith = a.toLowerCase().startsWith(lowerCaseValue);
+ const bStartsWith = b.toLowerCase().startsWith(lowerCaseValue);
+
+ // 两者都匹配开头时,短字符串优先
+ if (aStartsWith && bStartsWith) return a.length - b.length;
+ if (aStartsWith) return -1;
+ if (bStartsWith) return 1;
+
+ // 都不匹配时保持原顺序
+ return 0;
});
+
+ if (window.hljs?.getLanguage(value)) {
+ // Default languages and their aliases
+ matchLanguages = [value].concat(matchLanguages.filter(item => item !== value));
+ }
}
- const eventDetail = {languages: matchLanguages};
+ const eventDetail = {languages: value ? matchLanguages : hljsLanguages, type: "match", value, listElement};
if (protyle.app && protyle.app.plugins) {
protyle.app.plugins.forEach((plugin: any) => {
plugin.eventBus.emit("code-language-update", eventDetail);
});
}
- matchLanguages.forEach((item) => {
- if (inputElement.value === item) {
- matchInput = true;
- }
- html += `${item.replace(lowerCaseValue, "" + lowerCaseValue + "")}
`;
- });
- if (inputElement.value.trim() && !matchInput) {
- html = `${escapeHtml(inputElement.value.replace(/`| /g, "_"))}
${html}`;
- }
- html = `${window.siyuan.languages.clear}
` + html;
- listElement.innerHTML = html;
- if (listElement.childElementCount > 2 && !matchInput && inputElement.value.trim()) {
- listElement.firstElementChild.nextElementSibling.nextElementSibling.classList.add("b3-list-item--focus");
+ matchLanguages = eventDetail.languages;
+ if (value) {
+ matchLanguages.forEach((item) => {
+ if (value === item) {
+ isMatchLanguages = true;
+ html += `${item}
`;
+ } else {
+ html += `${highlightText(item, value)}
`;
+ }
+ });
} else {
- listElement.firstElementChild.nextElementSibling.classList.add("b3-list-item--focus");
+ matchLanguages.forEach((item) => {
+ html += `${item}
`;
+ });
}
+ if (value && !isMatchLanguages) {
+ html += `${escapeHtml(value.replace(/`| /g, "_"))}
`;
+ }
+ listElement.innerHTML = html;
+ listElement.firstElementChild.nextElementSibling.classList.add("b3-list-item--focus");
event.stopPropagation();
});
listElement.addEventListener("click", (event) => {