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 = `
- -
${html}
-
`; + 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) => {