Sublime Text 2 è un editor di testo altamente personalizzabile che sta catturando sempre più l'attenzione dei programmatori alla ricerca di uno strumento potente, veloce e moderno. Oggi, ricreeremo il mio famoso plug-in Sublime che invia CSS tramite l'API Nettuts + Prefixr per un facile cross-browser CSS.
Al termine, avrai una solida comprensione di come viene scritto il plug-in Sublime Prefixr e di essere pronto per iniziare a scrivere i tuoi plugin per l'editor!
Il modello di estensione per Sublime Text 2 è abbastanza completo.
Il modello di estensione per Sublime Text 2 è abbastanza completo. Ci sono modi per cambiare l'evidenziazione della sintassi, il chrome effettivo dell'editor e tutti i menu. Inoltre, è possibile creare nuovi sistemi di build, auto-completamenti, definizioni di linguaggio, snippet, macro, associazioni di tasti, collegamenti del mouse e plugin. Tutti questi diversi tipi di modifiche sono implementate tramite file organizzati in pacchetti.
Un pacchetto è una cartella che è memorizzata nel tuo Pacchi
directory. È possibile accedere alla directory dei pacchetti facendo clic sul Preferenze> Sfoglia pacchetti ... voce di menu. È anche possibile raggruppare un pacchetto in un singolo file creando un file zip e modificando l'estensione in .sublime-package
. Parleremo un po 'più in dettaglio della confezione in questo tutorial.
Sublime viene fornito in bundle con un certo numero di pacchetti diversi. La maggior parte dei pacchetti in bundle sono specifici per la lingua. Questi contengono definizioni di lingua, auto-completamenti e sistemi di compilazione. Oltre ai pacchetti di lingua, ci sono altri due pacchetti: Predefinito
e Utente
. IlPredefinito
Il pacchetto contiene tutti i binding di tasti standard, le definizioni dei menu, le impostazioni dei file e un sacco di plugin scritti in Python. Il Utente
il pacchetto è speciale in quanto è sempre caricato per ultimo. Ciò consente agli utenti di sovrascrivere i valori predefiniti personalizzando i file nella loro Utente
pacchetto.
Durante il processo di scrittura di un plug-in, il riferimento all'API di Sublime Text 2 è essenziale.
Durante il processo di scrittura di un plug-in, il riferimento all'API di Sublime Text 2 è essenziale. Inoltre, il Predefinito
il pacchetto funge da buon riferimento per capire come fare le cose e cosa è possibile. Gran parte delle funzionalità dell'editor è esposta tramite comandi. Qualsiasi operazione diversa dalla digitazione dei caratteri viene eseguita tramite i comandi. Visualizzando il Preferenze> Associazioni tasti - Predefinitovoce di menu, è possibile trovare un tesoro di funzionalità integrate.
Ora che la distinzione tra un plugin e un pacchetto è chiara, iniziamo a scrivere il nostro plugin.
Sublime è dotato di funzionalità che generano uno scheletro di codice Python necessario per scrivere un semplice plug-in. Seleziona il Strumenti> Nuovo plugin ... voce di menu, e un nuovo buffer verrà aperto con questo boilerplate.
import sublime, sublime_plugin class ExampleCommand (sublime_plugin.TextCommand): def run (self, edit): self.view.insert (edit, 0, "Hello, World!")
Qui puoi vedere i due moduli Sublime Python importati per consentire l'uso dell'API e viene creata una nuova classe di comando. Prima di modificare questo e iniziare a creare il nostro plugin, salviamo il file e attiviamo la funzionalità integrata.
Quando salviamo il file, creeremo un nuovo pacchetto per archiviarlo. Premere Ctrl + s (Windows / Linux) o cmd + s (OS X) per salvare il file. La finestra di dialogo di salvataggio si aprirà su Utente
pacchetto. Non salvare il file lì, ma cerca una cartella e crea una nuova cartella denominata Prefixr
.
Pacchetti / ... - OCaml / - Perl / - PHP / - Prefixr / - Python / - R / - Rails / ...
Ora salva il file all'interno della cartella Prefixr come Prefixr.py
. In realtà non importa quale sia il nome del file, solo che finisce .py
. Tuttavia, per convenzione useremo il nome del plugin per il nome del file.
Ora che il plugin è stato salvato, proviamo. Apri la console di Sublime premendo Ctrl +'. Questa è una console Python che ha accesso a theAPI. Inserisci il seguente Python per testare il nuovo plugin:
view.run_command ( 'esempio')
Tu dovresti vedere Ciao mondo
inserito all'inizio del file del plugin. Assicurati di annullare questa modifica prima di continuare.
Per i plugin, Sublime fornisce tre diversi tipi di comandi.
vista
oggetto Finestra
oggetto Dato che manipoleremo il contenuto di un file / buffer CSS con questo plugin, useremo il sublime_plugin.TextCommand
classe come base del nostro comando Prefixr personalizzato. Questo ci porta all'argomento di nominare le classi di comando.
Nello scheletro del plugin fornito da Sublime, noterai la classe:
class ExampleCommand (sublime_plugin.TextCommand):
Quando volevamo eseguire il comando, abbiamo eseguito il seguente codice nella console:
view.run_command ( 'esempio')
Sublime prenderà qualsiasi classe che estende uno dei sublime_plugin
classi
(TextCommand
, WindowCommand
o ApplicationCommand
), rimuovere il suffisso Comando
e quindi convertire il CamelCase
in underscore_notation
per il nome del comando.
Quindi, per creare un comando con il nome prefixr
, la classe deve essere PrefixrCommand
.
class PrefixrCommand (sublime_plugin.TextCommand):
Una delle funzionalità più utili di Sublime è la possibilità di avere selezioni multiple.
Ora che abbiamo il nostro plugin chiamato correttamente, possiamo iniziare il processo di acquisizione dei CSS dal buffer corrente e inviarlo all'API Prefixr. Una delle funzionalità più utili di Sublime è la possibilità di avere selezioni multiple. Mentre stiamo afferrando il testo selezionato, dobbiamo scrivere la nostra spina in modo da gestire non solo la prima selezione, ma tutte.
Dal momento che stiamo scrivendo un comando di testo, abbiamo accesso alla vista corrente tramite self.view
. Il sel ()
metodo del vista
l'oggetto restituisce un iterabile regionset
delle selezioni correnti. Iniziamo con la scansione di questi per parentesi graffe. Se le parentesi graffe non sono presenti, è possibile espandere la selezione alle parentesi circostanti per garantire che l'intero blocco sia prefissato. Se la nostra selezione includesse o meno le parentesi graffe, sarà utile in seguito per sapere se possiamo modificare lo spazio bianco e la formattazione sul risultato che otteniamo dall'API Prefixr.
braces = False sels = self.view.sel () per sel in sels: se self.view.substr (sel) .find ('')! = -1: parentesi = True
Questo codice sostituisce il contenuto dello scheletro correre()
metodo.
Se non abbiamo trovato parentesi graffe, passiamo in rassegna ogni selezione e regoliamo le selezioni sulla parentesi graffa di chiusura più vicina. Successivamente, usiamo il comando integrato expand_selection
con il a
argomento impostato parentesi
per assicurarci di avere il contenuto completo di ogni blocco CSS selezionato.
se non parentesi: new_sels = [] per sel in sels: new_sels.append (self.view.find ('\', sel.end ())) sels.clear () per sel in new_sels: sels.add (sel ) self.view.run_command ("expand_selection", "to": "parentesi")
Se si desidera ricontrollare il proprio lavoro fino a quel momento, si prega di confrontare la fonte con il file Prefixr-1.py
nel file zip del codice sorgente.
Per evitare che una connessione scarsa interrompa altri lavori, dobbiamo assicurarci che le chiamate all'API Prefixr avvengano in background.
A questo punto, le selezioni sono state espanse per afferrare l'intero contenuto di ogni blocco CSS. Ora, dobbiamo inviarli all'API Prefixr. Questa è una semplice richiesta HTTP, che useremo urllib
e urllib2
moduli per. Tuttavia, prima di iniziare a licenziare le richieste web, dobbiamo pensare a come una richiesta web potenzialmente in ritardo possa influire sulle prestazioni dell'editor. Se, per qualche motivo, l'utente si trova su una latenza elevata o una connessione lenta, le richieste all'API Prefixr potrebbero facilmente richiedere un paio di secondi o più.
Per evitare che una connessione scarsa interrompa altri lavori, dobbiamo assicurarci che le chiamate all'API Prefixr avvengano in background. Se non si conosce nulla sul threading, una spiegazione molto semplice è che i thread sono un modo in cui un programma può programmare più set di codici da eseguire apparentemente allo stesso tempo. È essenziale nel nostro caso perché consente al codice che sta inviando dati e in attesa di una risposta da parte dell'API Prefixr di impedire il congelamento dell'intera interfaccia utente Sublime.
Useremo Python threading
modulo per creare discussioni. Per utilizzare il modulo di threading, creiamo una nuova classe che si estende threading.Thread
chiamato PrefixrApiCall
. Classi che si estendono threading.Thread
includere a correre()
metodo che contiene tutto il codice da eseguire nel thread.
class PrefixrApiCall (threading.Thread): def __init __ (self, sel, string, timeout): self.sel = sel self.original = stringa self.timeout = timeout self.result = Nessuno threading.Thread .__ init __ (self) def run (autonomo): prova: data = urllib.urlencode ('css': self.original) request = urllib2.Request ('http://prefixr.com/api/index.php', data, header = " User-Agent ":" Sublime Prefixr ") http_file = urllib2.urlopen (request, timeout = self.timeout) self.result = http_file.read () return tranne (urllib2.HTTPError) as (e): err = '% s: errore HTTP% s che contatta l'API '% (__name__, str (e.code)) eccetto (urllib2.URLError) as (e): err ='% s: errore URL% s che contatta API '% (__name__, str ( e.reason)) sublime.error_message (err) self.result = False
Qui usiamo il thread __dentro__()
metodo per impostare tutti i valori che saranno necessari durante la richiesta web. Il correre()
il metodo contiene il codice e avvia la richiesta HTTP per l'API Prefixr. Poiché i thread operano in concomitanza con altro codice, non è possibile restituire direttamente i valori. Invece abbiamo impostato self.result
al risultato della chiamata.
Dal momento che abbiamo appena iniziato a utilizzare altri moduli nel nostro plugin, dobbiamo aggiungerli alle istruzioni di importazione nella parte superiore dello script.
import urllib import urllib2 import threading
Ora che abbiamo una classe threaded per eseguire le chiamate HTTP, dobbiamo creare un thread per ogni selezione. Per fare questo torniamo indietro nel correre()
metodo del nostro PrefixrCommand
classe e utilizzare il seguente ciclo:
threads = [] per sel in sels: string = self.view.substr (sel) thread = PrefixrApiCall (sel, string, 5) threads.append (thread) thread.start ()
Teniamo traccia di ogni thread che creiamo e quindi chiamiamo il inizio()
metodo per iniziare ciascuno.
Se si desidera ricontrollare il proprio lavoro fino a quel momento, si prega di confrontare la fonte con il file Prefixr-2.py
nel file zip del codice sorgente.
Ora che abbiamo iniziato le attuali richieste API di Prefixr, dobbiamo prima riassumere alcuni ultimi dettagli prima di gestire le risposte.
Per prima cosa, cancelliamo tutte le selezioni perché le abbiamo modificate prima. Più tardi li riporteremo in uno stato ragionevole.
self.view.sel (). clear ()
Inoltre iniziamo una nuova modificare
oggetto. Questo raggruppa le operazioni per annullare e ripristinare. Specifichiamo che stiamo creando un gruppo per il prefixr
comando.
edit = self.view.begin_edit ('prefixr')
Come passaggio finale, chiamiamo un metodo che scriverà successivamente che gestirà il risultato delle richieste API.
self.handle_threads (modifica, discussioni, parentesi graffe)
A questo punto i nostri thread sono in esecuzione, o forse addirittura completati. Successivamente, dobbiamo implementare il handle_threads ()
metodo a cui abbiamo appena fatto riferimento. Questo metodo sta per scorrere l'elenco dei thread e cercare i thread che non sono più in esecuzione.
def handle_threads (self, edit, threads, braces, offset = 0, i = 0, dir = 1): next_threads = [] per il thread nei thread: if thread.is_alive (): next_threads.append (thread) continua se thread. risultato == False: continue offset = self.replace (modifica, thread, bretelle, offset) threads = next_threads
Se un thread è ancora attivo, lo aggiungiamo all'elenco dei thread per ricontrollare più tardi. Se il risultato è stato un fallimento, lo ignoriamo, tuttavia per buoni risultati chiamiamo un nuovo sostituire()
metodo che scriveremo presto.
Se ci sono dei thread che sono ancora vivi, dobbiamo controllarli di nuovo a breve. Inoltre, è un buon miglioramento dell'interfaccia utente fornire un indicatore di attività per mostrare che il nostro plug-in è ancora in esecuzione.
if len (threads): # Questo attiva un piccolo indicatore di attività nell'area di stato prima = i% 8 dopo = (7) - prima se non dopo: dir = -1 se non prima: dir = 1 i + = dir self. view.set_status ('prefixr', 'Prefixr [% s =% s]'% \ ("* prima," * after)) sublime.set_timeout (lambda: self.handle_threads (modifica, discussioni, parentesi graffe, offset, i, dir), 100) ritorno
La prima sezione di codice utilizza un valore intero semplice memorizzato nella variabile io
spostare un =
avanti e indietro tra due parentesi. L'ultima parte è la più importante però. Questo dice a Sublime di eseguire il handle_threads ()
di nuovo, con nuovi valori, in altri 100 millisecondi. Questo è proprio come il setTimeout ()
funzione in JavaScript.
Il
lambda
keyword è una funzionalità di Python che ci consente di creare una nuova funzione anonima o anonima.
Il sublime.set_timeout ()
il metodo richiede una funzione o un metodo e il numero di millisecondi finché non deve essere eseguito. Senza lambda
potremmo dire che volevamo correre handle_threads ()
, ma non saremmo in grado di specificare i parametri.
Se tutti i thread sono completati, non è necessario impostare un altro timeout, ma al termine completiamo il nostro gruppo di annullamento e aggiorniamo l'interfaccia utente per far sapere all'utente che è stato fatto tutto.
self.view.end_edit (edit) self.view.erase_status ('prefixr') selezioni = len (self.view.sel ()) sublime.status_message ('Prefixr eseguito correttamente su% s selezione% s'% (selezioni, " se selezioni == 1 altro 's')))
Se si desidera ricontrollare il proprio lavoro fino a quel momento, si prega di confrontare la fonte con il file Prefixr-3.py
nel file zip del codice sorgente.
Con i nostri thread gestiti, ora abbiamo solo bisogno di scrivere il codice che sostituisce il CSS originale con il risultato dell'API Prefixr. Come abbiamo fatto riferimento in precedenza, scriveremo un metodo chiamato sostituire()
.
Questo metodo accetta un numero di parametri, incluso il modificare
oggetto per annullare, il thread che ha afferrato il risultato dall'API Prefixr, se la selezione originale includeva parentesi graffe e infine l'offset di selezione.
def replace (self, edit, thread, braces, offset): sel = thread.sel original = thread.original result = thread.result # Qui regoliamo ogni selezione per qualsiasi testo che abbiamo già inserito se offset: sel = sublime.Region (sel.begin () + offset, sel.end () + offset)
L'offset è necessario quando si ha a che fare con selezioni multiple. Quando sostituiamo un blocco di CSS con il prefisso CSS, la lunghezza di quel blocco aumenterà. L'offset assicura che stiamo sostituendo il contenuto corretto per le selezioni successive poiché il testo posiziona tutte le maiuscole su ciascuna sostituzione.
Il passo successivo è preparare il risultato dall'API Prefixr per essere rilasciato come CSS sostitutivo. Ciò include la conversione di terminazioni di linea e rientro per abbinare il documento corrente e la selezione originale.
result = self.normalize_line_endings (result) (prefisso, main, suffix) = self.fix_whitespace (originale, risultato, sel, parentesi graffe) self.view.replace (modifica, sel, prefisso + main + suffisso)
Come passaggio finale impostiamo la selezione dell'utente per includere la fine dell'ultima riga del nuovo CSS che abbiamo inserito, quindi restituiamo l'offset corretto da utilizzare per ulteriori selezioni.
end_point = sel.begin () + len (prefisso) + len (principale) self.view.sel (). add (sublime.Region (end_point, end_point)) offset di ritorno + len (prefisso + main + suffisso) - len ( originale)
Se si desidera ricontrollare il proprio lavoro fino a quel momento, si prega di confrontare la fonte con il file Prefixr-4.py
nel file zip del codice sorgente.
Abbiamo utilizzato due metodi personalizzati durante il processo di sostituzione per preparare il nuovo CSS per il documento. Questi metodi prendono il risultato di Prefixr e lo modificano per adattarsi al documento corrente.
normalize_line_endings ()
prende la stringa e si assicura che corrisponda alle terminazioni di riga del file corrente. Noi usiamo il impostazioni
classe dall'API Sublime per ottenere le terminazioni di linea appropriate.
def normalize_line_endings (self, string): string = string.replace ('\ r \ n', '\ n'). replace ('\ r', '\ n') line_endings = self.view.settings (). get ('default_line_ending') if line_endings == 'windows': string = string.replace ('\ n', '\ r \ n') elif line_endings == 'mac': string = string.replace ('\ n', '\ r') restituisce stringa
Il fix_whitespace ()
il metodo è un po 'più complicato, ma fa lo stesso tipo di manipolazione, solo per il rientro e lo spazio bianco nel blocco CSS. Questa manipolazione funziona davvero solo con un singolo blocco di CSS, quindi usciremo se una o più parentesi sono state incluse nella selezione originale.
def fix_whitespace (auto, originale, prefisso, sel, parentesi graffe): # Se le parentesi graffe sono presenti, possiamo eseguire tutte le magie di spazi bianchi se le parentesi graffe: return (", prefisso,")
Altrimenti, iniziamo determinando il livello di rientro del CSS originale. Questo viene fatto cercando gli spazi bianchi all'inizio della selezione.
(row, col) = self.view.rowcol (sel.begin ()) indent_region = self.view.find ('^ \ s +', self.view.text_point (row, 0)) se self.view.rowcol ( indent_region.begin ()) [0] == row: indent = self.view.substr (indent_region) else: indent = "
Successivamente abbiamo tagliato lo spazio bianco dal CSS prefissato e usato le impostazioni di visualizzazione correnti per far rientrare il CSS tagliato al livello originale usando tabulazioni o spazi a seconda delle impostazioni correnti dell'editor.
prefixed = prefixed.strip () prefixed = re.sub (re.compile ('^ \ s +', re.M), ", prefisso) settings = self.view.settings () use_spaces = settings.get ('translate_tabs_to_spaces' ) tab_size = int (settings.get ('tab_size', 8)) indent_characters = '\ t' se use_spaces: indent_characters = "* tab_size prefisso = prefixed.replace ('\ n', '\ n' + indent + indent_characters)
Finiamo il metodo usando lo spazio bianco iniziale e finale originale per garantire che il nuovo CSS prefissato si adatti esattamente al posto dell'originale.
match = re.search ('^ (\ s *)', original) prefisso = match.groups () [0] match = re.search ('(\ s *) \ Z', originale) suffix = match.groups () [0] return (prefisso, prefisso, suffisso)
Con il fix_whitespace ()
metodo abbiamo usato il modulo rex regular expression (re), quindi dobbiamo aggiungerlo all'elenco delle importazioni nella parte superiore dello script.
importa re
E con questo, abbiamo completato il processo di scrittura del prefixr
comando. Il prossimo passo per rendere il comando facile da eseguire fornendo una scorciatoia da tastiera e una voce di menu.
La maggior parte delle impostazioni e delle modifiche che possono essere apportate a Sublime vengono eseguite tramite file JSON, e questo è vero per le associazioni di tasti. Le associazioni di tasti sono in genere specifiche del sistema operativo, il che significa che per il plug-in dovranno essere creati tre file di collegamenti chiave. I file dovrebbero essere nominati Predefinito (Windows) .sublime-keymap
, Default (Linux) .sublime-keymap
e Default (OSX) .sublime-keymap
.
Prefixr / ... - Default (Linux) .sublime-keymap - Default (OSX) .sublime-keymap - Default (Windows) .sublime-keymap - Prefixr.py
Il .sublime-keymap
i file contengono un array JSON che contiene oggetti JSON per specificare i collegamenti dei tasti. Gli oggetti JSON devono contenere a chiavi
e comando
chiave e può anche contenere a args
chiave se il comando richiede argomenti. La parte più difficile in merito al prelievo di un legame chiave è garantire che il collegamento chiave non sia già utilizzato. Questo può essere fatto andando al Preferenze> Associazioni tasti - Predefinito voce del menu e cerca la combinazione di tasti che desideri utilizzare. Una volta trovata una rilegatura adeguatamente inutilizzata, aggiungila al tuo .sublime-keymap
File.
["keys": ["ctrl + alt + x"], "command": "prefixr"]
Normalmente i collegamenti dei tasti Linux e Windows sono gli stessi. Il tasto cmd su OS X è specificato dalla stringa super
nel .sublime-keymap
File. Quando si esegue il porting di un collegamento chiave tra i sistemi operativi, è comune per ctrl
chiave su Windows e Linux da scambiare super
su OS X. Questo non può, tuttavia, essere sempre il movimento della mano più naturale, quindi se possibile prova e prova i tuoi keybindings su una tastiera reale.
Uno degli aspetti più interessanti dell'estensione di Sublime è che è possibile aggiungere elementi alla struttura del menu creando .sublime-menù
File. I menufili devono essere nominati con nomi specifici per indicare quale menu influenzano:
Main.sublime-menù
controlla il menu principale del programmaSide Bar.sublime-menu
controlla il menu di scelta rapida su un file o una cartella nella barra lateraleContext.sublime-menù
controlla il menu del tasto destro del mouse su un file in fase di modificaCi sono una manciata di altri file di menu che riguardano vari altri menu nell'intera interfaccia. Navigando attraverso il Predefinito pacchetto è il modo più semplice per conoscere tutti questi.
Per Prefixr vogliamo aggiungere una voce di menu al modificare menu e alcune voci al Preferenze menu per le impostazioni. Il seguente esempio è la struttura JSON per il modificare voce di menu. Ho omesso le voci per il Preferenze menu dal momento che sono abbastanza prolissi essendo nidificati a pochi livelli.
["id": "modifica", "figli": ["id": "wrap", "command": "prefixr"]]
L'unico pezzo a cui prestare attenzione è il id
chiavi. Specificando il id
di una voce di menu esistente, è possibile aggiungere una voce senza ridefinire la struttura esistente. Se apri il Main.sublime-menù
file dal Predefinito
pacchetto e navigare in giro, è possibile determinare cosa id
vuoi aggiungere la tua voce a.
A questo punto il tuo pacchetto Prefixr dovrebbe apparire quasi identico alla versione ufficiale su GitHub.
Ora che hai avuto il tempo di scrivere un utile plug-in Sublime, è ora di entrare nella mano degli altri utenti.
Sublime supporta la distribuzione di un file zip di una directory di pacchetti come un modo semplice per condividere pacchetti. Basta semplicemente zippare la cartella del pacchetto e modificare l'estensione in
.sublime-package
. Ora altri utenti possono inserire questo nel loro Pacchetti installati directory e riavvia Sublime per installare il pacchetto.
Insieme alla facile disponibilità di molti utenti, il fatto che il pacchetto sia disponibile tramite Package Control garantisce che gli utenti vengano aggiornati automaticamente agli ultimi aggiornamenti.
Mentre questo può certamente funzionare, c'è anche un gestore di pacchetti forSublime chiamato Package Control che supporta un elenco principale di pacchetti e aggiornamenti automatici. Per aggiungere il pacchetto al canale predefinito, basta ospitarlo su GitHubor BitBucket e quindi inserire il file del canale (su GitHub o BitBucket), aggiungere il repository e inviare una richiesta di pull. Una volta accettata la richiesta di pull, il pacchetto sarà disponibile per migliaia di utenti che utilizzano Sublime. Insieme alla facile disponibilità di molti utenti, il fatto che il pacchetto sia disponibile tramite Package Control garantisce che gli utenti vengano aggiornati automaticamente agli ultimi aggiornamenti.
Se non si desidera ospitare su GitHub o BitBucket, esiste un sistema canale / repository customJSON che può essere utilizzato per ospitare ovunque, pur continuando a fornire il pacchetto a tutti gli utenti. Fornisce anche funzionalità avanzate come la specificazione della disponibilità di pacchetti per OS. Vedi la pagina PackageControl per maggiori dettagli.
Ora che abbiamo coperto i passaggi per scrivere un plug-in Sublime, è ora che tu ti tuffi! La community di plug-in di Sublime sta creando e pubblicando nuove funzionalità quasi ogni giorno. Con ogni versione, Sublime diventa sempre più potente e versatile. Il Forum Sublime Text è un ottimo posto per ricevere aiuto e parlare con gli altri di ciò che stai costruendo.