atularen / ngx-monaco-editor

Monaco Editor component for Angular 2 and Above
https://www.npmjs.com/package/ngx-monaco-editor
MIT License
428 stars 155 forks source link

How to highlights some words and make them clickable? #230

Open datavisorfrankhe opened 3 years ago

datavisorfrankhe commented 3 years ago

Hello, We are now using this component in our angular project, and one requirement is to highly certain words and make them clickable. I checked a lot solution, such as using

monaco.languages.setMonarchTokensProvider()

or something like this:

this.currentEditor.deltaDecorations([], [
    { range: new monaco.Range(3, 1, 5, 1), options: { isWholeLine: true, linesDecorationsClassName: 'myLineDecoration' }},
    { range: new monaco.Range(7, 1, 7, 24), options: { inlineClassName: 'myInlineDecoration' }},
]);

But unfortunately, nothing is working.

Can anyone help me to give a direction on how to meet the requirements?

maks-humeniuk commented 1 year ago

I also need to highlight custom text programmatically, and I thought https://stackoverflow.com/a/73887934/1611569 would work, but unfortunately createDecorationsCollection() doesn't exist anymore.

Moreover, window.monaco.editor.getModel() doesn't work (ERROR TypeError: Cannot read properties of undefined (reading 'toString') is thrown), so, I replaced it with window.monaco.editor.getModels()[0].

Then I found deltaDecorations() method, but it also doesn't work, and throws window.monaco.editor.deltaDecorations is not a function error.

I opened https://github.com/microsoft/monaco-editor/issues/3377, so you could watch it for possible solution. I just don't know how much of a original Monaco Editor is implemented in ngx-monaco-editor.

maks-humeniuk commented 1 year ago

Finally, I solved this.

Basically, deltaDecorations() is a method of editor model, not editor itself.

This should work.

import { editor } from 'monaco-editor';

const searchQuery = 'foo';
const editorModel: editor.ITextModel = (window as any).monaco.editor.getModels()[0];
const matches: editor.FindMatch[] = editorModel.findMatches(searchQuery, false, false, false, null, false);

matches.forEach((match: editor.FindMatch): void => {
  editorModel.deltaDecorations([], [
    {
      range: match.range,
      options: {
        isWholeLine: false,
        inlineClassName: 'highlight'
      }
    }
  ]);
});

Also you should define highlight class somewhere in CSS, and make sure it's visible in your component.

.highlight {
  background: #ffff00;
}

For testing I just added it in src/styles.scss.

And here's how it looks.

image

maks-humeniuk commented 1 year ago

Update: this is even better, since doesn't need (window as any).monaco thing, and attaches 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 searchQuery = 'foo';
  const editorModel: editor.ITextModel = editor.getModel();
  const matches: editor.FindMatch[] = editorModel.findMatches(searchQuery, false, false, false, null, false);

  if (matches.length > 0) {
    // scroll to the first search result
    editor.revealRangeInCenter(matches[0].range);

    // highlight all search results
    matches.forEach((match: editor.FindMatch): void => {
      editorModel.deltaDecorations([], [
        {
          range: match.range,
          options: {
            isWholeLine: false,
            inlineClassName: 'highlight'
          }
        }
      ]);
    });
  }
}