Apply line-based coloring for output
This commit is contained in:
@ -78,7 +78,7 @@ var CodeOceanEditor = {
|
||||
if ($('#output-' + index).isPresent()) {
|
||||
return $('#output-' + index);
|
||||
} else {
|
||||
var element = $('<pre class="mb-2">').attr('id', 'output-' + index);
|
||||
var element = $('<div class="mb-2 output-element">').attr('id', 'output-' + index);
|
||||
$('#output').append(element);
|
||||
return element;
|
||||
}
|
||||
@ -648,7 +648,7 @@ var CodeOceanEditor = {
|
||||
|
||||
augmentStacktraceInOutput: function () {
|
||||
if (this.tracepositions_regex) {
|
||||
$('#output>pre').each($.proxy(function(index, element) {
|
||||
$('#output > .output-element').each($.proxy(function(index, element) {
|
||||
element = $(element)
|
||||
|
||||
const text = _.escape(element.text());
|
||||
@ -656,16 +656,11 @@ var CodeOceanEditor = {
|
||||
|
||||
let matches;
|
||||
|
||||
// Switch both lines below to enable the output of images and render <IMG/> tags.
|
||||
// Also consider `printOutput` in evaluation.js
|
||||
|
||||
// let augmented_text = element.text();
|
||||
let augmented_text = element.html();
|
||||
while (matches = this.tracepositions_regex.exec(text)) {
|
||||
const frame = $('div.frame[data-filename="' + matches[1] + '"]')
|
||||
|
||||
if (frame.length > 0) {
|
||||
// augmented_text = augmented_text.replace(new RegExp(matches[0], 'g'), "<a href='#' data-file='" + matches[1] + "' data-line='" + matches[2] + "'>" + matches[0] + "</a>");
|
||||
augmented_text = augmented_text.replace(new RegExp(_.unescape(matches[0]), 'g'), "<a href='#' data-file='" + matches[1] + "' data-line='" + matches[2] + "'>" + matches[0] + "</a>");
|
||||
}
|
||||
}
|
||||
|
@ -189,7 +189,7 @@ CodeOceanEditorEvaluation = {
|
||||
},
|
||||
|
||||
clearOutput: function () {
|
||||
$('#output pre').remove();
|
||||
$('#output > .output-element').remove();
|
||||
CodeOceanEditorTurtle.hideCanvas();
|
||||
},
|
||||
|
||||
@ -207,50 +207,54 @@ CodeOceanEditorEvaluation = {
|
||||
return;
|
||||
}
|
||||
|
||||
if (output.stdout !== undefined && !output.stdout.startsWith("<img")) {
|
||||
output.stdout = _.escape(output.stdout);
|
||||
} else {
|
||||
const doc = new DOMParser().parseFromString(output.stdout, "text/html");
|
||||
const sanitizedStdout = this.sanitizeOutput(output.stdout);
|
||||
const sanitizedStderr = this.sanitizeOutput(output.stderr);
|
||||
|
||||
const element = this.findOrCreateOutputElement(index);
|
||||
const pre = $('<span>');
|
||||
|
||||
if (sanitizedStdout !== '') {
|
||||
if (colorize) {
|
||||
pre.addClass('text-success');
|
||||
}
|
||||
pre.append(sanitizedStdout)
|
||||
}
|
||||
|
||||
if (sanitizedStderr !== '') {
|
||||
if (colorize) {
|
||||
pre.addClass('text-warning');
|
||||
} else {
|
||||
pre.append('StdErr: ');
|
||||
}
|
||||
pre.append(sanitizedStderr);
|
||||
}
|
||||
|
||||
if (sanitizedStdout === '' && sanitizedStderr === '') {
|
||||
if (colorize) {
|
||||
pre.addClass('text-muted');
|
||||
}
|
||||
pre.text($('#output').data('message-no-output'))
|
||||
}
|
||||
|
||||
element.append(pre);
|
||||
},
|
||||
|
||||
sanitizeOutput: function (rawContent) {
|
||||
let sanitizedContent = _.escape(rawContent).replace(this.nonPrintableRegEx, "");
|
||||
|
||||
if (rawContent !== undefined && rawContent.trim().startsWith("<img")) {
|
||||
const doc = new DOMParser().parseFromString(rawContent, "text/html");
|
||||
// Get the parsed element, it is automatically wrapped in a <html><body> document
|
||||
const parsedElement = doc.firstChild.lastChild.firstChild;
|
||||
const sanitized_img = document.createElement('img');
|
||||
sanitized_img.src = parsedElement.src;
|
||||
output.stdout = sanitized_img.outerHTML;
|
||||
|
||||
if (parsedElement.src.startsWith("data:image")) {
|
||||
const sanitizedImg = document.createElement('img');
|
||||
sanitizedImg.src = parsedElement.src;
|
||||
sanitizedContent = sanitizedImg.outerHTML;
|
||||
}
|
||||
}
|
||||
|
||||
var element = this.findOrCreateOutputElement(index);
|
||||
// Switch all four lines below to enable the output of images and render <IMG/> tags.
|
||||
// Also consider `augmentStacktraceInOutput` in editor.js.erb
|
||||
if (!colorize) {
|
||||
if (output.stdout !== undefined && output.stdout !== '') {
|
||||
output.stdout = output.stdout.replace(this.nonPrintableRegEx, "")
|
||||
|
||||
element.append(output.stdout)
|
||||
//element.text(element.text() + output.stdout)
|
||||
}
|
||||
|
||||
if (output.stderr !== undefined && output.stderr !== '') {
|
||||
output.stderr = output.stderr.replace(this.nonPrintableRegEx, "")
|
||||
|
||||
element.append('StdErr: ' + output.stderr);
|
||||
//element.text('StdErr: ' + element.text() + output.stderr);
|
||||
}
|
||||
|
||||
} else if (output.stderr) {
|
||||
output.stderr = output.stderr.replace(this.nonPrintableRegEx, "")
|
||||
|
||||
element.addClass('text-warning').append(output.stderr);
|
||||
//element.addClass('text-warning').text(element.text() + output.stderr);
|
||||
this.QaApiOutputBuffer.stderr += output.stderr;
|
||||
} else if (output.stdout) {
|
||||
output.stdout = output.stdout.replace(this.nonPrintableRegEx, "")
|
||||
|
||||
element.addClass('text-success').append(output.stdout);
|
||||
//element.addClass('text-success').text(element.text() + output.stdout);
|
||||
this.QaApiOutputBuffer.stdout += output.stdout;
|
||||
} else {
|
||||
element.addClass('text-muted').text($('#output').data('message-no-output'));
|
||||
}
|
||||
return sanitizedContent;
|
||||
},
|
||||
|
||||
getDeadlineInformation: function(deadline, translation_key, otherwise) {
|
||||
|
@ -25,7 +25,7 @@ i.fa-solid, i.fa-regular, i.fa-solid {
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
|
||||
pre {
|
||||
pre, .output-element {
|
||||
background-color: #FAFAFA;
|
||||
margin: 0;
|
||||
padding: .25rem!important;
|
||||
|
@ -77,20 +77,13 @@
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
#outputInformation {
|
||||
#output {
|
||||
max-height: 500px;
|
||||
width: 100%;
|
||||
#output {
|
||||
white-space: pre;
|
||||
font-family: var(--bs-font-monospace);
|
||||
font-size: 14px;
|
||||
|
||||
.output-element {
|
||||
overflow: auto;
|
||||
margin: 2em 0;
|
||||
|
||||
p {
|
||||
margin: 0.5em;
|
||||
}
|
||||
|
||||
pre + pre {
|
||||
margin-top: 1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -73,6 +73,7 @@ class SubmissionsController < ApplicationController
|
||||
end
|
||||
end
|
||||
|
||||
# rubocop:disable Metrics/CyclomaticComplexity
|
||||
def run
|
||||
# These method-local socket variables are required in order to use one socket
|
||||
# in the callbacks of the other socket. As the callbacks for the client socket
|
||||
@ -167,7 +168,8 @@ class SubmissionsController < ApplicationController
|
||||
@testrun[:status] = :failed
|
||||
"\n#{t('exercises.implement.exit_failure', timestamp: l(Time.zone.now, format: :short), exit_code: exit_code)}"
|
||||
end
|
||||
send_and_store client_socket, {cmd: :write, stream: :stdout, data: "#{exit_statement}\n"}
|
||||
stream = @testrun[:status] == :ok ? :stdout : :stderr
|
||||
send_and_store client_socket, {cmd: :write, stream: stream, data: "#{exit_statement}\n"}
|
||||
if exit_code == 137
|
||||
send_and_store client_socket, {cmd: :status, status: :out_of_memory}
|
||||
@testrun[:status] = :out_of_memory
|
||||
@ -194,6 +196,7 @@ class SubmissionsController < ApplicationController
|
||||
ensure
|
||||
save_testrun_output 'run'
|
||||
end
|
||||
# rubocop:enable Metrics/CyclomaticComplexity:
|
||||
|
||||
def score
|
||||
hijack do |tubesock|
|
||||
|
@ -79,7 +79,7 @@ div.d-grid id='output_sidebar_uncollapsed' class='d-none col-sm-12 enforce-botto
|
||||
.heading = t('exercises.implement.error_hints.heading')
|
||||
ul.body.mb-0
|
||||
#output
|
||||
pre.overflow-scroll = t('exercises.implement.no_output_yet')
|
||||
.output-element.overflow-scroll = t('exercises.implement.no_output_yet')
|
||||
- if CodeOcean::Config.new(:code_ocean).read[:flowr][:enabled] && !@embed_options[:disable_hints] && !@embed_options[:hide_test_results]
|
||||
#flowrHint.mb-2.card.text-white.bg-info data-url=CodeOcean::Config.new(:code_ocean).read[:flowr][:url] role='tab'
|
||||
.card-header = t('exercises.implement.flowr.heading')
|
||||
|
Reference in New Issue
Block a user