diff --git a/packages/queryLanguage/src/index.ts b/packages/queryLanguage/src/index.ts index a5d01903..00cfbaad 100644 --- a/packages/queryLanguage/src/index.ts +++ b/packages/queryLanguage/src/index.ts @@ -3,4 +3,5 @@ import { parser } from "./parser"; type Tree = ReturnType; type SyntaxNode = Tree['topNode']; export type { Tree, SyntaxNode }; -export * from "./parser"; \ No newline at end of file +export * from "./parser"; +export * from "./parser.terms"; \ No newline at end of file diff --git a/packages/queryLanguage/src/parser.terms.ts b/packages/queryLanguage/src/parser.terms.ts index a123cf2b..7be7ac7a 100644 --- a/packages/queryLanguage/src/parser.terms.ts +++ b/packages/queryLanguage/src/parser.terms.ts @@ -1,10 +1,20 @@ // This file was generated by lezer-generator. You probably shouldn't edit it. export const - negate = 24, + negate = 21, Program = 1, OrExpr = 2, AndExpr = 3, NegateExpr = 4, PrefixExpr = 5, - ParenExpr = 19, - Term = 20 + ArchivedExpr = 6, + RevisionExpr = 7, + ContentExpr = 8, + FileExpr = 9, + ForkExpr = 10, + VisibilityExpr = 11, + RepoExpr = 12, + LangExpr = 13, + SymExpr = 14, + RepoSetExpr = 15, + ParenExpr = 16, + Term = 17 diff --git a/packages/queryLanguage/src/parser.ts b/packages/queryLanguage/src/parser.ts index f1cb1153..6a5bf415 100644 --- a/packages/queryLanguage/src/parser.ts +++ b/packages/queryLanguage/src/parser.ts @@ -3,16 +3,16 @@ import {LRParser} from "@lezer/lr" import {negateToken} from "./tokens" export const parser = LRParser.deserialize({ version: 14, - states: "'hOVQROOO!^QQO'#CbO!^QQO'#CcO!^QQO'#CdO!^QQO'#CeO!^QQO'#CfO!^QQO'#CgO!^QQO'#ChO!^QQO'#CiO!^QQO'#CjO!^QQO'#CkO!^QQO'#ClO!^QQO'#CmO!^QQO'#CnOOQP'#Ca'#CaOVQRO'#CoO!fQQO'#C`OOQP'#Cp'#CpOOQP'#Cy'#CyO#dQRO'#CxO#qQQO'#CxO#|QQO'#C^OOQO'#Cw'#CwQOQQOOOOQP'#C{'#C{OOQP,58|,58|OOQP,58},58}OOQP,59O,59OOOQP,59P,59POOQP,59Q,59QOOQP,59R,59ROOQP,59S,59SOOQP,59T,59TOOQP,59U,59UOOQP,59V,59VOOQP,59W,59WOOQP,59X,59XOOQP,59Y,59YO$RQQO,59ZOOQP,58z,58zOOQP'#Cq'#CqO$WQRO,58yOVQRO'#CrO$eQQO,58xOOQP1G.u1G.uOOQP-E6o-E6oO$pQRO'#CxOOQO'#Cx'#CxOOQO,59^,59^OOQO-E6p-E6p", - stateData: "%a~OjOS~Oh`OnPOpaOqaOrQOsROtSOuTOvUOwVOxWOyXOzYO{ZO|[O}]O!O_O~OphOqhO~OnPOrQOsROtSOuTOvUOwVOxWOyXOzYO{ZO|[O}]O!O_O~OgkX!QlX!PkX~PVOgkX!QlX!PkX~O!QzO~O!P|O~OgRa!QRa!PRa~PVO!QzOgQa!PQa~OglX!QlX!PlX~PVOpnrstuvwxyz{|}!Qqx~", - goto: "$ZpPPqu|!U!a!a!a!a!a!a!a!a!a!a!a!a!a!U|!j!qPPPP!w!}#UP#bTfO_SdO_R!Pz]bO_cyz!O[bO_cyz!ORw`_^O_`cyz!OSyc!OR}yQ{eR!R{QgORv_SeO_R!QzScO_Uxcy!OR!OzQiPQjQQkRQlSQmTQnUQoVQpWQqXQrYQsZQt[Ru]", - nodeNames: "⚠ Program OrExpr AndExpr NegateExpr PrefixExpr ArchivedExpr BranchExpr ContentExpr CaseExpr FileExpr ForkExpr PublicExpr RepoExpr RegexExpr LangExpr SymExpr TypeExpr RepoSetExpr ParenExpr Term", - maxTerm: 48, + states: "&rOVQROOO!TQQO'#CbO!TQQO'#CcO!TQQO'#CdO!TQQO'#CeO!TQQO'#CfO!TQQO'#CgO!TQQO'#ChO!TQQO'#CiO!TQQO'#CjO!TQQO'#CkOOQP'#Ca'#CaOVQRO'#ClO!]QQO'#C`OOQP'#Cm'#CmOOQP'#Cv'#CvO#QQRO'#CuO#_QQO'#CuO#jQQO'#C^OOQO'#Ct'#CtQOQQOOOOQP'#Cx'#CxOOQP,58|,58|OOQP,58},58}OOQP,59O,59OOOQP,59P,59POOQP,59Q,59QOOQP,59R,59ROOQP,59S,59SOOQP,59T,59TOOQP,59U,59UOOQP,59V,59VO#oQQO,59WOOQP,58z,58zOOQP'#Cn'#CnO#tQRO,58yOVQRO'#CoO$RQQO,58xOOQP1G.r1G.rOOQP-E6l-E6lO$^QRO'#CuOOQO'#Cu'#CuOOQO,59Z,59ZOOQO-E6m-E6m", + stateData: "$z~OgOS~Oe]OkPOm^On^OoQOpROqSOrTOsUOtVOuWOvXOwYOx[O~OmeOneO~OkPOoQOpROqSOrTOsUOtVOuWOvXOwYOx[O~OdhXziXyhX~PVOdhXziXyhX~OztO~OyvO~OdRazRayRa~PVOztOdQayQa~OdiXziXyiX~PVOmkopqrstuvwzns~", + goto: "#}mPPnry!R!^!^!^!^!^!^!^!^!^!^!Ry!g!nPPPP!t!z#RP#_TcO[SaO[Ryt]_O[`stx[_O[`stxRq]_ZO[]`stxSs`xRwsQubR{uQdORp[SbO[RztS`O[Ur`sxRxtQfPQgQQhRQiSQjTQkUQlVQmWQnXRoY", + nodeNames: "⚠ Program OrExpr AndExpr NegateExpr PrefixExpr ArchivedExpr RevisionExpr ContentExpr FileExpr ForkExpr VisibilityExpr RepoExpr LangExpr SymExpr RepoSetExpr ParenExpr Term", + maxTerm: 42, skippedNodes: [0], repeatNodeCount: 2, - tokenData: "!=_~RlOX!yXY#wYZ#wZp!ypq#wqr!yrs$Vsx!yxy%yyz&Oz#T!y#T#U&T#U#V.g#V#W4}#W#Y!y#Y#Z@V#Z#`!y#`#aHe#a#c!y#c#dMP#d#e!!m#e#f!y#f#g!)T#g#h!5V#h#i!8s#i;'S!y;'S;=`#q<%lO!y~#OZq~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!];'S!y;'S;=`#q<%lO!y~#tP;=`<%l!y~#|Rj~XY#wYZ#wpq#w~$YWOY$VZr$Vrs$rs#O$V#O#P$w#P;'S$V;'S;=`%s<%lO$V~$wOp~~$zRO;'S$V;'S;=`%T;=`O$V~%WXOY$VZr$Vrs$rs#O$V#O#P$w#P;'S$V;'S;=`%s;=`<%l$V<%lO$V~%vP;=`<%l$V~&OO!O~~&TO!P~~&Y]q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#f!y#f#g'R#g;'S!y;'S;=`#q<%lO!y~'W]q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#V!y#V#W(P#W;'S!y;'S;=`#q<%lO!y~(U]q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#[!y#[#](}#];'S!y;'S;=`#q<%lO!y~)S]q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#]!y#]#^){#^;'S!y;'S;=`#q<%lO!y~*Q]q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#j!y#j#k*y#k;'S!y;'S;=`#q<%lO!y~+O]q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#X!y#X#Y+w#Y;'S!y;'S;=`#q<%lO!y~+|]q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#W!y#W#X,u#X;'S!y;'S;=`#q<%lO!y~,zZq~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]-m!];'S!y;'S;=`#q<%lO!y~-tZn~q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!];'S!y;'S;=`#q<%lO!y~.l]q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]/e!]#f!y#f#g0_#g;'S!y;'S;=`#q<%lO!y~/lZr~q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!];'S!y;'S;=`#q<%lO!y~0d]q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#T!y#T#U1]#U;'S!y;'S;=`#q<%lO!y~1b]q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#b!y#b#c2Z#c;'S!y;'S;=`#q<%lO!y~2`]q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#V!y#V#W3X#W;'S!y;'S;=`#q<%lO!y~3^]q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#[!y#[#]4V#];'S!y;'S;=`#q<%lO!y~4[Zq~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]/e!];'S!y;'S;=`#q<%lO!y~5S_q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]6R!]#T!y#T#U6{#U#c!y#c#d:i#d;'S!y;'S;=`#q<%lO!y~6YZs~q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!];'S!y;'S;=`#q<%lO!y~7Q]q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#g!y#g#h7y#h;'S!y;'S;=`#q<%lO!y~8O]q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#X!y#X#Y8w#Y;'S!y;'S;=`#q<%lO!y~8|Zq~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]9o!];'S!y;'S;=`#q<%lO!y~9vZt~q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!];'S!y;'S;=`#q<%lO!y~:n]q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#b!y#b#c;g#c;'S!y;'S;=`#q<%lO!y~;l]q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#h!y#h#ia#c;'S!y;'S;=`#q<%lO!y~>f]q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#h!y#h#i?_#i;'S!y;'S;=`#q<%lO!y~?dZq~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]6R!];'S!y;'S;=`#q<%lO!y~@[_q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]AZ!]#]!y#]#^BT#^#c!y#c#dDw#d;'S!y;'S;=`#q<%lO!y~AbZu~q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!];'S!y;'S;=`#q<%lO!y~BY]q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#`!y#`#aCR#a;'S!y;'S;=`#q<%lO!y~CW]q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#X!y#X#YDP#Y;'S!y;'S;=`#q<%lO!y~DUZq~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]AZ!];'S!y;'S;=`#q<%lO!y~D|]q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#f!y#f#gEu#g;'S!y;'S;=`#q<%lO!y~Ez]q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#_!y#_#`Fs#`;'S!y;'S;=`#q<%lO!y~FxZq~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]Gk!];'S!y;'S;=`#q<%lO!y~GrZv~q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!];'S!y;'S;=`#q<%lO!y~Hj]q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#T!y#T#UIc#U;'S!y;'S;=`#q<%lO!y~Ih]q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#b!y#b#cJa#c;'S!y;'S;=`#q<%lO!y~Jf]q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#Z!y#Z#[K_#[;'S!y;'S;=`#q<%lO!y~KdZq~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]LV!];'S!y;'S;=`#q<%lO!y~L^Zz~q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!];'S!y;'S;=`#q<%lO!y~MU]q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#f!y#f#gM}#g;'S!y;'S;=`#q<%lO!y~NSfq~OX! hXZ!!bZp! hpq!!bqr! hrs!!bsx! hxz!!bz}! h}!O! h!O!Q! h!Q![!y![!]! h!]!c! h!c!}!y!}#R! h#R#S!y#S#T! h#T#o!y#o;'S! h;'S;=`!!g<%lO! h~! oZ!Q~q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!];'S!y;'S;=`#q<%lO!y~!!gO!Q~~!!jP;=`<%l! h~!!r]q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#i!y#i#j!#k#j;'S!y;'S;=`#q<%lO!y~!#p]q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#U!y#U#V!$i#V;'S!y;'S;=`#q<%lO!y~!$n]q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#`!y#`#a!%g#a;'S!y;'S;=`#q<%lO!y~!%l]q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#]!y#]#^!&e#^;'S!y;'S;=`#q<%lO!y~!&j]q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#V!y#V#W!'c#W;'S!y;'S;=`#q<%lO!y~!'hZq~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!(Z!];'S!y;'S;=`#q<%lO!y~!(bZw~q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!];'S!y;'S;=`#q<%lO!y~!)Y]q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!*R!]#X!y#X#Y!*{#Y;'S!y;'S;=`#q<%lO!y~!*YZx~q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!];'S!y;'S;=`#q<%lO!y~!+Q_q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#Z!y#Z#[!,P#[#d!y#d#e!/m#e;'S!y;'S;=`#q<%lO!y~!,U]q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#X!y#X#Y!,}#Y;'S!y;'S;=`#q<%lO!y~!-S]q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#l!y#l#m!-{#m;'S!y;'S;=`#q<%lO!y~!.QZq~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!.s!];'S!y;'S;=`#q<%lO!y~!.zZy~q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!];'S!y;'S;=`#q<%lO!y~!/r]q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#c!y#c#d!0k#d;'S!y;'S;=`#q<%lO!y~!0p]q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!*R!]#g!y#g#h!1i#h;'S!y;'S;=`#q<%lO!y~!1n]q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#X!y#X#Y!2g#Y;'S!y;'S;=`#q<%lO!y~!2l]q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#h!y#h#i!3e#i;'S!y;'S;=`#q<%lO!y~!3jZq~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!4]!];'S!y;'S;=`#q<%lO!y~!4dZ}~q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!];'S!y;'S;=`#q<%lO!y~!5[]q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#m!y#m#n!6T#n;'S!y;'S;=`#q<%lO!y~!6Y]q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#a!y#a#b!7R#b;'S!y;'S;=`#q<%lO!y~!7WZq~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!7y!];'S!y;'S;=`#q<%lO!y~!8QZ{~q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!];'S!y;'S;=`#q<%lO!y~!8x]q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!9q!]#m!y#m#n!:k#n;'S!y;'S;=`#q<%lO!y~!9xZ|~q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!];'S!y;'S;=`#q<%lO!y~!:p]q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#d!y#d#e!;i#e;'S!y;'S;=`#q<%lO!y~!;n]q~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#X!y#X#Y!Z#a#c!y#c#dBu#d#f!y#f#gFc#g#h!!i#h#j!y#j#k!&V#k;'S!y;'S;=`#q<%lO!y~#OZn~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!];'S!y;'S;=`#q<%lO!y~#tP;=`<%l!y~#|Rg~XY#wYZ#wpq#w~$YWOY$VZr$Vrs$rs#O$V#O#P$w#P;'S$V;'S;=`%s<%lO$V~$wOm~~$zRO;'S$V;'S;=`%T;=`O$V~%WXOY$VZr$Vrs$rs#O$V#O#P$w#P;'S$V;'S;=`%s;=`<%l$V<%lO$V~%vP;=`<%l$V~&OOx~~&TOy~~&Y]n~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#f!y#f#g'R#g;'S!y;'S;=`#q<%lO!y~'W]n~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#V!y#V#W(P#W;'S!y;'S;=`#q<%lO!y~(U]n~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#[!y#[#](}#];'S!y;'S;=`#q<%lO!y~)S]n~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#]!y#]#^){#^;'S!y;'S;=`#q<%lO!y~*Q]n~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#j!y#j#k*y#k;'S!y;'S;=`#q<%lO!y~+O]n~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#X!y#X#Y+w#Y;'S!y;'S;=`#q<%lO!y~+|]n~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#W!y#W#X,u#X;'S!y;'S;=`#q<%lO!y~,zZn~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]-m!];'S!y;'S;=`#q<%lO!y~-tZk~n~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!];'S!y;'S;=`#q<%lO!y~.l]n~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]/e!]#c!y#c#d0_#d;'S!y;'S;=`#q<%lO!y~/lZp~n~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!];'S!y;'S;=`#q<%lO!y~0d]n~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#b!y#b#c1]#c;'S!y;'S;=`#q<%lO!y~1b]n~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#h!y#h#i2Z#i;'S!y;'S;=`#q<%lO!y~2`]n~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#X!y#X#Y3X#Y;'S!y;'S;=`#q<%lO!y~3^]n~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#b!y#b#c4V#c;'S!y;'S;=`#q<%lO!y~4[]n~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#h!y#h#i5T#i;'S!y;'S;=`#q<%lO!y~5YZn~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]/e!];'S!y;'S;=`#q<%lO!y~6Q_n~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]7P!]#]!y#]#^7y#^#c!y#c#d:m#d;'S!y;'S;=`#q<%lO!y~7WZq~n~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!];'S!y;'S;=`#q<%lO!y~8O]n~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#`!y#`#a8w#a;'S!y;'S;=`#q<%lO!y~8|]n~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#X!y#X#Y9u#Y;'S!y;'S;=`#q<%lO!y~9zZn~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]7P!];'S!y;'S;=`#q<%lO!y~:r]n~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#f!y#f#g;k#g;'S!y;'S;=`#q<%lO!y~;p]n~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#_!y#_#``]n~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#T!y#T#U?X#U;'S!y;'S;=`#q<%lO!y~?^]n~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#b!y#b#c@V#c;'S!y;'S;=`#q<%lO!y~@[]n~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#Z!y#Z#[AT#[;'S!y;'S;=`#q<%lO!y~AYZn~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]A{!];'S!y;'S;=`#q<%lO!y~BSZu~n~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!];'S!y;'S;=`#q<%lO!y~Bz]n~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#f!y#f#gCs#g;'S!y;'S;=`#q<%lO!y~Cxfn~OXE^XZFWZpE^pqFWqrE^rsFWsxE^xzFWz}E^}!OE^!O!QE^!Q![!y![!]E^!]!cE^!c!}!y!}#RE^#R#S!y#S#TE^#T#o!y#o;'SE^;'S;=`F]<%lOE^~EeZz~n~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!];'S!y;'S;=`#q<%lO!y~F]Oz~~F`P;=`<%lE^~Fh]n~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]Ga!]#X!y#X#YHZ#Y;'S!y;'S;=`#q<%lO!y~GhZt~n~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!];'S!y;'S;=`#q<%lO!y~H`_n~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#d!y#d#eI_#e#j!y#j#kNw#k;'S!y;'S;=`#q<%lO!y~Id]n~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#c!y#c#dJ]#d;'S!y;'S;=`#q<%lO!y~Jb]n~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]Ga!]#g!y#g#hKZ#h;'S!y;'S;=`#q<%lO!y~K`]n~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#X!y#X#YLX#Y;'S!y;'S;=`#q<%lO!y~L^]n~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#h!y#h#iMV#i;'S!y;'S;=`#q<%lO!y~M[Zn~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]M}!];'S!y;'S;=`#q<%lO!y~NUZw~n~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!];'S!y;'S;=`#q<%lO!y~N|Zn~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]! o!];'S!y;'S;=`#q<%lO!y~! vZo~n~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!];'S!y;'S;=`#q<%lO!y~!!n]n~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#m!y#m#n!#g#n;'S!y;'S;=`#q<%lO!y~!#l]n~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#a!y#a#b!$e#b;'S!y;'S;=`#q<%lO!y~!$jZn~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!%]!];'S!y;'S;=`#q<%lO!y~!%dZv~n~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!];'S!y;'S;=`#q<%lO!y~!&[]n~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#]!y#]#^!'T#^;'S!y;'S;=`#q<%lO!y~!'Y]n~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#g!y#g#h!(R#h;'S!y;'S;=`#q<%lO!y~!(W]n~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#]!y#]#^!)P#^;'S!y;'S;=`#q<%lO!y~!)U]n~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#U!y#U#V!)}#V;'S!y;'S;=`#q<%lO!y~!*S]n~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#]!y#]#^!*{#^;'S!y;'S;=`#q<%lO!y~!+Q]n~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#`!y#`#a!+y#a;'S!y;'S;=`#q<%lO!y~!,O]n~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#]!y#]#^!,w#^;'S!y;'S;=`#q<%lO!y~!,|]n~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#h!y#h#i!-u#i;'S!y;'S;=`#q<%lO!y~!-z]n~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!]#m!y#m#n!.s#n;'S!y;'S;=`#q<%lO!y~!.xZn~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!/k!];'S!y;'S;=`#q<%lO!y~!/rZs~n~OX!yZp!yqr!ysx!yz}!y}!O!y!O![!y![!]!y!];'S!y;'S;=`#q<%lO!y", tokenizers: [negateToken, 0], topRules: {"Program":[0,1]}, - tokenPrec: 183, - termNames: {"0":"⚠","1":"@top","2":"OrExpr","3":"AndExpr","4":"NegateExpr","5":"PrefixExpr","6":"ArchivedExpr","7":"BranchExpr","8":"ContentExpr","9":"CaseExpr","10":"FileExpr","11":"ForkExpr","12":"PublicExpr","13":"RepoExpr","14":"RegexExpr","15":"LangExpr","16":"SymExpr","17":"TypeExpr","18":"RepoSetExpr","19":"ParenExpr","20":"Term","21":"expr+","22":"(or andExpr)+","23":"␄","24":"negate","25":"%mainskip","26":"space","27":"query","28":"andExpr","29":"expr","30":"archivedKw","31":"value","32":"quotedString","33":"word","34":"branchKw","35":"contentKw","36":"caseKw","37":"fileKw","38":"forkKw","39":"publicKw","40":"repoKw","41":"regexKw","42":"langKw","43":"symKw","44":"typeKw","45":"reposetKw","46":"\"(\"","47":"\")\"","48":"or"} + tokenPrec: 165, + termNames: {"0":"⚠","1":"@top","2":"OrExpr","3":"AndExpr","4":"NegateExpr","5":"PrefixExpr","6":"ArchivedExpr","7":"RevisionExpr","8":"ContentExpr","9":"FileExpr","10":"ForkExpr","11":"VisibilityExpr","12":"RepoExpr","13":"LangExpr","14":"SymExpr","15":"RepoSetExpr","16":"ParenExpr","17":"Term","18":"expr+","19":"(or andExpr)+","20":"␄","21":"negate","22":"%mainskip","23":"space","24":"query","25":"andExpr","26":"expr","27":"archivedKw","28":"value","29":"quotedString","30":"word","31":"revisionKw","32":"contentKw","33":"fileKw","34":"forkKw","35":"visibilityKw","36":"repoKw","37":"langKw","38":"symKw","39":"reposetKw","40":"\"(\"","41":"\")\"","42":"or"} }) diff --git a/packages/queryLanguage/src/query.grammar b/packages/queryLanguage/src/query.grammar index 72b98b87..76ab2e6f 100644 --- a/packages/queryLanguage/src/query.grammar +++ b/packages/queryLanguage/src/query.grammar @@ -32,21 +32,31 @@ NegateExpr { !negate negate (PrefixExpr | ParenExpr) } ParenExpr { "(" query ")" } PrefixExpr { - ArchivedExpr { archivedKw value } | - BranchExpr { branchKw value } | - ContentExpr { contentKw value } | - CaseExpr { caseKw value } | - FileExpr { fileKw value } | - ForkExpr { forkKw value } | - PublicExpr { publicKw value } | - RepoExpr { repoKw value } | - RegexExpr { regexKw value } | - LangExpr { langKw value } | - SymExpr { symKw value } | - TypeExpr { typeKw value } | - RepoSetExpr { reposetKw value } + ArchivedExpr | + RevisionExpr | + ContentExpr | + FileExpr | + ForkExpr | + VisibilityExpr | + RepoExpr | + LangExpr | + SymExpr | + RepoSetExpr } +RevisionExpr { revisionKw value } +ContentExpr { contentKw value } +FileExpr { fileKw value } +RepoExpr { repoKw value } +LangExpr { langKw value } +SymExpr { symKw value } +RepoSetExpr { reposetKw value } + +// Modifiers +ArchivedExpr { archivedKw value } +ForkExpr { forkKw value } +VisibilityExpr { visibilityKw value } + Term { quotedString | word } @@ -56,17 +66,14 @@ value { quotedString | word } @tokens { archivedKw { "archived:" } - branchKw { "branch:" | "b:" } + revisionKw { "rev:" } contentKw { "content:" | "c:" } - caseKw { "case:" } fileKw { "file:" | "f:" } forkKw { "fork:" } - publicKw { "public:" } + visibilityKw { "visibility:" } repoKw { "repo:" | "r:" } - regexKw { "regex:" } langKw { "lang:" } symKw { "sym:" } - typeKw { "type:" | "t:" } reposetKw { "reposet:" } or { "or" ![a-zA-Z0-9_] } @@ -81,9 +88,9 @@ value { quotedString | word } @precedence { quotedString, - archivedKw, branchKw, contentKw, caseKw, fileKw, - forkKw, publicKw, repoKw, regexKw, langKw, - symKw, typeKw, reposetKw, or, + archivedKw, revisionKw, contentKw, fileKw, + forkKw, visibilityKw, repoKw, langKw, + symKw, reposetKw, or, word } } \ No newline at end of file diff --git a/packages/queryLanguage/src/tokens.ts b/packages/queryLanguage/src/tokens.ts index ae7de456..15a02525 100644 --- a/packages/queryLanguage/src/tokens.ts +++ b/packages/queryLanguage/src/tokens.ts @@ -3,7 +3,7 @@ import { negate } from "./parser.terms"; // External tokenizer for negation // Only tokenizes `-` as negate when followed by a prefix keyword or `(` -export const negateToken = new ExternalTokenizer((input, stack) => { +export const negateToken = new ExternalTokenizer((input) => { if (input.next !== 45 /* '-' */) return; // Not a dash const startPos = input.pos; @@ -25,24 +25,22 @@ export const negateToken = new ExternalTokenizer((input, stack) => { } // Check if followed by a prefix keyword (by checking for keyword followed by colon) - // We need to look ahead to find the colon + // Look ahead until we hit a delimiter or colon const checkPos = input.pos; let foundColon = false; - let charCount = 0; - // Look ahead up to 10 characters to find a colon - while (charCount < 10 && ch >= 0) { + // Look ahead until we hit a delimiter or colon + while (ch >= 0) { if (ch === 58 /* ':' */) { foundColon = true; break; } + // Hit a delimiter (whitespace, paren, or quote) - not a prefix keyword if (ch === 32 || ch === 9 || ch === 10 || ch === 40 || ch === 41 || ch === 34) { - // Hit whitespace, paren, or quote - not a prefix break; } input.advance(); ch = input.next; - charCount++; } // Reset position diff --git a/packages/queryLanguage/test/negation.txt b/packages/queryLanguage/test/negation.txt index bb61e7db..98a8f0c6 100644 --- a/packages/queryLanguage/test/negation.txt +++ b/packages/queryLanguage/test/negation.txt @@ -62,21 +62,13 @@ Program(NegateExpr(PrefixExpr(LangExpr))) Program(NegateExpr(PrefixExpr(ContentExpr))) -# Negate branch prefix +# Negate revision prefix --branch:develop +-rev:develop ==> -Program(NegateExpr(PrefixExpr(BranchExpr))) - -# Negate case prefix - --case:yes - -==> - -Program(NegateExpr(PrefixExpr(CaseExpr))) +Program(NegateExpr(PrefixExpr(RevisionExpr))) # Negate archived prefix @@ -94,13 +86,13 @@ Program(NegateExpr(PrefixExpr(ArchivedExpr))) Program(NegateExpr(PrefixExpr(ForkExpr))) -# Negate public prefix +# Negate visibility prefix --public:no +-visibility:any ==> -Program(NegateExpr(PrefixExpr(PublicExpr))) +Program(NegateExpr(PrefixExpr(VisibilityExpr))) # Negate symbol prefix @@ -110,22 +102,6 @@ Program(NegateExpr(PrefixExpr(PublicExpr))) Program(NegateExpr(PrefixExpr(SymExpr))) -# Negate type prefix - --type:repo - -==> - -Program(NegateExpr(PrefixExpr(TypeExpr))) - -# Negate regex prefix - --regex:test.* - -==> - -Program(NegateExpr(PrefixExpr(RegexExpr))) - # Negate parentheses -(test) @@ -222,14 +198,6 @@ Program(NegateExpr(PrefixExpr(FileExpr))) Program(NegateExpr(PrefixExpr(RepoExpr))) -# Negate short form branch - --b:main - -==> - -Program(NegateExpr(PrefixExpr(BranchExpr))) - # Negate short form content -c:console @@ -238,14 +206,6 @@ Program(NegateExpr(PrefixExpr(BranchExpr))) Program(NegateExpr(PrefixExpr(ContentExpr))) -# Negate short form type - --t:file - -==> - -Program(NegateExpr(PrefixExpr(TypeExpr))) - # Negate with prefix in quotes -file:"test file.js" diff --git a/packages/queryLanguage/test/operators.txt b/packages/queryLanguage/test/operators.txt index a1aa9a44..0ff1f6d8 100644 --- a/packages/queryLanguage/test/operators.txt +++ b/packages/queryLanguage/test/operators.txt @@ -110,13 +110,13 @@ repo:project1 or repo:project2 Program(OrExpr(PrefixExpr(RepoExpr),PrefixExpr(RepoExpr))) -# OR with branch prefixes +# OR with revision prefixes -branch:main or branch:develop +rev:main or rev:develop ==> -Program(OrExpr(PrefixExpr(BranchExpr),PrefixExpr(BranchExpr))) +Program(OrExpr(PrefixExpr(RevisionExpr),PrefixExpr(RevisionExpr))) # OR with lang prefixes diff --git a/packages/queryLanguage/test/prefixes.txt b/packages/queryLanguage/test/prefixes.txt index 1213526d..5c350053 100644 --- a/packages/queryLanguage/test/prefixes.txt +++ b/packages/queryLanguage/test/prefixes.txt @@ -46,21 +46,13 @@ c:console.log Program(PrefixExpr(ContentExpr)) -# Branch prefix +# Revision prefix -branch:main +rev:main ==> -Program(PrefixExpr(BranchExpr)) - -# Branch prefix short form - -b:develop - -==> - -Program(PrefixExpr(BranchExpr)) +Program(PrefixExpr(RevisionExpr)) # Lang prefix @@ -70,14 +62,6 @@ lang:typescript Program(PrefixExpr(LangExpr)) -# Case prefix - -case:yes - -==> - -Program(PrefixExpr(CaseExpr)) - # Archived prefix archived:no @@ -94,13 +78,13 @@ fork:yes Program(PrefixExpr(ForkExpr)) -# Public prefix +# Visibility prefix - public -public:yes +visibility:public ==> -Program(PrefixExpr(PublicExpr)) +Program(PrefixExpr(VisibilityExpr)) # Symbol prefix @@ -110,30 +94,6 @@ sym:MyClass Program(PrefixExpr(SymExpr)) -# Type prefix - -type:file - -==> - -Program(PrefixExpr(TypeExpr)) - -# Type prefix short form - -t:repo - -==> - -Program(PrefixExpr(TypeExpr)) - -# Regex prefix - -regex:test.* - -==> - -Program(PrefixExpr(RegexExpr)) - # RepoSet prefix reposet:repo1,repo2 @@ -214,21 +174,13 @@ content:hello Program(PrefixExpr(ContentExpr)) -# Branch with slashes +# Revision with slashes -branch:feature/new-feature +rev:feature/new-feature ==> -Program(PrefixExpr(BranchExpr)) - -# Case values - -case:auto - -==> - -Program(PrefixExpr(CaseExpr)) +Program(PrefixExpr(RevisionExpr)) # RepoSet with multiple repos @@ -246,14 +198,6 @@ sym:package.Class.method Program(PrefixExpr(SymExpr)) -# Type variations - -type:filename - -==> - -Program(PrefixExpr(TypeExpr)) - # Lang with various languages lang:python @@ -278,21 +222,13 @@ fork:no Program(PrefixExpr(ForkExpr)) -# Public values +# Visibility prefix - private -public:no +visibility:private ==> -Program(PrefixExpr(PublicExpr)) - -# Regex with complex pattern - -regex:\w+\s*=\s*\d+ - -==> - -Program(PrefixExpr(RegexExpr)) +Program(PrefixExpr(VisibilityExpr)) # File with dashes diff --git a/packages/queryLanguage/test/quoted.txt b/packages/queryLanguage/test/quoted.txt index d55089d2..088ed0d3 100644 --- a/packages/queryLanguage/test/quoted.txt +++ b/packages/queryLanguage/test/quoted.txt @@ -102,13 +102,13 @@ content:"console.log" Program(PrefixExpr(ContentExpr)) -# Quoted string in branch prefix +# Quoted string in revision prefix -branch:"feature/my feature" +rev:"feature/my feature" ==> -Program(PrefixExpr(BranchExpr)) +Program(PrefixExpr(RevisionExpr)) # Multiple quoted strings @@ -286,22 +286,6 @@ content:"TODO: fix this" Program(PrefixExpr(ContentExpr)) -# Regex prefix with quoted pattern - -regex:"func\\s+\\w+" - -==> - -Program(PrefixExpr(RegexExpr)) - -# Case prefix with quoted value - -case:"yes" - -==> - -Program(PrefixExpr(CaseExpr)) - # Quoted string with at symbol "@decorator" @@ -486,9 +470,9 @@ Program(AndExpr(Term,PrefixExpr(FileExpr))) Program(Term) -# Quoted branch prefix +# Quoted revision prefix -"branch:main" +"rev:main" ==> diff --git a/packages/web/src/app/api/(server)/stream_search/route.ts b/packages/web/src/app/api/(server)/stream_search/route.ts index 118d32c6..06f6b812 100644 --- a/packages/web/src/app/api/(server)/stream_search/route.ts +++ b/packages/web/src/app/api/(server)/stream_search/route.ts @@ -16,7 +16,7 @@ import { PrismaClient, Repo } from '@sourcebot/db'; import { createLogger, env } from '@sourcebot/shared'; import { NextRequest } from 'next/server'; import * as path from 'path'; -import { parser } from '@sourcebot/query-language'; +import { parser as _parser } from '@sourcebot/query-language'; import { transformToZoektQuery } from './transformer'; const logger = createLogger('streamSearchApi'); @@ -69,13 +69,38 @@ export const POST = async (request: NextRequest) => { const { query, matches, contextLines, whole } = parsed.data; + const isCaseSensitivityEnabled = false; + const isRegexEnabled = false; + + const parser = _parser.configure({ + strict: true, + }) + const tree = parser.parse(query); - const zoektQuery = transformToZoektQuery(tree, query); + const zoektQuery = transformToZoektQuery({ + tree, + input: query, + isCaseSensitivityEnabled, + isRegexEnabled, + }); console.log(JSON.stringify(zoektQuery, null, 2)); const searchRequest: SearchRequest = { - query: zoektQuery, + query: { + and: { + children: [ + zoektQuery, + // { + // raw_config: { + // flags: [ + // 'FLAG_NO_FORKS', + // ] + // } + // } + ] + } + }, // query: { // and: { // // @todo: we should use repo_ids to filter out repositories that the user diff --git a/packages/web/src/app/api/(server)/stream_search/transformer.ts b/packages/web/src/app/api/(server)/stream_search/transformer.ts index 61dfb0b7..7744c5e7 100644 --- a/packages/web/src/app/api/(server)/stream_search/transformer.ts +++ b/packages/web/src/app/api/(server)/stream_search/transformer.ts @@ -1,221 +1,269 @@ import { Tree, SyntaxNode } from "@sourcebot/query-language"; -import { Q } from '@/proto/zoekt/webserver/v1/Q'; +import { Q } from '@/proto/zoekt/webserver/v1/Q'; +import { + Program, + AndExpr, + OrExpr, + NegateExpr, + ParenExpr, + PrefixExpr, + Term, + FileExpr, + RepoExpr, + RevisionExpr, + ContentExpr, + LangExpr, + SymExpr, + ArchivedExpr, + ForkExpr, + VisibilityExpr, + RepoSetExpr +} from '@sourcebot/query-language'; /** * Transform a Lezer parse tree into a Zoekt gRPC query */ -export function transformToZoektQuery(tree: Tree, input: string): Q { - return transformNode(tree.topNode, input); +export const transformToZoektQuery = ({ + tree, + input, + isCaseSensitivityEnabled, + isRegexEnabled, +}: { + tree: Tree; + input: string; + isCaseSensitivityEnabled: boolean; + isRegexEnabled: boolean; +}): Q => { + + const transformNode = (node: SyntaxNode): Q => { + switch (node.type.id) { + case Program: { + // Program wraps the actual query - transform its child + const child = node.firstChild; + if (!child) { + // Empty query - match nothing + return { const: false, query: "const" }; + } + return transformNode(child); + } + case AndExpr: + return { + and: { + children: getChildren(node).map(c => transformNode(c)) + }, + query: "and" + } + + case OrExpr: + return { + or: { + children: getChildren(node).map(c => transformNode(c)) + }, + query: "or" + }; + + case NegateExpr: { + // Find the child after the negate token + const negateChild = node.getChild("PrefixExpr") || node.getChild("ParenExpr"); + if (!negateChild) { + throw new Error("NegateExpr missing child"); + } + return { + not: { + child: transformNode(negateChild) + }, + query: "not" + }; + } + case ParenExpr: { + // Parentheses just group - transform the inner query + const innerQuery = node.getChild("query") || node.firstChild; + if (!innerQuery) { + return { const: false, query: "const" }; + } + return transformNode(innerQuery); + } + case PrefixExpr: + // PrefixExpr contains specific prefix types + return transformPrefixExpr(node); + + case Term: { + const termText = input.substring(node.from, node.to).replace(/^"|"$/g, ''); + + return isRegexEnabled ? { + regexp: { + regexp: termText, + case_sensitive: isCaseSensitivityEnabled, + file_name: false, + content: true + }, + query: "regexp" + } : { + substring: { + pattern: termText, + case_sensitive: isCaseSensitivityEnabled, + file_name: false, + content: true + }, + query: "substring" + }; + } + default: + console.warn(`Unhandled node type: ${node.type.name} (id: ${node.type.id})`); + return { const: true, query: "const" }; + } + } + + const transformPrefixExpr = (node: SyntaxNode): Q => { + // Find which specific prefix type this is + const prefixNode = node.firstChild; + if (!prefixNode) { + throw new Error("PrefixExpr has no child"); + } + + const prefixTypeId = prefixNode.type.id; + + // Extract the full text (e.g., "file:test.js") and split on the colon + const fullText = input.substring(prefixNode.from, prefixNode.to); + const colonIndex = fullText.indexOf(':'); + if (colonIndex === -1) { + throw new Error(`${prefixNode.type.name} missing colon`); + } + + // Get the value part after the colon and remove quotes if present + const value = fullText.substring(colonIndex + 1).replace(/^"|"$/g, ''); + + switch (prefixTypeId) { + case FileExpr: + return { + substring: { + pattern: value, + case_sensitive: isCaseSensitivityEnabled, + file_name: true, + content: false + }, + query: "substring" + }; + + case RepoExpr: + return { + repo: { + regexp: value + }, + query: "repo" + }; + + case RevisionExpr: + return { + branch: { + pattern: value, + exact: false + }, + query: "branch" + }; + + case ContentExpr: + return { + substring: { + pattern: value, + case_sensitive: isCaseSensitivityEnabled, + file_name: false, + content: true + }, + query: "substring" + }; + + case LangExpr: + return { + language: { + language: value + }, + query: "language" + }; + + case SymExpr: + // Symbol search wraps a pattern + return { + symbol: { + expr: { + substring: { + pattern: value, + case_sensitive: isCaseSensitivityEnabled, + file_name: false, + content: true + }, + query: "substring" + } + }, + query: "symbol" + }; + + case VisibilityExpr: { + const visibilityValue = value.toLowerCase(); + const flags: ('FLAG_ONLY_PUBLIC' | 'FLAG_ONLY_PRIVATE')[] = []; + + if (visibilityValue === 'public') { + flags.push('FLAG_ONLY_PUBLIC'); + } else if (visibilityValue === 'private') { + flags.push('FLAG_ONLY_PRIVATE'); + } + // 'any' means no filter + + return { + raw_config: { + flags + }, + query: "raw_config" + }; + } + + // @todo: handle this + case ArchivedExpr: { + const archivedValue = value.toLowerCase(); + const flags: ('FLAG_ONLY_ARCHIVED' | 'FLAG_NO_ARCHIVED')[] = []; + + if (archivedValue === 'yes') { + // 'yes' means include archived repositories (default) + } else if (archivedValue === 'no') { + flags.push('FLAG_NO_ARCHIVED'); + } else if (archivedValue === 'only') { + flags.push('FLAG_ONLY_ARCHIVED'); + } + + return { + raw_config: { + flags + }, + query: "raw_config" + }; + } + case ForkExpr: + // These are repo metadata filters + // They need to be handled via repo filters in Zoekt + // For now, return a const query (you might need custom handling) + console.warn(`${prefixNode.type.name} not yet implemented`); + return { const: true, query: "const" }; + + case RepoSetExpr: { + return { + repo_set: { + set: value.split(',').reduce((acc, s) => { + acc[s.trim()] = true; + return acc; + }, {} as Record) + }, + query: "repo_set" + }; + } + default: + throw new Error(`Unknown prefix type: ${prefixNode.type.name} (id: ${prefixTypeId})`); + } + } + + return transformNode(tree.topNode); } -function transformNode(node: SyntaxNode, input: string): Q { - const nodeName = node.type.name; - - switch (nodeName) { - case "Program": { - // Program wraps the actual query - transform its child - const child = node.firstChild; - if (!child) { - // Empty query - match nothing - return { const: false, query: "const" }; - } - return transformNode(child, input); - } - case "AndExpr": - return { - and: { - children: getChildren(node).map(c => transformNode(c, input)) - }, - query: "and" - } - - case "OrExpr": - return { - or: { - children: getChildren(node).map(c => transformNode(c, input)) - }, - query: "or" - }; - - case "NegateExpr": { - // Find the child after the negate token - const negateChild = node.getChild("PrefixExpr") || node.getChild("ParenExpr"); - if (!negateChild) { - throw new Error("NegateExpr missing child"); - } - return { - not: { - child: transformNode(negateChild, input) - }, - query: "not" - }; - } - case "ParenExpr": { - // Parentheses just group - transform the inner query - const innerQuery = node.getChild("query") || node.firstChild; - if (!innerQuery) { - return { const: false, query: "const" }; - } - return transformNode(innerQuery, input); - } - case "PrefixExpr": - // PrefixExpr contains specific prefix types - return transformPrefixExpr(node, input); - - case "Term": { - // Plain search term - becomes substring search in content - const termText = input.substring(node.from, node.to); - return { - substring: { - pattern: termText.replace(/^"|"$/g, ''), // Remove quotes if present - case_sensitive: false, - file_name: false, - content: true - }, - query: "substring" - }; - } - default: - console.warn(`Unhandled node type: ${nodeName}`); - return { const: true, query: "const" }; - } -} - -function transformPrefixExpr(node: SyntaxNode, input: string): Q { - // Find which specific prefix type this is - const prefixNode = node.firstChild; - if (!prefixNode) { - throw new Error("PrefixExpr has no child"); - } - - const prefixType = prefixNode.type.name; - - // Extract the full text (e.g., "file:test.js") and split on the colon - const fullText = input.substring(prefixNode.from, prefixNode.to); - const colonIndex = fullText.indexOf(':'); - if (colonIndex === -1) { - throw new Error(`${prefixType} missing colon`); - } - - // Get the value part after the colon and remove quotes if present - const value = fullText.substring(colonIndex + 1).replace(/^"|"$/g, ''); - - switch (prefixType) { - case "FileExpr": - return { - substring: { - pattern: value, - case_sensitive: false, - file_name: true, - content: false - }, - query: "substring" - }; - - case "RepoExpr": - return { - repo: { - regexp: value - }, - query: "repo" - }; - - case "BranchExpr": - return { - branch: { - pattern: value, - exact: false - }, - query: "branch" - }; - - case "ContentExpr": - return { - substring: { - pattern: value, - case_sensitive: false, - file_name: false, - content: true - }, - query: "substring" - }; - - case "CaseExpr": { - // case:yes/no wraps the next term with case sensitivity - const caseValue = value.toLowerCase(); - const isCaseSensitive = caseValue === "yes" || caseValue === "true"; - return { - substring: { - pattern: value, - case_sensitive: isCaseSensitive, - file_name: false, - content: true - }, - query: "substring" - }; - } - case "LangExpr": - return { - language: { - language: value - }, - query: "language" - }; - - case "SymExpr": - // Symbol search wraps a pattern - return { - symbol: { - expr: { - substring: { - pattern: value, - case_sensitive: false, - file_name: false, - content: true - }, - query: "substring" - } - }, - query: "symbol" - }; - case "RegexExpr": - return { - regexp: { - regexp: value, - case_sensitive: false, - file_name: false, - content: true - }, - query: "regexp" - }; - - // @todo: handle this - case "ArchivedExpr": - case "ForkExpr": - case "PublicExpr": - // These are repo metadata filters - // They need to be handled via repo filters in Zoekt - // For now, return a const query (you might need custom handling) - console.warn(`${prefixType} not yet implemented`); - return { const: true, query: "const" }; - - case "RepoSetExpr": { - return { - repo_set: { - set: value.split(',').reduce((acc, s) => { - acc[s.trim()] = true; - return acc; - }, {} as Record) - }, - query: "repo_set" - }; - } - default: - throw new Error(`Unknown prefix type: ${prefixType}`); - } -} - -function getChildren(node: SyntaxNode): SyntaxNode[] { +const getChildren = (node: SyntaxNode): SyntaxNode[] => { const children: SyntaxNode[] = []; let child = node.firstChild; while (child) {