From af989fc1654615f0df26171f137150256e763444 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20FR=C3=89VILLE?= Date: Thu, 25 Jan 2024 08:05:48 +0100 Subject: [PATCH] Update to CodeMirror 6 (#7) Update the editor to CodeMirror, using a [custom package](https://codefirst.iut.uca.fr/git/sandkasten/components/src/branch/main/projects/codemirror6-editor). All the Angular integration parts happen in this dedicated package, and the editor configuration is done here. Language selection has been refactored to dynamically import on the fly the appropriate CodeMirror modes (now called extensions). Co-authored-by: clfreville2 Reviewed-on: https://codefirst.iut.uca.fr/git/sandkasten/sandkasten-web/pulls/7 Reviewed-by: Colin FRIZOT Reviewed-by: Bastien OLLIER Reviewed-by: Hugo PRADIER Reviewed-by: Matis MAZINGUE --- package-lock.json | 225 +++++++++++++++--- package.json | 8 +- src/app/app.module.ts | 8 +- .../components/editor/editor.component.html | 34 +-- src/app/components/editor/editor.component.ts | 194 +++++---------- src/app/components/languages.ts | 49 ++++ src/main.ts | 3 - src/styles.scss | 3 - 8 files changed, 310 insertions(+), 214 deletions(-) create mode 100644 src/app/components/languages.ts diff --git a/package-lock.json b/package-lock.json index 49a7f13..b69ae85 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,12 +17,16 @@ "@angular/platform-browser": "^17.1.0", "@angular/platform-browser-dynamic": "^17.1.0", "@angular/router": "^17.1.0", - "@ctrl/ngx-codemirror": "^7.0.0", + "@codemirror/lang-cpp": "^6.0.2", + "@codemirror/lang-javascript": "^6.2.1", + "@codemirror/state": "^6.4.0", + "@codemirror/view": "^6.23.0", "@emailjs/browser": "^3.12.1", "@ngx-translate/core": "^15.0.0", "@ngx-translate/http-loader": "^8.0.0", + "@sandkasten/codemirror6-editor": "^0.0.2", "ansi-to-html": "^0.7.2", - "codemirror": "^5.65.16", + "codemirror": "^6.0.1", "rxjs": "~7.8.1", "sse.js": "^2.2.0", "tslib": "^2.6.2", @@ -2262,6 +2266,105 @@ "node": ">=6.9.0" } }, + "node_modules/@codemirror/autocomplete": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.12.0.tgz", + "integrity": "sha512-r4IjdYFthwbCQyvqnSlx0WBHRHi8nBvU+WjJxFUij81qsBfhNudf/XKKmmC2j3m0LaOYUQTf3qiEK1J8lO1sdg==", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0" + }, + "peerDependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@codemirror/commands": { + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.3.3.tgz", + "integrity": "sha512-dO4hcF0fGT9tu1Pj1D2PvGvxjeGkbC6RGcZw6Qs74TH+Ed1gw98jmUgd2axWvIZEqTeTuFrg1lEB1KV6cK9h1A==", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.4.0", + "@codemirror/view": "^6.0.0", + "@lezer/common": "^1.1.0" + } + }, + "node_modules/@codemirror/lang-cpp": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@codemirror/lang-cpp/-/lang-cpp-6.0.2.tgz", + "integrity": "sha512-6oYEYUKHvrnacXxWxYa6t4puTlbN3dgV662BDfSH8+MfjQjVmP697/KYTDOqpxgerkvoNm7q5wlFMBeX8ZMocg==", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@lezer/cpp": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-javascript": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.1.tgz", + "integrity": "sha512-jlFOXTejVyiQCW3EQwvKH0m99bUYIw40oPmFjSX2VS78yzfe0HELZ+NEo9Yfo1MkGRpGlj3Gnu4rdxV1EnAs5A==", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.6.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0", + "@lezer/javascript": "^1.0.0" + } + }, + "node_modules/@codemirror/language": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.0.tgz", + "integrity": "sha512-2vaNn9aPGCRFKWcHPFksctzJ8yS5p7YoaT+jHpc0UGKzNuAIx4qy6R5wiqbP+heEEdyaABA582mNqSHzSoYdmg==", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.23.0", + "@lezer/common": "^1.1.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0", + "style-mod": "^4.0.0" + } + }, + "node_modules/@codemirror/lint": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.4.2.tgz", + "integrity": "sha512-wzRkluWb1ptPKdzlsrbwwjYCPLgzU6N88YBAmlZi8WFyuiEduSd05MnJYNogzyc8rPK7pj6m95ptUApc8sHKVA==", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/search": { + "version": "6.5.5", + "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.5.tgz", + "integrity": "sha512-PIEN3Ke1buPod2EHbJsoQwlbpkz30qGZKcnmH1eihq9+bPQx8gelauUwLYaY4vBOuBAuEhmpDLii4rj/uO0yMA==", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/state": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.4.0.tgz", + "integrity": "sha512-hm8XshYj5Fo30Bb922QX9hXB/bxOAVH+qaqHBzw5TKa72vOeslyGwd4X8M0c1dJ9JqxlaMceOQ8RsL9tC7gU0A==" + }, + "node_modules/@codemirror/view": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.23.0.tgz", + "integrity": "sha512-/51px9N4uW8NpuWkyUX+iam5+PM6io2fm+QmRnzwqBy5v/pwGg9T0kILFtYeum8hjuvENtgsGNKluOfqIICmeQ==", + "dependencies": { + "@codemirror/state": "^6.4.0", + "style-mod": "^4.1.0", + "w3c-keyname": "^2.2.4" + } + }, "node_modules/@colors/colors": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", @@ -2271,20 +2374,6 @@ "node": ">=0.1.90" } }, - "node_modules/@ctrl/ngx-codemirror": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@ctrl/ngx-codemirror/-/ngx-codemirror-7.0.0.tgz", - "integrity": "sha512-qvIWtSTw/8fdXDnofBTX6LmTW9646HhawG2+Qyagf1vH40jCy0ZbHnkC20UYOVpUX+QCd1e/PQpkvWQ/1iGFzQ==", - "dependencies": { - "@types/codemirror": "^5.60.7", - "tslib": "^2.3.0" - }, - "peerDependencies": { - "@angular/core": ">=16.0.0-0", - "@angular/forms": ">=16.0.0-0", - "codemirror": "^5.65.9" - } - }, "node_modules/@discoveryjs/json-ext": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", @@ -2864,6 +2953,47 @@ "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==", "dev": true }, + "node_modules/@lezer/common": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.1.tgz", + "integrity": "sha512-yemX0ZD2xS/73llMZIK6KplkjIjf2EvAHcinDi/TfJ9hS25G0388+ClHt6/3but0oOxinTcQHJLDXh6w1crzFQ==" + }, + "node_modules/@lezer/cpp": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@lezer/cpp/-/cpp-1.1.2.tgz", + "integrity": "sha512-macwKtyeUO0EW86r3xWQCzOV9/CF8imJLpJlPv3sDY57cPGeUZ8gXWOWNlJr52TVByMV3PayFQCA5SHEERDmVQ==", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/highlight": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.0.tgz", + "integrity": "sha512-WrS5Mw51sGrpqjlh3d4/fOwpEV2Hd3YOkp9DBt4k8XZQcoTHZFB7sx030A6OcahF4J1nDQAa3jXlTVVYH50IFA==", + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@lezer/javascript": { + "version": "1.4.13", + "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.4.13.tgz", + "integrity": "sha512-5IBr8LIO3xJdJH1e9aj/ZNLE4LSbdsx25wFmGRAZsj2zSmwAYjx26JyU/BYOCpRQlu1jcv1z3vy4NB9+UkfRow==", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.1.3", + "@lezer/lr": "^1.3.0" + } + }, + "node_modules/@lezer/lr": { + "version": "1.3.14", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.3.14.tgz", + "integrity": "sha512-z5mY4LStlA3yL7aHT/rqgG614cfcvklS+8oFRFBYrs4YaWLJyKKM4+nN6KopToX0o9Hj6zmH6M5kinOYuy06ug==", + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, "node_modules/@ljharb/through": { "version": "2.3.11", "resolved": "https://registry.npmjs.org/@ljharb/through/-/through-2.3.11.tgz", @@ -3323,6 +3453,20 @@ "win32" ] }, + "node_modules/@sandkasten/codemirror6-editor": { + "version": "0.0.2", + "resolved": "https://codefirst.iut.uca.fr/git/api/packages/sandkasten/npm/%40sandkasten%2Fcodemirror6-editor/-/0.0.2/codemirror6-editor-0.0.2.tgz", + "integrity": "sha512-hwqtIlIJFT1muFRqY66OC6qYtxj8XS0yG0SfDWh856CrZRcjSed5q4JBWROZqyPerc7L4PQiO3YmUnzR0YtCOA==", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/common": "^17.1.0", + "@angular/core": "^17.1.0", + "@codemirror/state": "^6.4.0", + "@codemirror/view": "^6.23.0" + } + }, "node_modules/@schematics/angular": { "version": "17.1.0", "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-17.1.0.tgz", @@ -3482,14 +3626,6 @@ "@types/node": "*" } }, - "node_modules/@types/codemirror": { - "version": "5.60.15", - "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.15.tgz", - "integrity": "sha512-dTOvwEQ+ouKJ/rE9LT1Ue2hmP6H1mZv5+CCnNWu2qtiOe2LQa9lCprEY20HxiDmV/Bxh+dXjywmy5aKvoGjULA==", - "dependencies": { - "@types/tern": "*" - } - }, "node_modules/@types/connect": { "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", @@ -3547,7 +3683,8 @@ "node_modules/@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true }, "node_modules/@types/express": { "version": "4.17.21", @@ -3681,14 +3818,6 @@ "@types/node": "*" } }, - "node_modules/@types/tern": { - "version": "0.23.9", - "resolved": "https://registry.npmjs.org/@types/tern/-/tern-0.23.9.tgz", - "integrity": "sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw==", - "dependencies": { - "@types/estree": "*" - } - }, "node_modules/@types/ws": { "version": "8.5.10", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", @@ -4819,9 +4948,18 @@ } }, "node_modules/codemirror": { - "version": "5.65.16", - "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.16.tgz", - "integrity": "sha512-br21LjYmSlVL0vFCPWPfhzUCT34FM/pAdK7rRIZwa0rrtrIdotvP4Oh4GUHsu2E3IrQMCfRkL/fN3ytMNxVQvg==" + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz", + "integrity": "sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/commands": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/search": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0" + } }, "node_modules/color-convert": { "version": "1.9.3", @@ -5127,6 +5265,11 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/crelt": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==" + }, "node_modules/critters": { "version": "0.0.20", "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.20.tgz", @@ -10882,6 +11025,11 @@ "node": ">=6" } }, + "node_modules/style-mod": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.0.tgz", + "integrity": "sha512-Ca5ib8HrFn+f+0n4N4ScTIA9iTOQ7MaGS1ylHcoVqW9J7w2w8PzN6g9gKmTYgGEBH8e120+RCmhpje6jC5uGWA==" + }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -11514,6 +11662,11 @@ "node": ">=0.10.0" } }, + "node_modules/w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==" + }, "node_modules/watchpack": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", diff --git a/package.json b/package.json index e51187b..42484d5 100644 --- a/package.json +++ b/package.json @@ -19,12 +19,16 @@ "@angular/platform-browser": "^17.1.0", "@angular/platform-browser-dynamic": "^17.1.0", "@angular/router": "^17.1.0", - "@ctrl/ngx-codemirror": "^7.0.0", + "@codemirror/lang-cpp": "^6.0.2", + "@codemirror/lang-javascript": "^6.2.1", + "@codemirror/state": "^6.4.0", + "@codemirror/view": "^6.23.0", "@emailjs/browser": "^3.12.1", "@ngx-translate/core": "^15.0.0", "@ngx-translate/http-loader": "^8.0.0", + "@sandkasten/codemirror6-editor": "^0.0.2", "ansi-to-html": "^0.7.2", - "codemirror": "^5.65.16", + "codemirror": "^6.0.1", "rxjs": "~7.8.1", "sse.js": "^2.2.0", "tslib": "^2.6.2", diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 2ed0287..4542326 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -17,7 +17,7 @@ import { PrivacyPolicyComponent } from './components/privacy-policy/privacy-poli import { ReactiveFormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms'; -import { CodemirrorModule } from '@ctrl/ngx-codemirror'; +import { CodeMirrorComponent } from '@sandkasten/codemirror6-editor'; import { HttpClient, HttpClientModule } from '@angular/common/http'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; @@ -46,8 +46,7 @@ import { SafeHTMLPipe } from './safe-html.pipe'; AppRoutingModule, ReactiveFormsModule, FormsModule, - CodemirrorModule, - // Injection des HttpClient pour notre module de traduction + // Injection des HttpClient pour notre module de traduction HttpClientModule, // Initialisation du module de traduction TranslateModule.forRoot({ @@ -60,7 +59,8 @@ import { SafeHTMLPipe } from './safe-html.pipe'; deps: [HttpClient] }, defaultLanguage: 'fr' - }) + }), + CodeMirrorComponent ], providers: [TranslationService], bootstrap: [AppComponent] diff --git a/src/app/components/editor/editor.component.html b/src/app/components/editor/editor.component.html index 3945bba..fda73ff 100644 --- a/src/app/components/editor/editor.component.html +++ b/src/app/components/editor/editor.component.html @@ -1,35 +1,19 @@
- - + [extensions]="extensions" + >

 
   
- + @for (language of languages; track language.name) { + + }
@@ -38,10 +22,6 @@
-
-

Chargement: {{ loadingProgress }}%

-
-
diff --git a/src/app/components/editor/editor.component.ts b/src/app/components/editor/editor.component.ts index 593b443..f3bee3a 100644 --- a/src/app/components/editor/editor.component.ts +++ b/src/app/components/editor/editor.component.ts @@ -1,96 +1,55 @@ -import { Component, OnInit } from "@angular/core"; -import { Router } from "@angular/router"; -import { CodeExecutionService } from "src/app/services/codeExecution.service"; -import "codemirror/mode/shell/shell.js"; -import "codemirror/mode/clike/clike.js"; -import Convert from 'ansi-to-html'; - - -// Exemple de code pour chaque langage autorisé -const codeDefaults = { - "text/typescript": `const component = { - name: "@exemple/Typescript", - author: "Sandkasten", - repo: "https://codefirst.iut.uca.fr/git/sandkasten/sandkasten-web.git" -}; -const hello: string = 'Bonjour ceci est un test de code en typescript';`, - "text/javascript": `const component = { - name: "@exemple/Javascript", - author: "Sandkasten", - repo: "https://codefirst.iut.uca.fr/git/sandkasten/sandkasten-web.git" -}; -const hello = 'Bonjour ceci est un test de code en javascript';`, - "text/x-c++src": `#include -using namespace std; -int main() { - cout << "Bonjour ceci est un test de code en c++" << endl; - return 0; -}`, - "text/x-csrc": `#include -int main() { - printf("Bonjour ceci est un test de code en c"); - return 0; -}`, - "text/x-sh": `#!/bin/bash -echo "Bonjour ceci est un test de code en shell"`, -}; - -// Langages autorisés -type AllowedLanguage = keyof typeof codeDefaults; +import { Component, ViewChild } from '@angular/core'; +import { CodeExecutionService } from 'src/app/services/codeExecution.service'; +import { basicSetup } from 'codemirror'; +import { Compartment, Extension } from '@codemirror/state'; +import { CodeMirrorComponent } from '@sandkasten/codemirror6-editor'; +import { LanguageDescription } from '@codemirror/language'; +import { CODE_DEFAULTS, LANGUAGES } from '../languages'; @Component({ selector: "app-editor", templateUrl: "./editor.component.html", styleUrls: ["./editor.component.scss"], }) -export class EditorComponent implements OnInit { - loadingProgress: number = 0; // Pour suivre la progression du chargement +export class EditorComponent { isLoaded: boolean = false; // Pour vérifier si le chargement est terminé + readonly languages: LanguageDescription[] = LANGUAGES; // Mode par défaut - mode: AllowedLanguage = "text/typescript"; - options = { - lineNumbers: true, - mode: this.mode, - }; - defaults = codeDefaults; + 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) + }); + }); + } // Contenu de l'éditeur que l'on passera au serveur - editorContent: string = this.defaults[this.mode as keyof typeof codeDefaults]; + editorContent: string = CODE_DEFAULTS[this.selectedLanguage.name as keyof typeof CODE_DEFAULTS]; resultContent: string = ""; // Message d'erreur errorMessage: string = ""; - constructor( - private router: Router, - private codeExecutionService: CodeExecutionService - ) {} - - ngOnInit(): void { - // Appel à changeMode pour mettre à jour le contenu de l'éditeur et le mode - this.changeMode(); - const convert = new Convert(); - - this.codeExecutionService.getResult().subscribe((result) => { - if (result.type !== 'exit') { - this.resultContent += convert.toHtml(result.text); - } - }); - } + @ViewChild(CodeMirrorComponent) private codemirror!: CodeMirrorComponent; - // Change le langage de l'éditeur - changeMode(): void { - this.options = { - ...this.options, - mode: this.mode, - }; - this.editorContent = this.defaults[this.mode as keyof typeof codeDefaults]; - } + private readonly languageCompartment = new Compartment(); + protected readonly extensions: Extension[] = [ + basicSetup, + this.languageCompartment.of(this.selectedLanguage.support!) + ]; - // Affiche le contenu de l'éditeur - handleChange($event: Event): void { - console.log("ngModelChange", $event); + constructor( + private codeExecutionService: CodeExecutionService + ) { } // Efface le contenu de l'éditeur @@ -98,77 +57,34 @@ export class EditorComponent implements OnInit { this.editorContent = ""; } - loadFromFile(event: any): void { - const file = event.target.files[0]; - if (file) { - const allowedExtensions = ["sh", "js", "ts", "cpp", "c"]; - const extension = file.name.split(".").pop()?.toLowerCase(); - - if (extension && allowedExtensions.includes(extension)) { - // Identifiez le mode en fonction de l'extension - const modeMap: Record = { - sh: "text/x-sh", - js: "text/javascript", - ts: "text/typescript", - cpp: "text/x-c++src", - c: "text/x-csrc", - }; - this.mode = modeMap[extension]; + onRunButtonClicked() { + // Le code à exécuter est le contenu de l'éditeur + const codeToExecute = this.editorContent; - // Mettez à jour le contenu de l'éditeur et le mode - this.changeMode(); + this.codeExecutionService.executeCode(codeToExecute, this.selectedLanguage.name); - const reader = new FileReader(); - reader.onload = (e) => { - this.editorContent = e.target?.result as string; - this.errorMessage = ""; // Réinitialisez le message d'erreur en cas de succès. - }; - reader.readAsText(file); - } else { - this.errorMessage = - "Unsupported file type. Please select a file with one of the following extensions: sh, js, ts, cpp, c"; - console.error(this.errorMessage); - } - } + this.resultContent = ''; } - saveToFile(): void { - const languageExtensionMap: Record = { - "text/x-sh": "sh", - "text/javascript": "js", - "text/typescript": "ts", - "text/x-c++src": "cpp", - "text/x-csrc": "c", - }; - - if (languageExtensionMap[this.mode]) { - const extension = languageExtensionMap[this.mode]; - if (this.editorContent.trim() === "") { - this.errorMessage = "Cannot save an empty file."; - console.error(this.errorMessage); - } else { - this.errorMessage = ""; // Réinitialisez le message d'erreur en cas de succès. - const blob = new Blob([this.editorContent], { type: "text/plain" }); - const a = document.createElement("a"); - a.href = window.URL.createObjectURL(blob); - a.download = `code.${extension}`; - document.body.appendChild(a); - a.click(); - document.body.removeChild(a); + 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; + return; } - } else { - this.errorMessage = - "Unsupported language. Please select one of the following languages: shell, javascript, typescript, c++, c"; - console.error(this.errorMessage); } + 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); } - onRunButtonClicked() { - // Le code à exécuter est le contenu de l'éditeur - const codeToExecute = this.editorContent; - - this.codeExecutionService.executeCode(codeToExecute, this.mode); - - this.resultContent = ''; + 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(); } } diff --git a/src/app/components/languages.ts b/src/app/components/languages.ts new file mode 100644 index 0000000..abd7f0a --- /dev/null +++ b/src/app/components/languages.ts @@ -0,0 +1,49 @@ +import {LanguageDescription} from "@codemirror/language"; +import {javascript} from "@codemirror/lang-javascript"; + +export const CODE_DEFAULTS = { + 'C': /** @lang C */ `#include +int main() { + printf("Hello, World!\\n"); + return 0; +}`, + 'C++': /** @lang C++ */ `#include +int main() { + std::cout << "Hello, World!\\n"; + return 0; +}`, + 'JavaScript': /** @lang JS */ `console.log("Hello, World!");`, + 'TypeScript': /** @lang TS */ `console.log("Hello, World!");` +}; + +export const LANGUAGES = [ + LanguageDescription.of({ + name: 'C', + extensions: ['c', 'h', 'ino'], + load() { + return import("@codemirror/lang-cpp").then(m => m.cpp()) + } + }), + LanguageDescription.of({ + name: "C++", + alias: ['cpp'], + extensions: ['cpp', "c++", 'cc', 'cxx', 'hpp', "h++", 'hh', 'hxx'], + load() { + return import("@codemirror/lang-cpp").then(m => m.cpp()) + } + }), + LanguageDescription.of({ + name: 'JavaScript', + alias: ['ecmascript', 'js', 'node'], + extensions: ['js', 'mjs', 'cjs'], + support: javascript(), + }), + LanguageDescription.of({ + name: 'TypeScript', + alias: ['ts'], + extensions: ['ts'], + load() { + return import("@codemirror/lang-javascript").then(m => m.javascript({typescript: true})) + } + }), +]; diff --git a/src/main.ts b/src/main.ts index 01f6524..0a62114 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,6 +1,3 @@ -import 'codemirror/mode/javascript/javascript'; -import 'codemirror/mode/markdown/markdown'; - import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { AppModule } from './app/app.module'; diff --git a/src/styles.scss b/src/styles.scss index e78eea3..1d0b411 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -1,6 +1,3 @@ -@import 'codemirror/lib/codemirror.css'; -@import 'codemirror/theme/material.css'; - //region Colors $color-purple: #605FFC; $color-white: #FFFFFF;