#207 Syntax Highlighting
- Download:
- source codeProject Files in Zip (94.8 KB)
- mp4Full Size H.264 Video (19.9 MB)
- m4vSmaller H.264 Video (11.6 MB)
- webmFull Size VP8 Video (27.9 MB)
- ogvFull Size Theora Video (25 MB)
A volte c’è bisogno di mostrare estratti di codice in una applicazione Rails e per rendere il codice mostrato più leggibile, si può usare un evidenziatore di sintassi. Questo è ciò che sto facendo per quasi tutti gli episodi di questo sito, ma come faccio?
Ci sono una serie di alternative disponibili per aggiungere la colorazione della sintassi alle applicazioni Rails. La prima scelta da fare è decidere se si userà Rails per fare la colorazione del codice lato server, piuttosto che lato client con del JavaScript. Ci sono una serie di librerie JavaScript disponibili per questa seconda opzione, ma non ci occuperemo di queste, in quanto non sono specifiche di Rails. Piuttosto vi mostreremo tre diverse utility per l’evidenziazione del codice lato server, famore nel mondo Rails.
CodeRay
La prima, CodeRay. Può essere facilmente installata come gem, non ha dipendenze esterne ed è molto veloce. Comunque, è piuttosto limitato nelle funzionalità e nel supporto ai differenti linguaggi. Se riesce a soddisfare i vostri requisiti, comunque, rappresenta un’ottima soluzione. Lo vedremo più da vicino più avanti in questo episodio.
Ultraviolet
Un’altra alternativa è Ultraviolet. Anche questo è distribuito come gem, ma a differenza del primo, ha un paio di dipendenze esterne, per cui potrebbe essere leggermente piĆ¹ ostico da installare. Ultraviolet è più lento di CodeRay, ma di contro ha decisamente più funzionalità. Può avvalersi di file di sintassi Textmate e temi e perciò è potenzialmente in grado di trasformare molti più linguaggi rispetto a CodeRay, oltre che essere più flessibile rispetto al modo in cui gli snippet vengono mostrati.
Se scegliete Ultraviolet, è bene dare un’occhiata ad Harsh. Questo progetto è un plugin Rails che fornisce un pratico metodo helper per mostrare il codice evidenziato all’interno delle viste, oltre che un po’ di task rake per installare temi e gestirli. Il file README presente in tale progetto contiene istruzioni utili per installare Ultraviolet.
Pygments
Una terza popolare scelta è Pygments. Questo progetto è diverso da CodeRay e Ultraviolent in quanto è in realtà una libreria Python, ma dal momento che fornisce un’interfaccia da linea di comando, possiamo sfruttarla anche dalle nostre applicazioni Rails. Pygments è una libreria funzionalmente completa, ben supportata e usata in molti siti. Il rovescio della medaglia è che può rallentare parecchio se il parsing del codice coinvolge molte righe, per cui, nel caso la si usi, conviene ricorrere a tecniche di caching.
Per semplificare l’uso di Pygments con Rails, esiste un ottimo plugin chiamato highlight. Questo plugin fornisce un wrapper a Pygments che gli consente di essere invocato da semplici metodi helper direttamente all’interno delle viste. Le note di highlight raccomandano che tutto l’output generato sia mantenuto di cache, per mitigare i problemi di velocità citati poc’anzi. Per seguire il consiglio, tutto ciò che dobbiamo fare è racchiudere tutto l’output in un blocco cache
tipo questo:
<% cache do %> <%= highlight(:ruby, 'class Test; end') -%> <% end %>
Benchmarking
Per cui, ora che abbiamo fatto una panoramica sui tre concorrenti, vediamo come si comportano in termini di velocità, confrontandoli fra di loro. Un’applicazione potrebbe dover generare parecchio codice decorato, per cui dobbiamo valutare bene se ricorrere al caching o meno dell’output. Per confrontare ogni evidenziatore, useremo il seguente codice:
require 'rubygems' require 'benchmark' require 'coderay' require 'uv' path = __FILE__ content = File.read(__FILE__) # run it once to initialize CodeRay.scan("print 'hello'", 'ruby').div(:css => :class) Uv.parse("print 'test'", 'xhtml', 'ruby', true, 'amy') Benchmark.bm(11) do |b| b.report 'coderay' do 50.times { CodeRay.scan(content, 'ruby').div(:css => :class) } end b.report('ultraviolet') do 50.times { Uv.parse(content, 'xhtml', 'ruby', true, 'amy') } end b.report('pygments') do 50.times { `pygmentize -f html "#{path}"` } end end
Il programma di benchmark prende il contenuto del proprio stesso coidce sorgente e lo interpreta cinquanta volte con ogni libreria. Per Pygments usa il comando pygmentize
. I risultati sono qui mostrati:
$ ruby benchmark.rb user system total real coderay 0.310000 0.010000 0.320000 ( 0.312954) ultraviolet 4.860000 0.020000 4.880000 ( 4.886621) pygments 0.010000 0.120000 12.430000 ( 12.643173)
I risultati mostrano una po’ di differenze di performance fra ciascuna libreria. CodeRay è ovviamente la più veloce, impiegandoci 0.3 secondi per 50 iterazioni. Ultraviolet ci mette più di dieci volte tanto per svolgere lo stesso compito, mentre Pygments è stato ancora più lento, impiegandoci dodici secondi a completare il test. Date simili differenze di prestazioni, è bene comunque considerare nella valutazione anche l’aspetto delle funzionalità. Se si adotta la tecnica del caching, fra l’altro, l’aspetto della rapidità diventa meno significativo.
Usare CodeRay
Per il resto dell’episodio vi mostreremo come usare CodeRay in una applicazione Rails. CodeRay è il più semplice fra i tre da configurare e anche il più veloce, per cui non c’è necessità di preoccuparsi del caching.
Per illustrare CodeRay, useremo una semplice applicazione di blogging che potreste avere già visto in alcuni episodi precedenti. Lo useremo per aggiungere la possibilità di avere l'evidenziazione della sintassi per il codice presente negli articoli.
Quando gli autori modificano un articolo, vorremmo che potessero di aggiungere estratti di codice fra tag code
, tipo questo:
<code lang="ruby"> puts "Hello, world!" </code>
Quando un pezzo di codice tipo questo compare all’interno di un articolo, dovrebbe essere mostrato con un opportuna colorazione della sintassi.
Per fare tutto ciò per prima cosa dobbiamo aggiungere un riferimento al gem CodeRay nel file /config/environment.rb
. In questo caso stiamo lavorando con un’applicazione Rails 2. Se fosse stata scritta con Rails 3, avremmo modificato il file Gemfile invece del file environment.rb.
Rails::Initializer.run do |config| config.gem "coderay" config.gem "RedCloth" end
Più avanti faremo uso del Textile, così abbiamo anche aggiunto il gem di RedCloth qui. Esiste un gem che combina entrambi, ma in questo episodio abbiamo scelto di aggiungerli separatamente.
In seguito, aggiorniamo la vista show
del controller ArticleController
in modo tale che usi CodeRay per interpretare il testo fra tag code
ed evidenziarlo. Per farlo, possiamo racchiudere la linea di codice che mostra il contenuto dell’articolo in un nuovo metodo helper chiamato coderay
, in questo modo:
<% title @article.title %> <p class="author">from <%= @article.author %></p> <%= coderay(@article.content) %>
Il metodo helper deve andare nel nostro file application_helper.rb:
# Methods added to this helper will be available to all templates in the application. module ApplicationHelper def coderay(text) text.gsub(/\<code( lang="(.+?)")?\>(.+?)\<\/code\>/m) do CodeRay.scan($3, $2).div(:css => :class) end end end
Il metodo è piuttosto semplice. Accetta in ingresso del testo semplice che potrebbe contenere le sezioni che vorremmo evidenziare e chiama la gsub
su di esso, per trovare le sezioni che fanno match e sostituirle con quanto viene restituito dal blocco interno. L’espressione regolare cerca il tag di apertura code con un attributo opzionale lang
, e poi fa match con tutto ciò che si trova fra il tag code di apertura e quello di chiusura. Dentro al blocco viene chiamato il metodo CodeRay.scan
. Questo metodo accetta del testo e un linguaggio come argomenti, per cui gli passiamo $3
che rappresenta il gruppo di match dell’espressione regolare che corrisponde al testo fra tag code, e $2
che corrisponde al gruppo di match del contenuto dell’attributo lang
. Poi si chiama il .div
sul risultato, per avere il tutto contenuto all’interno di un elemento div
. L’opzione :css => :class
indica a CodeRay che tipo di stile CSS applicare. CodeRay ha un numero di diverse opzioni che si possono usare per controllare la struttura e l’aspetto dell’output: ulteriori informazioni si possono trovare nella documentazione.
Infine dobbiamo creare il foglio di stile per associare gli stili ad ogni classe di CodeRay. Per avere un tema che somigli a quello di Railscasts, Ryan Bates ha creato un foglio di stile che si può scaricare da Github. Prendiamo una copia di questo file e aggiungiamolo alla cartella /public/stylesheets della nostra applicazione:
.CodeRay { background-color: #232323; border: 1px solid black; font-family: 'Courier New', 'Terminal', monospace; color: #E6E0DB; padding: 3px 5px; overflow: auto; font-size: 12px; margin: 12px 0; } .CodeRay pre { margin: 0px; padding: 0px; } .CodeRay .an { color:#E7BE69 } /* html attribute */ .CodeRay .c { color:#BC9358; font-style: italic; } /* comment */ .CodeRay .ch { color:#509E4F } /* escaped character */ .CodeRay .cl { color:#FFF } /* class */ .CodeRay .co { color:#FFF } /* constant */ .CodeRay .fl { color:#A4C260 } /* float */ .CodeRay .fu { color:#FFC56D } /* function */ .CodeRay .gv { color:#D0CFFE } /* global variable */ .CodeRay .i { color:#A4C260 } /* integer */ .CodeRay .il { background:#151515 } /* inline code */ .CodeRay .iv { color:#D0CFFE } /* instance variable */ .CodeRay .pp { color:#E7BE69 } /* doctype */ .CodeRay .r { color:#CB7832 } /* keyword */ .CodeRay .rx { color:#A4C260 } /* regex */ .CodeRay .s { color:#A4C260 } /* string */ .CodeRay .sy { color:#6C9CBD } /* symbol */ .CodeRay .ta { color:#E7BE69 } /* html tag */ .CodeRay .pc { color:#6C9CBD } /* boolean */
C’è una classe CSS per ogni tipo di elemento del codice sorgente e i colori si possono facilmente cambiare per adattarli a un qualsiasi aspetto si preferisca per la propria applicazione.
Per fare applicare lo stile, lo dovremo aggiungere come riferimento nella sezione HEAD del file di layout dell’applicazione:
<%= stylesheet_link_tag 'blog', 'coderay' %>
Fatto tutto questo, possiamo modificare un articolo e, riguradando quanto inserito, ne dovremmo notare i contenuti di codice evidenziati.
Usare Textile
L’evidenziazione della sintassi è spesso usata con un semplice linguaggio di markup tipo Textile o Markdown. Per usare Textile nel nostro articolo, possiamo racchiudere il metodo coderay
nella vista show
in una chiamata a textilize
:
<%= textilize(coderay(@article.content)) %>
Non vogliamo che il codice restituito dal metodo coderay
sia interpretato da RedCloth, per cui ciò che dobbiamo fare è modificare leggermente il metodo, racchiudendo il suo output in un elemento notextile
, affinchè non venga interpretato:
def coderay(text) text.gsub(/\<code( lang="(.+?)")?\>(.+?)\<\/code\>/m) do content_tag("notextile", CodeRay.scan($3, $2).div(:css => :class)) end end
Se modifichiamo nuovamente l’articolo e aggiungiamo un po’ di codice di markup tipo questo:
The piano is a musical instrument played by means of a keyboard that produces sound by striking steel strings with felt hammers. The hammers immediately rebound allowing the strings to continue vibrating at their resonant frequency. These vibrations are transmitted through a bridge to a soundboard that amplifies them. * item * item 2 <code lang="ruby"> def hello puts 'Hello, world!' end </code>
l’articolo dovrebbe ora avere una lista non ordinata generata da Textile, oltre che il codice evidenziato:
E’ tutto per questo episodio sull’evidenziazione del codice. CodeRay semplifica l’aggiunta della decorazione del codice alle nostre applicazioni Rails e val bene darci un’occhiata se avete bisogno di una simile funzionalità.