diff --git a/src/app/components/editor/editor.component.html b/src/app/components/editor/editor.component.html
index df54939..b6ff0ef 100644
--- a/src/app/components/editor/editor.component.html
+++ b/src/app/components/editor/editor.component.html
@@ -1,107 +1,126 @@
diff --git a/src/app/components/editor/editor.component.ts b/src/app/components/editor/editor.component.ts
index d8793e5..dd8af4b 100644
--- a/src/app/components/editor/editor.component.ts
+++ b/src/app/components/editor/editor.component.ts
@@ -1,179 +1,216 @@
-import { Component, ViewChild } from '@angular/core';
-import { CodeExecutionService } from 'src/app/services/codeExecution.service';
-import { Compartment } 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 {Component, ViewChild} from '@angular/core';
+import {CodeExecutionService} from 'src/app/services/codeExecution.service';
+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 {Extension, EditorState, Compartment} from '@codemirror/state';
import {
- keymap,
- highlightSpecialChars,
- drawSelection,
- highlightActiveLine,
- dropCursor,
- rectangularSelection,
- crosshairCursor,
- lineNumbers,
- highlightActiveLineGutter,
- gutter,
+ keymap,
+ highlightSpecialChars,
+ drawSelection,
+ highlightActiveLine,
+ dropCursor,
+ rectangularSelection,
+ crosshairCursor,
+ lineNumbers,
+ highlightActiveLineGutter,
+ gutter,
+ EditorView,
} from '@codemirror/view';
-import { Extension, EditorState } from '@codemirror/state';
import {
- defaultHighlightStyle,
- syntaxHighlighting,
- indentOnInput,
- bracketMatching,
- foldGutter,
- foldKeymap,
+ defaultHighlightStyle,
+ syntaxHighlighting,
+ indentOnInput,
+ bracketMatching,
+ foldGutter,
+ indentUnit,
+ foldKeymap,
} from '@codemirror/language';
-import { defaultKeymap, history, historyKeymap } from '@codemirror/commands';
-import { searchKeymap, highlightSelectionMatches } from '@codemirror/search';
+import {defaultKeymap, history, historyKeymap, indentWithTab} from '@codemirror/commands';
+import {searchKeymap, highlightSelectionMatches} from '@codemirror/search';
import {
- autocompletion,
- completionKeymap,
- closeBrackets,
- closeBracketsKeymap,
+ autocompletion,
+ completionKeymap,
+ closeBrackets,
+ closeBracketsKeymap,
} from '@codemirror/autocomplete';
-import { lintKeymap } from '@codemirror/lint';
+import {lintKeymap} from '@codemirror/lint';
+
+
+const DEFAULT_INDENT_UNIT = 4;
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,
- ]),
+ 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,
+ indentWithTab,
+ ]),
])();
@Component({
- selector: 'app-editor',
- templateUrl: './editor.component.html',
- styleUrls: ['./editor.component.scss'],
- standalone: true,
- imports: [
- CodeMirrorComponent,
- ReactiveFormsModule,
- FormsModule,
- SafeHTMLPipe,
- ],
+ 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];
+ 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({})
+ ),
+ });
+ }
+
+ private _indentUnit = DEFAULT_INDENT_UNIT;
+ get indentUnit() {
+ return this._indentUnit;
+ }
+
+ set indentUnit(unit: number) {
+ this._indentUnit = unit;
+ this.codemirror.editor?.dispatch({
+ effects: this.indentUnitCompartment.reconfigure(
+ unit ? indentUnit.of(" ".repeat(unit)) : indentUnit.of(" ".repeat(DEFAULT_INDENT_UNIT))),
+ });
+ }
+
+ private _lineWrapping = true;
+ get lineWrapping() {
+ return this._lineWrapping;
+ }
+
+ set lineWrapping(wrap: boolean) {
+ this._lineWrapping = wrap;
+ this.codemirror.editor?.dispatch({
+ effects: this.lineWrappingCompartment.reconfigure(wrap ? EditorView.lineWrapping : []),
+ });
}
- 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!),
- ];
-
- constructor(private codeExecutionService: CodeExecutionService) {}
-
- // Efface le contenu de l'éditeur
- clear(): void {
- this.editorContent = '';
- }
-
- onRunButtonClicked() {
- // Le code à exécuter est le contenu de l'éditeur
- const codeToExecute = this.editorContent;
-
- this.codeExecutionService.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;
- }
+
+ // 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();
+ private readonly indentUnitCompartment = new Compartment();
+ private readonly lineWrappingCompartment = new Compartment();
+
+ protected readonly extensions: Extension[] = [
+ basicSetup,
+ this.gutterCompartment.of(lineNumbers()),
+ this.languageCompartment.of(this.selectedLanguage.support!),
+ this.indentUnitCompartment.of(indentUnit.of(" ".repeat(this._indentUnit))),
+ this.lineWrappingCompartment.of(EditorView.lineWrapping),
+ ];
+
+ constructor(private codeExecutionService: CodeExecutionService) {
+ }
+
+ // Efface le contenu de l'éditeur
+ clear(): void {
+ this.editorContent = '';
+ }
+
+ onRunButtonClicked() {
+ // Le code à exécuter est le contenu de l'éditeur
+ const codeToExecute = this.editorContent;
+
+ this.codeExecutionService.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();
}
- 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();
- }
}