Skip to content

Commit 112ee1d

Browse files
committed
fix: emmet issue
1 parent 23feb4a commit 112ee1d

File tree

1 file changed

+81
-24
lines changed

1 file changed

+81
-24
lines changed

src/lib/editorManager.js

Lines changed: 81 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414

1515
import { indentUnit } from "@codemirror/language";
1616
import { search } from "@codemirror/search";
17-
import { Compartment, EditorState, StateEffect } from "@codemirror/state";
17+
import { Compartment, EditorState, Prec, StateEffect } from "@codemirror/state";
1818
import { oneDark } from "@codemirror/theme-one-dark";
1919
import {
2020
EditorView,
@@ -26,6 +26,8 @@ import {
2626
} from "@codemirror/view";
2727
import {
2828
abbreviationTracker,
29+
EmmetKnownSyntax,
30+
emmetCompletionSource,
2931
emmetConfig,
3032
expandAbbreviation,
3133
wrapWithAbbreviation,
@@ -321,6 +323,30 @@ async function EditorManager($header, $body) {
321323
return exts;
322324
}
323325

326+
function createEmmetExtensionSet({
327+
syntax = EmmetKnownSyntax.html,
328+
tracker = {},
329+
config: emmetOverrides = {},
330+
} = {}) {
331+
const trackerExtension = abbreviationTracker({
332+
syntax,
333+
...tracker,
334+
});
335+
const { autocompleteTab = ["markup", "stylesheet"], ...restOverrides } =
336+
emmetOverrides || {};
337+
const emmetConfigExtension = emmetConfig.of({
338+
syntax,
339+
autocompleteTab,
340+
...restOverrides,
341+
});
342+
return [
343+
Prec.high(trackerExtension),
344+
wrapWithAbbreviation(),
345+
keymap.of([{ key: "Mod-e", run: expandAbbreviation }]),
346+
emmetConfigExtension,
347+
];
348+
}
349+
324350
function applyOptions(keys) {
325351
const filter = keys ? new Set(keys) : null;
326352
for (const spec of cmOptionSpecs) {
@@ -341,10 +367,32 @@ async function EditorManager($header, $body) {
341367
}
342368
}
343369

370+
// Plugin already wires CSS completions; attach extras for related syntaxes.
371+
const emmetCompletionSyntaxes = new Set([
372+
EmmetKnownSyntax.scss,
373+
EmmetKnownSyntax.less,
374+
EmmetKnownSyntax.sass,
375+
EmmetKnownSyntax.sss,
376+
EmmetKnownSyntax.stylus,
377+
EmmetKnownSyntax.postcss,
378+
]);
379+
380+
function maybeAttachEmmetCompletions(targetExtensions, syntax) {
381+
if (emmetCompletionSyntaxes.has(syntax)) {
382+
targetExtensions.push(
383+
EditorState.languageData.of(() => [
384+
{ autocomplete: emmetCompletionSource },
385+
]),
386+
);
387+
}
388+
}
389+
344390
// Create minimal CodeMirror editor
345391
const editorState = EditorState.create({
346392
doc: "",
347393
extensions: [
394+
// Emmet needs highest precedence so place before default keymaps
395+
...createEmmetExtensionSet({ syntax: EmmetKnownSyntax.html }),
348396
...createBaseExtensions(),
349397
getCommandKeymapExtension(),
350398
// Default theme
@@ -355,10 +403,6 @@ async function EditorManager($header, $body) {
355403
readOnlyCompartment.of(EditorState.readOnly.of(false)),
356404
// Editor options driven by settings via compartments
357405
...getBaseExtensionsFromOptions(),
358-
// Emmet abbreviation tracker and common keybindings
359-
abbreviationTracker(),
360-
wrapWithAbbreviation(),
361-
keymap.of([{ key: "Mod-e", run: expandAbbreviation }]),
362406
],
363407
});
364408

@@ -629,7 +673,10 @@ async function EditorManager($header, $body) {
629673
// Helper: apply a file's content and language to the editor view
630674
function applyFileToEditor(file) {
631675
if (!file || file.type !== "editor") return;
676+
const syntax = getEmmetSyntaxForFile(file);
632677
const baseExtensions = [
678+
// Emmet needs to precede default keymaps so tracker Tab wins over indent
679+
...createEmmetExtensionSet({ syntax }),
633680
...createBaseExtensions(),
634681
getCommandKeymapExtension(),
635682
// keep compartment in the state to allow dynamic theme changes later
@@ -640,6 +687,7 @@ async function EditorManager($header, $body) {
640687
...getBaseExtensionsFromOptions(),
641688
];
642689
const exts = [...baseExtensions];
690+
maybeAttachEmmetCompletions(exts, syntax);
643691
try {
644692
const langExtFn = file.currentLanguageExtension;
645693
let initialLang = [];
@@ -674,13 +722,6 @@ async function EditorManager($header, $body) {
674722
// ignore language extension errors; fallback to plain text
675723
}
676724

677-
// Emmet config: set syntax based on file/mode
678-
const syntax = getEmmetSyntaxForFile(file);
679-
exts.push(abbreviationTracker());
680-
exts.push(wrapWithAbbreviation());
681-
exts.push(keymap.of([{ key: "Mod-e", run: expandAbbreviation }]));
682-
exts.push(emmetConfig.of({ syntax }));
683-
684725
// Color preview plugin when enabled
685726
if (appSettings.value.colorPreview) {
686727
exts.push(colorView(true));
@@ -742,21 +783,37 @@ async function EditorManager($header, $body) {
742783
const mode = (file?.currentMode || "").toLowerCase();
743784
const name = (file?.filename || "").toLowerCase();
744785
const ext = name.includes(".") ? name.split(".").pop() : "";
745-
if (ext === "xml" || mode.includes("xml")) return "xml";
746-
if (ext === "jsx" || ext === "tsx") return "jsx";
747-
if (mode.includes("javascript") && (ext === "jsx" || ext === "tsx"))
748-
return "jsx";
749-
if (ext === "css" || mode.includes("css")) return "css";
750-
if (ext === "scss" || mode.includes("scss")) return "scss";
751-
if (ext === "sass" || mode.includes("sass")) return "sass";
786+
if (ext === "tsx" || mode.includes("tsx")) return EmmetKnownSyntax.tsx;
787+
if (ext === "jsx" || mode.includes("jsx")) return EmmetKnownSyntax.jsx;
788+
if (mode.includes("javascript") && (ext === "jsx" || ext === "tsx")) {
789+
return ext === "tsx" ? EmmetKnownSyntax.tsx : EmmetKnownSyntax.jsx;
790+
}
791+
if (ext === "css" || mode.includes("css")) return EmmetKnownSyntax.css;
792+
if (ext === "scss" || mode.includes("scss")) return EmmetKnownSyntax.scss;
793+
if (ext === "sass" || mode.includes("sass")) return EmmetKnownSyntax.sass;
794+
if (ext === "less" || mode.includes("less")) return EmmetKnownSyntax.less;
795+
if (ext === "sss" || mode.includes("sss")) return EmmetKnownSyntax.sss;
752796
if (ext === "styl" || ext === "stylus" || mode.includes("styl"))
753-
return "stylus";
754-
if (ext === "php" || mode.includes("php")) return "html"; // treat PHP as HTML for Emmet
755-
if (ext === "vue" || mode.includes("vue")) return "html"; // Emmet inside templates
797+
return EmmetKnownSyntax.stylus;
798+
if (ext === "postcss" || mode.includes("postcss"))
799+
return EmmetKnownSyntax.postcss;
800+
if (ext === "xml" || mode.includes("xml")) return EmmetKnownSyntax.xml;
801+
if (ext === "xsl" || mode.includes("xsl")) return EmmetKnownSyntax.xsl;
802+
if (ext === "haml" || mode.includes("haml")) return EmmetKnownSyntax.haml;
803+
if (
804+
ext === "pug" ||
805+
ext === "jade" ||
806+
mode.includes("pug") ||
807+
mode.includes("jade")
808+
)
809+
return EmmetKnownSyntax.pug;
810+
if (ext === "slim" || mode.includes("slim")) return EmmetKnownSyntax.slim;
811+
if (ext === "vue" || mode.includes("vue")) return EmmetKnownSyntax.vue;
812+
if (ext === "php" || mode.includes("php")) return EmmetKnownSyntax.html;
756813
if (ext === "html" || ext === "xhtml" || mode.includes("html"))
757-
return "html";
814+
return EmmetKnownSyntax.html;
758815
// Defaults to html per Emmet docs
759-
return "html";
816+
return EmmetKnownSyntax.html;
760817
}
761818

762819
const $vScrollbar = ScrollBar({

0 commit comments

Comments
 (0)