Switch to BiomeJS (#306)

This commit is contained in:
Gregory Schier
2025-11-23 08:38:13 -08:00
committed by GitHub
parent 2bac610efe
commit ec3e2e16a9
332 changed files with 3007 additions and 4097 deletions

View File

@@ -7,8 +7,7 @@ export class BetterMatchDecorator extends MatchDecorator {
updateDeco(update: ViewUpdate, deco: DecorationSet): DecorationSet {
if (!update.startState.selection.eq(update.state.selection)) {
return super.createDeco(update.view);
} else {
return super.updateDeco(update, deco);
}
return super.updateDeco(update, deco);
}
}

View File

@@ -26,7 +26,8 @@
}
&:not(.cm-focused) {
.cm-cursor, .cm-fat-cursor {
.cm-cursor,
.cm-fat-cursor {
@apply hidden;
}
}
@@ -77,7 +78,6 @@
.cm-gutterElement {
@apply cursor-default;
}
}
.cm-gutter-lint {
@@ -91,7 +91,7 @@
@apply cursor-default opacity-80 hover:opacity-100 transition-opacity;
@apply rounded-full w-[0.9em] h-[0.9em];
content: '';
content: "";
&.cm-lint-marker-error {
@apply bg-danger;
@@ -168,7 +168,8 @@
@apply outline-text;
@apply bg-text !important;
&, * {
&,
* {
@apply text-surface font-semibold !important;
}
}
@@ -199,8 +200,7 @@
}
.cm-editor .fold-gutter-icon::after {
@apply block w-1.5 h-1.5 p-0.5 border-transparent
border-l border-b border-l-[currentColor] border-b-[currentColor] content-[''];
@apply block w-1.5 h-1.5 p-0.5 border-transparent border-l border-b border-l-[currentColor] border-b-[currentColor] content-[''];
}
/* Rotate the fold gutter chevron when open */
@@ -270,7 +270,8 @@
/* Style the tooltip for popping up "open in browser" and other stuff */
a, button {
a,
button {
@apply text-text hover:bg-surface-highlight w-full h-sm flex items-center px-2 rounded;
}
@@ -279,7 +280,7 @@
&::after {
@apply text-text bg-text h-3 w-3 ml-1;
content: '';
content: "";
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100' fill='black' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M8.636 3.5a.5.5 0 0 0-.5-.5H1.5A1.5 1.5 0 0 0 0 4.5v10A1.5 1.5 0 0 0 1.5 16h10a1.5 1.5 0 0 0 1.5-1.5V7.864a.5.5 0 0 0-1 0V14.5a.5.5 0 0 1-.5.5h-10a.5.5 0 0 1-.5-.5v-10a.5.5 0 0 1 .5-.5h6.636a.5.5 0 0 0 .5-.5z'/%3E%3Cpath fill-rule='evenodd' d='M16 .5a.5.5 0 0 0-.5-.5h-5a.5.5 0 0 0 0 1h3.793L6.146 9.146a.5.5 0 1 0 .708.708L15 1.707V5.5a.5.5 0 0 0 1 0v-5z'/%3E%3C/svg%3E");
-webkit-mask-size: contain;
display: inline-block;
@@ -300,59 +301,59 @@
@apply opacity-80 italic;
&::after {
content: 'a' !important; /* Default (eg. for GraphQL) */
content: "a" !important; /* Default (eg. for GraphQL) */
}
&.cm-completionIcon-function::after {
content: 'f' !important;
content: "f" !important;
@apply text-info;
}
&.cm-completionIcon-variable::after {
content: 'x' !important;
content: "x" !important;
@apply text-primary;
}
&.cm-completionIcon-namespace::after {
content: 'n' !important;
content: "n" !important;
@apply text-warning;
}
&.cm-completionIcon-constant::after {
content: 'c' !important;
content: "c" !important;
@apply text-notice;
}
&.cm-completionIcon-class::after {
content: 'o' !important;
content: "o" !important;
}
&.cm-completionIcon-enum::after {
content: 'e' !important;
content: "e" !important;
}
&.cm-completionIcon-interface::after {
content: 'i' !important;
content: "i" !important;
}
&.cm-completionIcon-keyword::after {
content: 'k' !important;
content: "k" !important;
}
&.cm-completionIcon-method::after {
content: 'm' !important;
content: "m" !important;
}
&.cm-completionIcon-property::after {
content: 'a' !important;
content: "a" !important;
}
&.cm-completionIcon-text::after {
content: 't' !important;
content: "t" !important;
}
&.cm-completionIcon-type::after {
content: 't' !important;
content: "t" !important;
}
}
@@ -406,7 +407,7 @@
@apply appearance-none bg-none cursor-default;
}
button[name='close'] {
button[name="close"] {
@apply text-text-subtle hocus:text-text px-2 -mr-1.5 !important;
}
@@ -421,7 +422,7 @@
/* Hide the "All" button */
button[name='select'] {
button[name="select"] {
@apply hidden;
}
}

View File

@@ -28,7 +28,6 @@ import {
import { activeEnvironmentAtom } from '../../../hooks/useActiveEnvironment';
import type { WrappedEnvironmentVariable } from '../../../hooks/useEnvironmentVariables';
import { useEnvironmentVariables } from '../../../hooks/useEnvironmentVariables';
import { useRandomKey } from '../../../hooks/useRandomKey';
import { useRequestEditor } from '../../../hooks/useRequestEditor';
import { useTemplateFunctionCompletionOptions } from '../../../hooks/useTemplateFunctions';
import { editEnvironment } from '../../../lib/editEnvironment';
@@ -113,7 +112,7 @@ export function Editor({
disabled,
extraExtensions,
forcedEnvironmentId,
forceUpdateKey: forceUpdateKeyFromAbove,
forceUpdateKey,
format,
heightMode,
hideGutter,
@@ -144,10 +143,6 @@ export function Editor({
? allEnvironmentVariables.filter(autocompleteVariables)
: allEnvironmentVariables;
}, [allEnvironmentVariables, autocompleteVariables]);
// Track a local key for updates. If the default value is changed when the input is not in focus,
// regenerate this to force the field to update.
const [focusedUpdateKey, regenerateFocusedUpdateKey] = useRandomKey('initial');
const forceUpdateKey = `${forceUpdateKeyFromAbove}::${focusedUpdateKey}`;
if (settings && wrapLines === undefined) {
wrapLines = settings.editorSoftWrap;
@@ -223,7 +218,7 @@ export function Editor({
const effects = placeholderCompartment.current.reconfigure(ext);
cm.current?.view.dispatch({ effects });
},
[placeholder, type],
[placeholder],
);
// Update vim
@@ -233,12 +228,12 @@ export function Editor({
if (cm.current === null) return;
const current = keymapCompartment.current.get(cm.current.view.state) ?? [];
// PERF: This is expensive with hundreds of editors on screen, so only do it when necessary
if (settings.editorKeymap === 'default' && current === keymapExtensions['default']) return; // Nothing to do
if (settings.editorKeymap === 'vim' && current === keymapExtensions['vim']) return; // Nothing to do
if (settings.editorKeymap === 'vscode' && current === keymapExtensions['vscode']) return; // Nothing to do
if (settings.editorKeymap === 'emacs' && current === keymapExtensions['emacs']) return; // Nothing to do
if (settings.editorKeymap === 'default' && current === keymapExtensions.default) return; // Nothing to do
if (settings.editorKeymap === 'vim' && current === keymapExtensions.vim) return; // Nothing to do
if (settings.editorKeymap === 'vscode' && current === keymapExtensions.vscode) return; // Nothing to do
if (settings.editorKeymap === 'emacs' && current === keymapExtensions.emacs) return; // Nothing to do
const ext = keymapExtensions[settings.editorKeymap] ?? keymapExtensions['default'];
const ext = keymapExtensions[settings.editorKeymap] ?? keymapExtensions.default;
const effects = keymapCompartment.current.reconfigure(ext);
cm.current.view.dispatch({ effects });
},
@@ -324,6 +319,7 @@ export function Editor({
);
// Update the language extension when the language changes
// biome-ignore lint/correctness/useExhaustiveDependencies: none
useEffect(() => {
if (cm.current === null) return;
const { view, languageCompartment } = cm.current;
@@ -355,6 +351,7 @@ export function Editor({
]);
// Initialize the editor when ref mounts
// biome-ignore lint/correctness/useExhaustiveDependencies: Only reinitialize when necessary
const initEditorRef = useCallback(
function initEditorRef(container: HTMLDivElement | null) {
if (container === null) {
@@ -384,7 +381,7 @@ export function Editor({
!disableTabIndent ? keymap.of([indentWithTab]) : emptyExtension,
),
keymapCompartment.current.of(
keymapExtensions[settings.editorKeymap] ?? keymapExtensions['default'],
keymapExtensions[settings.editorKeymap] ?? keymapExtensions.default,
),
...getExtensions({
container,
@@ -434,7 +431,6 @@ export function Editor({
console.log('Failed to initialize Codemirror', e);
}
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[forceUpdateKey],
);
@@ -456,7 +452,7 @@ export function Editor({
updateContents(cm.current.view, defaultValue || '');
}
},
[defaultValue, readOnly, regenerateFocusedUpdateKey],
[defaultValue],
);
// Add bg classes to actions, so they appear over the text
@@ -628,7 +624,7 @@ function saveCachedEditorState(stateKey: string | null, state: EditorState | nul
// Save state in sessionStorage by removing doc and saving the hash of it instead.
// This will be checked on restore and put back in if it matches.
stateObj.docHash = md5(stateObj.doc);
delete stateObj.doc;
stateObj.doc = undefined;
try {
sessionStorage.setItem(computeFullStateKey(stateKey), JSON.stringify(stateObj));
@@ -670,7 +666,9 @@ function updateContents(view: EditorView, text: string) {
if (currentDoc === text) {
return;
} else if (text.startsWith(currentDoc)) {
}
if (text.startsWith(currentDoc)) {
// If we're just appending, append only the changes. This preserves
// things like scroll position.
view.dispatch({

View File

@@ -1,6 +1,7 @@
import type { Completion, CompletionContext, CompletionResult } from '@codemirror/autocomplete';
import { autocompletion, startCompletion } from '@codemirror/autocomplete';
import { LanguageSupport, LRLanguage, syntaxTree } from '@codemirror/language';
import type { SyntaxNode } from '@lezer/common';
import { parser } from './filter';
export interface FieldDef {
@@ -40,9 +41,10 @@ function wordBefore(doc: string, pos: number): { from: number; to: number; text:
function inPhrase(ctx: CompletionContext): boolean {
// Lezer node names from your grammar: Phrase is the quoted token
let n = syntaxTree(ctx.state).resolveInner(ctx.pos, -1);
for (; n; n = n.parent!) {
let n: SyntaxNode | null = syntaxTree(ctx.state).resolveInner(ctx.pos, -1);
while (n) {
if (n.name === 'Phrase') return true;
n = n.parent;
}
return false;
}

View File

@@ -1,23 +1,27 @@
// biome-ignore-all lint: Disable for generated file
// This file was generated by lezer-generator. You probably shouldn't edit it.
import {LRParser} from "@lezer/lr"
import {highlight} from "./highlight"
import { LRParser } from '@lezer/lr';
import { highlight } from './highlight';
export const parser = LRParser.deserialize({
version: 14,
states: "%QOVQPOOPeOPOOOVQPO'#CfOjQPO'#ChO!XQPO'#CgOOQO'#Cc'#CcOVQPO'#CaOOQO'#Ca'#CaO!oQPO'#C`O!|QPO'#C_OOQO'#C^'#C^QOQPOOPOOO'#Cp'#CpP#XOPO)C>jO#`QPO,59QO#eQPO,59ROOQO,58{,58{OVQPO'#CqOOQO'#Cq'#CqO#mQPO,58zOVQPO'#CrO#zQPO,58yPOOO-E6n-E6nOOQO1G.l1G.lOOQO'#Cm'#CmOOQO'#Ck'#CkOOQO1G.m1G.mOOQO,59],59]OOQO-E6o-E6oOOQO,59^,59^OOQO-E6p-E6p",
stateData: "$]~OiPQ~OUUOXQO]RO`TO~Oi[O~OUaXXaX]aX^[X`aXbaXcaXgaXWaX~O^_O~OUUOXQO]RO`TObaO~OcSXgSXWSX~P!^OcdOgRXWRX~Oi[O~Qh]WgO~O]hO`iO~OcSagSaWSa~P!^OcdOgRaWRa~OUbc]c~",
goto: "#hgPPhnryP!YPP!c!c!lPP!uP!xPP#U#[#bQZOR^QTYOQSXOQRmdUWOQdQ`USbWcRka_VOQUWacd_TOQUWacd_SOQUWacdRj_^TOQUWacdRi_Q]PRf]QcWRlcQeXRne",
nodeNames: "⚠ Query Expr OrExpr AndExpr Unary Not Primary RParen LParen Group Field FieldName Word Colon FieldValue Phrase Term And Or",
states:
"%QOVQPOOPeOPOOOVQPO'#CfOjQPO'#ChO!XQPO'#CgOOQO'#Cc'#CcOVQPO'#CaOOQO'#Ca'#CaO!oQPO'#C`O!|QPO'#C_OOQO'#C^'#C^QOQPOOPOOO'#Cp'#CpP#XOPO)C>jO#`QPO,59QO#eQPO,59ROOQO,58{,58{OVQPO'#CqOOQO'#Cq'#CqO#mQPO,58zOVQPO'#CrO#zQPO,58yPOOO-E6n-E6nOOQO1G.l1G.lOOQO'#Cm'#CmOOQO'#Ck'#CkOOQO1G.m1G.mOOQO,59],59]OOQO-E6o-E6oOOQO,59^,59^OOQO-E6p-E6p",
stateData:
'$]~OiPQ~OUUOXQO]RO`TO~Oi[O~OUaXXaX]aX^[X`aXbaXcaXgaXWaX~O^_O~OUUOXQO]RO`TObaO~OcSXgSXWSX~P!^OcdOgRXWRX~Oi[O~Qh]WgO~O]hO`iO~OcSagSaWSa~P!^OcdOgRaWRa~OUbc]c~',
goto: '#hgPPhnryP!YPP!c!c!lPP!uP!xPP#U#[#bQZOR^QTYOQSXOQRmdUWOQdQ`USbWcRka_VOQUWacd_TOQUWacd_SOQUWacdRj_^TOQUWacdRi_Q]PRf]QcWRlcQeXRne',
nodeNames:
'⚠ Query Expr OrExpr AndExpr Unary Not Primary RParen LParen Group Field FieldName Word Colon FieldValue Phrase Term And Or',
maxTerm: 25,
nodeProps: [
["openedBy", 8,"LParen"],
["closedBy", 9,"RParen"]
['openedBy', 8, 'LParen'],
['closedBy', 9, 'RParen'],
],
propSources: [highlight],
skippedNodes: [0,20],
skippedNodes: [0, 20],
repeatNodeCount: 3,
tokenData: ")f~RgX^!jpq!jrs#_xy${yz%Q}!O%V!Q![%[![!]%m!c!d%r!d!p%[!p!q'V!q!r(j!r!}%[#R#S%[#T#o%[#y#z!j$f$g!j#BY#BZ!j$IS$I_!j$I|$JO!j$JT$JU!j$KV$KW!j&FU&FV!j~!oYi~X^!jpq!j#y#z!j$f$g!j#BY#BZ!j$IS$I_!j$I|$JO!j$JT$JU!j$KV$KW!j&FU&FV!j~#bVOr#_rs#ws#O#_#O#P#|#P;'S#_;'S;=`$u<%lO#_~#|O`~~$PRO;'S#_;'S;=`$Y;=`O#_~$]WOr#_rs#ws#O#_#O#P#|#P;'S#_;'S;=`$u;=`<%l#_<%lO#_~$xP;=`<%l#_~%QOX~~%VOW~~%[OU~~%aS]~!Q![%[!c!}%[#R#S%[#T#o%[~%rO^~~%wU]~!Q![%[!c!p%[!p!q&Z!q!}%[#R#S%[#T#o%[~&`U]~!Q![%[!c!f%[!f!g&r!g!}%[#R#S%[#T#o%[~&ySb~]~!Q![%[!c!}%[#R#S%[#T#o%[~'[U]~!Q![%[!c!q%[!q!r'n!r!}%[#R#S%[#T#o%[~'sU]~!Q![%[!c!v%[!v!w(V!w!}%[#R#S%[#T#o%[~(^SU~]~!Q![%[!c!}%[#R#S%[#T#o%[~(oU]~!Q![%[!c!t%[!t!u)R!u!}%[#R#S%[#T#o%[~)YSc~]~!Q![%[!c!}%[#R#S%[#T#o%[",
tokenData:
")f~RgX^!jpq!jrs#_xy${yz%Q}!O%V!Q![%[![!]%m!c!d%r!d!p%[!p!q'V!q!r(j!r!}%[#R#S%[#T#o%[#y#z!j$f$g!j#BY#BZ!j$IS$I_!j$I|$JO!j$JT$JU!j$KV$KW!j&FU&FV!j~!oYi~X^!jpq!j#y#z!j$f$g!j#BY#BZ!j$IS$I_!j$I|$JO!j$JT$JU!j$KV$KW!j&FU&FV!j~#bVOr#_rs#ws#O#_#O#P#|#P;'S#_;'S;=`$u<%lO#_~#|O`~~$PRO;'S#_;'S;=`$Y;=`O#_~$]WOr#_rs#ws#O#_#O#P#|#P;'S#_;'S;=`$u;=`<%l#_<%lO#_~$xP;=`<%l#_~%QOX~~%VOW~~%[OU~~%aS]~!Q![%[!c!}%[#R#S%[#T#o%[~%rO^~~%wU]~!Q![%[!c!p%[!p!q&Z!q!}%[#R#S%[#T#o%[~&`U]~!Q![%[!c!f%[!f!g&r!g!}%[#R#S%[#T#o%[~&ySb~]~!Q![%[!c!}%[#R#S%[#T#o%[~'[U]~!Q![%[!c!q%[!q!r'n!r!}%[#R#S%[#T#o%[~'sU]~!Q![%[!c!v%[!v!w(V!w!}%[#R#S%[#T#o%[~(^SU~]~!Q![%[!c!}%[#R#S%[#T#o%[~(oU]~!Q![%[!c!t%[!t!u)R!u!}%[#R#S%[#T#o%[~)YSc~]~!Q![%[!c!}%[#R#S%[#T#o%[",
tokenizers: [0],
topRules: {"Query":[0,1]},
tokenPrec: 145
})
topRules: { Query: [0, 1] },
tokenPrec: 145,
});

View File

@@ -255,9 +255,9 @@ type Technique = 'substring' | 'fuzzy' | 'strict';
function includes(hay: string | undefined, needle: string, technique: Technique): boolean {
if (!hay || !needle) return false;
else if (technique === 'strict') return hay === needle;
else if (technique === 'fuzzy') return !!fuzzyMatch(hay, needle);
else return hay.indexOf(needle) !== -1;
if (technique === 'strict') return hay === needle;
if (technique === 'fuzzy') return !!fuzzyMatch(hay, needle);
return hay.indexOf(needle) !== -1;
}
export function evaluate(ast: Ast | null, doc: Doc): boolean {

View File

@@ -11,9 +11,10 @@ const REGEX =
const tooltip = hoverTooltip(
(view, pos, side) => {
const { from, text } = view.state.doc.lineAt(pos);
let match;
let match: RegExpExecArray | null;
let found: { start: number; end: number } | null = null;
// biome-ignore lint/suspicious/noAssignInExpressions: none
while ((match = REGEX.exec(text))) {
const start = from + match.index;
const end = start + match[0].length;
@@ -28,7 +29,7 @@ const tooltip = hoverTooltip(
return null;
}
if ((found.start == pos && side < 0) || (found.end == pos && side > 0)) {
if ((found.start === pos && side < 0) || (found.end === pos && side > 0)) {
return null;
}
@@ -37,7 +38,7 @@ const tooltip = hoverTooltip(
end: found.end,
create() {
const workspaceId = jotaiStore.get(activeWorkspaceIdAtom);
const link = text.substring(found!.start - from, found!.end - from);
const link = text.substring(found?.start - from, found?.end - from);
const dom = document.createElement('div');
const $open = document.createElement('a');
@@ -77,7 +78,7 @@ const tooltip = hoverTooltip(
},
);
const decorator = function () {
const decorator = () => {
const placeholderMatcher = new MatchDecorator({
regexp: REGEX,
decoration(match, view, matchStartPos) {

View File

@@ -12,7 +12,7 @@ export function jsonParseLinter() {
// syntax with repeating `1` characters, so it's valid JSON and the position is still correct.
const escapedDoc = doc.replace(TEMPLATE_SYNTAX_REGEX, '1');
jsonLintParse(escapedDoc);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
// biome-ignore lint/suspicious/noExplicitAny: none
} catch (err: any) {
if (!('location' in err)) {
return [];

View File

@@ -1,6 +1,5 @@
// This file was generated by lezer-generator. You probably shouldn't edit it.
export const
pairs = 1,
export const pairs = 1,
Key = 2,
Sep = 3,
Value = 4
Value = 4;

View File

@@ -1,19 +1,29 @@
// This file was generated by lezer-generator. You probably shouldn't edit it.
import {LRParser} from "@lezer/lr"
import {highlight} from "./highlight"
import { LRParser } from '@lezer/lr';
import { highlight } from './highlight';
export const parser = LRParser.deserialize({
version: 14,
states: "zQQOPOOOVOQO'#CaQQOPOOO[OSO,58{OOOO-E6_-E6_OaOQO1G.gOOOO7+$R7+$R",
stateData: "f~OQPO~ORRO~OSTO~OVUO~O",
goto: "]UPPPPPVQQORSQ",
nodeNames: "⚠ pairs Key Sep Value",
stateData: 'f~OQPO~ORRO~OSTO~OVUO~O',
goto: ']UPPPPPVQQORSQ',
nodeNames: '⚠ pairs Key Sep Value',
maxTerm: 7,
propSources: [highlight],
skippedNodes: [0],
repeatNodeCount: 1,
tokenData: "$]VRVOYhYZ#[Z![h![!]#o!];'Sh;'S;=`#U<%lOhToVQPSSOYhYZ!UZ![h![!]!m!];'Sh;'S;=`#U<%lOhP!ZSQPO![!U!];'S!U;'S;=`!g<%lO!UP!jP;=`<%l!US!rSSSOY!mZ;'S!m;'S;=`#O<%lO!mS#RP;=`<%l!mT#XP;=`<%lhR#cSVQQPO![!U!];'S!U;'S;=`!g<%lO!UV#vVRQSSOYhYZ!UZ![h![!]!m!];'Sh;'S;=`#U<%lOh",
tokenData:
"$]VRVOYhYZ#[Z![h![!]#o!];'Sh;'S;=`#U<%lOhToVQPSSOYhYZ!UZ![h![!]!m!];'Sh;'S;=`#U<%lOhP!ZSQPO![!U!];'S!U;'S;=`!g<%lO!UP!jP;=`<%l!US!rSSSOY!mZ;'S!m;'S;=`#O<%lO!mS#RP;=`<%l!mT#XP;=`<%lhR#cSVQQPO![!U!];'S!U;'S;=`!g<%lO!UV#vVRQSSOYhYZ!UZ![h![!]!m!];'Sh;'S;=`#U<%lOh",
tokenizers: [0, 1, 2],
topRules: {"pairs":[0,1]},
topRules: { pairs: [0, 1] },
tokenPrec: 0,
termNames: {"0":"⚠","1":"@top","2":"Key","3":"Sep","4":"Value","5":"(Key Sep Value \"\\n\")+","6":"␄","7":"\"\\n\""}
})
termNames: {
'0': '⚠',
'1': '@top',
'2': 'Key',
'3': 'Sep',
'4': 'Value',
'5': '(Key Sep Value "\\n")+',
'6': '␄',
'7': '"\\n"',
},
});

View File

@@ -1,4 +1,3 @@
// This file was generated by lezer-generator. You probably shouldn't edit it.
export const
Template = 1,
Text = 2
export const Template = 1,
Text = 2;

View File

@@ -1,16 +1,16 @@
// This file was generated by lezer-generator. You probably shouldn't edit it.
import {LRParser} from "@lezer/lr"
import { LRParser } from '@lezer/lr';
export const parser = LRParser.deserialize({
version: 14,
states: "[OQOPOOQOOOOO",
stateData: "V~OQPO~O",
goto: "QPP",
nodeNames: "⚠ Template Text",
states: '[OQOPOOQOOOOO',
stateData: 'V~OQPO~O',
goto: 'QPP',
nodeNames: '⚠ Template Text',
maxTerm: 3,
skippedNodes: [0],
repeatNodeCount: 0,
tokenData: "p~RRO;'S[;'S;=`j<%lO[~aRQ~O;'S[;'S;=`j<%lO[~mP;=`<%l[",
tokenizers: [0],
topRules: {"Template":[0,1]},
tokenPrec: 0
})
topRules: { Template: [0, 1] },
tokenPrec: 0,
});

View File

@@ -44,14 +44,13 @@ export function twigCompletion({ options }: TwigCompletionConfig) {
if (toMatch === null) return null;
const matchLen = toMatch.to - toMatch.from;
if (toMatch.from >0 && matchLen < MIN_MATCH_NAME) {
if (toMatch.from > 0 && matchLen < MIN_MATCH_NAME) {
return null;
}
const completions: Completion[] = options
.flatMap((o): Completion[] => {
const matchSegments = toMatch!.text.replace(/^\$/, '').split('.');
const matchSegments = toMatch.text.replace(/^\$/, '').split('.');
const optionSegments = o.name.split('.');
// If not on the last segment, only complete the namespace
@@ -59,7 +58,7 @@ export function twigCompletion({ options }: TwigCompletionConfig) {
const prefix = optionSegments.slice(0, matchSegments.length).join('.');
return [
{
label: prefix + '.*',
label: `${prefix}.*`,
type: 'namespace',
detail: 'namespace',
apply: (view, _completion, from, to) => {

View File

@@ -1,7 +1,7 @@
import { syntaxTree } from '@codemirror/language';
import type { Range } from '@codemirror/state';
import type { DecorationSet, ViewUpdate } from '@codemirror/view';
import { Decoration, ViewPlugin, WidgetType, EditorView } from '@codemirror/view';
import { Decoration, EditorView, ViewPlugin, WidgetType } from '@codemirror/view';
class PathPlaceholderWidget extends WidgetType {
readonly #clickListenerCallback: () => void;
@@ -23,7 +23,7 @@ class PathPlaceholderWidget extends WidgetType {
toDOM() {
const elt = document.createElement('span');
elt.className = `x-theme-templateTag x-theme-templateTag--secondary template-tag`;
elt.className = 'x-theme-templateTag x-theme-templateTag--secondary template-tag';
elt.textContent = this.rawText;
elt.addEventListener('click', this.#clickListenerCallback);
return elt;

View File

@@ -1,7 +1,7 @@
import { syntaxTree } from '@codemirror/language';
import type { Range } from '@codemirror/state';
import type { DecorationSet, ViewUpdate } from '@codemirror/view';
import { Decoration, ViewPlugin, WidgetType, EditorView } from '@codemirror/view';
import { Decoration, EditorView, ViewPlugin, WidgetType } from '@codemirror/view';
import type { SyntaxNodeRef } from '@lezer/common';
import { parseTemplate } from '@yaakapp-internal/templates';
import type { TwigCompletionOption } from './completion';

View File

@@ -1,8 +1,7 @@
// This file was generated by lezer-generator. You probably shouldn't edit it.
export const
Template = 1,
export const Template = 1,
Tag = 2,
TagOpen = 3,
TagContent = 4,
TagClose = 5,
Text = 6
Text = 6;

View File

@@ -1,18 +1,20 @@
// This file was generated by lezer-generator. You probably shouldn't edit it.
import {LRParser, LocalTokenGroup} from "@lezer/lr"
import {highlight} from "./highlight"
import { LocalTokenGroup, LRParser } from '@lezer/lr';
import { highlight } from './highlight';
export const parser = LRParser.deserialize({
version: 14,
states: "!^QQOPOOOOOO'#C_'#C_OYOQO'#C^OOOO'#Cc'#CcQQOPOOOOOO'#Cd'#CdO_OQO,58xOOOO-E6a-E6aOOOO-E6b-E6bOOOO1G.d1G.d",
stateData: "g~OUROYPO~OSTO~OSTOTXO~O",
goto: "nXPPY^PPPbhTROSTQOSQSORVSQUQRWU",
nodeNames: "⚠ Template Tag TagOpen TagContent TagClose Text",
states:
"!^QQOPOOOOOO'#C_'#C_OYOQO'#C^OOOO'#Cc'#CcQQOPOOOOOO'#Cd'#CdO_OQO,58xOOOO-E6a-E6aOOOO-E6b-E6bOOOO1G.d1G.d",
stateData: 'g~OUROYPO~OSTO~OSTOTXO~O',
goto: 'nXPPY^PPPbhTROSTQOSQSORVSQUQRWU',
nodeNames: '⚠ Template Tag TagOpen TagContent TagClose Text',
maxTerm: 10,
propSources: [highlight],
skippedNodes: [0],
repeatNodeCount: 2,
tokenData: "#]~RTOtbtu!hu;'Sb;'S;=`!]<%lOb~gTU~Otbtuvu;'Sb;'S;=`!]<%lOb~yUO#ob#p;'Sb;'S;=`!]<%l~b~Ob~~!c~!`P;=`<%lb~!hOU~~!kVO#ob#o#p#Q#p;'Sb;'S;=`!]<%l~b~Ob~~!c~#TP!}#O#W~#]OY~",
tokenizers: [1, new LocalTokenGroup("b~RP#P#QU~XP#q#r[~aOT~~", 17, 4)],
topRules: {"Template":[0,1]},
tokenPrec: 0
})
tokenData:
"#]~RTOtbtu!hu;'Sb;'S;=`!]<%lOb~gTU~Otbtuvu;'Sb;'S;=`!]<%lOb~yUO#ob#p;'Sb;'S;=`!]<%l~b~Ob~~!c~!`P;=`<%lb~!hOU~~!kVO#ob#o#p#Q#p;'Sb;'S;=`!]<%l~b~Ob~~!c~#TP!}#O#W~#]OY~",
tokenizers: [1, new LocalTokenGroup('b~RP#P#QU~XP#q#r[~aOT~~', 17, 4)],
topRules: { Template: [0, 1] },
tokenPrec: 0,
});

View File

@@ -1,10 +1,9 @@
// This file was generated by lezer-generator. You probably shouldn't edit it.
export const
url = 1,
export const url = 1,
Protocol = 2,
Host = 3,
Port = 4,
Path = 5,
Placeholder = 6,
PathSegment = 7,
Query = 8
Query = 8;

View File

@@ -1,19 +1,20 @@
// This file was generated by lezer-generator. You probably shouldn't edit it.
import {LRParser} from "@lezer/lr"
import {highlight} from "./highlight"
import { LRParser } from '@lezer/lr';
import { highlight } from './highlight';
export const parser = LRParser.deserialize({
version: 14,
states: "!|OQOPOOQYOPOOOTOPOOObOQO'#CdOjOPO'#C`OuOSO'#CcQOOOOOQ]OPOOOOOO,59O,59OOOOO-E6b-E6bOzOPO,58}O!SOSO'#CeO!XOPO1G.iOOOO,59P,59POOOO-E6c-E6c",
stateData: "!g~OQQORPO~OZRO[TO~OTWOUWO~OZROYSX[SX~O]YO~O^ZOYVa~O]]O~O^ZOYVi~OQRTUT~",
goto: "nYPPPPZPP^bhRVPTUPVQSPRXSQ[YR^[",
nodeNames: "⚠ url Protocol Host Path Placeholder PathSegment Query",
states:
"!|OQOPOOQYOPOOOTOPOOObOQO'#CdOjOPO'#C`OuOSO'#CcQOOOOOQ]OPOOOOOO,59O,59OOOOO-E6b-E6bOzOPO,58}O!SOSO'#CeO!XOPO1G.iOOOO,59P,59POOOO-E6c-E6c",
stateData: '!g~OQQORPO~OZRO[TO~OTWOUWO~OZROYSX[SX~O]YO~O^ZOYVa~O]]O~O^ZOYVi~OQRTUT~',
goto: 'nYPPPPZPP^bhRVPTUPVQSPRXSQ[YR^[',
nodeNames: '⚠ url Protocol Host Path Placeholder PathSegment Query',
maxTerm: 14,
propSources: [highlight],
skippedNodes: [0],
repeatNodeCount: 2,
tokenData: ".i~RgOs!jtv!jvw#Xw}!j}!O#r!O!P#r!P!Q%U!Q![%Z![!]'o!]!a!j!a!b+W!b!c!j!c!}+]!}#O#r#O#P!j#P#Q#r#Q#R!j#R#S#r#S#T!j#T#o+]#o;'S!j;'S;=`#R<%lO!jQ!oUUQOs!jt!P!j!Q!a!j!b;'S!j;'S;=`#R<%lO!jQ#UP;=`<%l!jR#`U^PUQOs!jt!P!j!Q!a!j!b;'S!j;'S;=`#R<%lO!jR#ycRPUQOs!jt}!j}!O#r!O!P#r!Q![#r![!]#r!]!a!j!b!c!j!c!}#r!}#O#r#O#P!j#P#Q#r#Q#R!j#R#S#r#S#T!j#T#o#r#o;'S!j;'S;=`#R<%lO!j~%ZOZ~V%de]SRPUQOs!jt}!j}!O#r!O!P#r!Q![%Z![!]#r!]!_!j!_!`&u!`!a!j!b!c!j!c!}%Z!}#O#r#O#P!j#P#Q#r#Q#R!j#R#S#r#S#T!j#T#o%Z#o;'S!j;'S;=`#R<%lO!jU&|Z]SUQOs!jt!P!j!Q![&u![!a!j!b!c!j!c!}&u!}#T!j#T#o&u#o;'S!j;'S;=`#R<%lO!jR'vcRPUQOs)Rt})R}!O)r!O!P)r!Q![)r![!])r!]!a)R!b!c)R!c!})r!}#O)r#O#P)R#P#Q)r#Q#R)R#R#S)r#S#T)R#T#o)r#o;'S)R;'S;=`)l<%lO)RQ)YUTQUQOs)Rt!P)R!Q!a)R!b;'S)R;'S;=`)l<%lO)RQ)oP;=`<%l)RR){cRPTQUQOs)Rt})R}!O)r!O!P)r!Q![)r![!])r!]!a)R!b!c)R!c!})r!}#O)r#O#P)R#P#Q)r#Q#R)R#R#S)r#S#T)R#T#o)r#o;'S)R;'S;=`)l<%lO)R~+]O[~V+fe]SRPUQOs!jt}!j}!O#r!O!P#r!Q![%Z![!],w!]!_!j!_!`&u!`!a!j!b!c!j!c!}+]!}#O#r#O#P!j#P#Q#r#Q#R!j#R#S#r#S#T!j#T#o+]#o;'S!j;'S;=`#R<%lO!jR-OdRPUQOs!jt}!j}!O#r!O!P#r!P!Q.^!Q![#r![!]#r!]!a!j!b!c!j!c!}#r!}#O#r#O#P!j#P#Q#r#Q#R!j#R#S#r#S#T!j#T#o#r#o;'S!j;'S;=`#R<%lO!jP.aP!P!Q.dP.iOQP",
tokenData:
".i~RgOs!jtv!jvw#Xw}!j}!O#r!O!P#r!P!Q%U!Q![%Z![!]'o!]!a!j!a!b+W!b!c!j!c!}+]!}#O#r#O#P!j#P#Q#r#Q#R!j#R#S#r#S#T!j#T#o+]#o;'S!j;'S;=`#R<%lO!jQ!oUUQOs!jt!P!j!Q!a!j!b;'S!j;'S;=`#R<%lO!jQ#UP;=`<%l!jR#`U^PUQOs!jt!P!j!Q!a!j!b;'S!j;'S;=`#R<%lO!jR#ycRPUQOs!jt}!j}!O#r!O!P#r!Q![#r![!]#r!]!a!j!b!c!j!c!}#r!}#O#r#O#P!j#P#Q#r#Q#R!j#R#S#r#S#T!j#T#o#r#o;'S!j;'S;=`#R<%lO!j~%ZOZ~V%de]SRPUQOs!jt}!j}!O#r!O!P#r!Q![%Z![!]#r!]!_!j!_!`&u!`!a!j!b!c!j!c!}%Z!}#O#r#O#P!j#P#Q#r#Q#R!j#R#S#r#S#T!j#T#o%Z#o;'S!j;'S;=`#R<%lO!jU&|Z]SUQOs!jt!P!j!Q![&u![!a!j!b!c!j!c!}&u!}#T!j#T#o&u#o;'S!j;'S;=`#R<%lO!jR'vcRPUQOs)Rt})R}!O)r!O!P)r!Q![)r![!])r!]!a)R!b!c)R!c!})r!}#O)r#O#P)R#P#Q)r#Q#R)R#R#S)r#S#T)R#T#o)r#o;'S)R;'S;=`)l<%lO)RQ)YUTQUQOs)Rt!P)R!Q!a)R!b;'S)R;'S;=`)l<%lO)RQ)oP;=`<%l)RR){cRPTQUQOs)Rt})R}!O)r!O!P)r!Q![)r![!])r!]!a)R!b!c)R!c!})r!}#O)r#O#P)R#P#Q)r#Q#R)R#R#S)r#S#T)R#T#o)r#o;'S)R;'S;=`)l<%lO)R~+]O[~V+fe]SRPUQOs!jt}!j}!O#r!O!P#r!Q![%Z![!],w!]!_!j!_!`&u!`!a!j!b!c!j!c!}+]!}#O#r#O#P!j#P#Q#r#Q#R!j#R#S#r#S#T!j#T#o+]#o;'S!j;'S;=`#R<%lO!jR-OdRPUQOs!jt}!j}!O#r!O!P#r!P!Q.^!Q![#r![!]#r!]!a!j!b!c!j!c!}#r!}#O#r#O#P!j#P#Q#r#Q#R!j#R#S#r#S#T!j#T#o#r#o;'S!j;'S;=`#R<%lO!jP.aP!P!Q.dP.iOQP",
tokenizers: [0, 1, 2],
topRules: {"url":[0,1]},
tokenPrec: 63
})
topRules: { url: [0, 1] },
tokenPrec: 63,
});