Skip to content
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 43 additions & 2 deletions ide/model.v
Original file line number Diff line number Diff line change
Expand Up @@ -980,9 +980,50 @@ fn (mut app IdeApp) save_document() !string {
return path
}

// companion_main_template uses a raw string with @PLACEHOLDER@ tokens instead of
// interpolation, because the generated source itself contains `${...}` and the
// new compiler mis-parses escaped `\$` inside an interpolated literal. The
// user-supplied names are emitted as double-quoted literals because vml_escape
// escapes `"` but not `'`.
const companion_main_template = r'module main

import ui2

const @CONST@ = $embed_file("@VML@").to_string()

fn build_form() ui2.Element {
return ui2.element_from_vml(@CONST@, ui2.bounds()) or {
eprintln("Could not load @VML@: ${err}")
ui2.screen(0x@BG@, [])
}
}

fn handle_form_event(event string) {
println("event: ${event}")
}

fn main() {
ui2.run_window("@NAME@", @W@, @H@, build_form, handle_form_event)
}
'

fn companion_main_source(app &IdeApp, vml_name string) string {
const_name := 'form_source'
return "module main\n\nimport ui2\n\nconst ${const_name} = \$embed_file('${vml_escape(vml_name)}').to_string()\n\nfn build_form() ui2.Element {\n\treturn ui2.element_from_vml(${const_name}, ui2.bounds()) or {\n\t\teprintln('Could not load ${vml_escape(vml_name)}: \${err}')\n\t\tui2.screen(0x${app.form_background:06x}, [])\n\t}\n}\n\nfn handle_form_event(event string) {\n\tprintln('event: \${event}')\n}\n\nfn main() {\n\tui2.run_window('${vml_escape(app.form_name)}', ${int(app.form_width)}, ${int(app.form_height)}, build_form, handle_form_event)\n}\n"
// replace_each substitutes in a single pass, so a name containing another
// placeholder cannot be expanded a second time.
return companion_main_template.replace_each([

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking: add regression coverage for the template's escaping and single-pass replacement

The existing test_saved_form_and_generated_main_compile_together only generates the default Form1 / form.vml case and checks the companion with the default compiler. Please extend coverage with an apostrophe-containing filename such as Mo's Form.vml and a placeholder-like filename such as @BG@.vml; quoted/backslash replacement values can be checked in a source-generation unit test without creating platform-invalid filenames. Assert that $embed_file, ${err}, and ${event} survive literally in the generated source, and that inserted @BG@ text is not substituted a second time. An automated -new-compiler build of the IDE would also catch the original host-compilation regression, which the current default-compiler companion check alone cannot cover.

@struckchure struckchure Sep 22, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in b4190a6:

  • test_companion_main_source_escapes_names_and_replaces_placeholders_once: form name Mo's "Form" \ @W@ and file name @BG@.vml; asserts the run_window literal escapes " and \ while passing ' through, that $embed_file("@BG@.vml"), ${err} and ${event} survive literally, that the @BG@/@W@ text coming in through user values is not expanded again, and that no placeholder token remains.
  • test_saved_form_and_generated_main_compile_together now generates and -checks companions for form.vml, Mo's Form.vml and @BG@.vml.
  • test_ide_builds_with_the_new_compiler: full -new-compiler build of ./ide (-check stops before the C stage where the original failure showed up). It skips with a note on a V that doesn't know the flag, which is the case for the 0.5.2 release binaries CI pins. Verified locally that it fails on the parent commit and passes here.

CI now runs v test ui2/ide on every matrix entry. One extra data point: on current V master the new compiler is the default (with a fallback to the cached 0.5.2), so there a plain v test ide against the old template already fails in the C stage with the undeclared err/event.

'@CONST@',
'form_source',
'@VML@',
vml_escape(vml_name),
'@BG@',
'${app.form_background:06x}',
'@NAME@',
vml_escape(app.form_name),
'@W@',
int(app.form_width).str(),
'@H@',
int(app.form_height).str(),
])
}

fn (mut app IdeApp) generate_companion(overwrite bool) !string {
Expand Down