|
AppMessages
|
The words Issie says to the user, where they are long enough to be worth reading as prose.
WHAT IS HERE. The Info window's four written tabs, the waveform simulator's five help panels,
the memory help, the twenty field explanations in the Properties pane, and the confirmation
popups whose body is several sentences rather than one - thirty-five messages, about 25,000
characters. They were spread through the view functions that show them, written as React
element trees with the words threaded between `str` and `bSpan` and `li` - fine to render and
very hard to read, which is a problem for text whose only job is to read well. Written as
markdown here, the whole of Issie's long-form help can be read end to end and reviewed as
writing.
WHAT IS NOT HERE, and should not be moved in.
- Short labels, button captions and headings. A caption is easier to judge beside the button it
sits on than in a list of thirty other strings.
- The Catalogue's component tooltips. They are short, they are one per component, and each sits
beside the component type it describes, which is what makes them easy to keep true.
- One-line confirmation bodies - "The current sheet has unsaved changes." A sentence that short
belongs with the buttons it is explaining.
- `failwithf` and `Log` messages. They are addressed to whoever is maintaining Issie, not to
the user, and belong at the point that fails.
- Anything assembled from a design: the components in a combinational loop, the ports of a
mismatched instance. The sentence around such a list can live here; the list cannot.
- The Keyboard Shortcuts tab, which is generated from the shortcut table so that it cannot
drift from the keys that actually fire.
THE RISK THIS RUNS. Text kept away from the code that shows it goes stale - Issie has had that
exact bug, a help menu item whose name had drifted from the panel it opened, so that choosing
it said "Feature not explained". What limits it is that only the words moved: which message to
show, and when, stays at the point that shows it, so a message cannot be reached by a route
this module knows nothing about. Every entry also carries a comment saying what it is and where
it appears, which is what makes an entry that no longer matches its use site noticeable while
reading.
That is weaker than a check. `MarkdownTests` reads every message here and fails on one that
will not render, but nothing yet fails on an entry that has quietly stopped being used, or on a
comment that has stopped being true. Both would be worth adding - the first is a source scan
for each entry's name, of the kind `SourceHygiene` already does for printf.
Markdown is rendered by `Markdown.render` - see that module for the subset supported. Tooltips
are the exception: they are drawn by CSS from a `data-tooltip` attribute, which can hold text
and nothing else, so tooltip entries are plain sentences with no markup.
|
|
BlockHelpers
|
|
|
Bridge (Module)
|
The main-process half of Issie's renderer bridge.
Every operation the renderer is allowed to ask the operating system for is registered here, as a
named channel with a fixed shape. The renderer half is src/Preload/preload.js, and the two are
meant to be read together: nothing should be exposed there that is not answered here, and nothing
answered here should take a request general enough to stand in for `require`.
Handlers use event.returnValue, the synchronous form, because the renderer's file wrappers return
values from inside Elmish update and cannot await. get-user-data in Main.fs already worked this
way before any of this existed.
|
|
Bridge (Module)
|
The renderer's half of the bridge to the main process.
Partner to src/Preload/preload.js and src/Main/Bridge.fs. This module is the only place the
renderer reads anything the operating system knows, and it exists so that the rest of the code
asks for `Bridge.staticDir` rather than reaching for `__static`, `process.platform` or
`@electron/remote` - none of which survive contextIsolation.
Everything here is a value rather than a function. These are constants for the life of the
process, so the preload fetches them once and this module just reads the result: no round trip,
no ordering to get wrong, and five operations that never have to exist on the bridge at all.
|
|
BuildUartHelpers
|
The debug UART, as the draw block sees it.
These were direct calls into IS-uart.js, imported into the renderer bundle. That file requires
the native `usb` module, which a renderer without node integration cannot load at all, so it now
lives in the main process and these are messages to it - see src/Main/Bridge.fs.
The signatures are unchanged, which is the whole reason this port was cheap: the operations were
already asynchronous, already took nothing but a viewer count, and already shared no objects with
their caller. Nothing in SheetUpdate.fs had to change.
|
|
BuildView
|
|
|
BusWidthInferer
|
|
|
BusWire
|
|
|
BusWireRoute
|
|
|
BusWireRoutingHelpers
|
|
|
BusWireSeparate
|
|
|
BusWireUpdate
|
|
|
BusWireUpdateHelpers
|
|
|
CanvasExtractor
|
|
|
CanvasStateAnalyser
|
|
|
CatalogueView
|
|
|
CodeEditorHelpers
|
|
|
CommonTypes
|
|
|
ComponentLibraries
|
|
|
ComponentSlots
|
|
|
ConstraintReduceView
|
|
|
ContextMenuBuilder
|
Building and popping up the right-click menus, for the main process only.
The menu DATA - which menus exist and what is on them - stays in ContextMenus.fs, which both
processes compile: the renderer needs it to ask for a menu by name and to recognise the item
that comes back. Only the part that builds an Electron menu is here, because it needs the
electron module, and requiring that is what a renderer without node integration cannot do.
|
|
CustomCompPorts
|
|
|
DevHarness
|
Driving Issie from outside it, for development and for automated checking.
Published as `window.issieDev` in a debug build, and reached from a terminal through
`scripts/drive.js`. It exists because the alternative - synthesising DOM events and reading
rendered text back - is both slow and wrong often enough to mislead: a click has to find the
right element, a wait has to guess how long a render takes, and text scraped from the DOM can
be a frame out of date while looking authoritative.
Four things, which is what that experience says are needed:
onNextRender when the update has been applied AND the view has run, so a caller can wait
for the app rather than for a stopwatch
send a named command, dispatched as the message the UI would have sent
state what the app currently is, as data rather than as rendered text
simRefs what is holding a simulation, which is the question the DOM cannot answer
`send` takes a NAME from a fixed table rather than a serialised Msg. A Msg is an F# union
carrying models, canvases and functions; nothing useful survives a round trip through JSON, and
a general dispatch-anything surface in a debug build is a hazard for the sake of messages
nobody wanted to send. Add a row to `commands` when a new one is needed - that is the point.
|
|
DiagramMainView
|
|
|
DiagramStyle
|
|
|
DrawHelpers
|
|
|
DrawModelType
|
|
|
Editor
|
|
|
ElectronModule
|
The `electron` module itself, for the main process only.
These bindings used to live in ElectronAPI.fs, which both projects compile - the renderer needs
its several hundred type definitions. But a binding is an import, and `import * from "electron"`
runs when the file loads, so the renderer was requiring electron simply by using its types. A
renderer without node integration cannot do that, and it is not a lazy failure: it throws as the
bundle loads, before any of Issie runs.
So the types stayed shared and the module came here, where requiring it is exactly right. If the
renderer ever appears to need one of these values again, that is the signal that something wants
a bridge operation instead.
|
|
ErrorCheck
|
|
|
ErrorCheckHelpers
|
|
|
ErrorCheckProcedural
|
|
|
EvalAlgebraic
|
|
|
EvalCompiled
|
Per-component reducers, chosen and bound once when the simulation is built.
fastReduce works out afresh, for every component of every clock step, things that were
settled when the simulation was built: which component type this is, whether it is on the
uint32 or the bigint path, which of its inputs are bigints, what its bus masks are, and
which arrays its ports live in. A reducer built here has all of that already: it closes
over the step arrays themselves and over the constants, and its body is only the work that
actually differs from step to step.
reducerFor returns None for a component type it does not handle, and the caller falls back
to fastReduce. So this file can be filled in a type at a time, and the fallback is always
the definition of what a reducer must do.
Two rules the closures depend on, both guaranteed by the caller (see installReducers):
- The step arrays captured here must be the ones the simulation actually uses. Reducers
are therefore installed after every re-linking pass, including the one
addWavesToFastSimulation does for custom components, and only for components that are
reduced. Capturing an array that is later re-pointed would silently simulate the wrong
signal.
- Values in the step arrays are already within their bus width (the masking invariant), so
a reducer masks its result exactly when its own operation can overflow, and reads
without masking. On the uint32 path a width of exactly 32 needs care: 1u <<< 32 is 1u.
|
|
EvalKernel
|
|
|
EvalReference
|
|
|
EvilHoverCache
|
|
|
FastBuild
|
|
|
FastCreate
|
|
|
FastExtract
|
|
|
FastOrder
|
|
|
FastRun
|
|
|
FastValidate
|
|
|
FilesIO
|
|
|
FileUpdate
|
|
|
GraphBuilder
|
|
|
GraphMerger
|
|
|
HashMap
|
|
|
Helpers
|
|
|
JSHelpers
|
|
|
KeyBindings
|
Turns a key press into an action, using the table in KeyTypes and the context the user is in.
This is the only place in Issie that should listen for keys. It replaces five mechanisms that
grew independently and never agreed with each other: the Electron menu accelerators, the
document.onkeydown reassigned on every render in SheetDisplay, the addEventListener
subscription in Renderer, the one React handler in SelectedComponentView, and the arrow-key
intercept in Update.
KeyTypes says what the shortcuts are; this module says what they do and delivers them.
|
|
KeyTypes
|
The one place every keyboard shortcut in Issie is described.
This module is deliberately pure: no Fable.React, no ElectronAPI, no ModelType, no Browser.Dom.
It says *what* the shortcuts are, not what they do and not how they are delivered. The binding
from an identity to an action lives in UI/KeyBindings.fs, which is the only module that needs
the model. Keeping the two apart is what lets the help table, the renderer menus and (later)
the context menus all be generated from one list instead of drifting apart, which is what
happened to the hand-written table in UIPopups.fs.
Platform is always a parameter, never read here, so this module has no dependency on how the
host discovers it.
|
|
Main
|
|
|
Markdown
|
A small markdown renderer for Issie's in-app help.
Issie's longer help text - the Info window, the waveform simulator's help panels, the bodies of
confirmation popups - used to be written as React element trees: `str` and `bSpan` and `li`
interleaved with the words, several hundred lines of it. Read as prose it was unreadable, which
is a problem for text whose whole job is to read well. It is now written as markdown in
AppMessages and rendered here.
The subset is not a guess: it is what that text was already doing, counted. Headings, bold,
italic, inline code, links, bullet and numbered lists, and tables - which is core GitHub
markdown with nothing left over. There are no images and no nested lists, so there is no
support for them; a line that tries will render as its own text rather than silently vanish.
TWO THINGS THIS DELIBERATELY DOES NOT DO.
It does not produce an HTML string. Rendering markdown by handing HTML to
`dangerouslySetInnerHTML` is the usual shortcut, and in a renderer with node integration, for
messages that interpolate sheet names the user chose, it is a bad trade. `render` builds React
elements, so there is no path from message text to executed markup.
It does not open links itself. A markdown link becomes an anchor with an `onClick` supplied by
the caller, because in Electron an ordinary `href` navigates the application window away from
Issie. Keeping the handler out here also keeps this module free of Electron and of anything
above it in compile order.
`parse` is pure and returns data, so the tests can read every message in AppMessages under
plain .NET without a browser. `render` is the only part that needs one.
|
|
MemoryData
|
|
|
MemoryEditorView
|
|
|
MenuHelpers
|
|
|
MiscMenuView
|
|
|
ModelHelpers
|
|
|
ModelType
|
|
|
NearleyBindings
|
|
|
Notifications
|
|
|
NumberHelpers
|
|
|
Optics
|
|
|
ParameterAnalysis
|
|
|
ParameterTypes
|
|
|
ParameterView
|
|
|
PathHelpers
|
Pure path arithmetic, shared by the Fable and .NET builds.
These were node's `path` under Fable and System.IO.Path under .NET - two implementations that
already disagreed (node's join normalises, Path.Join does not), and one of which disappears with
contextIsolation since `path` is a node module the renderer will not be able to require.
Joining strings is not a privileged operation and has no business crossing a process boundary, so
rather than becoming ten more bridge calls at 189 call sites this is one implementation for both
targets. It follows node's semantics, which is what the app was built against, and
Tests/Issie.Tests/PathHelperTests.fs holds it to them.
Separator handling is platform-dependent in the way node's is: on Windows both `/` and `\` divide
segments and output uses `\`; on POSIX only `/` divides and a backslash is an ordinary filename
character. The leading root - a drive, a UNC share, or `/` - is found but never rewritten by
dirname and basename, because node does not rewrite it either, and UNC paths matter here: Issie
is run from networked locations on cluster machines.
|
|
Playground
|
|
|
PopupHelpers
|
|
|
PropertiesHelp
|
What the fields in the Properties pane mean, and the label element that says so.
The Catalogue explains every component before you place it, in a tooltip written as a sentence.
The pane where you then configure the component you placed explained nothing at all: it showed
"Width (bits)", "Optional Ports", "LSB" and left the user to work out the rest. This closes that
gap without adding prose to the pane itself, which is short on room.
The text is keyed by the label the field displays, so a field acquires its explanation simply by
being labelled - no call site has to pass one, and the same label used in two places is
explained the same way in both. A label with no entry here renders exactly as it did before.
Keep each entry to what the field does and, where there is one, the thing people get wrong.
|
|
RamStore
|
How a read/write memory's contents are held while a simulation runs.
See [docs/dev/ramRepresentation.md] for why this exists and what it is worth. In short: the
contents used to be a `Map` and a fresh snapshot of it was stored for every
clock step, so a write cost an AVL path copy plus two boxed `BigInt`s, a read cost a descent
with boxed comparisons, and a long waveform simulation retained every version it had ever
made. Here
- a read during simulation is an array index,
- a write appends two numbers to shared flat arrays,
- the value an address held at any past step is a binary search of that address's own writes,
whose cost does not depend on how far the cursor moved, and
- nothing at all is allocated per step.
**Storage is CSR.** Every address that is ever written is given a *slot* number, and a slot's
writes are one contiguous run of the shared `IStep`/`IVal` arrays, delimited by `Start`. The
alternative - a growable list per address - costs a record and two list objects per address,
which is around 230 bytes for what may be a single 8-byte write. That is affordable for one
big RAM and is not for a hundred small ones, which between them have just as many addresses.
Recent writes go to a small tail and are folded in by `compact`.
**Where this file sits.** F# compile order is the dependency layering, and
`SimGraphTypes.SimulationComponentState` names this type, so it has to be compiled before
`SimGraphTypes.fs` and cannot live in the `FastSim` block with the rest of the simulator. It
depends on nothing but `CommonTypes`, which is what makes that possible.
**Mutability.** This is the same layer as the step arrays, which are already mutable, and
[docs/mutableState.md] allows it for a measured performance reason - the whole point here is
to stop allocating. Nothing mutable escapes: `Memory1` remains the immutable type everywhere
outside the running simulation, and `toMemory` builds one on demand.
|
|
Renderer
|
|
|
RotateScale
|
|
|
SelectedComponentView
|
|
|
Sheet
|
|
|
SheetCreator
|
|
|
SheetDescription
|
|
|
SheetDisplay
|
|
|
SheetLayout
|
|
|
SheetSnap
|
|
|
SheetUpdate
|
|
|
SheetUpdateHelpers
|
|
|
SimGraphTypes
|
|
|
SimpleJsonDotNet
|
Deserialises the JSON encoding used by Fable.SimpleJson, which is what the app writes into
every .dgm file. Without this, nothing running under .NET can open a sheet, because
Thoth.Json.Net - what `Helpers.jsonStringToState` otherwise uses there - cannot read what the
app wrote. The two encodings disagree about unions (Thoth writes an array; SimpleJson writes a
bare string for a nullary case and a single-property object otherwise), about options (Thoth
wraps the value; SimpleJson writes the value itself, or null) and about maps with structural
keys (SimpleJson writes an array of pairs).
Rather than enumerate the `SavedInfo` schema - which would mean every `ComponentType` case, and
a new one every time a component is added - this walks the target type by reflection, so it
covers whatever the types say.
Fable never sees any of this: under Fable the app calls SimpleJson itself, and Newtonsoft is not
available. Under .NET it reaches this project transitively through Thoth.Json.Net.
|
|
SimTypes
|
|
|
SimulationGraphAnalyser
|
|
|
SimulationView
|
|
|
Simulator
|
|
|
StepSimulationTop
|
|
|
Symbol
|
|
|
SymbolHelpers
|
|
|
SymbolPortHelpers
|
|
|
SymbolReplaceHelpers
|
|
|
SymbolResizeHelpers
|
|
|
SymbolUpdate
|
|
|
SymbolView
|
|
|
SynchronousUtils
|
|
|
TestParser
|
|
|
TestWorker
|
|
|
TimeHelpers
|
|
|
TopMenuView
|
|
|
TruthTableCreate
|
|
|
TruthTableReduce
|
|
|
TruthTableTypes
|
|
|
TruthTableUpdate
|
|
|
TruthTableView
|
|
|
UIPopups
|
|
|
Update
|
|
|
UpdateHelpers
|
|
|
Verilog
|
|
|
VerilogAST
|
|
|
VerilogTypes
|
|
|
Version
|
|
|
WaveSimHelpers
|
Miscellaneous helpers used tby waveform simulator
|
|
WaveSimHierarchy
|
The collapsed design hierarchy the wave selector shows.
A design whose sheets instantiate one another multiplies out: seven sheets of a few instances
each is tens of thousands of instances, and a selector with one entry per instance grows with
that rather than with the design somebody wrote. So the hierarchy shown has one node per SHEET
at each point in the tree, and the user says which instance of it they mean.
This module works out, once per render, everything both panes of the selector need: which nodes
there are, which instances each one could show, and which one it is showing. Both panes read the
same answer, so they cannot disagree about it.
|
|
WaveSimNavigation
|
All the code that determines which clock cycles are viewed in the simulator.
basic functions are zooming and panning (moving + or - in time). Additional function
is a sample-based zoom for viewing very long waveforms..
Also implement cursor control.
|
|
WaveSimRams
|
RAM display in waveform simulator
|
|
WaveSimSelect
|
|
|
WaveSimSelectHelpers
|
|
|
WaveSimStyle
|
Functions to style the DOM elements used in waveform simulator
|
|
WaveSimSVGs
|
Functions to make SVGs of waveforms from FastSimulation data
|
|
WaveSimTop
|
Top-level functions for Waveform Simulator
|
|
WaveSimTypes
|
|
|
WaveSimWaveforms
|
showWaveforms and subfunctions to display in DOM the waveforms
Waveform SVGs themselves are generated from simulation in WaveSimWaves module
|
|
WorkerInterface
|
|