diff --git a/swh/web/assets/src/bundles/revision/diff-utils.js b/swh/web/assets/src/bundles/revision/diff-utils.js
--- a/swh/web/assets/src/bundles/revision/diff-utils.js
+++ b/swh/web/assets/src/bundles/revision/diff-utils.js
@@ -302,6 +302,12 @@
   $(`#${diffId}-split-diff`).css('display', 'block');
 }
 
+function setLineNumbers(lnElt, lineNumbers) {
+  $(lnElt).attr('data-line-number', lineNumbers);
+  $(lnElt).children().attr('data-line-number', lineNumbers);
+  $(lnElt).siblings().attr('data-line-number', lineNumbers);
+}
+
 // to compute diff and process it for display
 export function computeDiff(diffUrl, diffId) {
 
@@ -356,195 +362,180 @@
         // code highlighting for unified diff
         $(`#${diffId}`).each((i, block) => {
           hljs.highlightBlock(block);
-          hljs.lineNumbersBlock(block);
+          hljs.lineNumbersBlockSync(block);
         });
 
-        // hljs.lineNumbersBlock is asynchronous so we have to postpone our
-        // next treatments by adding it at the end of the current js events queue
-        setTimeout(() => {
-
-          // process unified diff lines in order to generate side-by-side diffs text
-          // but also compute line numbers for unified and side-by-side diffs
-          let baseFromLine = '';
-          let baseToLine = '';
-          let fromToLines = [];
-          let fromLines = [];
-          let toLines = [];
-          let maxNumberChars = 0;
-          let diffFromStr = '';
-          let diffToStr = '';
-          let linesOffset = 0;
-
-          $(`#${diffId} .hljs-ln-numbers`).each((i, lnElt) => {
-            let lnText = lnElt.nextSibling.innerText;
-            let linesInfo = parseDiffHunkRangeIfAny(lnText);
-            let fromLine = '';
-            let toLine = '';
-            // parsed lines info from the diff output
-            if (linesInfo) {
-              baseFromLine = linesInfo[0];
-              baseToLine = linesInfo[1];
-              linesOffset = 0;
-              diffFromStr += (lnText + '\n');
-              diffToStr += (lnText + '\n');
-              fromLines.push('');
-              toLines.push('');
-            // line removed in the from file
-            } else if (lnText.length > 0 && lnText[0] === '-') {
-              baseFromLine = baseFromLine + 1;
-              fromLine = baseFromLine.toString();
-              fromLines.push(fromLine);
-              ++nbDeletions;
-              diffFromStr += (lnText + '\n');
-              ++linesOffset;
-            // line added in the to file
-            } else if (lnText.length > 0 && lnText[0] === '+') {
-              baseToLine = baseToLine + 1;
-              toLine = baseToLine.toString();
-              toLines.push(toLine);
-              ++nbAdditions;
-              diffToStr += (lnText + '\n');
-              --linesOffset;
-            // line present in both files
-            } else {
-              baseFromLine = baseFromLine + 1;
-              baseToLine = baseToLine + 1;
-              fromLine = baseFromLine.toString();
-              toLine = baseToLine.toString();
-              for (let j = 0; j < Math.abs(linesOffset); ++j) {
-                if (linesOffset > 0) {
-                  diffToStr += '\n';
-                  toLines.push('');
-                } else {
-                  diffFromStr += '\n';
-                  fromLines.push('');
-                }
+        // process unified diff lines in order to generate side-by-side diffs text
+        // but also compute line numbers for unified and side-by-side diffs
+        let baseFromLine = '';
+        let baseToLine = '';
+        let fromToLines = [];
+        let fromLines = [];
+        let toLines = [];
+        let maxNumberChars = 0;
+        let diffFromStr = '';
+        let diffToStr = '';
+        let linesOffset = 0;
+
+        $(`#${diffId} .hljs-ln-numbers`).each((i, lnElt) => {
+          let lnText = lnElt.nextSibling.innerText;
+          let linesInfo = parseDiffHunkRangeIfAny(lnText);
+          let fromLine = '';
+          let toLine = '';
+          // parsed lines info from the diff output
+          if (linesInfo) {
+            baseFromLine = linesInfo[0];
+            baseToLine = linesInfo[1];
+            linesOffset = 0;
+            diffFromStr += (lnText + '\n');
+            diffToStr += (lnText + '\n');
+            fromLines.push('');
+            toLines.push('');
+          // line removed in the from file
+          } else if (lnText.length > 0 && lnText[0] === '-') {
+            baseFromLine = baseFromLine + 1;
+            fromLine = baseFromLine.toString();
+            fromLines.push(fromLine);
+            ++nbDeletions;
+            diffFromStr += (lnText + '\n');
+            ++linesOffset;
+          // line added in the to file
+          } else if (lnText.length > 0 && lnText[0] === '+') {
+            baseToLine = baseToLine + 1;
+            toLine = baseToLine.toString();
+            toLines.push(toLine);
+            ++nbAdditions;
+            diffToStr += (lnText + '\n');
+            --linesOffset;
+          // line present in both files
+          } else {
+            baseFromLine = baseFromLine + 1;
+            baseToLine = baseToLine + 1;
+            fromLine = baseFromLine.toString();
+            toLine = baseToLine.toString();
+            for (let j = 0; j < Math.abs(linesOffset); ++j) {
+              if (linesOffset > 0) {
+                diffToStr += '\n';
+                toLines.push('');
+              } else {
+                diffFromStr += '\n';
+                fromLines.push('');
               }
-              linesOffset = 0;
-              diffFromStr += (lnText + '\n');
-              diffToStr += (lnText + '\n');
-              toLines.push(toLine);
-              fromLines.push(fromLine);
-            }
-            if (!baseFromLine) {
-              fromLine = '';
             }
-            if (!baseToLine) {
-              toLine = '';
-            }
-            fromToLines[i] = [fromLine, toLine];
-            maxNumberChars = Math.max(maxNumberChars, fromLine.length);
-            maxNumberChars = Math.max(maxNumberChars, toLine.length);
-          });
-
-          diffMaxNumberChars[diffId] = maxNumberChars;
-
-          // set side-by-side diffs text
-          $(`#${diffId}-from`).text(diffFromStr);
-          $(`#${diffId}-to`).text(diffToStr);
-
-          // code highlighting for side-by-side diffs
-          $(`#${diffId}-from, #${diffId}-to`).each((i, block) => {
-            hljs.highlightBlock(block);
-            hljs.lineNumbersBlock(block);
-          });
-
-          // hljs.lineNumbersBlock is asynchronous so we have to postpone our
-          // next treatments by adding it at the end of the current js events queue
-          setTimeout(() => {
-            // diff highlighting for added/removed lines on top of code highlighting
-            $(`.${diffId} .hljs-ln-numbers`).each((i, lnElt) => {
-              let lnText = lnElt.nextSibling.innerText;
-              if (lnText.startsWith('@@')) {
-                $(lnElt).parent().addClass('swh-diff-lines-info');
-                let linesInfoText = $(lnElt).parent().find('.hljs-ln-code .hljs-ln-line').text();
-                $(lnElt).parent().find('.hljs-ln-code .hljs-ln-line').children().remove();
-                $(lnElt).parent().find('.hljs-ln-code .hljs-ln-line').text('');
-                $(lnElt).parent().find('.hljs-ln-code .hljs-ln-line').append(`<span class="hljs-meta">${linesInfoText}</span>`);
-              } else if (lnText.length > 0 && lnText[0] === '-') {
-                $(lnElt).parent().addClass('swh-diff-removed-line');
-              } else if (lnText.length > 0 && lnText[0] === '+') {
-                $(lnElt).parent().addClass('swh-diff-added-line');
-              }
-            });
+            linesOffset = 0;
+            diffFromStr += (lnText + '\n');
+            diffToStr += (lnText + '\n');
+            toLines.push(toLine);
+            fromLines.push(fromLine);
+          }
+          if (!baseFromLine) {
+            fromLine = '';
+          }
+          if (!baseToLine) {
+            toLine = '';
+          }
+          fromToLines[i] = [fromLine, toLine];
+          maxNumberChars = Math.max(maxNumberChars, fromLine.length);
+          maxNumberChars = Math.max(maxNumberChars, toLine.length);
+        });
 
-            function setLineNumbers(lnElt, lineNumbers) {
-              $(lnElt).attr('data-line-number', lineNumbers);
-              $(lnElt).children().attr('data-line-number', lineNumbers);
-              $(lnElt).siblings().attr('data-line-number', lineNumbers);
-            }
+        diffMaxNumberChars[diffId] = maxNumberChars;
 
-            // set line numbers for unified diff
-            $(`#${diffId} .hljs-ln-numbers`).each((i, lnElt) => {
-              const lineNumbers = formatDiffLineNumbers(
-                fromToLines[i][0], fromToLines[i][1], maxNumberChars);
-              setLineNumbers(lnElt, lineNumbers);
-            });
+        // set side-by-side diffs text
+        $(`#${diffId}-from`).text(diffFromStr);
+        $(`#${diffId}-to`).text(diffToStr);
 
-            // set line numbers for the from side-by-side diff
-            $(`#${diffId}-from .hljs-ln-numbers`).each((i, lnElt) => {
-              setLineNumbers(lnElt, fromLines[i]);
-            });
+        // code highlighting for side-by-side diffs
+        $(`#${diffId}-from, #${diffId}-to`).each((i, block) => {
+          hljs.highlightBlock(block);
+          hljs.lineNumbersBlockSync(block);
+        });
 
-            // set line numbers for the to side-by-side diff
-            $(`#${diffId}-to .hljs-ln-numbers`).each((i, lnElt) => {
-              setLineNumbers(lnElt, toLines[i]);
-            });
+        // diff highlighting for added/removed lines on top of code highlighting
+        $(`.${diffId} .hljs-ln-numbers`).each((i, lnElt) => {
+          let lnText = lnElt.nextSibling.innerText;
+          if (lnText.startsWith('@@')) {
+            $(lnElt).parent().addClass('swh-diff-lines-info');
+            let linesInfoText = $(lnElt).parent().find('.hljs-ln-code .hljs-ln-line').text();
+            $(lnElt).parent().find('.hljs-ln-code .hljs-ln-line').children().remove();
+            $(lnElt).parent().find('.hljs-ln-code .hljs-ln-line').text('');
+            $(lnElt).parent().find('.hljs-ln-code .hljs-ln-line').append(`<span class="hljs-meta">${linesInfoText}</span>`);
+          } else if (lnText.length > 0 && lnText[0] === '-') {
+            $(lnElt).parent().addClass('swh-diff-removed-line');
+          } else if (lnText.length > 0 && lnText[0] === '+') {
+            $(lnElt).parent().addClass('swh-diff-added-line');
+          }
+        });
 
-            // last processing:
-            //  - remove the '+' and '-' at the beginning of the diff lines
-            //    from code highlighting
-            //  - add the "no new line at end of file marker" if needed
-            $(`.${diffId} .hljs-ln-code`).each((i, lnElt) => {
-              if (lnElt.firstChild) {
-                if (lnElt.firstChild.nodeName !== '#text') {
-                  let lineText = lnElt.firstChild.innerHTML;
-                  if (lineText[0] === '-' || lineText[0] === '+') {
-                    lnElt.firstChild.innerHTML = lineText.substr(1);
-                    let newTextNode = document.createTextNode(lineText[0]);
-                    $(lnElt).prepend(newTextNode);
-                  }
-                }
-                $(lnElt).contents().filter((i, elt) => {
-                  return elt.nodeType === 3; // Node.TEXT_NODE
-                }).each((i, textNode) => {
-                  let swhNoNewLineMarker = '[swh-no-nl-marker]';
-                  if (textNode.textContent.indexOf(swhNoNewLineMarker) !== -1) {
-                    textNode.textContent = textNode.textContent.replace(swhNoNewLineMarker, '');
-                    $(lnElt).append($(noNewLineMarker));
-                  }
-                });
-              }
-            });
+        // set line numbers for unified diff
+        $(`#${diffId} .hljs-ln-numbers`).each((i, lnElt) => {
+          const lineNumbers = formatDiffLineNumbers(
+            fromToLines[i][0], fromToLines[i][1], maxNumberChars);
+          setLineNumbers(lnElt, lineNumbers);
+        });
 
-            // hide the diff mode switch button in case of not generated diffs
-            if (data.diff_str.indexOf('Diffs are not generated for non textual content') !== 0) {
-              $(`#diff_${diffId} .diff-styles`).css('visibility', 'visible');
-            }
+        // set line numbers for the from side-by-side diff
+        $(`#${diffId}-from .hljs-ln-numbers`).each((i, lnElt) => {
+          setLineNumbers(lnElt, fromLines[i]);
+        });
 
-            setDiffVisible(diffId);
+        // set line numbers for the to side-by-side diff
+        $(`#${diffId}-to .hljs-ln-numbers`).each((i, lnElt) => {
+          setLineNumbers(lnElt, toLines[i]);
+        });
 
-            // highlight diff lines if provided in URL fragment
-            if (selectedDiffLinesInfo &&
-                  selectedDiffLinesInfo.diffPanelId.indexOf(diffId) !== -1) {
-              if (!selectedDiffLinesInfo.unified) {
-                showSplitDiff(diffId);
+        // last processing:
+        //  - remove the '+' and '-' at the beginning of the diff lines
+        //    from code highlighting
+        //  - add the "no new line at end of file marker" if needed
+        $(`.${diffId} .hljs-ln-code`).each((i, lnElt) => {
+          if (lnElt.firstChild) {
+            if (lnElt.firstChild.nodeName !== '#text') {
+              let lineText = lnElt.firstChild.innerHTML;
+              if (lineText[0] === '-' || lineText[0] === '+') {
+                lnElt.firstChild.innerHTML = lineText.substr(1);
+                let newTextNode = document.createTextNode(lineText[0]);
+                $(lnElt).prepend(newTextNode);
               }
-              const firstHighlightedLine = highlightDiffLines(
-                diffId, selectedDiffLinesInfo.startLines,
-                selectedDiffLinesInfo.endLines, selectedDiffLinesInfo.unified);
-
-              $('html, body').animate(
-                {
-                  scrollTop: firstHighlightedLine.offset().top - 50
-                },
-                {
-                  duration: 500
-                }
-              );
             }
-          });
+            $(lnElt).contents().filter((i, elt) => {
+              return elt.nodeType === 3; // Node.TEXT_NODE
+            }).each((i, textNode) => {
+              let swhNoNewLineMarker = '[swh-no-nl-marker]';
+              if (textNode.textContent.indexOf(swhNoNewLineMarker) !== -1) {
+                textNode.textContent = textNode.textContent.replace(swhNoNewLineMarker, '');
+                $(lnElt).append($(noNewLineMarker));
+              }
+            });
+          }
         });
+
+        // hide the diff mode switch button in case of not generated diffs
+        if (data.diff_str.indexOf('Diffs are not generated for non textual content') !== 0) {
+          $(`#diff_${diffId} .diff-styles`).css('visibility', 'visible');
+        }
+
+        setDiffVisible(diffId);
+
+        // highlight diff lines if provided in URL fragment
+        if (selectedDiffLinesInfo &&
+              selectedDiffLinesInfo.diffPanelId.indexOf(diffId) !== -1) {
+          if (!selectedDiffLinesInfo.unified) {
+            showSplitDiff(diffId);
+          }
+          const firstHighlightedLine = highlightDiffLines(
+            diffId, selectedDiffLinesInfo.startLines,
+            selectedDiffLinesInfo.endLines, selectedDiffLinesInfo.unified);
+
+          $('html, body').animate(
+            {
+              scrollTop: firstHighlightedLine.offset().top - 50
+            },
+            {
+              duration: 500
+            }
+          );
+        }
       }
     });
 }
diff --git a/swh/web/assets/src/utils/highlightjs.js b/swh/web/assets/src/utils/highlightjs.js
--- a/swh/web/assets/src/utils/highlightjs.js
+++ b/swh/web/assets/src/utils/highlightjs.js
@@ -1,5 +1,5 @@
 /**
- * Copyright (C) 2018-2019  The Software Heritage developers
+ * Copyright (C) 2018-2020  The Software Heritage developers
  * See the AUTHORS file at the top-level directory of this distribution
  * License: GNU Affero General Public License version 3, or any later version
  * See top-level LICENSE file for more information
@@ -11,3 +11,8 @@
 import 'highlightjs-line-numbers.js';
 import 'highlight.js/styles/github.css';
 import './highlightjs.css';
+
+// define a synchronous version of hljs.lineNumbersBlock
+hljs.lineNumbersBlockSync = function(block) {
+  block.innerHTML = hljs.lineNumbersValue(block.innerHTML);
+};