Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to efficiently have many readonly Monaco Diff Views on one page?

My understanding is that Monaco is optimized for editing, and for having showing one file at a time, with a fixed size editor that has its own scroll bar.

Instead I am trying to build one page with the diffs of multiple files below each other

  • allowing showing/hiding each file, up to ~100 files
  • hiding portions of the file that have not changed (and allowing to show them as context if desired)
  • not have one scrollbar per file, but one for the entire page
  • the files are usually view-only, but editing should be supported for one file at a time

I realize this is quite the departure from what Monaco was built for, but in the end it seems as if the same viewport and virtual rendering tricks would apply, so maybe it is somehow possible?

I tried creating one Monaco instance per file, but that starts getting really sluggish around 30 instances.

One pretty ugly workaround might be to have a single Monaco instance, concat all the files, and then work with ViewZones, custom line number providers and code folding providers to achieve the impression of multiple files. Is that as crazy as it sounds, or might that actually work?

Any other suggestions? Why does IStandaloneDiffEditor have standalone in the name? Does that mean there is another way to create many diff editors that is more efficient?

like image 780
Ole Avatar asked Jul 27 '18 22:07

Ole


1 Answers

Citate from your question: I tried creating one Monaco instance per file, but that starts getting really sluggish around 30 instances.

The solution for your question

As you mentioned the perfomance was sluggish. This is because your server or may be your client has not enough memory. You have to add more memory to the server or may be to the client for more perfomance. Because I do not have enough information I can not say it is the server or the client. But this method is not efficient.

Citate from your question: Why does IStandaloneDiffEditor have standalone in the name? Does that mean there is another way to create many diff editors that is more efficient?

Nothing from it. In Wikipedia I found the answer what standalone means:

Standalone software may refer to:

  • Computer software that can work offline, i.e. does not necessarily require network connection to function.
  • Software that is not a part of some bundled software.
  • A program that run as a separate computer process, not an add-on of an existing process.
  • Standalone program, a program that does not require operating system's services to run.
  • A portable application, which can be run without the need for installation procedure.

This means that standalone has nothing to do with single instance and you could have multiple instances of this editor. But you have to have more memory on your computer(s) to create 100 instances from this editor. And this it not efficient because you have 100 big JavaScript objects more in your memory.

On other services for display the difference between changed files they make it with DOM objects only or with DOM objects + one big instance from big JavaScript object which creates this objects, but not additional 100 big instances from big JavaScript objects.

Accordingly to this princip in this case you could use code from my recommended solution below and in background cleate only one instance from this difference editor. Then you have to put to this instance all your 100 files one after other and copy in each case from one file following DOM objects:

  • <div class="editor original showUnused" ...
  • <div class="editor modified showUnused" ...

This could you do for example with following code:

var diffPartContainars = document.querySelector('#container').querySelectorAll('.showUnused'),
    editorOriginalPartHTML,
    editorModifiedPartHTML;

for(var i = diffPartContainars.length; i--;)
{
    var obj = diffPartContainars[i],
        cln = obj.className;

    if(cln.indexOf('editor original') > -1)
    {
        obj.removeAttribute('style');
        editorOriginalPartHTML = obj.outerHTML;
    }
    if(cln.indexOf('editor modified') > -1)
    {
        obj.removeAttribute('style');
        editorModifiedPartHTML = obj.outerHTML;
    }
}

Then you have to delete from each editorOriginalPartHTML and editorModifiedPartHTML following DOM objects:

  • <div class="invisible scrollbar horizontal" ...
  • <canvas class="decorationsOverviewRuler" ...
  • <div class="visible scrollbar vertical" ...

and all other objects which you can not use. You can do it when you add editorOriginalPartHTML and editorModifiedPartHTML to your DOM. Then you can add around each from it one div object with suitable width, height and style="overflow:auto". And one thing you could do more: for each from this div objects you could add one onclick or onmouseover listener and then replace this div object view with your difference editor instance.

This is only one way in my opinion to be more efficient. Good luck!

The recommended efficient solution

The quickly, comfortable and efficient way to have only one instance of this editor and load a new sources on the click on the file names like below.

var diffEditor = null;
var filesContent =
{
    'SomeJavaScriptFile.js':
    {
        originalContent: 'alert("heLLo world!")',
        modifiedContent: 'alert("hello everyone!")',
        type: 'text/javascript'
    },
    'AnotherJavaScriptFile.js':
    {
        originalContent: 'function open(str)\n{\n\talert(str)\n}',
        modifiedContent: 'function output(value)\n{\n\tconsole.log(value)\n}',
        type: 'text/javascript'
    }
};

document.querySelector('#files').addEventListener('change', function(e)
{
    var fileName = this.options[this.selectedIndex].text,
        file = filesContent[fileName];

    openInDiffEditor(file);
});

function openInDiffEditor(file)
{
    if(!diffEditor)
        diffEditor = monaco.editor.createDiffEditor(document.querySelector('#container'));

    diffEditor.setModel({
        original: monaco.editor.createModel(file.originalContent, file.type),
        modified: monaco.editor.createModel(file.modifiedContent, file.type)
    });
}

//open the first file in select list:
var firstFileName = document.querySelector('#files').options[0].text;
openInDiffEditor(filesContent[firstFileName]);
<p>Please select one file on the left list to see the file differences after changes.</p>
<select id="files" size="3">
    <option selected>SomeJavaScriptFile.js</option>
    <option>AnotherJavaScriptFile.js</option>
</select>

<div id="container" style="height:100%;"></div>

And file contents you have to load over AJAX. But in the case if you do not understand how to load it then ask me and I will write it.

The screenshot of this recommended solution

enter image description here

like image 189
Bharata Avatar answered Nov 17 '22 05:11

Bharata