Comment créer une extension d'éditeur de code source Xcode

Ce que vous allez créer

introduction

Xcode est le principal environnement de développement intégré utilisé par des milliers de développeurs chaque jour. C'est un outil génial, mais vous souhaitez parfois personnaliser certaines de ses caractéristiques et de ses comportements pour mieux les adapter à votre flux de travail..

Jusqu'à Xcode 7, il était possible d'injecter du code dans Xcode au moment de l'exécution pour créer des plugins. Les plugins pourraient être soumis et distribués via une application géniale appelée Alcatraz. Ce n'est plus possible dans Xcode 8.

Xcode 8 valide chaque bibliothèque et chaque ensemble pour empêcher le code malveillant de s'exécuter sans votre permission. Au démarrage de Xcode, les plugins précédemment installés avec Alcatraz ne sont plus chargés. Mais tout n’est pas perdu, Apple a également annoncé à WWDC la possibilité de développer Extensions de l'éditeur de source Xcode afin que tout le monde puisse étendre les fonctionnalités d'édition de source existantes. Voyons ce que nous pouvons réaliser avec de telles extensions.

1. Commencer

Les extensions de l'éditeur de code source Xcode 8 constituent un premier pas dans la bonne direction. Si vous travaillez avec Xcode depuis un certain temps, vous vous êtes peut-être trouvé dans une situation où vous souhaitiez qu'une tâche spécifique puisse être automatisée dans Xcode. Les extensions d'éditeur source permettent aux applications tierces de modifier un fichier source, ce qui est exactement ce dont vous avez besoin pour accélérer votre flux de travail.

Pour l'instant, les extensions ne peuvent interagir qu'avec le code source. Cela signifie que tous les plugins disponibles via Alcatraz ne peuvent pas être remplacés par une extension de l'éditeur source. Mais qui sait ce que l'avenir apporte.

Il est important de comprendre que chaque extension doit être contenue dans une application MacOS. Vous pouvez, par exemple, ajouter des préférences et des explications sur l'action de l'extension dans l'application MacOS et la distribuer via le Mac App Store. Notez également que chaque extension fonctionne dans un processus séparé. Si l'extension se bloque, cela ne plantera pas Xcode. Au lieu de cela, il affichera un message indiquant que l'extension n'a pas pu terminer son travail..

En outre, les extensions ne pas avoir d'interface utilisateur et ils ne peuvent modifier le code directement que lorsque l'utilisateur appelle votre commande. Ils ne peuvent pas, par exemple, fonctionner en arrière-plan.

Je recommande de regarder la session WWDC 2016 sur les extensions de l'éditeur de source. Non seulement il explique comment commencer à développer des extensions d'éditeur de source Xcode, mais il présente également des astuces et des raccourcis pour accélérer votre développement..

2. aperçu

Dans ce tutoriel, nous allons développer une extension qui nettoie la syntaxe de fermeture dans Swift. Xcode complète automatiquement une syntaxe de fermeture avec les parenthèses, mais elles peuvent être omises pour des raisons de brièveté. C’est une tâche qui peut être facilement automatisée en l’enveloppant dans une extension de l’éditeur source.

L'explication plus courte de ce que nous allons développer est une extension qui transforme toute fermeture en syntaxe plus simple et plus propre. Regardez l'exemple ci-dessous.

// avant session.dataTask (avec: url) (données, réponse, erreur) dans // après session.dataTask (avec: url) données, réponse, erreur dans

3. Configuration du projet

Il va sans dire que ce tutoriel nécessite Xcode 8. Vous pouvez le télécharger à partir du site Web du développeur d’Apple. Il fonctionne sous OS X 10.11 et macOS 10.12..

Créer un nouveau OS X projet de type Application de cacao et lui donner le nom CleanClosureSyntax. Assurez-vous que vous avez défini la langue du projet sur Rapide. Nous allons utiliser le nouveau Swift 3 syntaxe dans ce tutoriel.

Nous allons laisser l'application macOS vide pour l'instant et nous pouvons nous concentrer sur la création de l'extension de l'éditeur de code source Xcode. Du Fichier menu, choisissez Nouveau> Cible… . Dans la barre latérale gauche, choisissez OS X et sélectionnez Extension de l'éditeur Xcode Source de la liste.

Cliquez sur Suivant Et mettre Nom du produit à Nettoyeur. Une nouvelle cible sera créée pour vous. Cliquez sur Activer si Xcode vous demande si la nouvelle création Schème devrait être activé.

4. Structure du projet

Analysons d'abord ce que Xcode vient de créer pour nous. Étendre le Nettoyeur dossier pour voir son contenu.

Nous ne modifierons pas SourceEditorExtension.swift dans ce tutoriel, mais il peut être utilisé pour personnaliser davantage votre extension. le extensionDidFinishLaunching () La méthode est appelée dès que l'extension est lancée afin que vous puissiez effectuer une initialisation si nécessaire. le commandDefinitions getter de propriété peut être utilisé si vous voulez afficher ou masquer de manière dynamique certaines commandes.

SourceEditorCommand.swift est l'entendre de l'extension. Ce fichier est l'endroit où vous allez implémenter la logique de l'extension. le effectuer (avec: completionHandler :) La méthode est appelée lorsque l'utilisateur lance votre extension. le XCSourceEditorCommandInvocation objet contient un tampon Cette propriété est utilisée pour accéder au code source du fichier actuellement sélectionné. Le gestionnaire d'achèvement doit être appelé avec une valeur néant si tout se passe bien, sinon passez le NSError exemple.

5. Mise en œuvre de l'extension

Maintenant que le projet contient toutes les cibles requises, nous sommes prêts à commencer à écrire l'extension. Pour récapituler, nous voulons supprimer les parenthèses de toute fermeture d'un fichier Swift. Cela peut être fait en trois étapes:

  • trouver les lignes qui contiennent une fermeture
  • enlever les deux parenthèses de cette ligne
  • remplacer la ligne modifiée

Commençons.

Nous pouvons utiliser une expression rationnelle (expression régulière) pour analyser chaque ligne de code et voir si elle contient une fermeture. Vous pouvez vous reporter au didacticiel d'Akiel sur Swift et les expressions régulières si vous souhaitez en savoir plus sur les expressions régulières. Vous pouvez utiliser RegExr pour tester vos expressions régulières. Regardez la capture d'écran suivante pour voir comment j'ai testé ma regex.

Ouvrir SourceEditorCommand.swift et modifier le effectuer (avec: completionHandler :) méthode pour ressembler à ceci:

func perform (avec invocation: XCSourceEditorCommandInvocation, completionHandler: (NSError?) -> Void) -> Void var updatedLineIndexes = [Int] () // 1. Recherchez les lignes contenant une syntaxe de fermeture pour lineIndex dans 0… < invocation.buffer.lines.count  let line = invocation.buffer.lines[lineIndex] as! NSString do  let regex = try RegularExpression(pattern: "\\.*\\(.+\\).+in", options: .caseInsensitive) let range = NSRange(0… < line.length) let results = regex.matches(in: line as String, options: .reportProgress, range: range) // 2. When a closure is found, clean up its syntax _ = results.map  result in let cleanLine = line.remove(characters: ["(", ")"], in: result.range) updatedLineIndexes.append(lineIndex) invocation.buffer.lines[lineIndex] = cleanLine   catch  completionHandler(error as NSError)   // 3. If at least a line was changed, create an array of changes and pass it to the buffer selections if !updatedLineIndexes.isEmpty  let updatedSelections: [XCSourceTextRange] = updatedLineIndexes.map  lineIndex in let lineSelection = XCSourceTextRange() lineSelection.start = XCSourceTextPosition(line: lineIndex, column: 0) lineSelection.end = XCSourceTextPosition(line: lineIndex, column: 0) return lineSelection  invocation.buffer.selections.setArray(updatedSelections)  completionHandler(nil) 

Rechercher des lignes avec la syntaxe de fermeture

Nous créons d’abord et divers Int les valeurs qui contiendront les index des lignes modifiées. C'est parce que nous ne voulons pas substituer toutes les lignes. Nous voulons remplacer uniquement les lignes que nous modifions.

Nous énumérons toutes les lignes de la invocation.buffer objet et nous essayons de trouver une correspondance pour la Expression régulière objet. Si je supprime les caractères d'échappement de l'expression régulière, cela ressemble à ce qui suit:

. * (. +). + in

Cette expression régulière correspond lorsqu'une chaîne présente les caractéristiques suivantes:

  • Il a une parenthèse ouverte bouclée (), suivi de 0 ou plusieurs caractères, sauf un caractère de nouvelle ligne (\ n).
  • Une parenthèse ouverte (() doit être retrouvé, suivi de 0 caractères ou plus. Cette partie doit contenir les paramètres de la fermeture.
  • Nous devons ensuite trouver une parenthèse fermante ()), suivi de 0 ou plusieurs caractères, qui sont les types de retour facultatifs.
  • Finalement, le dans mot-clé devrait être trouvé.

Si la Expression régulière objet ne parvient pas à trouver une correspondance (par exemple, si l'expression régulière n'est pas valide), nous appelons le AchèvementHandler avec l'erreur en paramètre. Si une chaîne qui correspond à toutes ces conditions est trouvée sur une ligne, nous avons correctement localisé une fermeture.

Syntaxe de nettoyage

Quand une correspondance est trouvée, on appelle une méthode utilitaire sur NSString cela supprime les parenthèses. Nous devons également passer dans la plage du match pour éviter de supprimer certaines autres parenthèses en dehors de la clôture..

Lignes de mise à jour

La dernière partie du code vérifie qu’au moins une ligne a été modifiée. Si cela est vrai, nous appelons setArray () pour remplacer les nouvelles lignes et les index corrects. Le gestionnaire d'achèvement est appelé avec la valeur néant afin que Xcode sache que tout s'est bien passé.

Nous devons encore mettre en œuvre le remove (characters: range :) méthode sur NSString. Ajoutons cette extension en haut du fichier.

extension NSString // Supprime les caractères donnés dans la plage func remove (caractères: [Caractère], dans la plage: NSRange) -> NSString var cleanString = self pour le caractère en caractères cleanString = cleanString.replacingOccurrences (of: String ), avec: "", options: .caseInsensitiveSearch, range: range) return cleanString

Cette méthode appelle replacementOccurrences (of: with: range :) sur NSString pour chaque personnage que nous voulons supprimer.

6. Essais

Xcode 8 est une excellente solution pour tester les extensions. Tout d’abord, si vous courez OS X 10.11 El Capitan, ouvrir Terminal, exécutez la commande suivante et redémarrez votre Mac.

sudo / usr / libexec / xpccachectl

Ceci fait, construisez et exécutez votre extension en sélectionnant le schéma approprié. Quand il demande quelle application à exécuter, recherchez Xcode et assurez-vous de sélectionner le bêta version de Xcode 8. Une nouvelle version de Xcode sera lancée avec l’icône de l’application grisée afin que vous puissiez reconnaître dans quelle instance de Xcode vous testez l’extension..

Dans la nouvelle instance Xcode, créez un nouveau projet ou ouvrez un projet existant, puis accédez à Editeur> Fermeture propre> Commande de l'éditeur source. Assurez-vous d’avoir au moins une fermeture dans le fichier Swift actuellement ciblé pour voir le résultat. Comme vous pouvez le voir dans l'animation suivante, notre extension fonctionne.

Commande de l'éditeur source est le nom par défaut d'une commande. Vous pouvez le modifier dans le Info.plist fichier de l'extension. Ouvrez-le et changez la chaîne en Syntaxe propre.

Nous pouvons également assigner un raccourci pour invoquer automatiquement le Syntaxe propre commander. Ouvrir Xcode Préférences et sélectionnez le Raccourcis clavier languette. Rechercher Syntaxe propre et la commande apparaîtra. Cliquez à droite de celui-ci et appuyez sur le raccourci que vous souhaitez utiliser, par exemple, Commande-Alt-Shift-+. Vous pouvez maintenant revenir au fichier source et appuyer sur ce raccourci pour l'appeler directement..

7. Trucs et astuces

Xcode 8 et les extensions de l'éditeur de source sont encore en version bêta au moment de la rédaction. Les conseils suivants peuvent vous aider à résoudre certains problèmes que vous pourriez rencontrer..

Si votre extension ne peut pas être sélectionnée dans l'instance de test Xcode, supprimez le com.apple.dt.Xcode.AttachToXPCService traiter et relancer l'extension.

Ne remplacez que les lignes que vous modifiez dans le tampon. Cela accélère l’extension et aura moins de chances d’être tué par Xcode. Cela peut arriver si Xcode pense qu'une commande de votre extension prend trop de temps.

Si vous souhaitez afficher plusieurs commandes, attribuez à chaque commande un identifiant différent et utilisez le commandIdentifier propriété sur le XCSourceEditorCommandInvocation objet à reconnaître lequel l'utilisateur a déclenché.

Conclusion

Créer une extension d'éditeur de source Xcode est vraiment facile. Si vous pouvez améliorer votre flux de travail et accélérer votre développement en créant une extension d'éditeur de source, poursuivez-le. Apple a introduit un nouveau moyen pour les développeurs de partager des plugins signés via le Mac App Store afin que vous puissiez publier votre travail et regarder les autres développeurs en tirer profit.

Vous pouvez trouver le code source de l'exemple de ce tutoriel sur GitHub.