From 7fe20bfffbdce1fa39b886f190f4dd1f50fca9a5 Mon Sep 17 00:00:00 2001 From: Kevin Jilissen Date: Tue, 16 Sep 2025 08:58:45 +0200 Subject: [PATCH] Ensure unique Monaco models for diff sources Monaco stores source code in a global service. The URIs of every source must be unique. As we are not editing the diff files anyway, we can make sure to only render them once and share them between diff viewers to reduce overhead. --- webapp/src/Twig/TwigExtension.php | 56 ++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 15 deletions(-) diff --git a/webapp/src/Twig/TwigExtension.php b/webapp/src/Twig/TwigExtension.php index 2d7df23586..2f5e09b1ab 100644 --- a/webapp/src/Twig/TwigExtension.php +++ b/webapp/src/Twig/TwigExtension.php @@ -43,6 +43,9 @@ class TwigExtension extends AbstractExtension implements GlobalsInterface { + /** + * @param array $renderedSources + */ public function __construct( protected readonly DOMJudgeService $dj, protected readonly ConfigurationService $config, @@ -55,7 +58,8 @@ public function __construct( protected readonly AuthorizationCheckerInterface $authorizationChecker, protected readonly RouterInterface $router, #[Autowire('%kernel.project_dir%')] - protected readonly string $projectDir + protected readonly string $projectDir, + protected array $renderedSources = [] ) {} public function getFunctions(): array @@ -917,6 +921,38 @@ public function codeEditor( sprintf($editor, $code, $editable ? 'false' : 'true', $mode, $extraForEdit)); } + /** + * Gets the JavaScript to get a Monaco model instance for the submission file. + * Renders the source code of the file as Monaco model, if not already rendered. + * @return string The JavaScript source assignable to a model variable. + */ + public function getMonacoModel(SubmissionFile $file): string + { + if (array_key_exists($file->getSubmitfileid(), $this->renderedSources)) { + return sprintf( + <<getSubmitfileid(), + $file->getFilename(), + ); + } + $this->renderedSources[$file->getSubmitfileid()] = true; + + return sprintf( + <<twig->getRuntime(EscaperRuntime::class)->escape($file->getSourcecode(), 'js'), + $file->getSubmitfileid(), + $file->getFilename(), + ); + } + public function showDiff(string $id, SubmissionFile $newFile, SubmissionFile $oldFile): string { $editor = << $(function() { require(['vs/editor/editor.main'], function () { - const originalModel = monaco.editor.createModel( - "%s", - undefined, - monaco.Uri.parse("diff-old/%s") - ); - const modifiedModel = monaco.editor.createModel( - "%s", - undefined, - monaco.Uri.parse("diff-new/%s") - ); + const originalModel = %s + const modifiedModel = %s const initialDiffMode = getDiffMode(); const radios = $("#diffselect-__EDITOR__ > input[name='__EDITOR__-mode']"); @@ -988,10 +1016,8 @@ public function showDiff(string $id, SubmissionFile $newFile, SubmissionFile $ol return sprintf( str_replace('__EDITOR__', $id, $editor), - $this->twig->getRuntime(EscaperRuntime::class)->escape($oldFile->getSourcecode(), 'js'), - $oldFile->getFilename(), - $this->twig->getRuntime(EscaperRuntime::class)->escape($newFile->getSourcecode(), 'js'), - $newFile->getFilename(), + $this->getMonacoModel($oldFile), + $this->getMonacoModel($newFile), ); }