detached 4bdfabf

GIR: Add property getters and setters generation support for primitive types

Signed-off-by: Felicitas Pojtinger <felicitas@pojtinger.com>
commit
4bdfabf961f1fd0946978d0a4ee993169ead4c5f
author
Felicitas Pojtinger · · 2025-10-30 06:42 UTC
committer
Jeroen Wijenbergh · · 2025-12-01 16:10 UTC
parent
661c030
signature
unsigned
5 files · +169 -1
M internal/gir/pass/pass.go +10 -0
@@ -365,6 +365,15 @@ func (p *Pass) writeGo(r types.Repository, gotemp *template.Template, dir string
365365 for _, impl := range cls.Implements {
366366 interfaces = append(interfaces, types.GetInterfaceFuncs(ns.Name, impl.Name, implemented, p.Types))
367367 }
368+ properties := make([]types.PropertyTemplate, 0, len(cls.Properties))
369+ for _, prop := range cls.Properties {
370+ propTemp := prop.Template(ns.Name, p.Types)
371+
372+ // TODO: Implement non-primitive types, then remove this
373+ if propTemp.GValueType != "" {
374+ properties = append(properties, propTemp)
375+ }
376+ }
368377 classes[fn] = append(classes[fn], types.ClassTemplate{
369378 Doc: cls.Doc.StringSafe(),
370379 Name: cls.Name,
@@ -373,6 +382,7 @@ func (p *Pass) writeGo(r types.Repository, gotemp *template.Template, dir string
373382 Receivers: receivers,
374383 Interfaces: interfaces,
375384 Functions: functions,
385+ Properties: properties,
376386 Signals: signals,
377387 TypeGetter: cls.GLibGetType,
378388 })
@@ -365,6 +365,15 @@ func (p *Pass) writeGo(r types.Repository, gotemp *template.Template, dir string
365 for _, impl := range cls.Implements {365 for _, impl := range cls.Implements {
366 interfaces = append(interfaces, types.GetInterfaceFuncs(ns.Name, impl.Name, implemented, p.Types))366 interfaces = append(interfaces, types.GetInterfaceFuncs(ns.Name, impl.Name, implemented, p.Types))
367 }367 }
368+ properties := make([]types.PropertyTemplate, 0, len(cls.Properties))
369+ for _, prop := range cls.Properties {
370+ propTemp := prop.Template(ns.Name, p.Types)
371+
372+ // TODO: Implement non-primitive types, then remove this
373+ if propTemp.GValueType != "" {
374+ properties = append(properties, propTemp)
375+ }
376+ }
368 classes[fn] = append(classes[fn], types.ClassTemplate{377 classes[fn] = append(classes[fn], types.ClassTemplate{
369 Doc: cls.Doc.StringSafe(),378 Doc: cls.Doc.StringSafe(),
370 Name: cls.Name,379 Name: cls.Name,
@@ -373,6 +382,7 @@ func (p *Pass) writeGo(r types.Repository, gotemp *template.Template, dir string
373 Receivers: receivers,382 Receivers: receivers,
374 Interfaces: interfaces,383 Interfaces: interfaces,
375 Functions: functions,384 Functions: functions,
385+ Properties: properties,
376 Signals: signals,386 Signals: signals,
377 TypeGetter: cls.GLibGetType,387 TypeGetter: cls.GLibGetType,
378 })388 })
M internal/gir/types/interface.go +12 -0
@@ -33,11 +33,23 @@ func ConvertInterface(currns string, ins string, inter Interface, implemented ma
3333 },
3434 })
3535 }
36+
37+ properties := make([]PropertyTemplate, 0, len(inter.Properties))
38+ for _, prop := range inter.Properties {
39+ propTemp := prop.Template(currns, kinds)
40+
41+ // TODO: Implement non-primitive types, then remove this
42+ if propTemp.GValueType != "" {
43+ properties = append(properties, propTemp)
44+ }
45+ }
46+
3647 name := util.SnakeToCamel(inter.Name)
3748 return InterfaceTemplate{
3849 Name: name,
3950 Doc: inter.Doc.StringSafe(),
4051 Methods: methods,
52+ Properties: properties,
4153 TypeGetter: inter.GLibGetType,
4254 }
4355 }
@@ -33,11 +33,23 @@ func ConvertInterface(currns string, ins string, inter Interface, implemented ma
33 },33 },
34 })34 })
35 }35 }
36+
37+ properties := make([]PropertyTemplate, 0, len(inter.Properties))
38+ for _, prop := range inter.Properties {
39+ propTemp := prop.Template(currns, kinds)
40+
41+ // TODO: Implement non-primitive types, then remove this
42+ if propTemp.GValueType != "" {
43+ properties = append(properties, propTemp)
44+ }
45+ }
46+
36 name := util.SnakeToCamel(inter.Name)47 name := util.SnakeToCamel(inter.Name)
37 return InterfaceTemplate{48 return InterfaceTemplate{
38 Name: name,49 Name: name,
39 Doc: inter.Doc.StringSafe(),50 Doc: inter.Doc.StringSafe(),
40 Methods: methods,51 Methods: methods,
52+ Properties: properties,
41 TypeGetter: inter.GLibGetType,53 TypeGetter: inter.GLibGetType,
42 }54 }
43 }55 }
M internal/gir/types/template.go +25 -0
@@ -391,6 +391,27 @@ type SignalsTemplate struct {
391391 Ret funcRetTemplate
392392 }
393393
394+type PropertyTemplate struct {
395+ // Doc is the documentation for the property
396+ Doc string
397+ // Name is the Go name for the property
398+ Name string
399+ // CName is the raw c name
400+ CName string
401+ // GoType is the Go type for the property
402+ GoType string
403+ // GValueType is the GObject Type constant (e.g. "TypeBooleanVal")
404+ GValueType string
405+ // SetMethod is the Value setter method name (e.g. "SetBoolean")
406+ SetMethod string
407+ // GetMethod is the Value getter method name (e.g. "GetBoolean")
408+ GetMethod string
409+ // Readable indicates if this property can be read
410+ Readable bool
411+ // Writable indicates if this property can be written
412+ Writable bool
413+}
414+
394415 type ClassTemplate struct {
395416 // Doc is the documentation for the class
396417 Doc string
@@ -406,6 +427,8 @@ type ClassTemplate struct {
406427 Interfaces []InterfaceTemplate
407428 // Functions are the Go function declarations
408429 Functions []FuncTemplate
430+ // Properties are the property getters and setters
431+ Properties []PropertyTemplate
409432 // Signals are helpers for ConnectX receivers
410433 Signals []SignalsTemplate
411434 // TypeGetter is the function to get the GLib type
@@ -417,6 +440,8 @@ type InterfaceTemplate struct {
417440 Name string
418441 // Methods is the methods that this interface defines
419442 Methods []InterfaceFuncTemplate
443+ // Properties are the property getters and setters
444+ Properties []PropertyTemplate
420445 // TypeGetter is the function to get the GLib type
421446 TypeGetter string
422447 }
@@ -391,6 +391,27 @@ type SignalsTemplate struct {
391 Ret funcRetTemplate391 Ret funcRetTemplate
392 }392 }
393 393
394+type PropertyTemplate struct {
395+ // Doc is the documentation for the property
396+ Doc string
397+ // Name is the Go name for the property
398+ Name string
399+ // CName is the raw c name
400+ CName string
401+ // GoType is the Go type for the property
402+ GoType string
403+ // GValueType is the GObject Type constant (e.g. "TypeBooleanVal")
404+ GValueType string
405+ // SetMethod is the Value setter method name (e.g. "SetBoolean")
406+ SetMethod string
407+ // GetMethod is the Value getter method name (e.g. "GetBoolean")
408+ GetMethod string
409+ // Readable indicates if this property can be read
410+ Readable bool
411+ // Writable indicates if this property can be written
412+ Writable bool
413+}
414+
394 type ClassTemplate struct {415 type ClassTemplate struct {
395 // Doc is the documentation for the class416 // Doc is the documentation for the class
396 Doc string417 Doc string
@@ -406,6 +427,8 @@ type ClassTemplate struct {
406 Interfaces []InterfaceTemplate427 Interfaces []InterfaceTemplate
407 // Functions are the Go function declarations428 // Functions are the Go function declarations
408 Functions []FuncTemplate429 Functions []FuncTemplate
430+ // Properties are the property getters and setters
431+ Properties []PropertyTemplate
409 // Signals are helpers for ConnectX receivers432 // Signals are helpers for ConnectX receivers
410 Signals []SignalsTemplate433 Signals []SignalsTemplate
411 // TypeGetter is the function to get the GLib type434 // TypeGetter is the function to get the GLib type
@@ -417,6 +440,8 @@ type InterfaceTemplate struct {
417 Name string440 Name string
418 // Methods is the methods that this interface defines441 // Methods is the methods that this interface defines
419 Methods []InterfaceFuncTemplate442 Methods []InterfaceFuncTemplate
443+ // Properties are the property getters and setters
444+ Properties []PropertyTemplate
420 // TypeGetter is the function to get the GLib type445 // TypeGetter is the function to get the GLib type
421 TypeGetter string446 TypeGetter string
422 }447 }
M internal/gir/types/types.go +72 -1
@@ -376,6 +376,7 @@ type Class struct {
376376 Methods []Method `xml:"http://www.gtk.org/introspection/core/1.0 method"`
377377 VirtualMethods []VirtualMethod `xml:"http://www.gtk.org/introspection/core/1.0 virtual-method"`
378378 Fields []Field `xml:"http://www.gtk.org/introspection/core/1.0 field"`
379+ Properties []Property `xml:"http://www.gtk.org/introspection/core/1.0 property"`
379380 Signals []Signal `xml:"http://www.gtk.org/introspection/glib/1.0 signal"`
380381 }
381382
@@ -565,6 +566,7 @@ type Interface struct {
565566 Methods []Method `xml:"http://www.gtk.org/introspection/core/1.0 method"`
566567 VirtualMethods []VirtualMethod `xml:"http://www.gtk.org/introspection/core/1.0 virtual-method"`
567568 Prerequisites []Prerequisite `xml:"http://www.gtk.org/introspection/core/1.0 prerequisite"`
569+ Properties []Property `xml:"http://www.gtk.org/introspection/core/1.0 property"`
568570 Signals []Signal `xml:"http://www.gtk.org/introspection/glib/1.0 signal"`
569571
570572 InfoAttrs
@@ -701,7 +703,76 @@ type Prerequisite struct {
701703 Name string `xml:"name,attr"`
702704 }
703705
704-type Property struct{}
706+type Property struct {
707+ XMLName xml.Name `xml:"http://www.gtk.org/introspection/core/1.0 property"`
708+ Name string `xml:"name,attr"`
709+ Writable bool `xml:"writable,attr"`
710+ Readable *bool `xml:"readable,attr"` // default true
711+ Construct bool `xml:"construct,attr"`
712+ ConstructOnly bool `xml:"construct-only,attr"`
713+ DefaultValue string `xml:"default-value,attr"`
714+ Getter string `xml:"getter,attr"`
715+ Setter string `xml:"setter,attr"`
716+ TransferOwnership string `xml:"transfer-ownership,attr"`
717+
718+ AnyType
719+ InfoElements
720+}
721+
722+func (p Property) IsReadable() bool {
723+ return p.Readable == nil || *p.Readable
724+}
725+
726+func (p *Property) Template(ns string, kinds KindMap) PropertyTemplate {
727+ var (
728+ goType = p.AnyType.Translate(ns, kinds)
729+ cName = p.Name
730+ gvalueType, setMethod, getMethod = mapGoTypeToGValue(goType)
731+ )
732+
733+ return PropertyTemplate{
734+ Doc: p.Doc.StringSafe(),
735+ Name: util.DashToCamel(cName),
736+ CName: cName,
737+ GoType: goType,
738+ GValueType: gvalueType,
739+ SetMethod: setMethod,
740+ GetMethod: getMethod,
741+ Readable: p.IsReadable(),
742+ Writable: p.Writable,
743+ }
744+}
745+
746+func mapGoTypeToGValue(goType string) (gvalueType, setMethod, getMethod string) {
747+ switch strings.TrimPrefix(goType, "*") {
748+ case "bool":
749+ return "TypeBooleanVal", "SetBoolean", "GetBoolean"
750+ case "byte":
751+ return "TypeUcharVal", "SetUchar", "GetUchar"
752+ case "int8":
753+ return "TypeCharVal", "SetSchar", "GetSchar"
754+ case "int":
755+ return "TypeIntVal", "SetInt", "GetInt"
756+ case "uint":
757+ return "TypeUintVal", "SetUint", "GetUint"
758+ case "int32":
759+ return "TypeLongVal", "SetLong", "GetLong"
760+ case "uint32":
761+ return "TypeUlongVal", "SetUlong", "GetUlong"
762+ case "int64":
763+ return "TypeInt64Val", "SetInt64", "GetInt64"
764+ case "uint64":
765+ return "TypeUint64Val", "SetUint64", "GetUint64"
766+ case "float32":
767+ return "TypeFloatVal", "SetFloat", "GetFloat"
768+ case "float64":
769+ return "TypeDoubleVal", "SetDouble", "GetDouble"
770+ case "string":
771+ return "TypeStringVal", "SetString", "GetString"
772+ default:
773+ return "", "", ""
774+ }
775+}
705776
706777 type Record struct {
707778 XMLName xml.Name `xml:"http://www.gtk.org/introspection/core/1.0 record"`
@@ -376,6 +376,7 @@ type Class struct {
376 Methods []Method `xml:"http://www.gtk.org/introspection/core/1.0 method"`376 Methods []Method `xml:"http://www.gtk.org/introspection/core/1.0 method"`
377 VirtualMethods []VirtualMethod `xml:"http://www.gtk.org/introspection/core/1.0 virtual-method"`377 VirtualMethods []VirtualMethod `xml:"http://www.gtk.org/introspection/core/1.0 virtual-method"`
378 Fields []Field `xml:"http://www.gtk.org/introspection/core/1.0 field"`378 Fields []Field `xml:"http://www.gtk.org/introspection/core/1.0 field"`
379+ Properties []Property `xml:"http://www.gtk.org/introspection/core/1.0 property"`
379 Signals []Signal `xml:"http://www.gtk.org/introspection/glib/1.0 signal"`380 Signals []Signal `xml:"http://www.gtk.org/introspection/glib/1.0 signal"`
380 }381 }
381 382
@@ -565,6 +566,7 @@ type Interface struct {
565 Methods []Method `xml:"http://www.gtk.org/introspection/core/1.0 method"`566 Methods []Method `xml:"http://www.gtk.org/introspection/core/1.0 method"`
566 VirtualMethods []VirtualMethod `xml:"http://www.gtk.org/introspection/core/1.0 virtual-method"`567 VirtualMethods []VirtualMethod `xml:"http://www.gtk.org/introspection/core/1.0 virtual-method"`
567 Prerequisites []Prerequisite `xml:"http://www.gtk.org/introspection/core/1.0 prerequisite"`568 Prerequisites []Prerequisite `xml:"http://www.gtk.org/introspection/core/1.0 prerequisite"`
569+ Properties []Property `xml:"http://www.gtk.org/introspection/core/1.0 property"`
568 Signals []Signal `xml:"http://www.gtk.org/introspection/glib/1.0 signal"`570 Signals []Signal `xml:"http://www.gtk.org/introspection/glib/1.0 signal"`
569 571
570 InfoAttrs572 InfoAttrs
@@ -701,7 +703,76 @@ type Prerequisite struct {
701 Name string `xml:"name,attr"`703 Name string `xml:"name,attr"`
702 }704 }
703 705
704-type Property struct{}706+type Property struct {
707+ XMLName xml.Name `xml:"http://www.gtk.org/introspection/core/1.0 property"`
708+ Name string `xml:"name,attr"`
709+ Writable bool `xml:"writable,attr"`
710+ Readable *bool `xml:"readable,attr"` // default true
711+ Construct bool `xml:"construct,attr"`
712+ ConstructOnly bool `xml:"construct-only,attr"`
713+ DefaultValue string `xml:"default-value,attr"`
714+ Getter string `xml:"getter,attr"`
715+ Setter string `xml:"setter,attr"`
716+ TransferOwnership string `xml:"transfer-ownership,attr"`
717+
718+ AnyType
719+ InfoElements
720+}
721+
722+func (p Property) IsReadable() bool {
723+ return p.Readable == nil || *p.Readable
724+}
725+
726+func (p *Property) Template(ns string, kinds KindMap) PropertyTemplate {
727+ var (
728+ goType = p.AnyType.Translate(ns, kinds)
729+ cName = p.Name
730+ gvalueType, setMethod, getMethod = mapGoTypeToGValue(goType)
731+ )
732+
733+ return PropertyTemplate{
734+ Doc: p.Doc.StringSafe(),
735+ Name: util.DashToCamel(cName),
736+ CName: cName,
737+ GoType: goType,
738+ GValueType: gvalueType,
739+ SetMethod: setMethod,
740+ GetMethod: getMethod,
741+ Readable: p.IsReadable(),
742+ Writable: p.Writable,
743+ }
744+}
745+
746+func mapGoTypeToGValue(goType string) (gvalueType, setMethod, getMethod string) {
747+ switch strings.TrimPrefix(goType, "*") {
748+ case "bool":
749+ return "TypeBooleanVal", "SetBoolean", "GetBoolean"
750+ case "byte":
751+ return "TypeUcharVal", "SetUchar", "GetUchar"
752+ case "int8":
753+ return "TypeCharVal", "SetSchar", "GetSchar"
754+ case "int":
755+ return "TypeIntVal", "SetInt", "GetInt"
756+ case "uint":
757+ return "TypeUintVal", "SetUint", "GetUint"
758+ case "int32":
759+ return "TypeLongVal", "SetLong", "GetLong"
760+ case "uint32":
761+ return "TypeUlongVal", "SetUlong", "GetUlong"
762+ case "int64":
763+ return "TypeInt64Val", "SetInt64", "GetInt64"
764+ case "uint64":
765+ return "TypeUint64Val", "SetUint64", "GetUint64"
766+ case "float32":
767+ return "TypeFloatVal", "SetFloat", "GetFloat"
768+ case "float64":
769+ return "TypeDoubleVal", "SetDouble", "GetDouble"
770+ case "string":
771+ return "TypeStringVal", "SetString", "GetString"
772+ default:
773+ return "", "", ""
774+ }
775+}
705 776
706 type Record struct {777 type Record struct {
707 XMLName xml.Name `xml:"http://www.gtk.org/introspection/core/1.0 record"`778 XMLName xml.Name `xml:"http://www.gtk.org/introspection/core/1.0 record"`
M templates/go +50 -0
@@ -164,6 +164,33 @@ func (x *{{$outer.Name}}Base) {{.Name}}({{conv .Args.API.Full}}) {{.Ret.Return}}
164164 }
165165 {{end}}
166166
167+{{range .Properties -}}
168+{{if .Writable}}
169+// SetProperty{{.Name}} sets the "{{.CName}}" property.
170+{{.Doc}}
171+func (x *{{$outer.Name}}Base) SetProperty{{.Name}}(value {{.GoType}}) {
172+ obj := {{if $NotGObject}}gobject.{{end}}Object{}
173+ obj.Ptr = x.GoPointer()
174+ var v {{if $NotGObject}}gobject.{{end}}Value
175+ v.Init({{if $NotGObject}}gobject.{{end}}{{.GValueType}})
176+ v.{{.SetMethod}}(value)
177+ obj.SetProperty("{{.CName}}", &v)
178+}
179+{{end}}
180+
181+{{if .Readable}}
182+// GetProperty{{.Name}} gets the "{{.CName}}" property.
183+{{.Doc}}
184+func (x *{{$outer.Name}}Base) GetProperty{{.Name}}() {{.GoType}} {
185+ obj := {{if $NotGObject}}gobject.{{end}}Object{}
186+ obj.Ptr = x.GoPointer()
187+ var v {{if $NotGObject}}gobject.{{end}}Value
188+ obj.GetProperty("{{.CName}}", &v)
189+ return v.{{.GetMethod}}()
190+}
191+{{end}}
192+{{end}}
193+
167194 {{range .Methods -}}
168195 var {{.Namespace}}X{{.FullName}} func(uintptr {{convc .Args.Pure.Types}}) {{.Ret.Raw}}
169196 {{end}}
@@ -276,6 +303,29 @@ func (c *{{.Name}}) SetGoPointer(ptr uintptr) {
276303 c.Ptr = ptr
277304 }
278305
306+{{range .Properties -}}
307+{{if .Writable}}
308+// SetProperty{{.Name}} sets the "{{.CName}}" property.
309+{{.Doc}}
310+func (x *{{$outer.Name}}) SetProperty{{.Name}}(value {{.GoType}}) {
311+ var v {{if $NotGObject}}gobject.{{end}}Value
312+ v.Init({{if $NotGObject}}gobject.{{end}}{{.GValueType}})
313+ v.{{.SetMethod}}(value)
314+ x.SetProperty("{{.CName}}", &v)
315+}
316+{{end}}
317+
318+{{if .Readable}}
319+// GetProperty{{.Name}} gets the "{{.CName}}" property.
320+{{.Doc}}
321+func (x *{{$outer.Name}}) GetProperty{{.Name}}() {{.GoType}} {
322+ var v {{if $NotGObject}}gobject.{{end}}Value
323+ x.GetProperty("{{.CName}}", &v)
324+ return v.{{.GetMethod}}()
325+}
326+{{end}}
327+{{end}}
328+
279329 {{range .Signals -}}
280330
281331 {{.Doc}}
@@ -164,6 +164,33 @@ func (x *{{$outer.Name}}Base) {{.Name}}({{conv .Args.API.Full}}) {{.Ret.Return}}
164 }164 }
165 {{end}}165 {{end}}
166 166
167+{{range .Properties -}}
168+{{if .Writable}}
169+// SetProperty{{.Name}} sets the "{{.CName}}" property.
170+{{.Doc}}
171+func (x *{{$outer.Name}}Base) SetProperty{{.Name}}(value {{.GoType}}) {
172+ obj := {{if $NotGObject}}gobject.{{end}}Object{}
173+ obj.Ptr = x.GoPointer()
174+ var v {{if $NotGObject}}gobject.{{end}}Value
175+ v.Init({{if $NotGObject}}gobject.{{end}}{{.GValueType}})
176+ v.{{.SetMethod}}(value)
177+ obj.SetProperty("{{.CName}}", &v)
178+}
179+{{end}}
180+
181+{{if .Readable}}
182+// GetProperty{{.Name}} gets the "{{.CName}}" property.
183+{{.Doc}}
184+func (x *{{$outer.Name}}Base) GetProperty{{.Name}}() {{.GoType}} {
185+ obj := {{if $NotGObject}}gobject.{{end}}Object{}
186+ obj.Ptr = x.GoPointer()
187+ var v {{if $NotGObject}}gobject.{{end}}Value
188+ obj.GetProperty("{{.CName}}", &v)
189+ return v.{{.GetMethod}}()
190+}
191+{{end}}
192+{{end}}
193+
167 {{range .Methods -}}194 {{range .Methods -}}
168 var {{.Namespace}}X{{.FullName}} func(uintptr {{convc .Args.Pure.Types}}) {{.Ret.Raw}}195 var {{.Namespace}}X{{.FullName}} func(uintptr {{convc .Args.Pure.Types}}) {{.Ret.Raw}}
169 {{end}}196 {{end}}
@@ -276,6 +303,29 @@ func (c *{{.Name}}) SetGoPointer(ptr uintptr) {
276 c.Ptr = ptr303 c.Ptr = ptr
277 }304 }
278 305
306+{{range .Properties -}}
307+{{if .Writable}}
308+// SetProperty{{.Name}} sets the "{{.CName}}" property.
309+{{.Doc}}
310+func (x *{{$outer.Name}}) SetProperty{{.Name}}(value {{.GoType}}) {
311+ var v {{if $NotGObject}}gobject.{{end}}Value
312+ v.Init({{if $NotGObject}}gobject.{{end}}{{.GValueType}})
313+ v.{{.SetMethod}}(value)
314+ x.SetProperty("{{.CName}}", &v)
315+}
316+{{end}}
317+
318+{{if .Readable}}
319+// GetProperty{{.Name}} gets the "{{.CName}}" property.
320+{{.Doc}}
321+func (x *{{$outer.Name}}) GetProperty{{.Name}}() {{.GoType}} {
322+ var v {{if $NotGObject}}gobject.{{end}}Value
323+ x.GetProperty("{{.CName}}", &v)
324+ return v.{{.GetMethod}}()
325+}
326+{{end}}
327+{{end}}
328+
279 {{range .Signals -}}329 {{range .Signals -}}
280 330
281 {{.Doc}}331 {{.Doc}}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 102

↑↓ moveenter openesc close