Extindeți TMS WEB Core cu bibliotecile JS cu Andrew: CodeMirror
Motivația.
De ce avem nevoie de un editor de cod? Primul beneficiu al unui editor de cod, în comparație cu un simplu editor de text sau un câmp editabil TWebMemo, de exemplu, este că de obicei vine cu evidențiere de sintaxă care este configurată pentru limbajul de programare pe care îl editați. Pentru Pascal, aceasta înseamnă că începutul și sfârșitul sunt afișate automat într-o culoare diferită, precum și orice alte cuvinte cheie rezervate legate de Pascal. Acest lucru înseamnă, de obicei, că diferitele tipuri de paranteze sunt afișate într-un mod care facilitează găsirea perechilor de paranteze potrivite sau că comentariile apar într-o culoare sau stil diferit. Poate că nu pare mult, dar dacă ați încercat vreodată să scrieți cod fără acest lucru, este o experiență foarte diferită. Desigur, probabil că ești foarte familiarizat cu acest tip de lucruri, deoarece IDE-ul Delphi face exact acest lucru (și mult, mult mai mult!) atunci când editează codul.
CodeMirror 5 vs. CodeMirror 6.
Înainte de a merge mai departe, hai să rezolvăm rapid asta. În mod normal, sunt 100% all-in când vine vorba de a folosi marginea de vârf a oricărei biblioteci JS, cu gândul că va avea probabil cel mai bun suport pentru cele mai recente browsere și cele mai active eforturi de dezvoltare, în comparație. la versiuni mai vechi ale aceleiași biblioteci. Și acest lucru se aplică într-adevăr și aici. Cu toate acestea, în acest moment sunt puțin prea departe de vârf. Până acum, CodeMirror 6 este într-adevăr o colecție de module care nu pot fi încărcate direct (poate cu ușurință ar fi mai precis) într-o aplicație web, așa cum am făcut. În schimb, diferitele module și dependențele lor (și dependențele acelor module etc.) trebuie să fie combinate într-un pachet (folosind ceva precum rollup sau webpack) care poate fi apoi servit și utilizat ca bibliotecă de o aplicație web tipică. Și deși acesta este un exercițiu util și interesant în sine, va trebui să-l păstrăm pentru o postare viitoare pe blog dacă există suficient interes pentru el.
Noțiuni de bază.
Rețineți că am optat pentru a specifica @5 ca versiune, așa că vom primi toate actualizările și patch-urile în ramura CodeMirror 5, fără să ne facem griji că va fi schimbat brusc la CodeMirror 6, în cazul în care vor decide să lanseze un anumit configurație în acest mod în viitor.
Apoi, pentru a-l vedea în acțiune, trebuie să conectăm biblioteca CodeMirror la un element HTML. Nu vom fi prea luxurii aici, dar haideți să depunem un pic de efort, doar ca să avem câteva capturi de ecran frumoase de inclus. Vom folosi două componente TWebHTMLDiv, una numită divBorder pentru a oferi editorului nostru niște colțuri rotunjite drăguțe și, în cadrul acesteia, un al doilea TWebHTMLDiv, care va fi punctul de montare pentru editorul CodeMirror în sine. Vom folosi, de asemenea, un șablon Bootstrap pentru a face acest lucru puțin mai ușor.
Pentru componenta divBorder, vom seta și un ElementID al divBorder și vom seta ElementClassName să fie „rounded border-dark”. Pentru divEditor, vom lăsa ElementClassName necompletat, dar vom seta ElementID la divEditor. Proprietatea de aliniere a lui divEditor a fost, de asemenea, setată la alClient, astfel încât să umple spațiul din divBorder. Apoi, în WebFormCreate, vom adăuga următorul cod.
procedura TForm1.WebFormCreate(Expeditor: TObject); ÎNCEPE asm this.editor = CodeMirror(document.getElementById('divEditor'), { lineNumbers: adevărat }); Sfârşit; divEditor.ElementHandle.firstElementChild.classList.Add('w-100'); divEditor.ElementHandle.firstElementChild.classList.Add('h-100'); divEditor.ElementHandle.firstElementChild.classList.Add('rounded'); Sfârşit;
Nimic prea special despre asta, doar activăm numerele de linie ca verificare pentru a ne asigura că ne aflăm într-o instanță CodeMirror. Clasele suplimentare adăugate ulterior sunt adăugate elementului părinte CodeMirror care este creat ca un copil al divEditor. Și tot ceea ce facem acolo este să ne asigurăm că ocupă spațiul pe care divEditor îl are la dispoziție (care, la rândul său, are aceeași dimensiune ca divBorder) cu aceeași rotunjire, așa că avem colțuri frumoase cu aspect curat. Dacă apoi adăugăm un text exemplu (cu rupturi de linie adăugate manual aici), obținem următoarele.
Bara de derulare din dreapta apare atunci când este nevoie și bara de defilare orizontală apare, de asemenea, dacă liniile se întind dincolo de locul în care sunt acum. Rețineți că aceasta este o captură de ecran din Chrome (v102). Privind același control în FireFox (v100), este aproape același lucru, dar cu un stil diferit față de bara de defilare. Vom ajunge la asta puțin mai târziu.
De asemenea, rețineți că unele cuvinte, cum ar fi „face” și „în”, au o culoare diferită. Acesta este CodeMirror care încearcă deja să aplice evidențierea de sintaxă, care va fi Pascal, deoarece acesta a fost primul „mod” pe care l-am enumerat în lista noastră de limbi acceptate. Deoarece „do” și „în” sunt cuvinte cheie în Pascal, acestea sunt evidențiate aici.
Limbajul de codare = Mod.
În CodeMirror, „modul” se referă la un anumit fișier JS în limbaj de codare care este folosit pentru a aplica lucruri precum evidențierea sintaxelor. Poate fi specificat în mod explicit când este creată instanța CodeMirror, prin trecerea unei opțiuni mode:. Pentru a vă face o idee despre cum arată toate acestea, iată câteva exemple în care modul a fost setat în mod explicit și a fost adăugat un eșantion diferit la editor.
modul: “pascal”
mod: {nume: „javascript”, json: true}
mod: „reducere”
modul: “sql”
modul: “css”
mod: {name:”xml”, htmlMode:true}
modul: “javascript”
Rețineți că aceste moduri diferite modifică ușor modul în care este introdus textul. De exemplu, schimbarea evidențierii sintaxei din mers când sunt găsite perechi de elemente care se potrivesc. Sau adăugarea indentării adecvate atunci când închideți un element care se potrivește. Markdown este probabil cel mai dramatic în modul în care gestionează toate acestea.
Personalități împărțite.
De obicei, o secțiune de cod va reprezenta un singur limbaj de programare și un singur mod va fi suficient pentru editare. Dar există cazuri în care mai multe limbi sunt folosite într-un singur fișier și, prin urmare, ar fi util să aveți mai multe moduri. Cea mai frecventă situație este atunci când HTML, CSS și JavaScript sunt amestecate în același fișier. Pentru a gestiona acest scenariu, CodeMirror are un alt mod care poate fi inclus. Se bazează pe că sunt disponibile și modurile XML, JavaScript și CSS.
modul: “htmlmixed”
Ne vine imediat în minte un alt exemplu? Un fișier sursă, mai multe limbaje de programare? Da, sigur o face. Pascal cu JavaScript încorporat! IDE-ul Delphi, oricât de fantastic este, nu este deosebit de abil să gestioneze exact această situație. Desigur, CodeMirror nu are nicio idee că cineva ar dori o astfel de combinație, dar are un mecanism de „suprapunere” mai generic care, în esență, ne permite să facem același lucru. Pentru a-l folosi, trebuie să includem un script „Addon” în Project.html nostru și apoi trebuie să îi spunem ce vrem să facă. În mod implicit, vrem să fie în modul „pascal” și vrem să trecem la modul „javascript” atunci când întâlnim un token „asm” (deschis), apoi oprim modul „javascript” și revenim la modul „pascal” când vom întâlni un „sfârșit”; jeton (închidere). În primul rând, fișierul suplimentar de care avem nevoie este acesta.
Acum avem nevoie doar de un pic de cod pentru a-i spune cum să comută modurile la „javascript” și când să comutăm înapoi.
asm CodeMirror.defineMode("asm", function(config) { returnează CodeMirror.multiplexingMode( CodeMirror.getMode(config, "text/x-pascal"), { deschis: „asm”, inchidere: "sfarsit;", mod: CodeMirror.getMode(config, "text/javascript"), delimStyle: „delimitare” } ); }); this.editor = CodeMirror(document.getElementById('divEditor'), { modul: "asm", lineNumbers: adevărat }); Sfârşit;
Rezultatul final este că CodeMirror comută înainte și înapoi după cum este necesar. Alte limbi ar putea fi adăugate în același mod, doar adăugând mai multe strofe la definiție.
modul: “asm”
Dincolo de aceasta, există și alte modificări posibile. De exemplu, actualizarea modului Pascal pentru a include „asm” ca cuvânt cheie rezervat sau modificarea modului în care sunt gestionați delimitatorii (fie că sunt tratați ca Pascal sau JavaScript, de exemplu). De asemenea, ar putea fi util să utilizați un delimitator diferit. Poate folosind blocuri asm definite cu asm { și } end; sau chiar cu {$IFNDEF WIN32) asm { și } se încheie cu {$ENDIF} ca modalități de a oferi o pauză mai solidă între codul Pascal și JavaScript. Acest lucru poate fi deosebit de util atunci când JavaScript are numeroase paranteze (potențial nepotrivite) și poate chiar un „sfârșit”; care apare în blocul său care aparține acolo, dar nu înseamnă neapărat sfârșitul blocului asm. Se întâmplă, sincer! Desigur, în cele din urmă, IDE-ul Delphi trebuie încă să poată compila codul rezultat, așa că nu puteți înnebuni prea mult aici.
Asa de. Mulți. Teme.
Și apoi specificați tema pe care o doriți ca una dintre opțiuni.
this.editor = CodeMirror(document.getElementById('divEditor'), { modul: "asm", tema: "darcula", lineNumbers: adevărat });
Aceasta înseamnă că puteți încărca mai multe teme, dacă sunteți atât de înclinat, și apoi să aplicați teme diferite la diferite instanțe CodeMirror. De exemplu, poate doriți ceva întunecat când editați SQL și ceva ușor când editați Pascal. Opțiuni abundă! Iată cum arată „darcula”.
tema: “darcula”
Dincolo de teme, CodeMirror generează cod HTML și CSS 100%, fără nicio etichetă
neplăcută, așa că sunteți liber să-l personalizați în continuare cât doriți, folosind CSS pentru a suprascrie aproape totul. Toate temele sunt, ele însele, doar fișiere CSS și nu îngrozitor de complexe. Deci, cea mai ușoară abordare este probabil să găsești o temă care este oarecum apropiată de ceea ce ți-ai dori și apoi să modifici acel fișier CSS, fie încorporându-l în propriul tău fișier CSS, fie creând o temă nouă și adăugându-l la proiect folosind aceiași pași ca mai sus pentru temele existente.
VI vs. EMACS 2022.
Există, desigur, mult mai mult la CodeMirror decât o mică evidențiere a sintaxelor, numere de linie și ceva culoare ici și colo. Unele lucruri pe care le poate face sunt puțin nebunești. Și unele sunt exact ceea ce v-ați aștepta de la orice editor de text modern.
și apoi
this.editor = CodeMirror(document.getElementById('divEditor'), { modul: "asm", tema: "darcula", lineNumbers: adevărat, keyMap: „vim” });
Și ai plecat și fugi. Puțin bizar, dar funcțional. CodeMirror nu pretinde că acest lucru este complet, dar are suport pentru toate lucrurile de bază, inclusiv comenzile de căutare/înlocuire, copiere/lipire și navigare de bază. Asigurați-vă că consultați demonstrația lor a acestei funcții și rețineți că este posibil să fie necesar să adăugați și alte părți pentru a obține efectul complet. De exemplu, adăugarea de câteva elemente pentru a afișa modul curent de editare și așa mai departe. Suportul Emacs poate fi adăugat în același mod (doar înlocuiți vim cu emacs în cele de mai sus), dar verificați notele despre asta. Se pare că unele dintre cele mai comune legături de taste Emacs vă vor face probabil foarte nefericiți, având în vedere echivalentele lor în browser! Ceea ce, dacă nu se poate aminti, erau ambele „probleme” uriașe pe vremuri – vi inducerea schizofreniei din diferitele „moduri” sale și Emacs fiind, ei bine, Emacs, cu mult prea multe taste modificatoare pentru tot ceea ce se poate imagina. După cum se spune, cu cât lucrurile se schimbă mai mult, cu atât rămân la fel.
Căutare și înlocuire.
Apropo de asta, căutarea și înlocuirea textului este o activitate destul de comună atunci când utilizați orice editor de text. Aici, principala problemă este că avem nevoie de un fel de interfață, adică un dialog, pentru a obține informațiile de care avem nevoie. Deci un pic mai lăutăresc, dar fezabil totuși. În loc să le parcurgem individual, să le tratăm doar ca pe un set. Și nici măcar nu trebuie să modificăm codul care creează instanța – adăugarea acestora face toată treaba pentru noi.
Defilare, defilare, defilare.
Despre acele bare de defilare. Compatibilitatea cu browser-ul a crescut cu salturi și limite de-a lungul anilor și suntem aproape în punctul în care, de cele mai multe ori, poți să te uiți la o pagină web și nici măcar să nu știi ce browser a redat-o. Acest lucru este ajutat de faptul că au rămas atât de puține motoare. Dar chiar și totuși, „browser-ul Chrome” – referindu-se la părțile sistemului de operare nativ ale browser-ului încă vizibile, nu browserul Google Chrome, încă tinde să dezvăluie ce browser utilizați. Și chiar dacă ascundeți bara de titlu, rămâneți cu o diferență flagrantă – barele de defilare. Acestea sunt încă redate diferit (sau poate nici măcar nu sunt redate deloc) în funcție de combinația particulară a furnizorului de browser și a sistemului de operare. Oamenii care lucrează la CodeMirror au avut în mod clar destul de asta și și-au adăugat propriile suprascrieri pentru bara de defilare. Ceea ce sună grozav (și de fapt este grozav!), dar, desigur, probabil că sunteți blocat cu alte lucruri non-CodeMirror care au bare de defilare, așa că, deși acest lucru ar putea fi grozav pentru instanțele dvs. CodeMirror, nu vă va ajuta dacă aveți mai multe lucruri pe aceeași pagină cu diferite stiluri de bare de defilare. Dar nu contează! Să încercăm unul. Codul „simplescrollbar” este ceea ce căutăm și vom introduce „scrollpastend” pentru același preț mic și mic aici:
Apoi, în codul de inițializare CodeMirror, trebuie să adăugăm ceva pentru a-i spune despre noile bare de defilare. Și apoi voila! Obținem o bară de derulare cu aspect mai frumos care (sperăm) funcționează și arată la fel, în toate browserele și sistemele de operare. Asigurați-vă că testați totuși! Dacă ai vrut să schimbi culoarea barelor de defilare, este doar un pic mai mult CSS. Sau dacă nu doriți să utilizați CSS, îl puteți seta programatic astfel.
this.editor = CodeMirror(document.getElementById('divEditor'), { modul: "asm", lineNumbers: adevărat, scrollbarStyle: „suprapunere” }); var scrollbars = document.querySelectorAll(".CodeMirror-overlayscroll-horizontal,.CodeMirror-overlayscroll-vertical"); scrollbars.forEach(funcție (el) { el.firstElementChild.style.backgroundColor = 'darkorange'; });
Rezultatul ar trebui să arate cam așa.
Există o mulțime de alte modificări CSS pe care le puteți adăuga, de asemenea, care afectează dimensiunea și așa mai departe. Iată ce am afișat pe o pagină de autentificare, în care culoarea se potrivește cu tema generală a site-ului (se presupune că este culoarea scoarței de copac…) cu o bară mai largă și un decalaj mai mare de la margine. Ceea ce este un pic de joc de mână, deoarece granița este mai departe de controlul în sine, care nu are graniță.
Înregistrare, înregistrare, înregistrare.
Un alt caz de utilizare solid pentru ceva de genul CodeMirror este similar cu cel de mai sus – un loc în care să înregistrați diferiți biți de date pe măsură ce se întâmplă. În astfel de cazuri, este posibil să doriți să faceți două lucruri. Mai întâi, adăugați o nouă intrare la orice text afișat în prezent. Și în al doilea rând, derulați conținutul, astfel încât să puteți vedea cea mai recentă intrare în partea de jos, așa cum se întâmplă. Un fel de „coada -f” pentru cei care sunt atât de înclinați. Iată un exemplu în care se folosește un temporizator pentru a adăuga text.
procedura TForm2.WebTimer1Timer(Expeditor: TObject); var addthis: String; ÎNCEPE addthis := chr(10)+FormatDateTime('hh:nn:ss.zzz',Now)+' Spune ceva interesant'; asm this.editor.replaceRange(addthis, {line: Infinity}); this.editor.execCommand("goDocEnd"); Sfârşit; Sfârşit;
Dacă aveți deja text pe care doriți să îl încărcați în CodeMirror, puteți utiliza ceva de genul this.editor.setValue(text) , unde textul poate include tipuri precum WideString.
Mai mult din Totul.
Cu elementele de bază în afara drumului, există încă o mulțime de alte funcții de găsit. Există comenzi pe care le puteți folosi pentru a evidenția textul sau chiar pentru a marca părți din acesta ca fiind doar pentru citire. Plierea codului este un subiect mare, cu o mulțime de exemple. Același lucru este valabil și pentru completarea codului. Și tratarea parantezelor (potrivirea sau adăugarea lor automată) este alta. În cea mai mare parte, aceasta implică aceiași pași pe care i-am acoperit deja. Adăugarea unei biblioteci JS sau a unui fișier CSS la Project.html și apoi configurarea unor opțiuni în inițializarea CodeMirror.
Dar există o grămadă de lucruri pe care le puteți face, care sunt doar comenzi, așa cum am făcut mai sus cu încărcarea conținutului, adăugarea conținutului sau schimbarea feței de vizualizare. Mai degrabă decât simple proprietăți la inițializare, acestea sunt lucruri pe care le puteți face pentru a interacționa activ atât cu conținutul CodeMirror, cât și cu oricine îl folosește. Acțiunile din clipboard se încadrează în această categorie, precum și se ocupă fie de obținerea textului selectat, fie de selectarea activă a textului automat. Sau inserând text într-o anumită poziție. O mulțime de opțiuni aici, în funcție de ceea ce încerci să faci.
Ce nu face CodeMirror.
Doar pentru a vă scuti de probleme de căutare, CodeMirror nu este deloc perfect. Sunt câteva lucruri mici pe care mi-aș dori foarte mult să le poată face, dar nu le face. Având în vedere că proiectul este open source, acestea sunt lucruri care ar putea fi create în cele din urmă ca un supliment sau modificate pentru o anumită instalare. Dar le menționez aici doar pentru a vă scuti de osteneala de a le căuta. Îndoielnic că vor face vreodată apariția în CodeMirror 5, cu CodeMirror 6 care rulează așa cum este.
- Spații virtuale. Dacă știi ce este asta, probabil că nu te-ai mulțumi niciodată cu un editor fără el. Și aș fi de acord cu tine 100%. Aceasta se referă la modul în care, atunci când deplasați cursorul în sus și în jos printr-un bloc de text, cursorul rămâne în aceeași coloană, indiferent de lungimea textului din fiecare rând. Deci, dacă cursorul dvs. se află la sfârșitul unei linii de text care are 50 de caractere și apoi treceți cu cursorul în sus la o linie care are doar 2 caractere, cursorul va rămâne în coloana 50. Acesta este modul în care funcționează Delphi IDE. Notepad++ face acest lucru (sau cel puțin are o opțiune de activare – a trecut atât de mult timp încât nu-mi amintesc dacă este activat în mod implicit) și orice editor IDE care se respectă ar trebui să aibă și asta. Dar CodeMirror nu. Minus 10 puncte!
- Blocați liniile. În timp ce CodeMirror are o mulțime de caracteristici de pliere a codului, nu (atât de aproape îmi pot da seama) nu are capacitatea de a desena linii care conectează delimitatorii blocurilor. De exemplu, în IDE-ul Delphi, dacă aveți un bloc de început/sfârșit, veți vedea o linie care leagă cele două. Așadar, dacă formatați codul așa cum ar trebui orice persoană sănătoasă, blocurile imbricate vor avea o serie de linii verticale paralele care arată acea imbricare, făcându-vă ușor să vedeți când lipsește un „sfârșit” sau poate că aveți prea multe.
- Completarea codului. Există câteva funcții de completare a codului disponibile prin câteva suplimente. Și în timp ce acestea funcționează, ele nu se află într-adevăr în același stagiu cu ceea ce suntem obișnuiți în IDE-ul Delphi (când completarea codului funcționează corect, adică!). Utilizarea CodeMirror ca înlocuitor complet pentru editorul de cod Delphi IDE este probabil o problemă grea, mai ales din cauza asta, dacă asta ar fi ceva, poate că cineva se gândea un pic să facă. Te-ai putea apropia, dar ar fi nevoie de puțină muncă pentru a-l face util în acest mod.
Dar, în ciuda acestor deficiențe, mă îndoiesc că există o altă bibliotecă JS care este la fel de capabilă ca CodeMirror sau la fel de populară în ceea ce privește integrarea cu alte biblioteci JS pe care le utilizați deja în proiectele dvs.
Hei! Dar Ace Editor? Sau Monaco?
Da, există și alte biblioteci JS pe care le puteți folosi pentru a satisface această nevoie specială, cu siguranță. Ace Editor este inclus chiar și ca componentă în TMS WEB Core. Și este, de asemenea, o bibliotecă foarte bună. L-am folosit inițial în unele dintre primele mele proiecte TMS WEB Core și am reușit să-l fac să facă toate lucrurile pe care mi-am dorit să facă. Dar în cele din urmă a fost schimbat în favoarea CodeMirror, din două motive. Unul prost și unul nu atât de prost.
Motivul prostesc a fost că, ori de câte ori încărcam un bloc de text în Ace Editor, orice text care era mai lat decât o anumită porțiune din lățimea componentei de pe pagină avea ca rezultat o bară de defilare orizontală. Și detest barele de defilare orizontale! Și nu vreau să spun că o bară de derulare ar apărea la 98% sau 99%. Ar părea la 60% sau cam asa ceva. Super enervant! Sigur, aș putea scăpa cu totul de bara de defilare orizontală, dar nu mi-am putut da seama niciodată cum să o fac să apară numai atunci când linia de text era de fapt mai mare decât lățimea componentei. Ceva de-a face cu o combinație a fonturilor pe care le foloseam și cine știe ce altceva. A trecut ceva timp de atunci, iar cunoștințele mele CSS sunt puțin diferite acum decât erau atunci, așa că numesc asta prost deoarece este probabil rezolvabil. Dar am pus orele atunci și nu am putut să-mi dau seama de ce se comportă greșit în acest mod.
Motivul nu atât de prostesc este că folosesc alte biblioteci JS care, ele însele, folosesc CodeMirror pentru a oferi funcționalități suplimentare. După cum am menționat la început, cele două editoare HTML pe care le-am folosit cel mai mult în proiectele TMS WEB Core sunt Summernote și SunEditor. Ambele se bazează pe CodeMirror pentru a oferi funcționalitatea lor de „vizualizare ca HTML”. Ar părea mai puțin decât ideal să aveți o altă bibliotecă pentru a efectua aceeași sarcină dacă CodeMirror este deja necesar. Alți editori HTML, cum ar fi CKEditor și TinyMCE, de exemplu, au și pluginuri CodeMirror, așa că aceasta nu este o practică obscură în niciun caz.
Nu am folosit Monaco, dar a apărut în unele căutări. Dacă sunteți în căutarea unei biblioteci JS pentru acest gen de lucruri, aș sugera CodeMirror ca un candidat solid, dar, prin toate mijloacele, aruncați o privire în jur și vedeți ce este disponibil. În comparațiile directe din ultimul timp, CodeMirror 6 pare să fie candidatul principal, așa că ar putea fi timpul să ne dăm seama despre ce este vorba despre toate aceste chestii din modul și să-i dai o șansă și acestuia.
Componenta JSExtend?
Andrew Simard.