Skip to content

Commit f720927

Browse files
authored
Programming exercises: Add problem statement as a selectable file in the editor with live preview (#11348)
1 parent b1a3f4f commit f720927

26 files changed

+731
-180
lines changed

src/main/webapp/app/exam/overview/exercises/programming/programming-exam-submission.component.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ <h3 class="text-align-left fw-normal">
1616
@if (exercise().allowOnlineEditor) {
1717
<div>
1818
<jhi-code-editor-container
19+
[forRepositoryView]="true"
1920
[editable]="!repositoryIsLocked"
2021
[participation]="studentParticipation()"
2122
[showEditorInstructions]="showEditorInstructions"

src/main/webapp/app/programming/manage/assess/code-editor-tutor-assessment-container/code-editor-tutor-assessment-container.component.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
<ng-template #assessment>
5454
@if (!loadingParticipation && !participationCouldNotBeFetched) {
5555
<jhi-code-editor-container
56+
[forRepositoryView]="true"
5657
[editable]="false"
5758
[participation]="participation"
5859
[feedbackSuggestions]="feedbackSuggestions"

src/main/webapp/app/programming/manage/code-editor/container/code-editor-container.component.html

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ <h4 class="editor-title"><ng-content select="[editorTitle]" /></h4>
3131
editorSidebarLeft
3232
[disableActions]="!editable"
3333
[displayOnly]="forRepositoryView"
34+
[showEditorInstructions]="showEditorInstructions"
3435
[unsavedFiles]="unsavedFiles | keys"
3536
[errorFiles]="errorFiles"
3637
[editorState]="editorState"
@@ -45,26 +46,39 @@ <h4 class="editor-title"><ng-content select="[editorTitle]" /></h4>
4546
(onToggleCollapse)="onToggleCollapse($event, CollapsableCodeEditorElement.FileBrowser)"
4647
/>
4748
<ng-container editorCenter>
48-
<jhi-code-editor-monaco
49-
[commitState]="commitState"
50-
[editorState]="editorState"
51-
[course]="course"
52-
[feedbacks]="feedbackForSubmission()"
53-
[feedbackSuggestions]="feedbackSuggestions"
54-
[readOnlyManualFeedback]="readOnlyManualFeedback"
55-
[disableActions]="!editable"
56-
[isTutorAssessment]="isTutorAssessment"
57-
[highlightDifferences]="highlightDifferences"
58-
[selectedFile]="selectedFile"
59-
[buildAnnotations]="annotations"
60-
[sessionId]="participation?.id ?? 'test'"
61-
(onFileContentChange)="onFileContentChange($event)"
62-
(onUpdateFeedback)="onUpdateFeedback.emit($event)"
63-
(onAcceptSuggestion)="onAcceptSuggestion.emit($event)"
64-
(onDiscardSuggestion)="onDiscardSuggestion.emit($event)"
65-
(onError)="onError($event)"
66-
(onFileLoad)="fileLoad($event)"
67-
/>
49+
@if (selectedFile === problemStatementIdentifier && showEditorInstructions && !forRepositoryView) {
50+
<jhi-code-editor-instructions
51+
(onToggleCollapse)="onToggleCollapse($event, CollapsableCodeEditorElement.Instructions)"
52+
(onError)="onError($event)"
53+
[isAssessmentMode]="isTutorAssessment"
54+
[disableCollapse]="true"
55+
[isEditor]="true"
56+
>
57+
<ng-content select="[editorCenter]" />
58+
</jhi-code-editor-instructions>
59+
} @else {
60+
<!-- Regular Code Editor -->
61+
<jhi-code-editor-monaco
62+
[commitState]="commitState"
63+
[editorState]="editorState"
64+
[course]="course"
65+
[feedbacks]="feedbackForSubmission()"
66+
[feedbackSuggestions]="feedbackSuggestions"
67+
[readOnlyManualFeedback]="readOnlyManualFeedback"
68+
[disableActions]="!editable"
69+
[isTutorAssessment]="isTutorAssessment"
70+
[highlightDifferences]="highlightDifferences"
71+
[selectedFile]="selectedFile"
72+
[buildAnnotations]="annotations"
73+
[sessionId]="participation?.id ?? 'test'"
74+
(onFileContentChange)="onFileContentChange($event)"
75+
(onUpdateFeedback)="onUpdateFeedback.emit($event)"
76+
(onAcceptSuggestion)="onAcceptSuggestion.emit($event)"
77+
(onDiscardSuggestion)="onDiscardSuggestion.emit($event)"
78+
(onError)="onError($event)"
79+
(onFileLoad)="fileLoad($event)"
80+
/>
81+
}
6882
</ng-container>
6983
<ng-container editorSidebarRight>
7084
@if (showEditorInstructions) {

src/main/webapp/app/programming/manage/code-editor/container/code-editor-container.component.ts

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
FileBadgeType,
1313
FileChange,
1414
FileType,
15+
PROBLEM_STATEMENT_IDENTIFIER,
1516
RenameFileChange,
1617
} from 'app/programming/shared/code-editor/model/code-editor.model';
1718
import { AlertService } from 'app/shared/service/alert.service';
@@ -111,9 +112,20 @@ export class CodeEditorContainerComponent implements OnChanges, ComponentCanDeac
111112
/** END WIP */
112113

113114
// WARNING: Don't initialize variables in the declaration block. The method initializeProperties is responsible for this task.
114-
selectedFile?: string;
115+
private selectedFileValue?: string;
115116
unsavedFilesValue: { [fileName: string]: string }; // {[fileName]: fileContent}
116117
fileBadges: { [fileName: string]: FileBadge[] };
118+
get selectedFile(): string | undefined {
119+
return this.selectedFileValue;
120+
}
121+
122+
set selectedFile(file: string | undefined) {
123+
this.selectedFileValue = file;
124+
}
125+
126+
get problemStatementIdentifier(): string {
127+
return PROBLEM_STATEMENT_IDENTIFIER;
128+
}
117129

118130
/** Code Editor State Variables **/
119131
editorState: EditorState;
@@ -196,21 +208,26 @@ export class CodeEditorContainerComponent implements OnChanges, ComponentCanDeac
196208
* in case of delete make sure to also remove all sub entities (files in folder).
197209
*/
198210
onFileChange<F extends FileChange>([, fileChange]: [string[], F]) {
199-
this.commitState = CommitState.UNCOMMITTED_CHANGES;
200211
if (fileChange instanceof CreateFileChange) {
201212
// Select newly created file
202213
if (fileChange.fileType === FileType.FILE) {
203214
this.selectedFile = fileChange.fileName;
215+
this.commitState = CommitState.UNCOMMITTED_CHANGES;
204216
}
205217
} else if (fileChange instanceof RenameFileChange || fileChange instanceof DeleteFileChange) {
218+
// Guard against PROBLEM_STATEMENT file operations - only allow FILE and FOLDER
219+
if (fileChange.fileType !== FileType.FILE && fileChange.fileType !== FileType.FOLDER) {
220+
return;
221+
}
222+
this.commitState = CommitState.UNCOMMITTED_CHANGES;
206223
this.unsavedFiles = this.fileService.updateFileReferences(this.unsavedFiles, fileChange);
207224
this.selectedFile = this.fileService.updateFileReference(this.selectedFile!, fileChange);
208225
}
209226
// If unsavedFiles are deleted, this can mean that the editorState becomes clean
210227
if (_isEmpty(this.unsavedFiles) && this.editorState === EditorState.UNSAVED_CHANGES) {
211228
this.editorState = EditorState.CLEAN;
212229
}
213-
this.monacoEditor.onFileChange(fileChange);
230+
this.monacoEditor?.onFileChange(fileChange);
214231

215232
this.onFileChanged.emit();
216233
}
@@ -233,7 +250,7 @@ export class CodeEditorContainerComponent implements OnChanges, ComponentCanDeac
233250
if (errorFiles.length) {
234251
this.onError('saveFailed');
235252
}
236-
this.monacoEditor.storeAnnotations(savedFiles);
253+
this.monacoEditor?.storeAnnotations(savedFiles);
237254
}
238255

239256
/**
@@ -273,11 +290,11 @@ export class CodeEditorContainerComponent implements OnChanges, ComponentCanDeac
273290
}
274291

275292
getText(): string {
276-
return this.monacoEditor.getText() ?? '';
293+
return this.monacoEditor?.getText() ?? '';
277294
}
278295

279296
getNumberOfLines(): number {
280-
return this.monacoEditor.getNumberOfLines() ?? 0;
297+
return this.monacoEditor?.getNumberOfLines() ?? 0;
281298
}
282299

283300
/**
@@ -286,7 +303,7 @@ export class CodeEditorContainerComponent implements OnChanges, ComponentCanDeac
286303
* @param endLine The last line to highlight.
287304
*/
288305
highlightLines(startLine: number, endLine: number): void {
289-
this.monacoEditor.highlightLines(startLine, endLine);
306+
this.monacoEditor?.highlightLines(startLine, endLine);
290307
}
291308

292309
onToggleCollapse(event: InteractableEvent, collapsableElement: CollapsableCodeEditorElement) {

src/main/webapp/app/programming/manage/code-editor/file-browser/code-editor-file-browser.component.html

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,12 @@ <h3 class="card-title justify-content-center">
8080
</div>
8181
<!--Treeview Item Template-->
8282
<ng-template #itemTemplate let-item="item" let-onCollapseExpand="onCollapseExpand">
83+
<!--Problem Statement-->
84+
@if (repositoryFiles?.[item.value] === FileType.PROBLEM_STATEMENT) {
85+
<jhi-code-editor-file-browser-problem-statement [item]="item" (onNodeSelect)="handleNodeSelected(item)" />
86+
}
8387
<!--folder-->
84-
@if (repositoryFiles[item.value] === FileType.FOLDER) {
88+
@if (repositoryFiles?.[item.value] === FileType.FOLDER) {
8589
<jhi-code-editor-file-browser-folder
8690
[item]="item"
8791
[isBeingRenamed]="!!renamingFile && renamingFile![0] === item.value"
@@ -100,11 +104,11 @@ <h3 class="card-title justify-content-center">
100104
/>
101105
}
102106
<!--file-->
103-
@if (repositoryFiles[item.value] === FileType.FILE) {
107+
@if (repositoryFiles?.[item.value] === FileType.FILE) {
104108
<jhi-code-editor-file-browser-file
105109
[item]="item"
106-
[hasChanges]="repositoryFilesWithInformationAboutChange ? repositoryFilesWithInformationAboutChange[item.value] : false"
107-
[badges]="fileBadges[item.value] || []"
110+
[hasChanges]="repositoryFilesWithInformationAboutChange?.[item.value] ?? false"
111+
[badges]="fileBadges?.[item.value] ?? []"
108112
[isBeingRenamed]="!!renamingFile && renamingFile![0] === item.value"
109113
[hasUnsavedChanges]="unsavedFiles && unsavedFiles.includes(item.value)"
110114
[hasError]="errorFiles && errorFiles.includes(item.value)"

0 commit comments

Comments
 (0)