Skip to content

Commit f0e438f

Browse files
committed
Add PG critic to the PG problem editor.
This adds the PG critic results from `WeBWorK::PG::Critic` to the PG problem editor. Note that the "Format Code" tab in the editor is now the "Code Maintenance" tab. This is because this new option is not a code formatter. All of the options fit into the category of code maintenance, so this is a better name.
1 parent 51cd331 commit f0e438f

File tree

7 files changed

+142
-203
lines changed

7 files changed

+142
-203
lines changed

bin/check_modules.pl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ =head1 DESCRIPTION
8888
Net::OAuth
8989
Opcode
9090
Pandoc
91+
Perl::Critic
9192
Perl::Tidy
9293
PHP::Serialization
9394
Pod::Simple::Search

htdocs/js/PGProblemEditor/pgproblemeditor.js

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,15 @@
161161
?.addEventListener('change', () => (deleteBackupCheck.checked = true));
162162
}
163163

164+
const renderArea = document.getElementById('pgedit-render-area');
165+
166+
const scrollToRenderArea = () => {
167+
// Scroll to the top of the render window if the current scroll position is below that.
168+
const renderAreaRect = renderArea.getBoundingClientRect();
169+
const topBarHeight = document.querySelector('.webwork-logo')?.getBoundingClientRect().height ?? 0;
170+
if (renderAreaRect.top < topBarHeight) window.scrollBy(0, renderAreaRect.top - topBarHeight);
171+
};
172+
164173
// Send a request to the server to perltidy the current PG code in the CodeMirror editor.
165174
const tidyPGCode = () => {
166175
const request_object = { courseID: document.getElementsByName('courseID')[0]?.value };
@@ -177,6 +186,8 @@
177186
fetch(webserviceURL, { method: 'post', mode: 'same-origin', body: new URLSearchParams(request_object) })
178187
.then((response) => response.json())
179188
.then((data) => {
189+
if (data.error) throw new Error(data.error);
190+
if (!data.result_data) throw new Error('An invalid response was received.');
180191
if (data.result_data.status) {
181192
if (data.result_data.errors) {
182193
renderArea.innerHTML =
@@ -219,6 +230,8 @@
219230
fetch(webserviceURL, { method: 'post', mode: 'same-origin', body: new URLSearchParams(request_object) })
220231
.then((response) => response.json())
221232
.then((data) => {
233+
if (data.error) throw new Error(data.error);
234+
if (!data.result_data) throw new Error('An invalid response was received.');
222235
if (request_object.pgCode === data.result_data.pgmlCode) {
223236
showMessage('There were no changes to the code.', true);
224237
} else {
@@ -247,24 +260,26 @@
247260
fetch(webserviceURL, { method: 'post', mode: 'same-origin', body: new URLSearchParams(request_object) })
248261
.then((response) => response.json())
249262
.then((data) => {
263+
if (data.error) throw new Error(data.error);
264+
if (!data.result_data) throw new Error('An invalid response was received.');
250265
renderArea.innerHTML = data.result_data.html;
266+
scrollToRenderArea();
251267
})
252-
.catch((err) => {
253-
console.log(err);
254-
showMessage(`Error: ${err?.message ?? err}`);
255-
});
268+
.catch((err) => showMessage(`Error: ${err?.message ?? err}`));
256269
};
257270

258271
document.getElementById('take_action')?.addEventListener('click', async (e) => {
259-
if (document.getElementById('current_action')?.value === 'format_code') {
272+
if (document.getElementById('current_action')?.value === 'code_maintenance') {
260273
e.preventDefault();
261-
if (document.querySelector('input[name="action.format_code"]:checked').value == 'tidyPGCode') {
274+
if (document.querySelector('input[name="action.code_maintenance"]:checked').value === 'tidyPGCode') {
262275
tidyPGCode();
263276
} else if (
264-
document.querySelector('input[name="action.format_code"]:checked').value == 'convertCodeToPGML'
277+
document.querySelector('input[name="action.code_maintenance"]:checked').value === 'convertCodeToPGML'
265278
) {
266279
convertCodeToPGML();
267-
} else if (document.querySelector('input[name="action.format_code"]:checked').value == 'runPGCritic') {
280+
} else if (
281+
document.querySelector('input[name="action.code_maintenance"]:checked').value === 'runPGCritic'
282+
) {
268283
runPGCritic();
269284
}
270285
return;
@@ -328,7 +343,6 @@
328343
});
329344

330345
const renderURL = `${webworkConfig?.webwork_url ?? '/webwork2'}/render_rpc`;
331-
const renderArea = document.getElementById('pgedit-render-area');
332346
const fileType = document.getElementsByName('file_type')[0]?.value;
333347

334348
// This is either the div containing the CodeMirror editor or the problemContents textarea in the case that
@@ -412,11 +426,7 @@
412426
}
413427

414428
adjustIFrameHeight();
415-
416-
// Scroll to the top of the render window if the current scroll position is below that.
417-
const renderAreaRect = renderArea.getBoundingClientRect();
418-
const topBarHeight = document.querySelector('.webwork-logo')?.getBoundingClientRect().height ?? 0;
419-
if (renderAreaRect.top < topBarHeight) window.scrollBy(0, renderAreaRect.top - topBarHeight);
429+
scrollToRenderArea();
420430
});
421431

422432
const render = () =>

lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ the submit button pressed (the action).
105105
Requested actions and aliases
106106
View/Reload action = view
107107
Generate Hardcopy: action = hardcopy
108-
Format Code: action = format_code
108+
Code Maintenance: action = code_maintenance
109109
Save: action = save
110110
Save as: action = save_as
111111
Append: action = add_problem
@@ -133,15 +133,15 @@ use SampleProblemParser qw(getSampleProblemCode generateMetadata);
133133
use constant DEFAULT_SEED => 123456;
134134

135135
# Editor tabs
136-
use constant ACTION_FORMS => [qw(view hardcopy format_code save save_as add_problem revert)];
136+
use constant ACTION_FORMS => [qw(view hardcopy code_maintenance save save_as add_problem revert)];
137137
use constant ACTION_FORM_TITLES => {
138-
view => x('View/Reload'),
139-
hardcopy => x('Generate Hardcopy'),
140-
format_code => x('Format Code'),
141-
save => x('Save'),
142-
save_as => x('Save As'),
143-
add_problem => x('Append'),
144-
revert => x('Revert'),
138+
view => x('View/Reload'),
139+
hardcopy => x('Generate Hardcopy'),
140+
code_maintenance => x('Code Maintenance'),
141+
save => x('Save'),
142+
save_as => x('Save As'),
143+
add_problem => x('Append'),
144+
revert => x('Revert'),
145145
};
146146

147147
my $BLANKPROBLEM = 'newProblem.pg';
@@ -862,9 +862,9 @@ sub view_handler ($c) {
862862
return;
863863
}
864864

865-
# The format_code action is handled by javascript. This is provided just in case
865+
# The code_maintenance action is handled by javascript. This is provided just in case
866866
# something goes wrong and the handler is called.
867-
sub format_code_handler { }
867+
sub code_maintenance_handler { }
868868

869869
sub hardcopy_handler ($c) {
870870
# Redirect to problem editor page.

lib/WebworkWebservice/ProblemActions.pm

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ use warnings;
2121

2222
use Data::Structure::Util qw(unbless);
2323

24-
use WeBWorK::PG::Tidy qw(pgtidy);
25-
use WeBWorK::PG::ConvertToPGML qw(convertToPGML);
26-
use WeBWorK::PG::PGProblemCritic qw(analyzePGcode);
24+
use WeBWorK::PG::Tidy qw(pgtidy);
25+
use WeBWorK::PG::ConvertToPGML qw(convertToPGML);
26+
use WeBWorK::PG::Critic qw(critiquePGCode);
2727

2828
sub getUserProblem {
2929
my ($invocant, $self, $params) = @_;
@@ -183,16 +183,13 @@ sub convertCodeToPGML {
183183

184184
sub runPGCritic {
185185
my ($invocant, $self, $params) = @_;
186-
my $pg_critic_results = analyzePGcode($params->{pgCode});
187-
188-
my $html_output = $self->c->render_to_string(
189-
template => 'ContentGenerator/Instructor/PGProblemEditor/pg_critic',
190-
results => $pg_critic_results
191-
);
192186

193187
return {
194188
ra_out => {
195-
html => $html_output
189+
html => $self->c->render_to_string(
190+
template => 'ContentGenerator/Instructor/PGProblemEditor/pg_critic',
191+
results => [ critiquePGCode($params->{pgCode}) ]
192+
)
196193
},
197194
text => 'The script pg-critic has been run successfully.'
198195
};

templates/ContentGenerator/Instructor/PGProblemEditor/format_code_form.html.ep renamed to templates/ContentGenerator/Instructor/PGProblemEditor/code_maintenance_form.html.ep

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
% last unless $c->{is_pg};
22
<div>
33
<div class="form-check">
4-
<%= radio_button 'action.format_code' => 'tidyPGCode',
5-
id => 'action_format_code_perltidy', class => 'form-check-input', checked => undef =%>
6-
<%= label_for 'action_format_code_perltidy', class => 'form-check-label', begin =%>
4+
<%= radio_button 'action.code_maintenance' => 'tidyPGCode',
5+
id => 'action_code_maintenance_perltidy', class => 'form-check-input', checked => undef =%>
6+
<%= label_for 'action_code_maintenance_perltidy', class => 'form-check-label', begin =%>
77
<%== maketext('Reformat the code using perltidy.') =%>
88
<% end =%>
99
<a class="help-popup" data-bs-content="<%== maketext('Perltidy is a reformatting '
@@ -15,31 +15,31 @@
1515
</a>
1616
</div>
1717
<div class="form-check">
18-
<%= radio_button 'action.format_code' => 'convertCodeToPGML',
19-
id => 'action_format_code_convert_PGML', class => 'form-check-input'=%>
20-
<%= label_for 'action_format_code_convert_PGML', class => 'form-check-label', begin =%>
18+
<%= radio_button 'action.code_maintenance' => 'convertCodeToPGML',
19+
id => 'action_code_maintenance_convert_PGML', class => 'form-check-input'=%>
20+
<%= label_for 'action_code_maintenance_convert_PGML', class => 'form-check-label', begin =%>
2121
<%== maketext('Convert the code to PGML') =%>
2222
<% end =%>
2323
<a class="help-popup" data-bs-content="<%== maketext('This option converts the text blocks '
2424
. 'in the problem code to PGML and updates the loadMacros to include PGML and drop others. '
2525
. 'This can be used as a first pass of the conversion, however the author will still need '
2626
. 'to ensure the problem functions. One area of attention should be the answer blanks, '
2727
. 'which may not be converted correctly.') =%>"
28-
data-bs-placement="top" data-bs-toggle="popover" role="button">
28+
data-bs-placement="top" data-bs-toggle="popover" role="button">
2929
<i aria-hidden="true" class="fas fa-question-circle"></i>
3030
<span class="visually-hidden"><%= maketext('PGML Conversion Help') %></span>
3131
</a>
3232
</div>
3333
<div class="form-check">
34-
<%= radio_button 'action.format_code' => 'runPGCritic',
35-
id => 'action_format_code_run_pgcritic', class => 'form-check-input'=%>
36-
<%= label_for 'action_format_code_run_pgcritic', class => 'form-check-label', begin =%>
34+
<%= radio_button 'action.code_maintenance' => 'runPGCritic',
35+
id => 'action_code_maintenance_run_pgcritic', class => 'form-check-input'=%>
36+
<%= label_for 'action_code_maintenance_run_pgcritic', class => 'form-check-label', begin =%>
3737
<%== maketext('Run the PG Critic Analyzer') =%>
3838
<% end =%>
3939
<a class="help-popup" data-bs-content="<%== maketext('This option runs the PG Critic '
40-
. 'code analyzer, which gives suggestions on using more modern PG constructs and '
41-
. 'ensure that you include important features. ') =%>"
42-
data-bs-placement="top" data-bs-toggle="popover" role="button">
40+
. 'code analyzer which gives suggestions on using modern PG constructs and '
41+
. 'ensures that you include important features.') =%>"
42+
data-bs-placement="top" data-bs-toggle="popover" role="button">
4343
<i aria-hidden="true" class="fas fa-question-circle"></i>
4444
<span class="visually-hidden"><%= maketext('PG Critic Help') %></span>
4545
</a>

0 commit comments

Comments
 (0)