An original handover-message editor offers a template with a case reference, a next action, and the same case reference repeated at the end. The author should enter the reference once, move to the next action, and leave the snippet at a final writing position. That editing sequence is different from completing one fixed token.
Return a snippet through a scoped completion provider
In an existing Monaco application, handoverModel is the dedicated plain-text model for this message editor:
const handoverUri = handoverModel.uri.toString();
const handoverSnippets =
monaco.languages.registerCompletionItemProvider('plaintext', {
provideCompletionItems(model, position) {
if (model.uri.toString() !== handoverUri) {
return { suggestions: [] };
}
const word = model.getWordUntilPosition(position);
return {
suggestions: [{
label: 'handoff',
kind: monaco.languages.CompletionItemKind.Snippet,
detail: 'Case reference and next action',
insertText: [
'Case: ${1:CASE-001}',
'Next: ${2:Confirm delivery}',
'Reference: $1',
'$0'
].join('\n'),
insertTextRules:
monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
range: new monaco.Range(
position.lineNumber, word.startColumn,
position.lineNumber, word.endColumn
)
}]
};
}
});
monaco is the already-loaded Monaco API. Register this once for the feature's lifetime, after creating the intended model. The range replaces the word before the requested cursor position; it does not replace an arbitrarily selected passage. The CompletionItem contract documents the range and snippet insertion rule.
The provider intentionally offers a plain-text handover template. Do not copy the URI scope unchanged into every editor instance. The scoped completion guide covers ownership and replacement ranges for application-specific suggestions.
Use numbered fields for the actual writing sequence
${1:CASE-001} supplies the first selected field and its default. ${2:Confirm delivery} is the second. $1 mirrors the first field's value elsewhere, and $0 marks the final cursor position. Microsoft documents these conventions in its snippet syntax reference.
After accepting the template, the author's intended result is:
Case: CASE-214
Next: Ask the receiving desk to confirm arrival
Reference: CASE-214
These are fictional writing values. The provider does not find a real case, authorize access, or check whether delivery occurred. If the service requires a particular case-reference format, validate it at the actual submission boundary.
Use separate numbers when two fields should remain independent. Repeating $1 means that updating the first field changes its mirrors while that snippet editing session is active; it does not create a permanent relationship in the saved text.
Account for keys and escaping in the host application
In an active snippet session, use Tab to move forward through stops and Shift+Tab to move backward under the editor's configured key handling. Verify that your host's Tab-focus mode and custom keybindings do not intercept the intended operation. The suggestion picker and active snippet are separate states: confirm that accepting a suggestion actually starts the snippet before diagnosing Tab movement.
For literal snippet-control characters, follow the documented escaping rules. There are two parsing layers when snippet text lives in a JavaScript string: JavaScript reads the string first, then the snippet parser reads its value. Do not add dollar signs and backslashes by trial and error in a production template; inspect the actual inserted text with a small fixture.
Dispose this provider when its owning feature is removed:
handoverSnippets.dispose();
Removing the provider prevents future contributions from this registration. It does not erase a template already inserted into a message.
Save ordinary completed text
In the running application, type handoff, accept the suggestion, replace CASE-001 with CASE-214, and check that the Reference line mirrors it. Advance to the next-action field and replace its default, then advance to the final writing position. Try cancellation and ordinary Tab navigation outside the snippet too.
At Save, read handoverModel.getValue() and use the existing message service. The saved string should contain the completed wording, with no literal ${1:...} syntax. Reopen it as ordinary text: changing its first case reference later should not silently change the second. The temporary editing assistance has ended; the message remains editable content.