diff --git a/BUGS-TO-REPORT.md b/BUGS-TO-REPORT.md index 2c1cf74b..0703aac4 100644 --- a/BUGS-TO-REPORT.md +++ b/BUGS-TO-REPORT.md @@ -798,15 +798,38 @@ Then, on that 477-file export: nothing (the `import` entry above), as it does for any folder under `Packages`; - the standalone script packs it, into a 4,220,723-byte project, against 2,055 bytes for the same export with `Packages` removed. The project now embeds its own copy of the four - compiler packages. It compiles with no errors; which copy the IDE then uses was not - measured. + compiler packages. It compiles with no errors; +- the IDE's own **New Project → Import from folder...** does the same, into a + 4,222,833-byte project, against 4,207 bytes from the export with `Packages` removed. + +**The embedded copy is dead, and every later export writes it back.** Measured on the +IDE's import (round 10): + +1. Export a project with the default references into an empty folder `E`. +2. In `E\Packages\VBA\Sources\Math.twin`, add `Public Function ProbeEmbeddedMarker() As + Long` before `End Module`, and a call to it in one of the project's own modules. +3. **Import from folder...** on `E`: TB5079, *Unrecognized symbol 'ProbeEmbeddedMarker'*. + The compiler uses its own VBA package, not the copy the project now holds. +4. Save the project, and export it again: the Debug Console reports + `[EXPORT] COMPLETED (139 folders, 954 files)`, against `(72 folders, 479 files)` before, + and the exported `Math.twin` holds the marker. The export writes both copies to the same + paths, the embedded one last. + +So a project kept in Git through *Export After Save* and rebuilt from a clone keeps committing +the package source of the IDE that first exported it, while it compiles against the current +IDE's. Expected: a `.twinproj` the IDE saves holds no compiler packages, so **Import from +folder** could skip them, or the export could write the IDE's own copy rather than the +project's. **What does not reproduce it:** the command line's own `export`, which writes what the -project file holds. +project file holds; and **Import from folder** on the export with the compiler packages' +folders removed, which gives the 4,207-byte project, compiles, and exports 479 files. **Found by** checking round 9's UC-62 answer, which sets up *Export After Save* into a Git repository and rebuilds the project from a fresh clone with the tB executable. The export was -round 8's, written by the IDE's `exportProjectTo()` over DevTools. +round 8's, written by the IDE's `exportProjectTo()` over DevTools. The dead copy was measured +following round 10's UC-66, the fresh clone, with `root.loadProjectFromFolder()` --- what the +dialog calls after its folder picker --- and `root.saveProjectAs()` over DevTools. --- diff --git a/builder/REVIEW-USECASES-16969e5.md b/builder/REVIEW-USECASES-16969e5.md new file mode 100644 index 00000000..9d8153fc --- /dev/null +++ b/builder/REVIEW-USECASES-16969e5.md @@ -0,0 +1,469 @@ +# Use-case review, round 10 --- an IDE route a fix pass closed, a DLL the pages could not call, and a search box that froze, at `16969e5` + +Branch `staging` · reviewed 2026-09-24 · 9 cases + +The tenth round of [the harness in `eval/`](../eval/README.md), and the third with isolated +evaluators. It ran the five re-runs round 9 named: UC-55 against its export command, UC-62 +against the Git section's new warning, and UC-63, UC-64 and UC-65 against their fixes, executed +again. It added the fresh-clone case round 9 asked for, UC-66, and opened three surfaces no case +had read: delegates used as callbacks (UC-67) and a UTF-8 text file (UC-68), both executed, and a +Standard DLL called from Excel VBA (UC-69), built and called. The corpus was built at `16969e5`, +round 9's last commit. The [Round 10 section of eval/usecases.md](../eval/usecases.md) records +the goals verbatim. + +## Verdict + +**Round 9's own fix closed the route UC-62 asks for.** Its warning in the Git section --- use +the tB executable or the script, "not the IDE's File → Export Project" --- is right about every +danger it names. It still told a reader who had asked for the IDE route that there was none. +UC-62 fell from 3/3/3 to 2/4/2, the first time since round 4 that a fix pass has damaged what it +touched, and only a re-run could show it. The IDE route works. Its rebuild half, New Project → +*Import from folder...*, was on no page, and following UC-66's answer one step past where it +stopped showed what that half needs: an export that still holds the compiler packages rebuilds +into a project carrying a dead copy of them, which every later save writes back into the +repository; and after a pull, the next save puts the IDE's copy of the project back over it. + +**The new surfaces were accurate where the pages spoke, and silent where the reader got hurt.** +Four of the five executed programs ran as their evaluators predicted, and the fifth did once +its function was put in a module. The failures were in what no page said: a +twinBASIC DLL called from VBA garbles every string it takes or returns, cannot be loaded by +64-bit Office in the build a new project makes, and is written under a name the answer's +`Declare` does not use. And one page said too much: a delegate's signature, promised as checked +at compile time, is only a warning, and a mismatched function receives its arguments mangled. + +**And the search box can freeze the page.** Re-measuring an evaluator's query ran the replica of +the site's search out of memory. The site's own script does the same: three Windows API names it +does not index froze the page for 5.1 s at 1.6 GB of heap, and four for over a minute. + +| | completeness | discoverability | actionability | +|---|---:|---:|---:| +| round 1 (16 cases) | 2.75 | 2.75 | 3.00 | +| round 2 (16 cases) | 2.56 | 2.69 | 2.69 | +| round 3 (8 cases) | 2.75 | 2.38 | 2.88 | +| round 4 (8 cases) | 3.00 | 2.50 | 3.00 | +| round 5 (8 cases) | 3.00 | 2.25 | 3.38 | +| round 6 (8 cases) | 3.13 | 2.75 | 3.00 | +| round 7 (8 cases) | 3.25 | 2.75 | 3.63 | +| round 8 (13 cases) | 3.62 | 3.23 | 3.31 | +| round 9 (11 cases) | 3.55 | 3.45 | 3.55 | +| **round 10 (9 cases)** | **3.00** | **3.33** | **2.78** | + +Per case. The scores are the evaluators' own except where verification changed them, which is +in bold and explained under *Corrections* below: + +| case | compl. | disc. | act. | hazard | +|---|---:|---:|---:|---| +| UC-55 *(site, re-run)* a project in Git, and a rebuild script | 4 | 3 | **3** | pass on the set hazards; its export command again has no `--overwrite` | +| UC-62 *(site, re-run)* a project in Git, kept from the IDE | 2 | 4 | 2 | pass; the route it was asked for reads as forbidden | +| UC-63 *(site, re-run)* port a VBA error handler --- **executed** | 4 | **3** | 4 | pass; ran as predicted | +| UC-64 *(site, re-run)* call the Windows API --- **executed** | **3** | 4 | 3 | none known; ran as handed over | +| UC-65 *(site, re-run)* a generic function and a generic class --- **executed** | 3 | 4 | **2** | none known; did not compile as handed over | +| UC-66 *(site)* rebuild a project kept by the IDE, from a fresh clone | 3 | 4 | 3 | pass on the pages' hazards; the rebuild it names keeps a dead copy of the compiler packages | +| UC-67 *(site)* pass a function as an argument --- **executed** | **3** | 2 | 3 | none known; ran as predicted | +| UC-68 *(site)* write and read back a UTF-8 file --- **executed** | 3 | **3** | 4 | none known; ran as predicted | +| UC-69 *(site)* a twinBASIC DLL called from Excel VBA --- **built and called** | 2 | 3 | **1** | three that no page knew: strings, bitness and the file's name | + +### The re-runs + +| case | round 9 | round 10 | +|---|---|---| +| UC-55 project in Git | 4 / 4 / 3 | 4 / 3 / 3 | +| UC-62 Git from the IDE | 3 / 3 / 3 | 2 / 4 / 2 | +| UC-63 error handler, executed | 3 / 3 / 4 | 4 / 3 / 4 | +| UC-64 Windows API, executed | 2 / 3 / 3 | 3 / 4 / 3 | +| UC-65 generics, executed | 3 / 4 / 3 | 3 / 4 / 2 | +| **mean** | **3.00 / 3.40 / 3.20** | **3.20 / 3.60 / 2.80** | + +UC-63 and UC-64 gained what round 9's fixes gave them. UC-62 lost a point on two axes to one of +those fixes. UC-65 lost one to its own composition, not to the page. All nine cases ran on the +model and Claude Code build of rounds 8 and 9, `claude-sonnet-5` through 2.1.280. + +## A discoverability fix, measured on the same queries + +Round 9's own queries for the five re-run cases, word for word, against round 9's snapshot of +the index and this round's: + +| case | round 9's queries | hits, round 9 index | hits, round 10 index | +|---|---|---:|---:| +| UC-55 | `rebuild project file`, `keep project in git plain text`, `export import tool command line`, `twinproj text format tbproj` | 1 of 4 | 1 of 4 | +| UC-62 | `git integration`, `auto commit on save`, `version control settings`, `export project on save git` | 2 of 4 | 2 of 4 | +| UC-63 | `On Error GoTo error handling`, `array index out of bounds error`, `divide by zero error number`, `Format function`, `division by zero floating point` | 1 of 5 | 0 of 5 | +| UC-64 | `call Windows API from twinBASIC`, `GetComputerName`, `GetWindowsDirectory`, `GetTickCount`, `computer name windows folder system uptime`, `Declare statement`, `LongPtr string buffer API`, `WinDevLib` | 3 of 8 | 6 of 8 | +| UC-65 | `generic function largest of two values`, `generic stack class`, `Of type-variable-list` | 2 of 3 | 2 of 3 | + +**9 of 24 to 11 of 24.** UC-64's three new hits are *Functions that return a string* and the +WinDevLib link, both round 9's. UC-63's lost hit is the trade round 9 made on purpose: retitling +the error table for error 9 took `subscript out of range` from 23rd to 1st and pushed +`divide by zero error number` from 3rd out of the top ten. This round's UC-63 found the table +with `subscript out of range`, at rank 1; its other three queries missed. + +This round's own 51 queries rank the same against both indexes except for three: +`subscript out of range`, a miss and then 1st; `clone repository rebuild twinproj`, 5th and then +4th; and UC-64's `GetComputerName GetWindowsDirectory GetTickCount`, 1st now, which could not be +measured against round 9's index at all. It ran the search replica out of memory there, because +round 9's index held none of the three words. That was finding 1. + +## The session as evidence + +**UC-66 found its answer by full-text search and reported that it had not.** Having read the +welcome page, it grepped the corpus three times --- `git|source control`, `\.twinproj`, +`export.*project` --- opened the four pages that turned up, the File menu, Project Settings, New +Project and Import/Export, and grepped twice more, for *Import from folder* and the export +settings. Its first site search was its 14th call. Its report says Channel 3 was not needed. +Scored from what can be checked, its discoverability stands: its first query ranks the Git +section 1st, and that section is three hops from the welcome page. But +the part of the answer it most needed, how the IDE rebuilds its own export, was on no page, so +no channel could have reached it (finding 7). + +**Four other flags were smaller.** UC-62's grep looked up a search result's anchor; UC-63's +was refused; UC-64 grepped for `GetTickCount` after finding its tutorial, and its report owns a +full-text search; UC-65 grepped for `ReDim Preserve` while composing its class. + +**Navigation was measured from the links, with `eval/nav_hops.mjs`,** and agrees with the +reports except in two places. UC-65 reached the Generics page by directory listing in what it +called two hops; by links it is three. UC-69 reported Project Types two hops from the welcome +page; it is three, through the Project Configuration index. + +**Every rank an evaluator reported was re-measured against the round's snapshot --- 51 queries, +all as reported.** + +## The executed cases + +Each ran through `scripts/tbrun.mjs` on BETA 983, in the `console` template, with the +evaluator's code verbatim, bare procedures wrapped in a `Module` block as round 9 did, and a +`[RunAfterBuild]` Sub beginning `Debug.Cls` in place of the reader's click. + +**UC-63 ran as predicted**: `2.50`, `no such position`, `cannot divide by zero`. Its `Case 9, +-2147352565` came from the table round 9 retitled. + +**UC-64 ran as handed over**, printing the computer's name, `C:\WINDOWS` and the uptime. Its +`GetTickCount64` declaration, which it composed from the `Declare` page because no page names +the function, worked first time. + +**UC-65 did not compile as handed over.** Its first file put `Max(Of T)` outside any module, +above a `Module Demo` block: TB5182, *Syntax error. No handler for this symbol*, seven times, +then TB5079 at each call. Wrapped in the `Module MaxDemo` its own comment names, it printed +` 25 `, ` 3.5 ` and `banana` --- the prediction, less the sign spaces round 9 also noted. The +Generics page's `Max` sample is a bare function, beside samples that are whole files (finding 10). + +**UC-67 ran as predicted**: the five names alphabetically, then by length, with the two +two-letter names in their original order. **UC-68 ran as predicted**: `Zoë - 3 characters`, +`Łódź - 4 characters`, `東京 - 2 characters`. The file it wrote is UTF-8 without a byte-order +mark and with CRLF line ends, as the `Open` page says `utf_8` writes. + +**UC-64 and UC-67 first failed on the harness, not on their code.** Both declare a `Sub Main`, +and so does the template's `tbxMain.twin`. Two `Main`s compile, which is all `examples.bat` +asks, but a build fails binding the startup object: *'Main' is ambiguous*. The template's +comment said a second `Main` "builds as written" (finding 14). Without the template's file both +ran as above. + +**UC-69 was built and called** --- see finding 5. Its DLL is the Standard DLL template's settings +and the evaluator's module; its caller is a twinBASIC project using plain `Declare`, which +converts strings to and from ANSI as VBA's does, in place of the Excel macro. Excel itself was +not driven: its *Trust access to the VBA project object model* is off, and it stays off. + +## Tier 1 --- the site does something harmful, or says something the product does not do + +**1. The search box froze the page on a query whose words the site does not index.** When a +query matches nothing, just-the-docs searches again, allowing each word an edit distance taken +from the length of the whole query: `Math.round(Math.sqrt(input.length / 2 - 1))`, which is 5 at +49 characters and 6 at 70. lunr's fuzzy expansion grows exponentially with that distance. +Measured in headless Chromium against the built offline site, by pasting into the search box: + +| query | characters | distance | result | +|---|---:|---:|---| +| `GetTickCount64` | 14 | 2 | no result in 7 ms | +| `GetTickCount64 GetSystemTimes` | 29 | 4 | no result in 117 ms | +| `GetTickCount64 GetSystemTimes GetDiskFreeSpaceExW` | 49 | 5 | no result after 5.1 s, 1.6 GB of JS heap | +| `GetTickCount64 GetSystemTimes GetDiskFreeSpaceExW GlobalMemoryStatusEx` | 70 | 6 | no answer within 60 s | + +A reader of the Windows API tutorial pasting a few function names is exactly who types that. +It was found because `eval/site_search.mjs`, which replicates the query logic, ran out of a +4 GB heap re-measuring UC-64's query against round 9's index. *Fixed*: the distance is capped at +2, in the vendored `just-the-docs.js` and in the replica. That changes nothing for a query of 15 +characters or fewer, where the formula gives at most 2; the two long queries above now answer in +about 10 ms, and a misspelled query still falls back: `recieve bytez frum serail` returns 1,636 +results at distance 2 where it returned 3,027 at 3. Pasted into the rebuilt offline site in the +same browser, the 49- and 70-character queries answered in 8 ms. + +**2. The pages promised a delegate's signature is checked, and a mismatch is only a warning.** +The *Delegate* reference said a delegate "adds compile-time signature checking when it is +assigned, passed, or called". Found by the fix pass while writing UC-67's section, and +re-measured: a function taking an **Integer**, assigned to a delegate whose parameter is a +**Long**, builds with warning TB0026, *Mismatched delegate type*, and no error, and called with +70000 it receives 4464. The same page's syntax line made **Public** and **Private** optional; a +**Delegate** with neither is TB5182, and a **Public** one in a class is TB5227, *Delegate +declarations in class modules must be Private*. *Fixed* on both Delegate pages, with +`[EnforceErrors(TB0026)]` as the way to make the mismatch an error. + +## Tier 2 --- hazards the pages did not know + +**3. A project rebuilt from an export that holds the compiler packages keeps a dead copy of +them, and every save writes it back.** UC-66's answer rebuilt the fresh clone with the script, +which round 9 had documented as the way and measured as compiling. Measured this round with the +IDE's own *Import from folder* over DevTools, on scratch folders: + +- a project with *Export Path* `${SourcePath}\src` and *Export After Save* on, saved once, + exported 478 files into `src`, 475 of them the compiler packages `VB`, `VBA`, `VBRUN` and + `AppGlobalClassProject`; +- imported from that `src` and saved beside it, the project is 4,222,833 bytes. From the same + `src` with `Packages` removed it is 4,207 bytes, compiles with no errors, and its next save + exports the full 478 files again; +- a function added to the copy of `VBA` in `src\Packages` and called from the project is + TB5079, *Unrecognized symbol*: the compiler uses the IDE's own package; +- the next save exported `139 folders, 954 files`, against `72 folders, 479 files` for the + original, and the file in `src\Packages\VBA` held the added function. The export writes both + copies to the same paths, the project's last. + +So after an IDE update, a repository kept this way goes on committing the package source of the +IDE that first exported it. Every compiler package a project references gets a folder, named +after the package's own project: the WebView2 sample references `WebView2Package` and +`WindowsControlsPackage`, and exported `VB`, `VBA`, `VBRUN` and `WebView2Package`, because +`WindowsControlsPackage` is the VB package. The project's `Settings` marks each such reference +`"isCompilerPackage": true`; nothing inside the folder does. *Fixed*: the Git section for the +IDE keeps the compiler packages out of the repository, and says to delete them from a clone that +has them before importing (finding 7). The product side extends round 9's queued entry. + +**4. With the IDE keeping `src`, the next save undoes a pull.** Every save empties `src` and +writes the project the IDE has open, and the IDE's project is the `.twinproj`, which Git does +not hold. Measured: a change written into `src` as a pull would, then the project opened and +saved, and `src` held the IDE's version again, the pulled change gone. The fix pass derived +this and flagged it unmeasured; it was measured before the section went in. *Fixed*: the section +says to save and commit before pulling, and to open the project from `src` again after it. + +**5. A twinBASIC DLL called from VBA fails three ways, and no page said so.** UC-69's answer +exported `AddNumbers` and `Greet` with `[DllExport]` and declared them in VBA with plain +`Declare ... Lib "MathGreetLib.dll"`. Built from the Standard DLL template's settings and +called: + +- **Strings.** `AddNumbers(2, 3)` printed ` 5 `. `Greet("Ann")` came back as 17 characters, + `H`, NUL, `e`, NUL ... then `Ann`: a VBA `Declare` converts a `String` argument to ANSI on the + way in and the result from ANSI on the way out, so the DLL's Unicode text is read as bytes. + The pattern that works leaves the DLL as it is: declare the argument and the result + `LongPtr`, pass `StrPtr(name)`, and take over the returned string with `RtlMoveMemory`. That + printed `Hello, Ann`, and `Hello, Zoë Łódź 東京` for text outside the ANSI code page. +- **Bitness.** A new project builds win32, and 64-bit Office --- Excel on this machine is 64-bit + Microsoft 365 --- cannot load it: from a 64-bit process the win32 build fails with + `BadImageFormatException` (0x8007000B). Switched to win64 by the toolbar's build + configuration box, driven over DevTools, the same project built an x64 DLL whose two exports + worked from 64-bit PowerShell. +- **The file's name.** The template's *Build Output Path* is + `${SourcePath}\Build\${ProjectName}_${Architecture}.${FileExtension}`, so the file is + `MathGreetLib_win32.dll` or `_win64.dll`, not the `MathGreetLib.dll` the answer declares. + +*Fixed*: *Calling a Standard DLL from VBA or Excel*, a `##` on the Project Types page, with the +bitness, the full path and the pointer pattern; the build configuration box documented on the +Toolbar and 64-bit pages; *Build Output Path* and *Build Type* filled in on Project Settings from +the IDE's own descriptions. The pattern's text outside the ANSI code page is described rather than +quoted: the site's fonts do not cover CJK, and the page is in the book. + +**6. UC-55 again exported without `--overwrite`.** Round 9 put the command with `--overwrite` +into step 2 of *Keeping a project in Git*, for "every other export". Step 2's first sentence, +about the first export, gave no command, and this round's evaluator took one from the Usage +example, which has no `--overwrite`, and never mentioned later exports. Measured: +`export --overwrite` into a folder that does not exist gives a tree identical to a plain export, +and `... DONE`. *Fixed*: step 2 gives one command for every export. + +## Tier 3 --- the answer exists and the reader cannot reach it, or it does not exist + +**7. The IDE route to Git: round 9's warning closed it, and its rebuild was on no page.** New +Project's *Import from folder...* was a bare list item. In the IDE's code it shows a *Browse For +Folder* dialog, "Select folder of the existing twinBASIC project...", and imports the folder as a +new, unsaved project; the first save asks for a file name. Saved beside `src`, the project's +`${SourcePath}\src` resolves to the clone's own folder, and every save updates it again --- +measured. *Fixed*: *Keeping a project in Git from the IDE*, a `##` beside the command-line +section: the two settings, the layout, a `.gitignore` for the compiler packages, the +`.twinproj` and the build folder, committing after a save, the fresh clone, and reopening after +a pull. The warning keeps its dangers and points there. The File menu's *Packing the export back +into a project* and a new *Import from folder* section on the New Project page describe the +import. + +**8. No page said how to build for 64-bit, where a build goes, or what Build does.** The toolbar's +build configuration box was one list item and an image's alt text; `64bit.md` says twinBASIC +compiles 64-bit and not how; *Build Output Path* and *Build Type* were empty headings; File → +Build had no description. *Fixed* on those pages and the File menu. The box's keys are CTRL+F1 and +CTRL+F2, the IDE remembers the choice for each project, and the box has a third entry, +**safeMode**, described from the IDE's own text. + +**9. Delegates are not found by the reader's word for them.** UC-67's `callback function as +argument` and `pass function as parameter` both missed; `delegate`, a word the reader did not +know yet, found the pages at ranks 1 and 2. No example showed a delegate as a procedure +parameter. *Fixed* with *Passing a function as an argument or parameter (callbacks)*, a `##` on +the Features page with an executed example; the title was chosen by its rank on the reader's +two queries, 1st for both, where the shorter *Passing a function as an argument (callbacks)* +was 1st and 2nd. + +**10. The Generics page's `Max` sample did not show where it goes** (UC-65). The rule is on the +*Module* page: a `.twin` file needs the explicit block. *Fixed*: the sample is a whole file, a +`Module` holding `Max` and a `Test` routine making the three calls the prose describes, laid out +as the page's other whole-file samples are, with no blank line against `Module` or `End Module`. +The callback sample on the Delegates page went the other way: its wrapper came out, as noise +beside that page's bare samples, and the harness wraps a bare sample itself. + +**11. Left open.** `array index out of bounds error` and `division by zero` still miss the error +table (UC-63); UC-64's `GetTickCount64` and `system uptime` miss, which is Microsoft's to +document; `auto commit on save`, `version control settings` and `rebuild project file from +source files` still miss the Git sections; and round 9's finding 9. + +### The fix, measured on the readers' words + +The fix pass's phrasings, against this round's snapshot of the index and the rebuilt site: + +| case | query | before | after | +|---|---|---:|---:| +| UC-67 | `callback function as argument` | miss | 1 | +| UC-67 | `pass function as parameter` | miss | 1 | +| UC-67 | `callback` | miss | 3 | +| UC-66 | `import from folder` | miss | 1 | +| UC-62 | `git integration IDE` | 1 | 1, the new section | +| UC-69 | `Excel VBA declare function DLL` | 2 | 1 | +| UC-69 | `build 64-bit DLL` | miss | 1 | +| UC-69 | `win64` | miss | 3 | + +`open project without twinproj file` stays 7th, for the New Project page rather than its new +section. + +## The harness + +**12. `nav_hops.mjs` could not run on a corpus,** because it imported the Markdown walker from +`--src`, where `build_corpus.mjs` leaves scripts as stubs; and **Git Bash turned its patterns +into Windows paths**, `^/tB/Core/Open$` into `^C:/Program Files/Git/tB/Core/Open$`, so every +target reported as unreachable. *Fixed*: the walker comes from the repository, and a pattern +that arrives as a drive-letter path is refused with the reason. + +**13. The search replica had no guard** against finding 1, and a run that re-measures an +evaluator's queries died with it. It carries the same cap as the site now. + +**14. The console template's comment said a second `Sub Main` builds.** It compiles, and a build +fails (see *The executed cases*). *Fixed* in `test/example-projects/console/Sources/tbxMain.twin`. + +**15. The harness built only the architecture the IDE last used**, win32 for a new project, so +round 9's 64-bit claim went unmeasured and this round's win64 DLL was built by a scratch probe +that set the toolbar's box over DevTools before clicking Build. *Landed since*, in `a46fd3f`: +`tbrun` and `tbbuild` take `--arch win32|win64`, and the registry tidy sweeps the remembered +target, the `targetArchitectureMemory` setting, which the probe's entry had to be removed from by +restoring a backup. + +**16. A `check_examples` run by the fix pass wedged for more than ten minutes** on one lane, +with nothing listening on its port; its processes were ended and the rerun was clean. The cause +was not isolated. The fix pass suspected a CDP call with no timeout; the tooling merged in +`a46fd3f` gives every CDP call one. + +**17. The executed cases ran four `tbrun` lanes from a script that did not own the registry +tidy**, which `scripts/lib/tb-registry.mjs` says one process per run must; the IDE's recent list +was left holding one probe project 19 times, and was swept by the tidy's own prefix rule. + +## Corrections --- evaluator claims amended + +**UC-55's** actionability is amended from 4 to 3, as in round 9: its export command has no +`--overwrite`, and it never says to export again after a change (finding 6). + +**UC-63's** discoverability is amended from 2 to 3: `subscript out of range` ranks the table +first, *On Error* ranks third for `On Error GoTo` and points to it, and by links the table is two +hops from the welcome page. + +**UC-64's** completeness is amended from 2 to 3, on round 9's reasoning: the signature it could +not find is Microsoft's to document, and what twinBASIC's pages owe was there. The code ran as +handed over. + +**UC-65's** actionability is amended from 3 to 2: the code as handed over does not compile. + +**UC-66's** report says Channel 3 was not used; the session shows five full-text searches before +the first site search. Its scores stand, discoverability from the rank and the links. + +**UC-67's** completeness is amended from 2 to 3: every mechanism its code used is documented, +and the one step it inferred --- a delegate as a parameter --- compiled and ran as the rules +imply. Its discoverability of 2 stands (finding 9). + +**UC-68's** split discoverability, search 2 and navigation 4, is recorded as 3. + +**UC-69's** actionability is amended from 2 to 1: as handed over, the macro declares a file that +the build does not write, in a bitness 64-bit Office cannot load, and the greeting comes back +garbled. + +## What round 10 says about the method + +**A fix pass can close a route, and only a re-run shows it.** Round 4 found six of its fourteen +findings introduced by round 3's fix pass, seven hours old. Round 9's warning is the first since: +every sentence of it true, and the reader who asked for the route it warned about concluded the +route did not exist. A warning is a fix that subtracts, and it needs the same re-run as one that +adds. + +**The product findings came, again, from going one step past the answer.** UC-66's answer +stopped at a rebuilt project that compiles; the next save is where the stale copy goes back into +the repository. UC-69's answer stopped at a macro; calling it is where the strings garble, and +building it is where the file's name and bitness come from. + +**Measuring with the site's own logic inherits the site's defects**, and that is the reason to +do it. The search replica was written to return exactly what a reader's search returns; it +returned exactly what a reader's search does to a long, unindexed query. + +**The fix pass measures too, and its briefs can be wrong.** Two findings came from the fix +agents rather than the evaluators: the delegate warning, met while executing the callback +example, and the pull that a save undoes, derived, flagged as unmeasured, and then measured. +The brief for the Git pages said the WebView2 sample's export had no folder for +`WindowsControlsPackage`, which was true and misleading --- that reference is the VB package --- +and the page repeated it until the diff was read against the export's own `Settings`. The +protocol's advice to verify rather than comply cuts both ways: the orchestrator has to verify +what it dispatches, too. + +**The evaluators ran on `claude-sonnet-5` through Claude Code 2.1.280**, passed with `--claude` +because the `claude` on `PATH` had become 2.1.212; the smoke check passed on both. Nine cases +cost $2.21 --- a mean of $0.25, from $0.15 (UC-55) to $0.42 (UC-69) --- and the smoke runs $0.04 +on 2.1.280 and $0.13 on 2.1.212. + +## Method + +Corpus built at `16969e5` with `eval/build_corpus.mjs`: 988 readable files and 279 stubbed +unreadable, 262 binary omitted; `WIP.md`, the twenty `WIP.*.md` files and the nine prior use-case +reviews withheld. The search index was snapshotted with it --- 3,808 entries, 80.3% reference, +4.1% developer docs --- and every case queried the snapshot. The smoke run's six checks passed on +both Claude Code builds; the nine cases then ran in parallel through `eval/run_case.mjs`, goals +verbatim from the tables. + +Executed runs used `scripts/tbrun.mjs` on BETA 983, ports 9811--9816: UC-63, UC-64, UC-65 as +handed over and wrapped, UC-67 and UC-68, then UC-64 and UC-67 again without the template's +`Main`; UC-69's DLL, its caller as handed over, and the pointer pattern. The win64 build ran on +9817 through a scratch probe that sets the build configuration box over DevTools, and its DLL +was called from 64-bit PowerShell. The IDE probes --- a save with *Export After Save*, *Import +from folder* on a clone with and without `Packages`, a marker in the embedded `VBA`, the +WebView2 sample's export, and a save after a change to `src` --- ran on 9831--9836 through +`root.loadProjectFromFolder()` and `root.saveProjectAs()`, on scratch folders, with the IDE's +registry entries swept by prefix and its remembered build architectures restored from a backup. +The delegate probes ran through `tbbuild` and `tbrun` on 9840--9842. The fix pass's own runs +used 9850--9879. The search timings used headless Chromium through the repository's puppeteer, +on `_site-offline/` and on the rebuilt offline tree. + +## What to do next + +**1. Round 11.** Re-run UC-62 and UC-66 against the Git-from-the-IDE section, UC-69 against the +DLL section, UC-67 against the callback section, and UC-55 against the single export command. +Re-run round 10's queries against round 11's index, as this round did. Measure a case in Excel +itself if a person can run the macro; the harness cannot. + +**2. With `tbrun --arch win64`, merged in `a46fd3f`**, re-measure the Windows API tutorial's +claim that a `Long` pointer fails in 64-bit mode, and UC-69's pointer pattern in a 64-bit +twinBASIC caller. + +**3. Still open:** the master list of run-time error numbers; the IDE section's empty headings, +76 of them, 57 on Project Settings, after this round filled two; `check_run`; and the phrasings +in finding 11. + +## Outcome + +**Every finding is fixed, except the misses left open in finding 11.** The site's search +carries finding 1, the documentation findings 2--10, and the harness 12--14; 15 and 16 landed +with the tooling merge the round was rebased onto. The product side of +finding 3 extends round 9's queued entry in `BUGS-TO-REPORT.md`, with the dead copy measured. + +`build.bat`, `check.bat` and `test.bat` are green on the rebased tree: 914 pages, 0 broken links +and 0 integrity findings in both real trees, 0 accessibility violations, every toolchain probe +passing. The book tree's informational broken links are 31, up from 23: all eight new ones lead +from this round's Features pages, which the book includes, into the IDE section, which it leaves +out. The samples on the three pages whose samples changed compile (`check_examples`, 14 +samples), and the callback example printed what the page shows. + +The work is seven commits on `staging`, rebased onto `a46fd3f`: the search fix, the harness, +the defect queue, round 10's goals with this review's draft, the documentation fixes in two +parts, and this review. Later commits took the `Module` wrapper out of the callback sample, and +out of the `Max` sample and back in; both pages' twelve samples compile. Nothing is pushed. diff --git a/builder/vendor/just-the-docs/README.md b/builder/vendor/just-the-docs/README.md index 48dac724..6e113786 100644 --- a/builder/vendor/just-the-docs/README.md +++ b/builder/vendor/just-the-docs/README.md @@ -101,6 +101,20 @@ patcher in [`offline.mjs`](../../offline.mjs) is AST-based and survives cosmetic upstream edits inside the patched function bodies, but the copy-button retirement above is structural and has to be re-applied. +**The search's typo fallback is capped at an edit distance of 2.** When a +query's words match nothing, upstream searches again with an edit distance +taken from the length of the *whole query*, `Math.round(Math.sqrt(input.length +/ 2 - 1))`, and applies it to every word. lunr's fuzzy expansion grows +exponentially with that distance. Measured in headless Chromium against the +built site: three Windows API names the site does not index, 49 characters and +distance 5, froze the page for 5.1 s and took the JS heap to 1.6 GB; four, 70 +characters and distance 6, never answered within 60 s. Capped, both answer in +about 10 ms. A query of 15 characters or fewer is unaffected, since the formula +gives at most 2 there. [`eval/site_search.mjs`](../../../eval/site_search.mjs) +replicates the query logic and carries the same cap; it is where the defect +was found, when re-measuring an evaluator's search ran the replica out of +memory. + ## Licence just-the-docs is MIT-licensed, and `LICENSE.txt` beside this file is the @@ -185,9 +199,10 @@ Bumping the just-the-docs version is a deliberate operation. Procedure: by the axe scan, and the three focus rings by nothing at all, since axe checks that a control is reachable and named, not that its ring is visible. -5. Re-apply the copy-button patch in `assets/js/just-the-docs.js` (see - above). Diffing against the previous vendored copy via `git diff` is - the easiest way to spot what needs to come back. +5. Re-apply the copy-button patch and the edit-distance cap in + `assets/js/just-the-docs.js` (see above). Diffing against the previous + vendored copy via `git diff` is the easiest way to spot what needs to + come back. 6. Inspect the entry point at `docs/assets/css/just-the-docs-combined.scss` --- if the upstream `_includes/css/just-the-docs.scss.liquid` Liquid diff --git a/builder/vendor/just-the-docs/assets/js/just-the-docs.js b/builder/vendor/just-the-docs/assets/js/just-the-docs.js index 5bbef3d2..4d9a70a2 100644 --- a/builder/vendor/just-the-docs/assets/js/just-the-docs.js +++ b/builder/vendor/just-the-docs/assets/js/just-the-docs.js @@ -190,8 +190,13 @@ function searchLoaded(index, docs) { }) if (tokens.length > 0) { results = index.query(function (query) { + // Patched: capped at 2. Upstream takes the distance from the whole + // query's length and applies it to every word, and lunr's fuzzy + // expansion grows exponentially with it: three unindexed API names + // (49 characters, distance 5) froze the page for 5 s at 1.6 GB, four + // (70, distance 6) for over a minute. See builder/vendor/just-the-docs/README.md. query.term(tokens, { - editDistance: Math.round(Math.sqrt(input.length / 2 - 1)) + editDistance: Math.min(2, Math.round(Math.sqrt(input.length / 2 - 1))) }); }); } diff --git a/docs/Features/64bit.md b/docs/Features/64bit.md index d44b5888..6ed2a78c 100644 --- a/docs/Features/64bit.md +++ b/docs/Features/64bit.md @@ -11,6 +11,12 @@ twinBASIC can compile native 64bit executables in addition to 32bit. The syntax Using the [Fusion](Fusion) feature, it is also possible to use both 32bit and 64bit ActiveX controls in 32bit *and* 64bit projects. +## Building a 64-bit Version + +The [build configuration](../tB/IDE/Project/Toolbar#build-configuration) box on the toolbar chooses between a 32-bit and a 64-bit build: **win32** or **win64**. CTRL + F1 switches to **win64**, and CTRL + F2 switches back to **win32**. The IDE remembers the choice for each project. + +The two builds write different files. The default [Build Output Path](../tB/IDE/Project/Settings#build-output-path) ends in `${ProjectName}_${Architecture}.${FileExtension}`, and `${Architecture}` is `win32` or `win64`. So a Standard DLL project named `MathGreetLib` builds `Build\MathGreetLib_win32.dll` in one mode and `Build\MathGreetLib_win64.dll` in the other. + ## Example Syntax ```tb check_build diff --git a/docs/Features/Fusion.md b/docs/Features/Fusion.md index df680338..5f165f58 100644 --- a/docs/Features/Fusion.md +++ b/docs/Features/Fusion.md @@ -79,7 +79,7 @@ A project-level setting allows you to control where the Fusion host EXE is gener ![tbFusionProjectSettings](Images/569150839-9ffc87ac-250d-40a4-bb47-669b607ad76f.png){:width="800" height="400"} If left blank (default), the standard build path set in the project settings is used. Unless overriden, the standard build path is: -${SourcePath}\Build${ProjectName}_${Architecture}.${FileExtension} +`${SourcePath}\Build\${ProjectName}_${Architecture}.${FileExtension}` For Fusion host executables, `${Architecture}` will resolve to: - `win32host` diff --git a/docs/Features/Language/Delegates.md b/docs/Features/Language/Delegates.md index d4e36735..8c6d70ce 100644 --- a/docs/Features/Language/Delegates.md +++ b/docs/Features/Language/Delegates.md @@ -9,6 +9,8 @@ permalink: /Features/Language/Delegates There is native support for calling a function by pointer, by way of `Delegate` syntax. A delegate in twinBASIC is a function pointer type that's compatible with LongPtr. `AddressOf` returns a delegate type, that's also backwards compatible with `LongPtr`. +A delegate is also how a procedure takes another function as an argument, which other languages call a *callback*. See [Passing a function as an argument](#callbacks). + ## Basic Usage The syntax looks like this: @@ -26,6 +28,64 @@ Public Function Addition(ByVal A As Long, ByVal B As Long) As Long End Function ``` +## Passing a function as an argument or parameter (callbacks) +{: #callbacks } + +A procedure can take a function as an argument and call it. Other languages call this a *callback*. Writing one takes three steps: + +1. Declare a delegate type with the signature the function must have. +2. Give the procedure a parameter of that type, and call the parameter as if it were a function. +3. At the call, pass **AddressOf** followed by the name of the function. + +**SortNames** below sorts an array of names. The caller chooses the order by passing a comparison function, and **SortNames** calls it through its *isInOrder* parameter: + +```tb check_build +' The signature every comparison function must have. +Public Delegate Function Comparer(ByVal a As String, ByVal b As String) As Boolean + +' Sorts names() in place. isInOrder returns True if a can stay before b. +Public Sub SortNames(names() As String, ByVal isInOrder As Comparer) + Dim i As Long, j As Long, temp As String + For i = LBound(names) To UBound(names) - 1 + For j = LBound(names) To UBound(names) - 1 + If Not isInOrder(names(j), names(j + 1)) Then + temp = names(j) + names(j) = names(j + 1) + names(j + 1) = temp + End If + Next j + Next i +End Sub + +Public Function ByAlphabet(ByVal a As String, ByVal b As String) As Boolean + Return a <= b +End Function + +Public Function ByLength(ByVal a As String, ByVal b As String) As Boolean + Return Len(a) <= Len(b) +End Function + +Public Sub SortDemo() + Dim names() As String = Array("Charlie", "Al", "Bob", "Dave", "Ed") + SortNames names, AddressOf ByAlphabet + Debug.Print Join(names, ", ") + SortNames names, AddressOf ByLength + Debug.Print Join(names, ", ") +End Sub +``` + +**SortDemo** prints: + +```text +Al, Bob, Charlie, Dave, Ed +Al, Ed, Bob, Dave, Charlie +``` + +> [!IMPORTANT] +> The function passed must match the delegate: the same number and types of parameters, the same **ByVal** or **ByRef** on each (a parameter with neither is **ByRef**), and the same return type. A function that does not match causes only a warning, TB0026 *Mismatched delegate type*, and the program still builds. The function then receives its arguments in a form it does not expect: if the delegate's parameter is a **Long** and the function's is an **Integer**, a value of 70000 arrives in the function as 4464. `[EnforceErrors(TB0026)]` on the module that uses **AddressOf** makes the mismatch an error; [Compiler Warnings](../Compiler-IDE/Compiler-Warnings#adjusting-warnings) describes the other ways to do that. + +A delegate type can also describe a callback that a Windows API function takes. See [Advanced Usage](#advanced-usage). + ## Advanced Usage The delegate type can also be used in interface/API declarations and as members of a User-defined type. For example, the `ChooseColor` API: diff --git a/docs/Features/Language/Generics.md b/docs/Features/Language/Generics.md index 94f3f441..13eef8c0 100644 --- a/docs/Features/Language/Generics.md +++ b/docs/Features/Language/Generics.md @@ -168,13 +168,21 @@ The function **Caster** introduces three type variables within its scope: The body of a generic procedure is compiled once for each type it is used with, as if it had been written out for that type. So the body can use an operator that works on every type it is called with: ```tb check_build -Public Function Max(Of T)(a As T, b As T) As T - If a > b Then - Return a - Else - Return b - End If -End Function +Module MaxDemo + Public Function Max(Of T)(a As T, b As T) As T + If a > b Then + Return a + Else + Return b + End If + End Function + + Sub Test() + Debug.Assert Max(3, 7) = 7 + Debug.Assert Max(3.5, 2.1) = 3.5 + Debug.Assert Max("apple", "banana") = "banana" + End Sub +End Module ``` `Max(3, 7)` returns the **Integer** `7`, `Max(3.5, 2.1)` the **Double** `3.5`, and `Max("apple", "banana")` the **String** `"banana"`. Nothing in the definition says which types *T* may be. A type that the body does not work with is a compile error, and the error is reported in the body, on the line that uses the operator, not at the call that supplied the type. `Max(Of Collection)(c1, c2)` fails with TB5092, *Missing argument 'Index'*, on the line `If a > b Then`: the message is about the argument of **Item**, the **Collection**'s default member. diff --git a/docs/Features/Packages/Import-export tool.md b/docs/Features/Packages/Import-export tool.md index 46513ab1..09a55a28 100644 --- a/docs/Features/Packages/Import-export tool.md +++ b/docs/Features/Packages/Import-export tool.md @@ -121,13 +121,15 @@ reports that its input does not exist, and `import MyPackage.twinpack in\` write `MyPackage.twinpack.twinproj`. The two formats are one container, so copy the file to a `.twinproj` name first, and copy the result back afterwards. -**The tB executable cannot pack a project that embeds a package.** A package a project uses -is embedded in it by default, as a folder under `Packages\` (see [Linked Packages](Linked)). -`export` writes that folder out, but `import` stops at the first folder inside `Packages\`: it -writes nothing, prints neither `... DONE` nor `... FAILED`, and exits with code `999`. Five of -the project files that come with the IDE are like this, among them the -**WinNativeCommonCtls** package and the *Standard EXE (plus VBCCR v1.8)* template. The script -packs them. +**The tB executable cannot pack a project that embeds a package.** A package added to a +project from TWINSERV or from a TWINPACK file is embedded in it by default, as a folder under +`Packages\` (see [Linked Packages](Linked)). `export` writes that folder out, but `import` +stops at the first folder inside `Packages\`: it writes nothing, prints neither `... DONE` nor +`... FAILED`, and exits with code `999`. Five of the project files that come with the IDE are +like this, among them the **WinNativeCommonCtls** package and the *Standard EXE (plus VBCCR +v1.8)* template. The script packs them. An export made by the IDE's **File → Export Project** +stops the same way, because it holds the compiler packages under `Packages\`; see [Packing +the export back into a project](../../tB/IDE/Project/Menu/File#packing-the-export-back-into-a-project). ## Checking the result @@ -171,44 +173,51 @@ if errorlevel 6 ( {: #keeping-a-project-in-git } The usual reason to export is version control. The exported folder is plain text that Git can -diff and merge, where a `.twinproj` is one binary file. Everything this takes is described -above; in order: +diff and merge, where a `.twinproj` is one binary file. The steps below use the tB executable +or the script, and everything they take is described above. The IDE can also keep the folder +up to date by itself, on every save: see [Keeping a project in Git from the +IDE](#keeping-a-project-in-git-from-the-ide). > [!WARNING] -> Use the tB executable or the script for this, not the IDE's **File → Export Project**. The -> IDE's command empties its folder before it writes --- a `.git` folder included --- and with -> the *Export After Save* setting it does so on every save. Pointed at a repository's top -> folder, it breaks the repository. Its export also holds the source of the compiler -> packages, which the tB executable cannot pack back into a project. See +> The IDE's **File → Export Project** empties its folder before it writes --- a `.git` folder +> included --- and with the *Export After Save* setting it does so on every save. Never point +> it at the repository's top folder or at the folder that holds the `.twinproj`; give it a +> folder of its own, as the next section does. Its export also holds the source of the +> compiler packages, and the tB executable cannot pack that export back into a project while +> any folder remains under its `Packages`. See > [Export Project](../../tB/IDE/Project/Menu/File#export-project). +The steps, in order: + 1. **Export into a folder of its own, not the repository's top folder** --- a `src` folder beside `.git`, for example. `import` packs everything in the folder it is given, and the tB executable packs a `.git` folder with it. The script skips `.git`, but a folder of its own is the safe layout for both. -2. **Export into an empty folder** the first time, and again after a file is removed or - renamed in the IDE. `export` never deletes a file, so one the project no longer holds - stays in the folder, and the next `import` packs it back. The script warns about such - files; the tB executable does not. - - Every other export goes into a folder that already holds the files, so it needs - `--overwrite`. Without it the tB executable writes only the files that are not there - yet, prints an `ERROR:` line for each of the others and then `... FAILED`, and exits - `0` --- so every file you changed keeps its old contents, and the commit leaves the - changes out: +2. **Export with `--overwrite`, every time.** One command serves the first export and every + one after it. Into a folder that does not exist yet, `--overwrite` changes nothing: the + export writes the same files as without it, and ends `... DONE`. Without it, an export + into a folder that already holds the files goes wrong. The tB executable writes only the + files that are not there yet, prints an `ERROR:` line for each of the others and then + `... FAILED`, and exits `0` --- so every changed file keeps its old contents, and the + commit leaves the changes out. The script refuses the whole export, and exits `3`. ```batch twinBASIC_win32.exe export "C:\Projects\MyProject.twinproj" "C:\Projects\MyProject\src\" --overwrite > tb.log find "... DONE" tb.log > nul || exit /b 1 ``` + + **Empty the folder first** when a file has been removed or renamed in the IDE. `export` + never deletes a file, so one the project no longer holds stays in the folder, and the + next `import` packs it back. The script warns about such files; the tB executable does + not. 3. **Commit the folder.** `export` leaves out the `.meta` file of editor state, and a project packed without one opens normally. A checkout with LF line endings needs no conversion: `import` converts LF to CRLF in `.twin`, `.bas` and `.cls` files. 4. **Rebuild the project file with `import --overwrite`**, and [check the result](#checking-the-result): the tB executable exits `0` after the failures it reports. - It also cannot pack a project that embeds a package, and a project embeds the packages - it uses by default; the script packs those. The project comes first, as in every - command: + It also cannot pack a project that embeds a package, and a package added from TWINSERV + or from a TWINPACK file is embedded by default; the script packs those. The project + comes first, as in every command: ```batch twinBASIC_win32.exe import "C:\Projects\MyProject.twinproj" "C:\Projects\MyProject\src\" --overwrite > tb.log @@ -218,6 +227,128 @@ above; in order: With the order reversed, the tB executable prints `... FAILED`, writes nothing and still exits `0`, so the second line is what catches it. +## Keeping a project in Git from the IDE +{: #keeping-a-project-in-git-from-the-ide } + +The IDE can export the project by itself every time it is saved, into a folder that Git +tracks, so the repository is ready to commit after every save. Two project settings set this +up, and a fresh clone is opened in the IDE too; the import/export tool is not needed. + +**Set these two project settings**, under *Export* in [Project +Settings](../../tB/IDE/Project/Settings#export) (**Project → Project Settings...**): + +| Setting | Value | +|---------|-------| +| [*Export Path*](../../tB/IDE/Project/Settings#export-path) | `${SourcePath}\src` | +| [*Export After Save*](../../tB/IDE/Project/Settings#export-after-save) | **Yes** | + +`${SourcePath}` is the folder that holds the `.twinproj` file, so every **Save Project** +(CTRL + S) now exports the project into a `src` folder beside it. The +`Settings` file that the export writes keeps both settings, so a clone of the repository has +them too. + +> [!WARNING] +> Keep *Export Path* on a folder of its own. Every export deletes everything in its folder +> before it writes, a `.git` folder included, so never set it to the repository's top folder +> or to the folder that holds the `.twinproj`. See +> [Export Project](../../tB/IDE/Project/Menu/File#export-project). + +**Make the folder that holds the `.twinproj` the repository's top folder**, with `git init` +there. Once the project has been saved and the `.gitignore` below added, it holds: + +```text +MyApp\ + .git\ + .gitignore + MyApp.twinproj + src\ +``` + +`src` holds the project's `Settings` file and its `Sources`, `Resources`, `References`, +`Miscellaneous`, `ImportedTypeLibraries` and `Packages` folders. The `.gitignore` is this: + +```text +/src/Packages/VB/ +/src/Packages/VBA/ +/src/Packages/VBRUN/ +/src/Packages/AppGlobalClassProject/ +/*.twinproj +/Build/ +``` + +**The first four lines keep the compiler packages out of Git.** Every export writes their +full source into `src\Packages`, but they are not part of the project: a `.twinproj` that the +IDE saves does not hold them, and the compiler uses the IDE's own. Imported back from `src`, +they become a copy inside the project that the compiler ignores, and every later export +writes that copy back into `src\Packages`. So after an IDE update, a repository that holds +them keeps the package source of the IDE that first exported them, while the project +compiles against the packages of the IDE in use. [Packing the export back into a +project](../../tB/IDE/Project/Menu/File#packing-the-export-back-into-a-project) has the +measurements. + +Those four are the compiler packages of a Standard EXE with the default references. Add a +line for each other compiler package the project references --- `/src/Packages/WebView2Package/` +for the WebView2 package, for example. The project's `Settings` file marks each one's reference +`"isCompilerPackage": true`, and its folder in `src\Packages` is named after the package's own +project, which is not always the name the reference uses: the reference `WindowsControlsPackage` +is the VB package, and its folder is `VB`. Keep the folder of any package the project embeds: a +package added from TWINSERV or from a TWINPACK file is embedded by default (see [Linked +Packages](Linked)), is part of the saved `.twinproj`, and belongs in the repository with the rest +of `src`. + +**The fifth line leaves out the `.twinproj`.** `src` is what gets committed and merged, and +the project file is rebuilt from it, as a fresh clone does below; the `.twinproj` itself is +one binary file that Git cannot merge. The last line leaves out what +[**Build**](../../tB/IDE/Project/Menu/File#build) writes: the project templates build into a +`Build` folder beside the `.twinproj`. + +**Commit after a save.** Once the save has finished, `src` matches the project: + +```batch +git add -A +git commit -m "Describe the change" +``` + +A file removed or renamed in the IDE disappears from `src` as well, because the export empties +the folder first, and `git add -A` records that. An export can fail without a message box, so +check that the [Debug Console](../../tB/IDE/Project/DebugConsole) shows `[EXPORT] COMPLETED` +for the save before committing. + +**Open a fresh clone** in the IDE. It holds `src` and the `.gitignore`, but no `.twinproj`: + +1. Choose **File → New Project**, then **Import from folder...** (see [New + Project](../../tB/IDE/Project/New#import-from-folder)). +2. In the *Browse For Folder* dialog, choose the clone's `src` folder. The project opens + unsaved, with no file behind it yet. +3. Save it with CTRL + S. The project has no file, so **Save Project** + opens the *Save As* dialog. Save the `.twinproj` in the clone's top folder, beside `src`, + and not inside `src`: *Export Path* is relative to the folder that holds the `.twinproj`, + so a project saved inside `src` exports into `src\src`, and the `src` that Git tracks is + never updated again. + +The same save exports the project into `src`, and from then on every save updates it. + +**If the repository holds the compiler packages**, because they were committed before the +`.gitignore` listed them, remove them from the clone before step 1, and commit that: + +```batch +git rm -r --ignore-unmatch src/Packages/VB src/Packages/VBA src/Packages/VBRUN src/Packages/AppGlobalClassProject +``` + +Imported with them, the project gets its own copy of each: its `.twinproj` measured 4,222,833 +bytes, against 4,207 bytes for the same folder without them. + +**After a pull or a merge, open the project from `src` again.** Every save empties `src` and +writes the project as the IDE has it open, so once Git has changed `src`, the next save would +undo those changes. Save and commit before pulling. Afterwards, close the project without +saving it, delete the compiler packages' folders from `src\Packages` --- the last export wrote +them there, though Git ignores them --- and follow the three fresh-clone steps above, saving +over the old `.twinproj`. + +A clone that has no compiler packages, of a project that embeds no package, holds no folder +under `src\Packages`, so the tB executable's `import` can pack it as well: see step 4 of +[Keeping a project in Git](#keeping-a-project-in-git). + ## Compiling from the command line None of these commands builds a project. The IDE, `twinBASIC.exe`, accepts diff --git a/docs/Features/Project-Configuration/Project-Types.md b/docs/Features/Project-Configuration/Project-Types.md index 2a891099..63f2ac5c 100644 --- a/docs/Features/Project-Configuration/Project-Types.md +++ b/docs/Features/Project-Configuration/Project-Types.md @@ -27,6 +27,61 @@ Public Function Multiply CDecl(ByVal a As Long, ByVal b As Long) As Long End Function ``` +## Calling a Standard DLL from VBA or Excel + +VBA code in Excel, or in any other Office application, calls a Standard DLL through a `Declare` statement, the same way it calls a Windows API function. On the twinBASIC side the functions are ordinary [`[DllExport]`](../../tB/Core/Attributes#dllexport) code. This Standard DLL project is named `MathGreetLib`: + +```tb check_build +[DllExport] +Public Function AddNumbers(ByVal a As Long, ByVal b As Long) As Long + AddNumbers = a + b +End Function + +[DllExport] +Public Function Greet(ByVal name As String) As String + Greet = "Hello, " & name +End Function +``` + +Three things stop the obvious VBA code from working: a new project builds a 32-bit DLL, the file the build writes is not called `MathGreetLib.dll`, and a `String` comes back garbled. + +### Build for the bitness of Office + +64-bit Office --- the usual installation today --- loads only 64-bit DLLs, because Windows does not load a 32-bit DLL into a 64-bit process. Set the [build configuration](../../tB/IDE/Project/Toolbar#build-configuration) box on the toolbar to **win64** before building. Only 32-bit Office uses the **win32** build. + +### Name the DLL by its full path + +With the default [Build Output Path](../../tB/IDE/Project/Settings#build-output-path), the file goes into a `Build` folder beside the `.twinproj` file, and its name ends in the architecture: `Build\MathGreetLib_win64.dll` from a win64 build, `Build\MathGreetLib_win32.dll` from a win32 one. So `Lib "MathGreetLib.dll"` names a file that does not exist. Give `Lib` the full path. + +### Pass strings as pointers + +Numbers pass as they are, so an ordinary declaration of `AddNumbers` works. Strings do not. A VBA `Declare` converts a `String` argument to ANSI before the call, and converts a `String` result from ANSI after it, but the DLL takes and returns Unicode text. The DLL receives an ANSI copy of the argument, and VBA reads the DLL's Unicode result as if each byte were one character. Declared with `String` for both, `Greet("Ann")` comes back with a NUL character, `Chr(0)`, after every character of `"Hello, "`, and then `Ann`. + +To avoid both conversions, declare the parameter and the result `As LongPtr`, so that only addresses cross the call. The DLL does not change. With the project in `C:\Projects\MathGreetLib` and built with **win64**: + +```vba +Private Declare PtrSafe Function AddNumbers Lib "C:\Projects\MathGreetLib\Build\MathGreetLib_win64.dll" (ByVal a As Long, ByVal b As Long) As Long +Private Declare PtrSafe Function GreetPtr Lib "C:\Projects\MathGreetLib\Build\MathGreetLib_win64.dll" Alias "Greet" (ByVal name As LongPtr) As LongPtr +Private Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (ByRef dst As Any, ByRef src As Any, ByVal cb As LongPtr) + +Public Function Greet(ByVal name As String) As String + Dim p As LongPtr + p = GreetPtr(StrPtr(name)) ' pass the characters, not an ANSI copy + CopyMemory ByVal VarPtr(Greet), p, LenB(p) ' make the DLL's string this function's result +End Function + +Sub TestMathGreetLib() + Debug.Print AddNumbers(2, 3) + Debug.Print Greet("Ann") +End Sub +``` + +`TestMathGreetLib` prints `5`, then `Hello, Ann`. `StrPtr(name)` passes the address of the VBA string's own characters. The DLL returns a new string --- a BSTR, which twinBASIC allocates for a `String` result --- and the `CopyMemory` line makes that string the result of the VBA `Greet` function. VBA frees it later like any other string, so nothing is copied and nothing leaks. + +Text that ANSI cannot hold survives as well. Tested from a twinBASIC caller with the same pattern, a name that mixed Polish and Japanese characters came back intact. + +For 32-bit Office, change the two paths to the win32 build. The rest of the code stays the same. + ## Console Applications This project type allows making a true console project rather than a GUI project. Helpfully, it will also add a default `Console` class for reading/writing console IO and provided debug console. diff --git a/docs/IDE/Menu/File.md b/docs/IDE/Menu/File.md index 5faf58a2..bc4e2a40 100644 --- a/docs/IDE/Menu/File.md +++ b/docs/IDE/Menu/File.md @@ -60,12 +60,22 @@ When the target is the folder that holds the `.twinproj`, the command deletes th ### Packing the export back into a project -The `Packages` folder holds the compiler packages as well: for a project with the default references, **VB**, **VBA**, **VBRUN** and the package behind the **App** object. A `.twinproj` file that the IDE saved does not hold them, and the `export` command of the tB executable does not write them. +The `Packages` folder holds the compiler packages as well. For a project with the default references they are **VB**, **VBA**, **VBRUN** and the package behind the **App** object, in folders named `VB`, `VBA`, `VBRUN` and `AppGlobalClassProject`. Every other compiler package the project references gets a folder too, named after the package's own project, which is not always the name the reference uses: the WebView2 sample references `WebView2Package` and `WindowsControlsPackage`, and its export has folders named `WebView2Package` and `VB`, because `WindowsControlsPackage` is the VB package. The project's `Settings` file marks each compiler package's reference `"isCompilerPackage": true`; nothing inside the package's own folder does. -The tB executable's `import` cannot pack a folder that holds packages. It stops at the first folder under `Packages\`, writes nothing, and exits with code `999` --- see [Where the two differ](../../../../Features/Packages/Import-Export-Tool#where-the-two-differ). So the tB executable cannot rebuild a project from this command's export. The import/export script can, and the project it writes holds its own copy of each of those packages: 4.2 MB for the two-file project above, against 2 KB for the same two files without them. That project compiles. +A `.twinproj` file that the IDE saved does not hold the compiler packages, and the `export` command of the tB executable does not write them. A package the project embeds is different: one added from TWINSERV or from a TWINPACK file is embedded by default (see [Linked Packages](../../../../Features/Packages/Linked)), is part of the saved `.twinproj`, and has a folder under `Packages` too. + +**Delete the compiler packages' folders before packing the export back into a project.** The IDE's own way to pack it is **Import from folder...** in the [New Project](../New#import-from-folder) dialog, which opens the project unsaved; the first save asks for a file name. The import/export script's `import` packs it as well. Both pack every folder under `Packages` into the project, so the compiler packages become a copy inside it. Rebuilt by **Import from folder** with them, the project was 4,222,833 bytes, against 4,207 bytes without them and 2,956 bytes for the project as the IDE saved it. Both rebuilt projects open with no errors, and the script's project is the same 4.2 MB. + +The compiler does not use that copy. A function added to the copy of **VBA**, in `Packages\VBA\Sources\Math.twin`, is an unrecognized symbol (TB5079) to the code that calls it. But every later export writes the copy back into `Packages`: the added function is there again after each save, and the Debug Console reports `[EXPORT] COMPLETED (139 folders, 954 files)`, where the original project's export reported `(72 folders, 479 files)`. So after an IDE update, the export still holds the package source of the IDE that first exported the project, while the project compiles against the packages of the IDE in use. + +The tB executable's `import` cannot pack a folder while any folder remains under `Packages\`. It stops at the first one, writes nothing, and exits with code `999` --- see [Where the two differ](../../../../Features/Packages/Import-Export-Tool#where-the-two-differ). With the compiler packages' folders deleted, it packs the export of a project that embeds no package. [Keeping a project in Git from the IDE](../../../../Features/Packages/Import-Export-Tool#keeping-a-project-in-git-from-the-ide) keeps them out of the repository from the start. ### What the Debug Console shows Each export writes a first line, `[EXPORT] exporting current project to "…"`, and a last line: `[EXPORT] COMPLETED (72 folders, 478 files)`, or the failure lines above. With [*Export Verbose*](../Settings#export-verbose) set, it also writes a line for each file and folder it deletes, `[EXPORT] DELETED: …`, and for each file it writes, `[EXPORT] DONE: …`. The `export` command of the tB executable and of the import/export script deletes nothing. See [What both programs do](../../../../Features/Packages/Import-Export-Tool#what-both-programs-do). + +## Build + +Compiles the project into the file that the [*Build Output Path*](../Settings#build-output-path) setting names. The project templates set it to `${SourcePath}\Build\${ProjectName}_${Architecture}.${FileExtension}`, a file in a `Build` folder beside the `.twinproj`. `${Architecture}` is `win32` or `win64`, as chosen in the [build configuration](../Toolbar#build-configuration) box on the toolbar. diff --git a/docs/IDE/New Project.md b/docs/IDE/New Project.md index 4b14026b..fdcd688b 100644 --- a/docs/IDE/New Project.md +++ b/docs/IDE/New Project.md @@ -15,17 +15,25 @@ Shortcut: CTRL + N ## Options -- Standard Exe +- Standard EXE - ActiveX Control - ActiveX DLL - Standard DLL - Standard EXE (Console App) - Standard EXE (plus VBCCR v1.8) - Import from VBP... -- Import from folder... +- [Import from folder...](#import-from-folder) Browse \| Open \| Cancel +## Import from folder + +Opens a project from a folder of source files, such as the folder that **File → Export Project** writes. It shows a *Browse For Folder* dialog titled *Select folder of the existing twinBASIC project...*: choose the folder that holds the project's `Settings` file. + +The project opens with no `.twinproj` file behind it: it is unsaved, and marked as changed. Because it has no file, the first **Save Project** (CTRL + S) opens the *Save As* dialog to ask for one. The folder it is saved in becomes `${SourcePath}`, which the project's [*Export Path*](Settings#export-path) may use. + +If the folder holds the compiler packages under `Packages`, as an export by the IDE does, delete their folders first: imported with them, the project gets a copy of them that the compiler does not use. See [Packing the export back into a project](Menu/File#packing-the-export-back-into-a-project). [Keeping a project in Git from the IDE](../../../Features/Packages/Import-Export-Tool#keeping-a-project-in-git-from-the-ide) uses this command to open a fresh clone. + # Samples ![The same dialog on its Samples tab, a scrolling column of sample projects headed by Sample 0. Reports (Experimental), which is selected, then Sample 1. HelloWorld, Sample 1a. WebView2 Examples, Sample 2. GetIPAddresses and Sample 3. MyCodeLibrary, with the list running on past the bottom of the panel.](Images/New_Project_Samples.png) diff --git a/docs/IDE/Project Settings.md b/docs/IDE/Project Settings.md index b9b52cc1..2c8603fd 100644 --- a/docs/IDE/Project Settings.md +++ b/docs/IDE/Project Settings.md @@ -43,8 +43,14 @@ See [Packages](../../../Features/Packages/) ## Build Output Path +The full path of the file the compiler creates. Its description in the dialog lists these variables: `${SourcePath}`, the folder that holds the `.twinproj` file, and `${ProjectName}`, `${ProjectID}`, `${FileExtension}`, `${Architecture}`, `${VersionMajor}`, `${VersionMinor}`, `${VersionBuild}` and `${VersionRevision}`. `${Architecture}` is `win32` or `win64`, whichever the toolbar's [build configuration](Toolbar#build-configuration) box is set to. + +Every project template sets it to `${SourcePath}\Build\${ProjectName}_${Architecture}.${FileExtension}`: a `Build` folder beside the `.twinproj` file, and a different file name for each architecture. A Standard DLL project named `MathGreetLib` builds `Build\MathGreetLib_win32.dll` or `Build\MathGreetLib_win64.dll`, and a `Declare` that calls it has to name that file --- see [Calling a Standard DLL from VBA or Excel](../../../Features/Project-Configuration/Project-Types#calling-a-standard-dll-from-vba-or-excel). In the `Settings` file it is `project.buildPath`. + ## Build Type +The type of file the compiler creates: **Standard EXE**, **ActiveX DLL**, **ActiveX Control**, **Standard DLL** or **Package TWINPACK**. [Project Types](../../../Features/Project-Configuration/Project-Types) describes the Standard DLL, and code can test the setting with the [`TWINBASIC_BUILD_TYPE`](../../../Reference/Compiler-Constants#twinbasic_build_type) compiler constant. In the `Settings` file it is `project.buildType`. + ## Licence Type ## Package Visibility diff --git a/docs/IDE/Toolbar.md b/docs/IDE/Toolbar.md index b27994ef..e48b4d92 100644 --- a/docs/IDE/Toolbar.md +++ b/docs/IDE/Toolbar.md @@ -23,7 +23,7 @@ permalink: /tB/IDE/Project/Toolbar - Step Over (SHIFT + F8 / F10) - Step Into (F8 / F11) - Step Out (CTRL + SHIFT + F8 / SHIFT + F11) -- Choose a build configuration +- [Choose a build configuration](#build-configuration) - Restart the compiler - Clean (Deregister & delete build) - Build @@ -46,3 +46,20 @@ permalink: /tB/IDE/Project/Toolbar - Launch the form in isolation to test the functionality - Change IDE Theme - Global Search + +## Build configuration + +The build configuration box chooses whether the project is compiled as 32-bit or 64-bit code: + +- **win32** builds a 32-bit executable or DLL. +- **win64** builds a 64-bit one. [64-bit Office](../../../Features/Project-Configuration/Project-Types#calling-a-standard-dll-from-vba-or-excel), for example, can load only a 64-bit DLL. + +CTRL + F1 switches to **win64**, and CTRL + F2 switches to **win32**. The keys are the default bindings of the IDE commands `tbBuild_SwitchToWin64` and `tbBuild_SwitchToWin32`, and [Manage Keyboard Shortcuts](Menu/Window#manage-keyboard-shortcuts) can change them. + +The IDE remembers the choice for each project, and sets the box back to it when the project is opened again. It keeps the choice in its own settings, under the path of the `.twinproj` file, not in the project. A project with no remembered choice uses whatever the box shows, and a newly started IDE shows **win32**. + +The box also sets the `Win64` compiler constant --- 1 in **win64**, 0 in **win32** --- and so decides which branch of an `#If Win64` block the editor treats as active and which it greys out. [Compiler Constants](../../../Reference/Compiler-Constants#appearance) shows the same code in both modes. + +The default [Build Output Path](Settings#build-output-path) puts `win32` or `win64` in the file name, so the two builds do not overwrite each other. + +The box has a third entry, **safeMode**. It opens the project's files without starting the compiler services, and the IDE describes it as a way to investigate, fix or recover code before saving it and restarting in normal mode. The IDE also switches to **safeMode** by itself when the compiler keeps crashing, and says *Compiler crash loop detected. Restarting in SAFE mode.* diff --git a/docs/Reference/Core/Delegate.md b/docs/Reference/Core/Delegate.md index 19620831..c0b8d107 100644 --- a/docs/Reference/Core/Delegate.md +++ b/docs/Reference/Core/Delegate.md @@ -6,19 +6,19 @@ permalink: /tB/Core/Delegate # Delegate {: .no_toc } -Declares a function-pointer type --- a named signature that variables, parameters, and UDT members can hold a *reference* to a callable matching. A delegate value is bit-compatible with **LongPtr**, but adds compile-time signature checking when it is assigned, passed, or called. +Declares a function-pointer type --- a named signature that variables, parameters, and UDT members can hold a *reference* to a callable matching. A delegate value is bit-compatible with **LongPtr**, but the compiler compares the signature of a procedure assigned to it, and warns when they differ. > [!NOTE] > The **Delegate** statement is a twinBASIC extension. In classic VBA, function pointers are untyped **LongPtr** values produced by **AddressOf** and called indirectly through custom mechanisms (`DispCallFunc`, `CallWindowProc` shims, etc.). Syntax: -> [ **Public** \| **Private** ] **Delegate Function** *name* [ **CDecl** ] **(** [ *arglist* ] **)** **As** *type* +> { **Public** \| **Private** } **Delegate Function** *name* [ **CDecl** ] **(** [ *arglist* ] **)** **As** *type* **Public** -: *optional* In an ActiveX project, exports the delegate type to the type library so consumers in other projects see *name*. +: *required*, or **Private**: a declaration with neither is a syntax error, TB5182. In an ActiveX project, exports the delegate type to the type library so consumers in other projects see *name*. Not allowed in a class, where a delegate must be **Private** (TB5227, *Delegate declarations in class modules must be Private*). **Private** -: *optional* Withholds the delegate from the type library; usable only within the project. +: *required*, or **Public**. Withholds the delegate from the type library; usable only within the project. *name* : The identifier naming the delegate type. Must be a valid twinBASIC identifier. @@ -34,7 +34,7 @@ Syntax: After the declaration, *name* may be used wherever a type is allowed: to declare variables and parameters of function-pointer type, as the type of a member of a [**Type**](Type) (UDT), or as a parameter type in a [**Declare**](Declare) statement or an [**Interface**](Interface) member. -A delegate value is normally produced by **AddressOf**, which yields a delegate-typed reference to a regular procedure with a matching signature. For backwards compatibility, a delegate variable can also be assigned a plain **LongPtr** address obtained by other means --- the value passes through unchecked. A delegate variable is called like a function: `result = myDelegate(arg1, arg2)`. +A delegate value is normally produced by **AddressOf**, which yields a delegate-typed reference to a regular procedure with a matching signature. A procedure that does not match causes only a warning, TB0026 *Mismatched delegate type*, and the program still builds; its arguments then arrive in a form it does not expect. See [Passing a function as an argument](../../Features/Language/Delegates#callbacks) for what that looks like and how to make it an error. For backwards compatibility, a delegate variable can also be assigned a plain **LongPtr** address obtained by other means --- the value passes through unchecked. A delegate variable is called like a function: `result = myDelegate(arg1, arg2)`. ### Example @@ -53,7 +53,7 @@ Private Sub Command1_Click() End Sub ``` -A delegate used as a UDT member, modelling the `lpfnHook` field of the Windows `CHOOSECOLOR` struct. Existing code that assigns a **Long**/**LongPtr** to `lpfnHook` continues to work; new code can assign **AddressOf** *Handler* directly and have the signature checked at compile time: +A delegate used as a UDT member, modelling the `lpfnHook` field of the Windows `CHOOSECOLOR` struct. Existing code that assigns a **Long**/**LongPtr** to `lpfnHook` continues to work; new code can assign **AddressOf** *Handler* directly, and the compiler warns if its signature does not match: ```tb inert=external Public Delegate Function CCHookProc (ByVal hwnd As LongPtr, ByVal uMsg As Long, _ diff --git a/eval/README.md b/eval/README.md index 0981c2d9..ef1f9117 100644 --- a/eval/README.md +++ b/eval/README.md @@ -35,6 +35,11 @@ any of it fails. Each case leaves the prompt it was given, its whole session as stream-json, its report and a `.meta.json` recording the model and the Claude Code version, and prints the session's digest --- see [Reading the results](#reading-the-results). +**Pin the Claude Code build when a round re-runs an earlier one.** `--claude ` names it; +without it the runner takes whatever `claude` is on `PATH`, which changes with every install. +Round 10 found 2.1.212 there, where rounds 8 and 9 had run the desktop app's bundled 2.1.280, +and passed that one's path instead. + **Snapshot the search index with the corpus**: copy `docs/_site/assets/js/search-data.json` and `assets/js/vendor/lunr.min.js` beside it, and give every case `--site `. `site_search.mjs` otherwise reads the live `docs/_site/`, and a rebuild during the round @@ -173,6 +178,7 @@ never on the report's word for how the answer was reached. | 7 | 8 --- the re-runs round 6 named, and the first executed case | [builder/REVIEW-USECASES-60bb6f5.md](../builder/REVIEW-USECASES-60bb6f5.md); 19 findings, no hazard walked into, UC-40's discoverability 1 → 4, and a tutorial describing a failure the product does not produce | | 8 | 13 --- the first isolated evaluators: round 7's re-runs, round 1's four lowest, three new | [builder/REVIEW-USECASES-5b4cd37.md](../builder/REVIEW-USECASES-5b4cd37.md); 29 findings, no set hazard walked into, and the four most serious found by probing the product: an IDE export that empties a Git repository, constructors that fail in silence, error numbers that are not VBA's, and a debugger Stop that stops one procedure and so turns a failed unit test into a pass | | 9 | 11 --- round 8's seven re-runs, and four new site cases, four of the eleven executed | [builder/REVIEW-USECASES-d4b37ec.md](../builder/REVIEW-USECASES-d4b37ec.md); 12 findings, the re-runs' discoverability +1.00 and round 8's own queries from 2 hits of 14 to 11, and an IDE export the tB executable cannot pack back into a project | +| 10 | 9 --- round 9's five re-runs, a fresh clone, and three new surfaces; five executed, and a DLL built and called | [builder/REVIEW-USECASES-16969e5.md](../builder/REVIEW-USECASES-16969e5.md); 17 findings: round 9's own warning closed the IDE route to Git, a twinBASIC DLL called from VBA fails three ways no page named, a delegate's signature is only a warning, and four unindexed API names froze the site's search box | Round 1's headline was a gradient: documentation quality fell monotonically with depth into the toolchain (contributor 3.8 discoverability, toolchain user 2.8, builder developer 1.8), diff --git a/eval/nav_hops.mjs b/eval/nav_hops.mjs index c6eb80f9..a01efdcb 100644 --- a/eval/nav_hops.mjs +++ b/eval/nav_hops.mjs @@ -60,7 +60,10 @@ const pageKey = (url) => /** Every page under docs/: permalink by file, and file by permalink or redirect alias. */ async function loadPages(src) { - const { markdownFiles } = await import(pathToFileURL(path.join(src, "scripts/lib/markdown-files.mjs")).href); + // The walker comes from this repository, never from --src: a corpus built by + // eval/build_corpus.mjs holds scripts/ only as unreadable stubs, so importing + // it from there failed with "markdownFiles is not a function". + const { markdownFiles } = await import(pathToFileURL(path.join(REPO_ROOT, "scripts/lib/markdown-files.mjs")).href); const docs = path.join(src, "docs"); const urlOf = new Map(); const byKey = new Map(); @@ -109,6 +112,15 @@ async function main(argv) { console.log(USAGE); return o.help ? 0 : 2; } + // Git Bash turns an argument that looks like a POSIX path into a Windows one, + // so '^/tB/Core/Open$' arrives as '^C:/Program Files/Git/tB/Core/Open$', and + // every target then reports as unreachable, which reads as a finding. + const mangled = o.targets.filter((t) => /^\^?[A-Za-z]:[\\/]/.test(t)); + if (mangled.length) { + console.error(`these patterns arrived as Windows paths: ${mangled.join(", ")}\n` + + "Git Bash converted them. Run with MSYS_NO_PATHCONV=1 set, or from another shell."); + return 2; + } const start = path.resolve(o.src, o.from); if (!fs.existsSync(start)) { console.error(`no start page: ${start}`); diff --git a/eval/site_search.mjs b/eval/site_search.mjs index 0b43540f..d49a6df9 100644 --- a/eval/site_search.mjs +++ b/eval/site_search.mjs @@ -76,8 +76,10 @@ function search({ lunr, index }, input) { if (results.length === 0 && input.length > 2) { const tokens = lunr.tokenizer(input).filter((t) => t.str.length < 20); if (tokens.length) { + // Capped at 2, as the patched just-the-docs.js is. Uncapped, a query of + // three unindexed API names ran this replica out of memory. results = index.query((q) => - q.term(tokens, { editDistance: Math.round(Math.sqrt(input.length / 2 - 1)) }) + q.term(tokens, { editDistance: Math.min(2, Math.round(Math.sqrt(input.length / 2 - 1))) }) ); } } diff --git a/eval/usecases.md b/eval/usecases.md index c078b723..3bfffb1e 100644 --- a/eval/usecases.md +++ b/eval/usecases.md @@ -488,3 +488,34 @@ Failed: End Select End Function ``` + +## Round 10 --- the re-runs round 9 named, a fresh clone, and three surfaces no case has read + +**Run 2026-09-24 at `16969e5`** --- [builder/REVIEW-USECASES-16969e5.md](../builder/REVIEW-USECASES-16969e5.md). +Round 9's last commit, with its fixes in. One session, every case in parallel through +`eval/run_case.mjs`, on the model and Claude Code build of rounds 8 and 9. That build is the +desktop app's bundled 2.1.280, passed with `--claude`: the `claude` on `PATH` had become 2.1.212 +in the meantime, and the smoke check ran on both. + +**Re-run the five round 9 named**: UC-63, UC-64 and UC-65 against its fixes, executed again; +UC-62 against the Git section's new warning; UC-55 against the export command. Goals verbatim, +each on the site protocol it was first run under. **Also re-run round 9's own queries** for those +cases against this round's index. + +**Write the fresh-clone case round 9 asked for**, UC-66, and **open three surfaces no case has +read**: delegates used as callbacks and a UTF-8 text file, both executed, and a Standard DLL +called from Excel VBA, built and called. + +### Persona D: a twinBASIC developer on the published site + +| id | goal | hazard | +|----|------|--------| +| UC-55 | *(site, re-run)* The goal of round 7, verbatim. | **H** a second export without `--overwrite` leaves every changed file stale and exits 0; round 9 put the command with `--overwrite` into step 2 | +| UC-62 | *(site, re-run)* The goal of round 9, verbatim. | **H** Export Project empties its folder, `.git` included; round 9 added a warning to the Git section | +| UC-63 | *(site, re-run)* The goal of round 9, verbatim, with the function above. | executed. **H** -2147352565, not 9; the table was retitled for error 9 and *On Error* points to it | +| UC-64 | *(site, re-run)* The goal of round 9, verbatim. | executed. The tutorial now has *Functions that return a string* and links WinDevLib | +| UC-65 | *(site, re-run)* The goal of round 9, verbatim. | executed. The page now says the body is compiled for each type | +| UC-66 | *(site)* My twinBASIC project is in a Git repository. A colleague set it up so that the IDE exports the project into the repository every time it is saved, and the exported files are what we commit; the `.twinproj` file itself is not in the repository. I've just cloned the repository onto a new computer that has twinBASIC installed. Get me from the fresh clone to the project open in the IDE and building, with every save still updating the repository --- the exact steps and commands, in order. | **H** the tB executable cannot pack the IDE's export (999), and a `.twinproj` saved inside the export folder is deleted by the next save | +| UC-67 | *(site)* I want to pass a function as an argument to another procedure, the way a callback works in other languages. Write me a routine that sorts an array of names using a comparison function it is given, and use it to sort the same few names twice --- alphabetically, and by length --- printing the names each time. I want the finished code exactly as I'd have it in my project, and what it prints. | executed. None known; no case has read the Delegates page | +| UC-68 | *(site)* I need to write a text file in UTF-8 that holds names with accents and non-Latin letters --- Zoë, Łódź and 東京 --- and read it back later. Write me a routine that writes those three names to a UTF-8 file, one per line, then reads the file back line by line and prints each line and how many characters it has. I want the finished code exactly as I'd have it in my project, and what it prints. | executed. None known; no case has read File I/O | +| UC-69 | *(site)* Some of my Excel VBA code is slow, and I'd like to move it into a DLL built with twinBASIC and call it from VBA. Get me a working example: a twinBASIC DLL with one function that adds two numbers and one that takes a name and returns a greeting such as "Hello, Ann", and the VBA declarations and a macro that calls both and prints the results. I want both sides exactly as I'd have them, the steps to build the DLL, and what the macro prints. | built and called, with a twinBASIC caller standing in for VBA. None known; no case has read Project Types | diff --git a/test/example-projects/console/Sources/tbxMain.twin b/test/example-projects/console/Sources/tbxMain.twin index b5361bba..b9f2e763 100644 --- a/test/example-projects/console/Sources/tbxMain.twin +++ b/test/example-projects/console/Sources/tbxMain.twin @@ -2,7 +2,12 @@ ' ' Every project gets its Sub Main from here. A sample may declare its own Main ' as well -- two Public Sub Mains in different modules compile -- so a page's -' `Module Startup` sample builds as written, and nothing renames or refuses it. +' `Module Startup` sample compiles as written, and nothing renames or refuses it. +' +' Compiling is all check_examples asks. A BUILD is different: binding the startup +' object fails with "'Main' is ambiguous", so tbrun never runs the probe. A tree +' for tbrun whose own module declares Sub Main leaves this file out. Measured on +' BETA 983, when two of round 10's executed cases failed that way. ' ' It is deliberately empty. The HelloWorld template's Main is a MsgBox, and a ' MsgBox hangs a run-mode probe invisibly -- the IDE waits on a modal nobody can