Syntax highlighting using Angulars pipes and regular expressions

Besides story-telling code writing syntax highlighting is an important thing in order to let other people read your code easily. Since there are many different programming languages with each its own syntax, there are quite some rules to have in mind in order to colorize the code successfully.

The setup

This article starts building a basic Angular pipe you can use like in the following snippet and fills it slowly with additional content.
<div [innerHTML]="code | syntaxPipe"></div>
Angular pipes a great way to modify or transform text in a similar way for each input. So if we are going to display some code, we'd like to transform our code so that it is colorized everytime in the same way. The folder structure of the pipe and its config looks like follows syntax-rules/ index.ts colors.ts SyntaxConfig.ts [language].ts syntax.pipe.ts
Let's start with the pipe:
import { Pipe, PipeTransform } from '@angular/core'; import { DomSanitizer } from '@angular/platform-browser'; import { syntaxRules } from './syntax-rules'; @Pipe({ name: 'syntaxPipe' }) export class SyntaxPipe implements PipeTransform { constructor(private sanitizer: DomSanitizer) {} transform(code: string) { code = code ? escapeHtml(code) : ''; const lines = code.split('\n'); const syntax = lines[0].split('syntax:')[1]; const setting = syntaxRules.find(o => o.lang === syntax); if (setting) { lines.shift(); code = lines.join('\n'); // Here we are going to search the text using regular expressions return this.sanitizer.bypassSecurityTrustHtml(code); } else { return code; } } } function escapeHtml(unsafe) { return unsafe .replace(/&/g, '&amp;') .replace(/</g, '&lt;') .replace(/>/g, '&gt;') .replace(/"/g, '&quot;') .replace(/'/g, '&#039;'); }
This pipe filters the first line and wants to find a string similar to syntax:typescript or whatever the syntax is. If it has found a phrase like this it matches the word behind the colon with the defined languages in ./syntax-rules. The folders index file exports an Array of our defined language configurations
import { typescript } from './typescript'; import { cpp } from './cpp'; import { html } from './html'; import { SyntaxConfig } from './SyntaxConfig'; export const syntaxRules: SyntaxConfig[] = [ typescript, cpp, html ];
SyntaxConfig is just a TypeScript interface.
export interface SyntaxConfig { lang: string; keywords: { [key: string]: string }; strings: string; comments?: string; tags?: string[]; decorators?: string; }

Defining language rules

The language file follow the SyntaxConfig interface. Within there you can define the language as a string ID (lang). The specify the keywords, afterwards set the colors for strings, comments and decorators.
import { colors } from './colors'; import { SyntaxConfig } from './SyntaxConfig'; export const typescript: SyntaxConfig = { lang: 'typescript', keywords: { from: colors.purple, import: colors.purple, export: colors.purple, return: colors.purple, for: colors.purple, if: colors.purple, else: colors.purple, while: colors.purple, try: colors.purple, catch: colors.purple, function: colors.blue, class: colors.blue, interface: colors.blue, private: colors.blue, public: colors.blue, imports: colors.blue, const: colors.blue, let: colors.blue, var: colors.blue, constructor: colors.blue, implements: colors.blue, extends: colors.blue }, strings: colors.red, comments: colors.grey, decorators: colors.yellow };

Regular expressions

Let's get started
for (const rule of Object.entries(setting.keywords)) { const regexp = new RegExp(`\\b${rule[0]}\\b`, 'g'); code = code.replace(regexp, `\<span style="color: ${rule[1]};"\>$&\<\/span\>`); }

Comments

Any questions? Want to join the discussion?

Sign in or sign up