MurhafSousli / ngx-highlightjs

Angular syntax highlighting module
https://ngx-highlight.netlify.app/
MIT License
278 stars 35 forks source link

Allow usage outside [highlight] with `class="language-html` #184

Closed muuvmuuv closed 3 years ago

muuvmuuv commented 3 years ago

Feature Description

Hey, I would like to use hightlight.js outside the [highlight] implementation in some cases. We have a code component as a wrapper for plain code (/wo hightlighting) and with highlighting and line-numbers (with ngx-highlightjs).

Unfortunately, I can only have highlighting when used as an import of the component, but not with ng-content. If ngx-hightlightjs exposed highlightjs to the window object, I could use the default implementation of class="language-html.

Sure, I would need to add those languages myself, but I would have more control and could use it elsewhere.

An alternative would be a new directive which allows ng-content inside <code>. So I could do something like this:

<pre><code [highlight]="code" [lineNumbers]="lineNumbers"><ng-content></ng-content></code></pre>

So it would always prefer the contents of "code" but if undefined use the default contents of <code>.

muuvmuuv commented 3 years ago

Made a Stackblitz: https://stackblitz.com/edit/ngx-highlightjs-xfgbms?file=src%2Fapp%2Fhome%2Fhome.component.html

MurhafSousli commented 3 years ago

This is not possible with the directive, because if the code is set as the content of the element, it will be replaced with the highlighted code and when the line number is activated, it will also cause the content to change. therefore, the directive will no longer be able to react on content change

muuvmuuv commented 3 years ago

But what if I don't need content change functionallity like in a User Interface guideline where I just want to display the code of a component we use? Would like to get highlightjs for this content too without putting everything in TS variables.

MurhafSousli commented 3 years ago

You can use the highlighting service https://github.com/MurhafSousli/ngx-highlightjs/blob/master/projects/ngx-highlightjs/src/lib/highlight.service.ts

muuvmuuv commented 3 years ago

This works, thanks! Anyway, a simpler API would be cool:

  @ViewChild('plain', { static: false })
  readonly plain: ElementRef<HTMLElement>

  constructor(
    private cdr: ChangeDetectorRef,
    @Inject(HIGHLIGHT_OPTIONS) private hljsOptions: HighlightOptions,
    private hljsService: HighlightJS,
    private renderer: Renderer2
  ) {}

  ngAfterViewInit(): void {
    if (!this.code) {
      this.highlightNgContent()
    }
  }

  private highlightNgContent(): void {
    const text = this.plain.nativeElement.textContent || ''
    const languages = Object.keys(this.hljsOptions.languages || {})

    this.hljsService.highlightAuto(text, languages).subscribe((response) => {
      this.setHighlightedHtml(response)

      if (this.lineNumbers) {
        this.addLineNumbers()
      }
    })
  }

  private setHighlightedHtml(response: HighlightResult): void {
    animationFrameScheduler.schedule(() => {
      this.renderer.addClass(this.plain.nativeElement, 'language-' + response.language)
      this.renderer.setAttribute(
        this.plain.nativeElement,
        'data-relevance',
        String(response.relevance)
      )
      this.renderer.setProperty(this.plain.nativeElement, 'innerHTML', response.value)
    })
  }

  private addLineNumbers(): void {
    animationFrameScheduler.schedule(() => {
      firstValueFrom(this.hljsService.lineNumbersBlock(this.plain.nativeElement))
      this.renderer.addClass(this.plain.nativeElement, 'hljs-line-numbers')
    })
  }

EDIT: updated with line-numbers