diff --git a/ivette/src/dome/src/renderer/text/buffers.js b/ivette/src/dome/src/renderer/text/buffers.js
index ebab6e603aba83c5d8f79094c61861da1bc8cfec..bf6a2f190b7cef9f13fcb453838b51250dcfaed0 100644
--- a/ivette/src/dome/src/renderer/text/buffers.js
+++ b/ivette/src/dome/src/renderer/text/buffers.js
@@ -82,6 +82,8 @@ export class RichTextBuffer extends Emitter {
     super();
     const { mode , maxlines } = props ;
     this._doc = new CodeMirror.Doc('',mode);
+    this._operations = 0 ;
+    this._editors = [] ;
     this._stacked = [] ;
     this._edited = false ;
     this._focused = false ;
@@ -439,18 +441,87 @@ is blocked.
   // --------------------------------------------------------------------------
 
   /**
-     @summary Linked CodeMirror document.
+     @summary Bind this buffer to a CodeMirror instance.
+     @param {CodeMirror} cm - code mirror instance to link this document in.
      @description
-     Returns a CodeMirror document linked to this buffer (with shared history).
+     Uses CodeMirror linked documents to allow several CodeMirror instances to be linked
+     to the same buffer.
   */
-  linkedDoc() { return this._doc.linkedDoc( { sharedHist: true } ); }
+  link(cm) {
+    const newDoc = this._doc.linkedDoc( { sharedHist: true } );
+    cm.swapDoc( newDoc );
+    this._editors.push(cm);
+    if (this._operations > 0) cm.startOperation();
+  }
 
   /**
      @summary Release a linked CodeMirror document.
+     @param {CodeMirror} cm - the code mirror instance to unlink
+     @param {Document} previous document of the instance.
+     @description
+     Unlinks a CodeMirror document previously linked by `link(cm)`.
+  */
+  unlink(cm) {
+    const oldDoc = cm.getDoc();
+    this._doc.unlinkDoc( oldDoc );
+    this._editors = this._editors.filter((cm0) => cm0 !== cm);
+    if (this._operations > 0) cm.endOperation();
+  }
+
+  /**
+     @summary Iterates over each linked CodeMirror instances
+     @description
+     The operation `fn` is performed on each code mirror
+     instance currently linked to this buffer.
+   */
+  forEach(fn) {
+    this._editors.forEach(fn);
+  };
+
+  // --------------------------------------------------------------------------
+  // --- Stacked Operations
+  // --------------------------------------------------------------------------
+
+  /**
+     @summary Batch heavy operations on editors
+     @param {function} job - a function performing the operations (can return a promise)
+     @return {promise} the promised job
      @description
-     Unlinks a CodeMirror document obtained by `linkedDoc()`.
+     Uses code mirror `cm.startOperation()` and `cm.sendOperation()` on all
+     linked editors to batch the updating operations performed on the
+     buffer. The batched updates can run asynchronously.
   */
-  unlinkDoc(doc) { this._doc.unlinkDoc( doc ); }
+  operation(job) {
+
+    // Protect each start/end call against error
+    const forEachEditor = (fn) => {
+      this._editors.forEach((cm) => {
+        try { fn(cm); } catch(e) { console.err('[Dome.text.buffers]',e); }
+      });
+    };
+
+    // Invariant: this._operations is the number of batched job still running
+    // Invariant: when pending job are running, all linked this._editors are started
+    // Second invariant is also maintained by link and unlink methods
+
+    const startOperation = () => {
+      this._operations ++;
+      if (this._operations == 1)
+        forEachEditor((cm) => cm.startOperation());
+    };
+
+    const endOperation = () => {
+      this._operations --;
+      if (this._operations == 0) {
+        forEachEditor((cm) => cm.endOperation());
+      }
+    };
+
+    return Promise.resolve()
+      .then(startOperation)
+      .then(job)
+      .finally(endOperation);
+  }
 
 }
 
diff --git a/ivette/src/dome/src/renderer/text/editors.js b/ivette/src/dome/src/renderer/text/editors.js
index 6040d63c4eb90b0fade6c5516bd388f2497bd4de..db98d1a8fb8a88164d73a7ddc00bea35230313b5 100644
--- a/ivette/src/dome/src/renderer/text/editors.js
+++ b/ivette/src/dome/src/renderer/text/editors.js
@@ -112,8 +112,8 @@ export class Text extends React.Component {
               className,     /* ignored */
               style,         /* ignored */
               ...config } = this.props ;
-      const value = buffer ? buffer.linkedDoc() : "" ;
-      const cm = this.codeMirror = new CodeMirror(elt, { value });
+      const cm = this.codeMirror = new CodeMirror(elt, { value: "" });
+      if (buffer) buffer.link(cm);
       // Passing all options to constructor does not work (Cf. CodeMirror's BTS)
       for (var opt in config) cm.setOption( opt , config[opt] );
       cm.on('update',this.handleUpdate);
@@ -134,7 +134,7 @@ export class Text extends React.Component {
       Dome.off('dome.update',this.refresh);
       const { buffer } = this.props ;
       if (cm && buffer) {
-        buffer.unlinkDoc(cm.getDoc());
+        buffer.unlink(cm);
         buffer.off('decorated',this.handleUpdate);
         buffer.off('scroll',this.handleScrollTo);
       }
@@ -343,9 +343,9 @@ export class Text extends React.Component {
               selection:newSelect,
               ...newConfig } = newProps ;
       if (oldBuffer !== newBuffer) {
-        const newDoc = newBuffer.linkedDoc();
-        const oldDoc = cm.swapDoc( newDoc );
-        oldBuffer.unlinkDoc( oldDoc );
+        if (oldBuffer) oldBuffer.unlink(cm);
+        if (newBuffer) newBuffer.link(cm);
+        else cm.clear();
       }
       // Incremental update options
       var opt ;
diff --git a/ivette/src/renderer/ASTview.tsx b/ivette/src/renderer/ASTview.tsx
index 78c071618b827b2b70fe1f4b9d7cb3b51ec9be76..2399b2410f5bbafc71e72290935fa1a529daa215 100644
--- a/ivette/src/renderer/ASTview.tsx
+++ b/ivette/src/renderer/ASTview.tsx
@@ -42,12 +42,14 @@ async function loadAST(buffer: any, theFunction?: string, theMarker?: string) {
         endpoint: 'kernel.ast.printFunction',
         params: theFunction,
       });
-      buffer.clear();
-      if (!data)
-        buffer.log('// No code for function ', theFunction);
-      printAST(buffer, data);
-      if (theMarker)
-        buffer.scroll(theMarker, undefined);
+      buffer.operation(() => {
+        buffer.clear();
+        if (!data)
+          buffer.log('// No code for function ', theFunction);
+        printAST(buffer, data);
+        if (theMarker)
+          buffer.scroll(theMarker, undefined);
+      });
       return;
     })();
   }