@@ -119,6 +119,16 @@ func (g *Generator) generateComponent(comp *guixast.Component) []ast.Decl {
119119 // Generate constructor
120120 decls = append (decls , g .generateConstructor (comp ))
121121
122+ // Check if component has channel parameters
123+ hasChannels := g .hasChannelParams (comp )
124+
125+ // Generate BindApp method if there are channels
126+ if hasChannels {
127+ decls = append (decls , g .generateBindAppMethod (comp ))
128+ // Generate listener methods for each channel
129+ decls = append (decls , g .generateChannelListenerMethods (comp )... )
130+ }
131+
122132 // Generate Render method
123133 decls = append (decls , g .generateRenderMethod (comp ))
124134
@@ -130,6 +140,16 @@ func (g *Generator) generateComponent(comp *guixast.Component) []ast.Decl {
130140 return decls
131141}
132142
143+ // hasChannelParams checks if a component has any channel parameters
144+ func (g * Generator ) hasChannelParams (comp * guixast.Component ) bool {
145+ for _ , param := range comp .Params {
146+ if param .Type != nil && (param .Type .IsChannel || param .Type .IsChan ) {
147+ return true
148+ }
149+ }
150+ return false
151+ }
152+
133153// generatePropsStruct generates a Props struct for component parameters
134154func (g * Generator ) generatePropsStruct (comp * guixast.Component ) * ast.GenDecl {
135155 fields := make ([]* ast.Field , len (comp .Params ))
@@ -267,6 +287,22 @@ func (g *Generator) generateComponentStruct(comp *guixast.Component) *ast.GenDec
267287 Names : []* ast.Ident {ast .NewIdent (capitalize (param .Name ))},
268288 Type : g .typeToAST (param .Type ),
269289 })
290+
291+ // For channel parameters, add a current value field
292+ if param .Type != nil && (param .Type .IsChannel || param .Type .IsChan ) {
293+ // Extract the element type from the channel
294+ var elemType ast.Expr
295+ if param .Type .Generic != nil {
296+ elemType = g .typeToAST (param .Type .Generic )
297+ } else {
298+ elemType = g .typeToAST (& guixast.Type {Name : param .Type .Name })
299+ }
300+
301+ fields = append (fields , & ast.Field {
302+ Names : []* ast.Ident {ast .NewIdent ("current" + capitalize (param .Name ))},
303+ Type : elemType ,
304+ })
305+ }
270306 }
271307
272308 return & ast.GenDecl {
@@ -683,12 +719,11 @@ func (g *Generator) generateExpr(expr *guixast.Expr) ast.Expr {
683719 }
684720
685721 if expr .ChannelOp != nil {
686- return & ast.UnaryExpr {
687- Op : token .ARROW ,
688- X : & ast.SelectorExpr {
689- X : ast .NewIdent ("c" ),
690- Sel : ast .NewIdent (capitalize (expr .ChannelOp .Channel )),
691- },
722+ // Instead of reading from the channel (which would block),
723+ // use the current value field that's updated by the channel listener
724+ return & ast.SelectorExpr {
725+ X : ast .NewIdent ("c" ),
726+ Sel : ast .NewIdent ("current" + capitalize (expr .ChannelOp .Channel )),
692727 }
693728 }
694729
@@ -822,14 +857,15 @@ func (g *Generator) typeToAST(t *guixast.Type) ast.Expr {
822857
823858 var base ast.Expr = ast .NewIdent (t .Name )
824859
825- if t .IsChannel {
860+ // Handle channel types
861+ // IsChannel && IsChan means "<-chan T" (receive-only)
862+ // IsChan only means "chan T" (bidirectional)
863+ if t .IsChannel && t .IsChan {
826864 base = & ast.ChanType {
827865 Dir : ast .RECV ,
828866 Value : base ,
829867 }
830- }
831-
832- if t .IsChan {
868+ } else if t .IsChan {
833869 base = & ast.ChanType {
834870 Dir : ast .SEND | ast .RECV ,
835871 Value : base ,
@@ -982,6 +1018,178 @@ func (g *Generator) generateUpdateMethod(comp *guixast.Component) *ast.FuncDecl
9821018 }
9831019}
9841020
1021+ // generateBindAppMethod generates the BindApp method for components with channels
1022+ func (g * Generator ) generateBindAppMethod (comp * guixast.Component ) * ast.FuncDecl {
1023+ stmts := []ast.Stmt {
1024+ // c.app = app
1025+ & ast.AssignStmt {
1026+ Lhs : []ast.Expr {
1027+ & ast.SelectorExpr {
1028+ X : ast .NewIdent ("c" ),
1029+ Sel : ast .NewIdent ("app" ),
1030+ },
1031+ },
1032+ Tok : token .ASSIGN ,
1033+ Rhs : []ast.Expr {ast .NewIdent ("app" )},
1034+ },
1035+ }
1036+
1037+ // For each channel parameter, start a listener
1038+ for _ , param := range comp .Params {
1039+ if param .Type != nil && (param .Type .IsChannel || param .Type .IsChan ) {
1040+ // if c.ChannelName != nil { c.startChannelNameListener() }
1041+ stmts = append (stmts , & ast.IfStmt {
1042+ Cond : & ast.BinaryExpr {
1043+ X : & ast.SelectorExpr {
1044+ X : ast .NewIdent ("c" ),
1045+ Sel : ast .NewIdent (capitalize (param .Name )),
1046+ },
1047+ Op : token .NEQ ,
1048+ Y : ast .NewIdent ("nil" ),
1049+ },
1050+ Body : & ast.BlockStmt {
1051+ List : []ast.Stmt {
1052+ & ast.ExprStmt {
1053+ X : & ast.CallExpr {
1054+ Fun : & ast.SelectorExpr {
1055+ X : ast .NewIdent ("c" ),
1056+ Sel : ast .NewIdent ("start" + capitalize (param .Name ) + "Listener" ),
1057+ },
1058+ },
1059+ },
1060+ },
1061+ },
1062+ })
1063+ }
1064+ }
1065+
1066+ return & ast.FuncDecl {
1067+ Recv : & ast.FieldList {
1068+ List : []* ast.Field {
1069+ {
1070+ Names : []* ast.Ident {ast .NewIdent ("c" )},
1071+ Type : & ast.StarExpr {
1072+ X : ast .NewIdent (comp .Name ),
1073+ },
1074+ },
1075+ },
1076+ },
1077+ Name : ast .NewIdent ("BindApp" ),
1078+ Type : & ast.FuncType {
1079+ Params : & ast.FieldList {
1080+ List : []* ast.Field {
1081+ {
1082+ Names : []* ast.Ident {ast .NewIdent ("app" )},
1083+ Type : & ast.StarExpr {
1084+ X : & ast.SelectorExpr {
1085+ X : ast .NewIdent ("runtime" ),
1086+ Sel : ast .NewIdent ("App" ),
1087+ },
1088+ },
1089+ },
1090+ },
1091+ },
1092+ },
1093+ Body : & ast.BlockStmt {
1094+ List : stmts ,
1095+ },
1096+ }
1097+ }
1098+
1099+ // generateChannelListenerMethods generates listener methods for each channel parameter
1100+ func (g * Generator ) generateChannelListenerMethods (comp * guixast.Component ) []ast.Decl {
1101+ var decls []ast.Decl
1102+
1103+ for _ , param := range comp .Params {
1104+ if param .Type != nil && (param .Type .IsChannel || param .Type .IsChan ) {
1105+ // Generate: func (c *Component) startChannelNameListener() { go func() { for val := range c.ChannelName { c.currentChannelName = val; c.app.Update() } }() }
1106+ decls = append (decls , & ast.FuncDecl {
1107+ Recv : & ast.FieldList {
1108+ List : []* ast.Field {
1109+ {
1110+ Names : []* ast.Ident {ast .NewIdent ("c" )},
1111+ Type : & ast.StarExpr {
1112+ X : ast .NewIdent (comp .Name ),
1113+ },
1114+ },
1115+ },
1116+ },
1117+ Name : ast .NewIdent ("start" + capitalize (param .Name ) + "Listener" ),
1118+ Type : & ast.FuncType {},
1119+ Body : & ast.BlockStmt {
1120+ List : []ast.Stmt {
1121+ // go func() { ... }()
1122+ & ast.GoStmt {
1123+ Call : & ast.CallExpr {
1124+ Fun : & ast.FuncLit {
1125+ Type : & ast.FuncType {},
1126+ Body : & ast.BlockStmt {
1127+ List : []ast.Stmt {
1128+ // for val := range c.ChannelName { ... }
1129+ & ast.RangeStmt {
1130+ Key : nil ,
1131+ Value : ast .NewIdent ("val" ),
1132+ Tok : token .DEFINE ,
1133+ X : & ast.SelectorExpr {
1134+ X : ast .NewIdent ("c" ),
1135+ Sel : ast .NewIdent (capitalize (param .Name )),
1136+ },
1137+ Body : & ast.BlockStmt {
1138+ List : []ast.Stmt {
1139+ // c.currentChannelName = val
1140+ & ast.AssignStmt {
1141+ Lhs : []ast.Expr {
1142+ & ast.SelectorExpr {
1143+ X : ast .NewIdent ("c" ),
1144+ Sel : ast .NewIdent ("current" + capitalize (param .Name )),
1145+ },
1146+ },
1147+ Tok : token .ASSIGN ,
1148+ Rhs : []ast.Expr {ast .NewIdent ("val" )},
1149+ },
1150+ // if c.app != nil { c.app.Update() }
1151+ & ast.IfStmt {
1152+ Cond : & ast.BinaryExpr {
1153+ X : & ast.SelectorExpr {
1154+ X : ast .NewIdent ("c" ),
1155+ Sel : ast .NewIdent ("app" ),
1156+ },
1157+ Op : token .NEQ ,
1158+ Y : ast .NewIdent ("nil" ),
1159+ },
1160+ Body : & ast.BlockStmt {
1161+ List : []ast.Stmt {
1162+ & ast.ExprStmt {
1163+ X : & ast.CallExpr {
1164+ Fun : & ast.SelectorExpr {
1165+ X : & ast.SelectorExpr {
1166+ X : ast .NewIdent ("c" ),
1167+ Sel : ast .NewIdent ("app" ),
1168+ },
1169+ Sel : ast .NewIdent ("Update" ),
1170+ },
1171+ },
1172+ },
1173+ },
1174+ },
1175+ },
1176+ },
1177+ },
1178+ },
1179+ },
1180+ },
1181+ },
1182+ },
1183+ },
1184+ },
1185+ },
1186+ })
1187+ }
1188+ }
1189+
1190+ return decls
1191+ }
1192+
9851193// Helper functions
9861194
9871195func capitalize (s string ) string {
0 commit comments