You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
222 lines
6.3 KiB
222 lines
6.3 KiB
import { Component, Input, ViewChild } from '@angular/core';
|
|
import { BackendService } from 'src/app/services/backendService.service';
|
|
import { Compartment, StateEffect } from '@codemirror/state';
|
|
import { CodeMirrorComponent } from '@sandkasten/codemirror6-editor';
|
|
import { LanguageDescription } from '@codemirror/language';
|
|
import { CODE_DEFAULTS, LANGUAGES } from '../languages';
|
|
import { SafeHTMLPipe } from '../../safe-html.pipe';
|
|
import { ReactiveFormsModule, FormsModule } from '@angular/forms';
|
|
import { Router } from '@angular/router';
|
|
import {
|
|
keymap,
|
|
highlightSpecialChars,
|
|
drawSelection,
|
|
highlightActiveLine,
|
|
dropCursor,
|
|
rectangularSelection,
|
|
crosshairCursor,
|
|
lineNumbers,
|
|
highlightActiveLineGutter,
|
|
gutter,
|
|
} from '@codemirror/view';
|
|
import { Extension, EditorState } from '@codemirror/state';
|
|
import {
|
|
defaultHighlightStyle,
|
|
syntaxHighlighting,
|
|
indentOnInput,
|
|
bracketMatching,
|
|
foldGutter,
|
|
foldKeymap,
|
|
} from '@codemirror/language';
|
|
import { defaultKeymap, history, historyKeymap } from '@codemirror/commands';
|
|
import { searchKeymap, highlightSelectionMatches } from '@codemirror/search';
|
|
import {
|
|
autocompletion,
|
|
completionKeymap,
|
|
closeBrackets,
|
|
closeBracketsKeymap,
|
|
} from '@codemirror/autocomplete';
|
|
import { lintKeymap } from '@codemirror/lint';
|
|
import {
|
|
Connection,
|
|
getDocument,
|
|
peerExtension,
|
|
} from '../../services/connection.service';
|
|
|
|
const basicSetup: Extension = (() => [
|
|
highlightActiveLineGutter(),
|
|
highlightSpecialChars(),
|
|
history(),
|
|
foldGutter(),
|
|
drawSelection(),
|
|
dropCursor(),
|
|
EditorState.allowMultipleSelections.of(true),
|
|
indentOnInput(),
|
|
syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
|
|
bracketMatching(),
|
|
closeBrackets(),
|
|
autocompletion(),
|
|
rectangularSelection(),
|
|
crosshairCursor(),
|
|
highlightActiveLine(),
|
|
highlightSelectionMatches(),
|
|
keymap.of([
|
|
...closeBracketsKeymap,
|
|
...defaultKeymap,
|
|
...searchKeymap,
|
|
...historyKeymap,
|
|
...foldKeymap,
|
|
...completionKeymap,
|
|
...lintKeymap,
|
|
]),
|
|
])();
|
|
|
|
@Component({
|
|
selector: 'app-editor',
|
|
templateUrl: './editor.component.html',
|
|
styleUrls: ['./editor.component.scss'],
|
|
standalone: true,
|
|
imports: [
|
|
CodeMirrorComponent,
|
|
ReactiveFormsModule,
|
|
FormsModule,
|
|
SafeHTMLPipe,
|
|
],
|
|
})
|
|
export class EditorComponent {
|
|
isLoaded: boolean = false; // Pour vérifier si le chargement est terminé
|
|
|
|
readonly languages: LanguageDescription[] = LANGUAGES;
|
|
// Mode par défaut
|
|
private _selectedLanguage = this.languages.find(
|
|
(lang) => lang.name === 'JavaScript'
|
|
)!;
|
|
get selectedLanguage(): LanguageDescription {
|
|
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) => {
|
|
this.codemirror.editor?.dispatch({
|
|
effects: this.languageCompartment.reconfigure(language),
|
|
});
|
|
});
|
|
}
|
|
private _linesNumbers: boolean = true;
|
|
get linesNumbers() {
|
|
return this._linesNumbers;
|
|
}
|
|
set linesNumbers(lines: boolean) {
|
|
this._linesNumbers = lines;
|
|
this.codemirror.editor?.dispatch({
|
|
effects: this.gutterCompartment.reconfigure(
|
|
lines ? lineNumbers() : gutter({})
|
|
),
|
|
});
|
|
}
|
|
|
|
// Contenu de l'éditeur que l'on passera au serveur
|
|
editorContent: string =
|
|
CODE_DEFAULTS[this.selectedLanguage.name as keyof typeof CODE_DEFAULTS];
|
|
resultContent: string = '';
|
|
|
|
// Message d'erreur
|
|
errorMessage: string = '';
|
|
|
|
@ViewChild(CodeMirrorComponent) private codemirror!: CodeMirrorComponent;
|
|
|
|
private readonly languageCompartment = new Compartment();
|
|
private readonly gutterCompartment = new Compartment();
|
|
protected readonly extensions: Extension[] = [
|
|
basicSetup,
|
|
this.gutterCompartment.of(lineNumbers()),
|
|
this.languageCompartment.of(this.selectedLanguage.support!),
|
|
];
|
|
|
|
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.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() {
|
|
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();
|
|
}
|
|
}
|