1

I am trying to use Monaco Editor for XML language, but XML string is not getting formatted while the editor instance is being rendered, whereas its working fine for JSON.

I also don't find "Format Document" option in Context Menu of Editor(if it is opened with XML language). Does Monaco editor support XML formatting by default?

Peter
  • 2,796
  • 1
  • 17
  • 29
Rama Rao M
  • 2,961
  • 11
  • 44
  • 64
  • Please provide a [minimal, reproducible example](https://stackoverflow.com/help/minimal-reproducible-example). At least provide the code you have tried. FYI, XML **is** supported by default with Monaco. Also, you are asking two different questions: 1) does Monaco support XML files and 2) Does Monaco by default provide an XML formatter. – Peter Jul 16 '19 at 18:39

2 Answers2

7

It appears that Monaco Editor doesn't format xml by default. A colleague of mine - @thewahome, had to write code to format the xml. If you're interested on how to format xml the following snippet will help.

function formatXml(xml: any) {
  const PADDING = ' '.repeat(2);
  const reg = /(>)(<)(\/*)/g;
  let pad = 0;

  xml = xml.replace(reg, '$1\r\n$2$3');

  return xml.split('\r\n').map((node: any, index: number) => {
    let indent = 0;
    if (node.match(/.+<\/\w[^>]*>$/)) {
      indent = 0;
    } else if (node.match(/^<\/\w/) && pad > 0) {
      pad -= 1;
    } else if (node.match(/^<\w[^>]*[^\/]>.*$/)) {
      indent = 1;
    } else {
      indent = 0;
    }

    pad += indent;

    return PADDING.repeat(pad - indent) + node;
  }).join('\r\n');
}
Japheth Obala
  • 81
  • 1
  • 3
1

The support for XML in Monaco is strange. As far as I know, there is no XML formatting enabled. I would suggest formatting XML only once on your backend and sending it already formatted. Client-side formatting can be a potential bottleneck in your app if you edit large texts.

But if you still prefer client-side formatting, it can be done using HTML formatter as suggested in this GitHub comment.

Actions Format Document and Reindent Lines will be enabled with the following configuration. Monaco will even follow XML indention as you type.

You can play around with this in Monaco editor playground. (just copy, paste, run):

// Monaco doesn't support XML formatting. But it's possible to use HTML formatting.
// https://github.com/microsoft/monaco-editor/issues/1509#issuecomment-864441191
monaco.languages.html.registerHTMLLanguageService(
    "xml",
    {
        format: {
            wrapLineLength: 0,
            preserveNewLines: false,
        }
    },
    { documentFormattingEdits: true },
);

const editor = monaco.editor.create(document.getElementById('container'), {
    value: "<group><content><value text=\"Text\"></value><!--\nPress Ctrl + Shift + F\nto reformat this XML\n--></content></group>",
    language: "xml",
    tabSize: 2,
    autoIndent: "full",
    detectIndentation: true,
    formatOnType: true,
    formatOnPaste: true,
});

monaco.languages.setLanguageConfiguration("xml", {
    "indentationRules": {
        "increaseIndentPattern": new RegExp("<(?!\\?|[^>]*\\/>)([-_.A-Za-z0-9]+)(?=\\s|>)\\b[^>]*>(?!.*<\\/\\1>)|<!--(?!.*-->)|\\{[^}\"']*$"),
        "decreaseIndentPattern": new RegExp("^\\s*(<\\/(?!html)[-_.A-Za-z0-9]+\\b[^>]*>|-->|})"),
    },
});

// Format document and reindned lines with Ctrl + Shift + F
editor.addAction({
    id: "formatDocumentAndReindentLines",
    label: "Format Document and Reindent Lines",
    keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyMod.Shift | monaco.KeyCode.KeyF],
    run: async (editor) => {
        await editor.getAction("editor.action.formatDocument").run();
        // Reindent is here for multiline comments
        return editor.getAction("editor.action.reindentlines").run();
    },
});

indentationRules are borrowed from vscode HTML extension configuration and simplified. More information about it here.

BlueManCZ
  • 417
  • 6
  • 12