Code Highlight with Blazor

Previously I had a post on adding code highlight to Blazor websites. In this post I will improve the previous solution by using the Monaco Editor instead of the PrismJS library.

Syntax Highlight

Syntax highlight is used colorize the source code based on each word's category. For example, keywords can be one color or type names and variables can use another font color. In Blazor previously I have shown how PrismJS can be used to highlight source code. Another approach is to use Monaco Editor. Monaco Editor is a code editor that powers VS Code. It has tremendous amount of features, way more than required for code highlighting. Using it for code highlight might sound controversial, but it is the same component as used by one of the most used code editors, so it looks and feels familiar.

The Monaco Editor is a JavaScript based component, so it can be used through JavaScript interop within Blazor. Fortunately, there is already an existing Blazor interop version of Monaco Editor, BlazorMonaco. This version is a community based Blazor component, wrapping the Monaco Editor.

Using Monaco Editor

Using the Monaco Editor for code highlight is relatively simple. First, add the Monaco Editor component to the html page. Here I wrap it in a <pre> tag. I do this to match the cases where I cannot use the Monaco Editor, but I prefer to display code preserving line breaks and spaces.

<pre><BlazorMonaco.MonacoEditor ConstructionOptions="@GetOptions" /></pre>

Secondly, a BlazorMonaco.Bridge.StandaloneEditorConstructionOptions object is provided to the ConstructionOptions component parameter, which describes the behavior of the editor. In this options, enable the readonly mode, and disable all other editor features.

public BlazorMonaco.Bridge.StandaloneEditorConstructionOptions GetOptions(BlazorMonaco.MonacoEditor editor)
{
  int lineHeight = 19;
  var totalHeight = lineHeight * Code.Lines.Count;
  return new BlazorMonaco.Bridge.StandaloneEditorConstructionOptions()
  {
    AutomaticLayout = true,
    Language = "csharp",
    Value = SourceCode.ToString(),
    Scrollbar = new BlazorMonaco.Bridge.ScrollbarOptions() {
      UseShadows = false,
      Vertical = "Hidden",
      AlwaysConsumeMouseWheel = false,
      VerticalScrollbarSize = 0,
      VerticalSliderSize = 0,
      Horizontal = "Auto"
    },
    ScrollBeyondLastColumn = 0,
    ScrollBeyondLastLine = false,
    GlyphMargin = false,
    Folding = false,
    Contextmenu = false,
    CodeLens = false,
    Minimap = new BlazorMonaco.Bridge.MinimapOptions() { Enabled = false },
    ReadOnly = true,
    LineNumbers = "off",
    LineDecorationsWidth = "0",
    Lightbulb = new BlazorMonaco.Bridge.LightbulbOptions() { Enabled = false },
    RenderIndentGuides = false,
    RenderFinalNewline = false,
    RenderValidationDecorations = "off",
    OverviewRulerBorder = false,
    OverviewRulerLanes = 0,
    Theme = "vs-light",
    FixedOverflowWidgets = true,
    LineHeight = lineHeight,
    Dimension = new BlazorMonaco.Bridge.Dimension { Height = totalHeight },
    ColorDecorators = true,
    OccurrencesHighlight = true,
  };
}

To customize the Monaco Editor for syntax highlighting, I perform the following steps:

  • Set a Language, in this example I set the value csharp

  • Set the Value property with the source code to display

  • Set scrolling options. Here I disable vertical scrolling, as I want the whole code to be visible vertically. Also disable scrolling beyond the last line. We only need horizontal scrolling, which is configured to be 'Auto', so the scroller is only visible when required.

  • Enable Readonly mode.

  • Disable context menu, margins, code lens, minimap, rulers, indent guides, lightbulb. These features are not essential for code highlighting. One might prefer leaving minimap, but for short examples, like this one above, it is not really required.

  • LineNumbers are disabled here, but one might prefer to leave it enabled. It depends on personal preference. In this case I disabled it, so it matches with the non-highlighted code segments.

  • Set a theme supported by the editor.

  • In the above example I match line height with non-highlighted code's lineheight.

  • Set the height of the code editor, so vertical scrolling isn't required. With all margins turned off the height can be calculated using the line height and the number of lines of the source code. Unfortunataly, I did not find an easy way to have this automatically calculated and set by the browser at the time of writing this post.

  • Finally, I enabled OccurrencesHighlight so when a code segment is selected, the same segments are also highlighted by the editor. I find this feature very useful as a reader, to quickly select a method name and find all its usages in the code segment displayed.

Conclusion

In conclusion Monaco editor can do a lot more to what is required for code highlighting. As in the above example, most editor features are disabled. The benefits of using the Monaco Editor: it is highly customizable. When someone decides that line numbers are required, it is a single switch to enable it, or using a different theme. I found existing code highlighting solutions less customizable in this same sense.

Monaco Editor provides a familiar a look-and-feel, with exact match for coloring C# code, familiar way to highlight occurrences, and a copy-paste behavior which works as if we copied code from VS Code. I found the Monaco Editor Blazor component a very powerful control. Hence I use it in this blog as well, with a similar setup as shown above. The above settings may provide a reasonable getting started settings, but I am pretty sure that this blog will also evolve the current settings to improve the readability of the code examples.