Take an operations notebook with BEGIN SECTION and END SECTION markers on separate lines. A reviewer wants to collapse a long equipment list while reading the handover instructions. Those markers are application text conventions, so the application must define their exact pairing rules.
Return ranges for a deliberately small format
The following integration fragment assumes Monaco and an existing notebookModel are initialized. It accepts nonnested sections with exact standalone markers. Other lines remain ordinary source.
const notebookUri = notebookModel.uri.toString();
const foldingRegistration =
monaco.languages.registerFoldingRangeProvider('plaintext', {
provideFoldingRanges(model) {
if (model.uri.toString() !== notebookUri) return [];
const ranges = [];
let start = null;
for (let line = 1; line <= model.getLineCount(); line++) {
const text = model.getLineContent(line);
if (text === 'BEGIN SECTION') {
if (start !== null) return [];
start = line;
} else if (text === 'END SECTION') {
if (start === null) return [];
if (line > start) ranges.push({ start, end: line });
start = null;
}
}
return start === null ? ranges : [];
}
});
This strict sample returns no ranges if any marker is unmatched or nested. A production notebook can report that structural problem separately. The provider's empty result itself supplies no visible error message. Use model markers if the application should explain a malformed section.
The language selector is plaintext, but the URI check limits this provider's contribution to the notebook it owns. Without that check, the same marker convention could affect unrelated plain-text editors in the shared Monaco runtime. A dedicated registered language is another architecture choice for a full custom format.
Check the line boundary with a small record
Use this authored fixture in the target model:
Handover notebook
BEGIN SECTION
Recorder: cabinet B
Spare cable: drawer 4
END SECTION
Call the desk before closing.
The expected result from this provider is { start: 2, end: 5 }. The range starts after line 2's last character and ends after line 5's last character. Thus the opening marker remains visible when the section is folded, while the closing marker belongs to the hidden range. These boundaries follow Monaco's range contract; they are not character offsets.
Enable folding in the target editor and use its normal fold control. Inspect the configured foldingStrategy: an indentation-only strategy does not use the custom provider as intended. The editor options document these controls. Existing language providers and editor limits may also affect the combined folding UI.
Save the source and release the provider
Collapsing a section does not delete its lines. Save notebookModel.getValue() through the application's usual storage path, including the equipment list. A visual fold state is a separate preference if the application chooses to remember it. The CodeMirror folding guide covers that same storage boundary using a different editor API.
Dispose the registration when this notebook feature is removed:
foldingRegistration.dispose();
In the integrated application, check the valid fixture, an unmatched marker, a nested section, and another plain-text model. Confirm that only the intended valid notebook receives this provider's ranges. Reopen the saved source and expand the section before handing over the notebook so the hidden equipment details receive the same review as the visible instructions.