Format the code
continuous-integration/drone/push Build is passing Details

pull/13/head
Colin FRIZOT 11 months ago
parent d86f2779a5
commit 1b96aced93

@ -1,227 +1,227 @@
import {Component, Input, ViewChild} from '@angular/core'; import { Component, Input, ViewChild } from '@angular/core';
import {BackendService} from 'src/app/services/backendService.service'; import { BackendService } from 'src/app/services/backendService.service';
import {Compartment, StateEffect} from '@codemirror/state'; import { Compartment, StateEffect } from '@codemirror/state';
import {CodeMirrorComponent} from '@sandkasten/codemirror6-editor'; import { CodeMirrorComponent } from '@sandkasten/codemirror6-editor';
import {LanguageDescription} from '@codemirror/language'; import { LanguageDescription } from '@codemirror/language';
import {CODE_DEFAULTS, LANGUAGES} from '../languages'; import { CODE_DEFAULTS, LANGUAGES } from '../languages';
import {SafeHTMLPipe} from '../../safe-html.pipe'; import { SafeHTMLPipe } from '../../safe-html.pipe';
import {ReactiveFormsModule, FormsModule} from '@angular/forms'; import { ReactiveFormsModule, FormsModule } from '@angular/forms';
import {Router} from '@angular/router'; import { Router } from '@angular/router';
import { import {
keymap, keymap,
highlightSpecialChars, highlightSpecialChars,
drawSelection, drawSelection,
highlightActiveLine, highlightActiveLine,
dropCursor, dropCursor,
rectangularSelection, rectangularSelection,
crosshairCursor, crosshairCursor,
lineNumbers, lineNumbers,
highlightActiveLineGutter, highlightActiveLineGutter,
gutter, gutter,
} from '@codemirror/view'; } from '@codemirror/view';
import {Extension, EditorState} from '@codemirror/state'; import { Extension, EditorState } from '@codemirror/state';
import { import {
defaultHighlightStyle, defaultHighlightStyle,
syntaxHighlighting, syntaxHighlighting,
indentOnInput, indentOnInput,
bracketMatching, bracketMatching,
foldGutter, foldGutter,
foldKeymap, foldKeymap,
} from '@codemirror/language'; } from '@codemirror/language';
import {defaultKeymap, history, historyKeymap} from '@codemirror/commands'; import { defaultKeymap, history, historyKeymap } from '@codemirror/commands';
import {searchKeymap, highlightSelectionMatches} from '@codemirror/search'; import { searchKeymap, highlightSelectionMatches } from '@codemirror/search';
import { import {
autocompletion, autocompletion,
completionKeymap, completionKeymap,
closeBrackets, closeBrackets,
closeBracketsKeymap, closeBracketsKeymap,
} from '@codemirror/autocomplete'; } from '@codemirror/autocomplete';
import {lintKeymap} from '@codemirror/lint'; import { lintKeymap } from '@codemirror/lint';
import { import {
Connection, Connection,
getDocument, getDocument,
peerExtension, peerExtension,
} from '../../services/connection.service'; } from '../../services/connection.service';
import {environment} from "../../../environments/environment"; import { environment } from '../../../environments/environment';
const basicSetup: Extension = (() => [ const basicSetup: Extension = (() => [
highlightActiveLineGutter(), highlightActiveLineGutter(),
highlightSpecialChars(), highlightSpecialChars(),
history(), history(),
foldGutter(), foldGutter(),
drawSelection(), drawSelection(),
dropCursor(), dropCursor(),
EditorState.allowMultipleSelections.of(true), EditorState.allowMultipleSelections.of(true),
indentOnInput(), indentOnInput(),
syntaxHighlighting(defaultHighlightStyle, {fallback: true}), syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
bracketMatching(), bracketMatching(),
closeBrackets(), closeBrackets(),
autocompletion(), autocompletion(),
rectangularSelection(), rectangularSelection(),
crosshairCursor(), crosshairCursor(),
highlightActiveLine(), highlightActiveLine(),
highlightSelectionMatches(), highlightSelectionMatches(),
keymap.of([ keymap.of([
...closeBracketsKeymap, ...closeBracketsKeymap,
...defaultKeymap, ...defaultKeymap,
...searchKeymap, ...searchKeymap,
...historyKeymap, ...historyKeymap,
...foldKeymap, ...foldKeymap,
...completionKeymap, ...completionKeymap,
...lintKeymap, ...lintKeymap,
]), ]),
])(); ])();
@Component({ @Component({
selector: 'app-editor', selector: 'app-editor',
templateUrl: './editor.component.html', templateUrl: './editor.component.html',
styleUrls: ['./editor.component.scss'], styleUrls: ['./editor.component.scss'],
standalone: true, standalone: true,
imports: [ imports: [
CodeMirrorComponent, CodeMirrorComponent,
ReactiveFormsModule, ReactiveFormsModule,
FormsModule, FormsModule,
SafeHTMLPipe, SafeHTMLPipe,
], ],
}) })
export class EditorComponent { export class EditorComponent {
isLoaded: boolean = false; // Pour vérifier si le chargement est terminé isLoaded: boolean = false; // Pour vérifier si le chargement est terminé
readonly languages: LanguageDescription[] = LANGUAGES; readonly languages: LanguageDescription[] = LANGUAGES;
// Mode par défaut // Mode par défaut
private _selectedLanguage = this.languages.find( private _selectedLanguage = this.languages.find(
(lang) => lang.name === 'JavaScript' (lang) => lang.name === 'JavaScript'
)!; )!;
get selectedLanguage(): LanguageDescription { get selectedLanguage(): LanguageDescription {
return this._selectedLanguage; return this._selectedLanguage;
}
set selectedLanguage(value: LanguageDescription) {
this._selectedLanguage = value;
if (value.name in CODE_DEFAULTS) {
this.editorContent =
CODE_DEFAULTS[value.name as keyof typeof CODE_DEFAULTS];
} }
this.selectedLanguage.load().then((language) => {
set selectedLanguage(value: LanguageDescription) { this.codemirror.editor?.dispatch({
this._selectedLanguage = value; effects: this.languageCompartment.reconfigure(language),
if (value.name in CODE_DEFAULTS) { });
this.editorContent = });
CODE_DEFAULTS[value.name as keyof typeof CODE_DEFAULTS]; }
}
this.selectedLanguage.load().then((language) => { private _linesNumbers: boolean = true;
this.codemirror.editor?.dispatch({ get linesNumbers() {
effects: this.languageCompartment.reconfigure(language), return this._linesNumbers;
}); }
});
} set linesNumbers(lines: boolean) {
this._linesNumbers = lines;
private _linesNumbers: boolean = true; this.codemirror.editor?.dispatch({
get linesNumbers() { effects: this.gutterCompartment.reconfigure(
return this._linesNumbers; lines ? lineNumbers() : gutter({})
} ),
});
set linesNumbers(lines: boolean) { }
this._linesNumbers = lines;
this.codemirror.editor?.dispatch({ // Contenu de l'éditeur que l'on passera au serveur
effects: this.gutterCompartment.reconfigure( editorContent: string =
lines ? lineNumbers() : gutter({}) CODE_DEFAULTS[this.selectedLanguage.name as keyof typeof CODE_DEFAULTS];
), resultContent: string = '';
});
} // Message d'erreur
errorMessage: string = '';
// Contenu de l'éditeur que l'on passera au serveur
editorContent: string = @ViewChild(CodeMirrorComponent) private codemirror!: CodeMirrorComponent;
CODE_DEFAULTS[this.selectedLanguage.name as keyof typeof CODE_DEFAULTS];
resultContent: string = ''; private readonly languageCompartment = new Compartment();
private readonly gutterCompartment = new Compartment();
// Message d'erreur protected readonly extensions: Extension[] = [
errorMessage: string = ''; basicSetup,
this.gutterCompartment.of(lineNumbers()),
@ViewChild(CodeMirrorComponent) private codemirror!: CodeMirrorComponent; this.languageCompartment.of(this.selectedLanguage.support!),
];
private readonly languageCompartment = new Compartment();
private readonly gutterCompartment = new Compartment(); private client: WebSocket | undefined;
protected readonly extensions: Extension[] = [
basicSetup, @Input()
this.gutterCompartment.of(lineNumbers()), set idRoom(idRoom: string) {
this.languageCompartment.of(this.selectedLanguage.support!), if (idRoom === undefined) {
]; return;
private client: WebSocket | undefined;
@Input()
set idRoom(idRoom: string) {
if (idRoom === undefined) {
return;
}
// this.client = new WebSocket(`ws://127.0.0.1:3000/live/${idRoom}`);
this.client = new WebSocket(`${environment.webSocketUrl}/live/${idRoom}`);
this.client.addEventListener('open', async () => {
let conn = new Connection(this.client!);
let {version, doc} = await getDocument(conn);
this.codemirror.editor?.dispatch({
changes: {
from: 0,
to: this.codemirror.editor.state.doc.length,
insert: doc,
},
});
this.codemirror.editor?.dispatch({
effects: StateEffect.appendConfig.of([peerExtension(version, conn)]),
});
});
}
constructor(
private router: Router,
private backendService: BackendService
) {
backendService.getResult().subscribe((msg) => {
if (msg.type === 'stdout' || msg.type === 'stderr') {
this.resultContent += msg.text;
}
});
}
// Efface le contenu de l'éditeur
clear(): void {
this.editorContent = '';
}
async onCreateRoomButtonClicked() {
const idRoom = await this.backendService.createRoom(this.editorContent);
await this.router.navigate([`./editor-live/${idRoom}`]);
}
onRunButtonClicked() {
// Le code à exécuter est le contenu de l'éditeur
const codeToExecute = this.editorContent;
this.backendService.executeCode(codeToExecute, this.selectedLanguage.name);
this.resultContent = '';
}
loadFromFile(event: Event) {
const file = (event.target as HTMLInputElement).files![0];
for (const language of this.languages) {
if (language.extensions.some((ext) => file.name.endsWith(`.${ext}`))) {
this.selectedLanguage = language;
const reader = new FileReader();
reader.onload = (event) => {
this.editorContent = event.target!.result as string;
this.errorMessage = '';
};
reader.readAsText(file);
return;
}
}
const extensions = this.languages.flatMap((lang) => lang.extensions);
this.errorMessage = `Unsupported language. Please select one of the following languages: ${extensions.join(', ')}.`;
console.error(this.errorMessage);
} }
saveToFile() { // this.client = new WebSocket(`ws://127.0.0.1:3000/live/${idRoom}`);
const blob = new Blob([this.editorContent], {type: 'text/plain'}); this.client = new WebSocket(`${environment.webSocketUrl}/live/${idRoom}`);
const a = document.createElement('a'); this.client.addEventListener('open', async () => {
a.download = `code.${this.selectedLanguage.extensions![0]}`; let conn = new Connection(this.client!);
a.href = URL.createObjectURL(blob); let { version, doc } = await getDocument(conn);
a.click();
this.codemirror.editor?.dispatch({
changes: {
from: 0,
to: this.codemirror.editor.state.doc.length,
insert: doc,
},
});
this.codemirror.editor?.dispatch({
effects: StateEffect.appendConfig.of([peerExtension(version, conn)]),
});
});
}
constructor(
private router: Router,
private backendService: BackendService
) {
backendService.getResult().subscribe((msg) => {
if (msg.type === 'stdout' || msg.type === 'stderr') {
this.resultContent += msg.text;
}
});
}
// Efface le contenu de l'éditeur
clear(): void {
this.editorContent = '';
}
async onCreateRoomButtonClicked() {
const idRoom = await this.backendService.createRoom(this.editorContent);
await this.router.navigate([`./editor-live/${idRoom}`]);
}
onRunButtonClicked() {
// Le code à exécuter est le contenu de l'éditeur
const codeToExecute = this.editorContent;
this.backendService.executeCode(codeToExecute, this.selectedLanguage.name);
this.resultContent = '';
}
loadFromFile(event: Event) {
const file = (event.target as HTMLInputElement).files![0];
for (const language of this.languages) {
if (language.extensions.some((ext) => file.name.endsWith(`.${ext}`))) {
this.selectedLanguage = language;
const reader = new FileReader();
reader.onload = (event) => {
this.editorContent = event.target!.result as string;
this.errorMessage = '';
};
reader.readAsText(file);
return;
}
} }
const extensions = this.languages.flatMap((lang) => lang.extensions);
this.errorMessage = `Unsupported language. Please select one of the following languages: ${extensions.join(', ')}.`;
console.error(this.errorMessage);
}
saveToFile() {
const blob = new Blob([this.editorContent], { type: 'text/plain' });
const a = document.createElement('a');
a.download = `code.${this.selectedLanguage.extensions![0]}`;
a.href = URL.createObjectURL(blob);
a.click();
}
} }

@ -1,59 +1,58 @@
import {Injectable} from '@angular/core'; import { Injectable } from '@angular/core';
import {SSE} from 'sse.js'; import { SSE } from 'sse.js';
import {Observable, Subject} from 'rxjs'; import { Observable, Subject } from 'rxjs';
import {environment} from "../../environments/environment"; import { environment } from '../../environments/environment';
export type ExecutionMessage = { export type ExecutionMessage = {
type: 'stdout' | 'stderr' | 'exit'; type: 'stdout' | 'stderr' | 'exit';
text: string; text: string;
}; };
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root',
}) })
export class BackendService { export class BackendService {
private apiUrl = environment.apiUrl; private apiUrl = environment.apiUrl;
private resultSubject = new Subject<ExecutionMessage>(); private resultSubject = new Subject<ExecutionMessage>();
constructor() { constructor() {}
}
async createRoom(code: string) {
async createRoom(code: string) { const reponse = await fetch(`${this.apiUrl}/live`, {
const reponse = await fetch(`${this.apiUrl}/live`, { method: 'POST',
method: 'POST', headers: {
headers: { 'Content-Type': 'application/json',
'Content-Type': 'application/json', },
}, body: JSON.stringify({ code }),
body: JSON.stringify({code}), });
}); return reponse.text();
return reponse.text(); }
}
executeCode(code: string, language: string) {
executeCode(code: string, language: string) { const sse = new SSE(`${this.apiUrl}/run`, {
const sse = new SSE(`${this.apiUrl}/run`, { method: 'POST',
method: 'POST', headers: {
headers: { 'Content-Type': 'application/json',
'Content-Type': 'application/json', Accept: 'text/event-stream',
Accept: 'text/event-stream', },
}, payload: JSON.stringify({ code, language }),
payload: JSON.stringify({code, language}), });
});
sse.addEventListener('message', (event: MessageEvent<string>) => {
sse.addEventListener('message', (event: MessageEvent<string>) => { const result = event.data;
const result = event.data;
// @ts-expect-error The type is not declared although present
// @ts-expect-error The type is not declared although present const type = event.id;
const type = event.id; const text = decodeURIComponent(result.replace(/%00/g, ''));
const text = decodeURIComponent(result.replace(/%00/g, '')); if (type === 'end') {
if (type === 'end') { sse.close();
sse.close(); }
} this.resultSubject.next({ type, text });
this.resultSubject.next({type, text}); });
}); }
}
getResult(): Observable<ExecutionMessage> {
getResult(): Observable<ExecutionMessage> { return this.resultSubject.asObservable();
return this.resultSubject.asObservable(); }
}
} }

@ -1,5 +1,5 @@
export const environment = { export const environment = {
production: true, production: true,
apiUrl: 'https://tododomain.com', apiUrl: 'https://tododomain.com',
webSocketUrl: 'ws://tododomain.com', webSocketUrl: 'ws://tododomain.com',
}; };

@ -1,5 +1,5 @@
export const environment = { export const environment = {
production: false, production: false,
apiUrl: 'http://localhost:3000', apiUrl: 'http://localhost:3000',
webSocketUrl: 'ws://localhost:3000', webSocketUrl: 'ws://localhost:3000',
}; };

Loading…
Cancel
Save