Reputation: 905
I am highlighting the text in the textarea using the following approach in angular.
https://stackblitz.com/edit/angular-textarea-highlight?file=src%2Fapp%2Fapp.component.ts
How can we achieve the same in monaco editor. Is there a way I can use Monaco editor to do this?
My Approach:
const acceptedList = ['do', 'have'];
// let editor1 = monaco.editor.create( ...
let model = editor1.getModel();
for (let i = 0; i < model.getLineCount(); i++) {
let line = model.getLineContent(i);
// here highlight the line if contains from acceptedList
}
Trying to loop through the lines and highlight if it matches our condition. Is it the right approach? how can we highlight the line in monaco editor?
Upvotes: 6
Views: 7893
Reputation: 13814
createDecorationsCollection
is the way to go but there's an important bit of information in the method's comments:
These decorations will be automatically cleared when the editor's model changes.
Here's how I implemented it:
editor.onDidChangeModelContent(() => {
editor.createDecorationsCollection([
{
range: new monaco.Range(3, 1, 3, 1),
options: {
isWholeLine: true,
glyphMarginClassName: "myClassName",
},
},
]);
});
When you're done with it, don't forget to clean up. This will vary depending on your framework but here's the typical way to do it with React:
useEffect(() => {
const handler = editor.onDidChangeModelContent(() => {
// ...
});
return () => {
editor.dispose();
};
}, [])
Upvotes: 0
Reputation: 540
@hbarnett91's answer really helped me a lot, but unfortunately it didn't work, because createDecorationsCollection()
doesn't exist anymore.
My setup:
So, here's how I solved it (with types).
import { editor } from 'monaco-editor';
const acceptedList = ['do', 'have'];
const editorModel: editor.ITextModel = (window as any).monaco.editor.getModels()[0];
acceptedList.forEach((item: string): void => {
const matches: editor.FindMatch[] = editorModel.findMatches(item, false, false, false, null, false);
matches.forEach((match: editor.FindMatch): void => {
editorModel.deltaDecorations([], [
{
range: match.range,
options: {
isWholeLine: false,
inlineClassName: 'highlight'
}
}
]);
});
});
And CSS of course.
.highlight {
background: #ffff00;
}
Apart from replacing createDecorationsCollection()
with deltaDecorations()
, I also had to use getModels()[0]
instead of getModel()
, since the latter was throwing ERROR TypeError: Cannot read properties of undefined (reading 'toString')
.
And here's how it looks.
Here's how to use getModel()
properly, and avoid (window as any).monaco
by sticking the logic to exact editor instance.
<ngx-monaco-editor
[options]="editorOptions"
[model]="model"
[(ngModel)]="value"
(ngModelChange)="onValueChange()"
(onInit)="onInit($event)"
></ngx-monaco-editor>
import { editor } from 'monaco-editor';
onInit(editor: editor.ICodeEditor): void {
const acceptedList = ['do', 'have'];
const editorModel: editor.ITextModel = editor.getModel();
acceptedList.forEach((item: string): void => {
const matches: editor.FindMatch[] = editorModel.findMatches(item, false, false, false, null, false);
if (matches.length > 0) {
matches.forEach((match: editor.FindMatch): void => {
editorModel.deltaDecorations([], [
{
range: match.range,
options: {
isWholeLine: false,
inlineClassName: 'highlight'
}
}
]);
});
}
});
}
Upvotes: 4
Reputation: 63
If I'm understanding correctly, you'll want to use the findMatches
and createDecorationsCollection
APIs. Instead of looping through each, individual line of the editor, you might do something like this:
acceptedList.forEach(item => {
var matches = editor1.getModel().findMatches(item);
matches.forEach(match => {
editor1.createDecorationsCollection([
{
range: match.range,
options: {
isWholeLine: false,
inlineClassName: "someClassName"
}
},
]);
});
})
Then in your stylesheet you'd have something like:
.someClassName {
background: #FFFF00;
}
Each match has a property range
which is a collection of integers like (startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number)
.
This ought to apply the class someClassName
to every span of text within the range of every match returned from the editor.
I just got done implementing something like this in an bespoke editor and had to do lots of digging through the Monaco Editor docs. If you need clarification on how to remove the decorations after they've been applied, feel free to ask, but it's a whole different can of worms.
EDIT: Rereading your question, it sounds like you may want to highlight the entire line with the matching word? I haven't used this option, but in the decorations collection you might try setting the option isWholeLine
to true? I'm not sure if this would work, but it might be a step in the right direction if that's what you're going for. Otherwise, this solution should work like the example you shared.
Upvotes: 1