- Zig 90.5%
- C 4.5%
- Objective-C 2.8%
- Ink 2.2%
DVUI Editor
A simple vscode style text editor written in Zig with DVUI.
DVUI Editor Markdown DVUI Editor Ink DVUI Editor CSV
Contributing
Adding support for a new file type
file_widgets/ is the home for file-format-specific widgets. Each format typically needs two files:
*EditorWidget.zig— a split-pane layout combining an editor and a preview (seeInkEditorWidget,MarkDownWidget, orJsonWidgetas examples).*PreviewWidget.zig— renders or runs the file content live (seeInkPreviewWidget,MarkDownPreviewWidget, orJsonPreviewWidgetas examples).
Steps to wire up a new format:
-
Create your widget files in
file_widgets/src/. The widget module must export:pubconstwidget_id="my_format";// unique snake_case idpubconstwidget_name="My Format Editor";// shown in the infobarpubfnfileTypePriority(kind:FileType.Kind)?u8{...}pubfninit(...)MyFormatWidget{...}pubfndeinit(self:MyFormatWidget)void{...}pubfnprocessEvents(self:*MyFormatWidget)void{...}If the widget needs persistent state (parsed AST, scroll position, etc.), also export:
pubconstState=MyFormatPreviewWidget.State; -
Register it in
file_widgets/src/root.zig— two lines only:pubconstMyFormatWidget=@import("MyFormatWidget.zig");// top of file// ...constwidget_table=.{// ... existing widgets ...MyFormatWidget,// ← add here};WidgetKind,WidgetState, and all dispatch are generated automatically from the table. -
Register the file extension in
core_widgets/src/FileType.zig. -
Declare any new C libraries in
file_widgets/build.zig.
Widget priority
Each widget declares pub fn fileTypePriority(kind: FileType.Kind) ?u8. Return null if the widget doesn't support a given kind; return an integer otherwise — lower wins (the widget is shown by default). Established values:
| Priority | Widgets |
|---|---|
| 50 | Format-specific editors (Markdown, Ink, JSON, CSV) |
| 100 | CodeEditorWidget (source files) |
| 200 | TextEditWidget (all editable files) |
| 255 | TextEditWidget for unknown (no-extension) files |
The infobar automatically shows a dropdown when more than one widget supports the open file, letting the user override the default.
Testing a new file type widget
Add a file tests/<format>_test.zig following the pattern of the existing widget tests (e.g. tests/json_test.zig):
- Set up a
FileRefwith a small inline fixture and the correct path extension soFileType.fromPathroutes correctly. - Create the preview state (e.g.
FooPreviewWidget.State = .{}), callsettle(), anddefer state.deinit(allocator). - Assert parsed content — check state fields that the widget populates during rendering (parsed AST, table, transcript, etc.) and verify that specific fixture values appear in them.
- Assert dvui visibility — add a
.tagto a key rendered widget (e.g. the success-path container) and calldvui.testing.expectVisible("my-tag")after settle. - Wire it up in
build.zig— the loop at the bottom ofbuild()that callsaddTestfor eachtests/*.zigfile picks up new files automatically.
Advanced note:
CsvPreviewWidgetusesGridWidgetwhich requires multiple frames to measure cell heights. Use afor (0..20) |_| { _ = try dvui.testing.step(frame.f); }loop instead ofsettle().
Credits
- Fizzy for the code-base that was used as a starting point.
- David Vanderson for all the help and DVUI.