L'histoire de l'écriture de Java dans Emacs

L'histoire de l'écriture de Java dans Emacs

Bonjour, je suis Peter Russo. Je ferai de mon mieux pour sélectionner le gouverneur! !! Cet article est l'article du 21ème jour de Emacs Advent Calandar 2016. Au fait, récemment, j'ai pu écrire Java dans Emacs, et cette fois j'aimerais en parler.

Environnement de développement Emacs et Java

Lorsqu'il s'agit de développement Java, il est courant d'utiliser des IDE. Actuellement, soit Eclipse ou IntelliJ? C'est comme ça?

L'IDE est certainement utile, mais il a toujours une image lourde. Si vous utilisez Emacs comme principal de base, vous ne pouvez pas dissiper l'image qui semble lourde par tous les moyens De nombreuses personnes souhaitent également développer Java avec Emacs.

Quels sont les moyens d'écrire Java dans Emacs?

Pour n'en nommer que quelques-uns, vous utiliseriez les packages suivants:

Tous ont eu du mal à les installer il y a longtemps, mais maintenant ils sont tous très faciles à essayer afin qu'ils puissent être installés via MELPA.

Maintenant, je voudrais voir quel type de paquet est chaque paquet.

JDEE

Depuis les temps anciens, Emacs a un énorme paquet basé sur CEDET appelé JDEE []. Il y a beaucoup de fonctionnalités. Puisqu'il a été enregistré auprès du MELPA, on peut dire que l'introduction est considérablement plus faible qu'auparavant.

J'ai récemment déplacé le référentiel sur GitHub et j'essaie de reprendre le développement, mais il existe encore de nombreuses parties non prises en charge de la syntaxe Java 5 et supérieure, telles que les génériques et les instructions étendues. De plus, comme il est basé sur CEDET, il sera plus difficile de le maintenir. À l'origine, les outils de construction ne prennent en charge que Ant et les fonctionnalités fondamentalement prises en charge sont également anciennes. (Les volontaires peuvent avoir ajouté diverses fonctions, mais toutes n'ont pas été fusionnées dans l'unité principale.)

JDEE [] a des fonctionnalités anciennes mais il est nombreux, il semble donc que certains utilisateurs utilisent encore JDEE [] avec une personnalisation individuelle.

Malabar-mode

Malabar-mode [] prend en charge Maven 3 et Java 6. Il y a beaucoup de soutien pour Maven, et c'est le package qui fonctionnait le mieux il y a dix ans.

C'était également mort pendant un certain temps et le mainteneur a été remplacé pour reprendre le développement. En conséquence, la série 1.x a été enregistrée auprès de MELPA et l'introduction de la série 1.x a été très faible.

La version suivante, 2.0, était également en cours de développement, mais Malabar-mode [] lui-même repose sur CEDET, et 2.0 nécessite la dernière version de CEDET, donc l'introduction a été assez difficile. Pour cette raison, il ne fonctionne souvent pas de manière stable et le développement est presque arrêté maintenant.

De nombreux analyseurs CEDET ne fonctionnent pas dans certains cas. Cela est dû à la spécification que CEDET utilise le temps d'inactivité pour l'analyse et la mise en cache. Par conséquent, l'achèvement peut ne pas s'afficher correctement immédiatement après l'ouverture du fichier ou il peut prendre un certain temps pour terminer. (S'il n'y a pas de résultat d'analyse, il commence à analyser sur place, mais c'est extrêmement lent)

Eclim

C'était à l'origine un port d'Emacs pour écrire Java dans Vim. Ceci est également enregistré auprès de MELPA, de sorte que l'introduction a considérablement diminué.

De nombreuses fonctionnalités non implémentées par rapport aux fonctionnalités originales Eclim ont été implémentées. Vous pouvez utiliser de manière transparente des fonctionnalités de type Eclipse telles que l'optimisation de l'importation, la refactorisation et l'exécution de tâches Ant et Maven, ainsi que la complétion. Puisqu'il utilise les fonctions d'Eclipse, il prend en charge Java 8 ainsi que les génériques. Cependant, étant donné que Eclim [] utilise toujours les fonctions d'Eclipse lui-même dans Emacs, il se peut que vous ne compreniez pas certaines parties sans avoir d'abord connaissance d'Eclipse.

En passant, l'intégration Java dans Visual Studio Code est également basée sur Eclipse (JDT).

ENSIME (ENJIME)

C'est également l'un des favoris actuels. ENSIME [] a une forte image d'environnement de développement Scala, mais il est déjà compatible Java et peut bénéficier d'Emacs. Une autre force est que de nombreux outils de construction sont pris en charge. Cependant, la configuration initiale prend du temps. Ce n'est pas une histoire qui demande du temps et des efforts. Cela signifie qu'après avoir exécuté la commande, rendez-vous dans un magasin éloigné qui vous donne envie d'y aller occasionnellement pour le déjeuner.

La partie compatible Java est appelée ENJIME, et même les fichiers Java fonctionnent simplement en activant le mode ensime. La complémentation fonctionne même avec Lambda etc., et le fonctionnement est léger. Cependant, bien que les bases soient les mêmes que dans ENSIME [], il n'y a actuellement aucune documentation du tout, donc si vous essayez de l'utiliser uniquement avec Java, il se peut qu'il y ait certaines parties que vous ne comprenez pas.

D'ailleurs, j'ai moi-même été invité à coopérer par la commission ENSIME.

Choix réaliste

Actuellement, si vous choisissez, il semble que vous aurez deux choix: Eclim [] ou ENSIME []. Cependant, Eclim [] nécessite Eclipse et est fastidieux à configurer, et c'est essentiellement comme exécuter Eclipse en arrière-plan. Alors pourquoi ne pas simplement utiliser Eclipse? Je me sens comme ça.

En ce qui concerne ENJIME, les paramètres initiaux de chaque outil de construction sont également un peu gênants pour ENJIME. Une autre impression négative est que la documentation fait trop défaut et qu'il est trop difficile de savoir si elle peut être personnalisée. Il peut être un inconvénient que la mise en place prenne énormément de temps.

Développement d'un nouvel environnement de développement

Donc, au final, à moins d'avoir un environnement qui vous convient (Représentant Russo), vous n'avez pas d'autre choix que de le développer. Je n'ai pas développé ce domaine à partir de zéro, donc ce serait peut-être une bonne idée de l'essayer. J'ai pensé, j'ai augmenté mon poids et commencé le développement. Au fait, je suis nouveau dans l'écriture d'un élisp décent.

À propos de la politique de conception

Tout d'abord, voyons ce que vous (le représentant Russo) voulez.

S'il y a quelque chose comme celui écrit ci-dessus, cela semble pratique. Alors, quel type de design faut-il faire pour le réaliser?

Méthode du modèle client-serveur

La première politique de conception majeure est le modèle client-serveur, qui réduit le traitement sur le client (c'est-à-dire Emacs cette fois). Cette méthode est le résultat de l'apprentissage des erreurs de nos prédécesseurs, et ENJIME et d'autres sont également de telles méthodes. Autant que possible, elisp est développé avec une politique de spécialisation uniquement dans la partie affichage et non pas d'écriture d'un énorme elisp.

Les raisons pour lesquelles vous n'écrivez pas elisp autant que possible sont les suivantes.

  1. Le problème des pauvres elisp
  2. problèmes de vitesse de traitement elisp
  3. Problèmes de traitement parallèle

Certains fous essaient de l'implémenter uniquement avec elisp, ce qui est une erreur. En regardant elisp seul, la vitesse de traitement n'est pas si mauvaise. Cependant, c'est le cas pour un traitement simple. En regardant les spécifications du langage elisp et les fonctions standard, il est difficile de dire qu'il est riche, donc pour décrire le traitement que vous souhaitez réaliser Vous devez écrire une quantité raisonnable de code, et vous devez exécuter autant de code. Même si vous utilisez le package comme bibliothèque, la quantité de code sera réduite, mais le traitement lui-même ne sera pas éliminé. À une vitesse d'exécution modérée, vous vous sentirez toujours lent. De plus, elisp ne peut pas traiter en parallèle. Maintenant que le multicœur est devenu courant, c'est une douleur. Par conséquent, elisp n'écrit pas beaucoup de logique et implémente la fonction principale côté serveur.

Bien entendu, cette méthode a également ses inconvénients. L'état du tampon actuellement écrit sur Emacs existe dans la mémoire d'Emacs et Le fait est qu'il n'est pas visible du côté serveur. Par conséquent, l'analyse syntaxique ne peut pas être effectuée en temps réel. Enregistrez-le une fois, reflétez-le dans un fichier, analysez-le par le serveur et renvoyez le résultat. Vous devrez prendre des mesures telles que. Il est également difficile de savoir si le fichier source enregistré est un fichier source syntaxiquement correct. Dans certains cas, le fichier source est à mi-chemin Cela peut être reflété. Je pense que cette partie est une partie divisible. Cette fois, lors de l'enregistrement du fichier, le serveur analyse le fichier source et gère l'état du fichier source sur le serveur. Si le fichier source n'est pas syntaxiquement correct, arrêtez l'analyse sur place et ne reflétez pas le résultat de l'analyse. Inversement, si vous ajoutez une nouvelle variable, elle ne sera analysée que si vous la sauvegardez, et elle ne sera pas complétée. Cette méthode est la même pour ENJIME et ainsi de suite. Cependant, il y a des parties douloureuses. Ce sera très difficile à écrire si vous complétez avec une chaîne de méthodes, etc. Par conséquent, nous ajouterons un traitement pour atténuer ces problèmes.

De plus, si vous adoptez le modèle de serveur, vous pourrez choisir librement le client. Cela signifie qu'il peut être porté sur Vim, Atom et VSCode avec moins d'effort. ENSIME [], Eclim [] sont exactement cette méthode, et il est très facile de supporter divers éditeurs.

langage de développement

Alors dans quelle langue le serveur doit-il être développé? Je voudrais en parler. À l'origine, l'une des motivations pour le développer était la sortie de l'API Gradle Tooling. Comme son nom l'indique, il vous permet de faire fonctionner Gradle directement via l'API. La configuration initiale de ENSIME [] etc. ne pourrait pas être effectuée sans appliquer Gradle Plugin, mais de telles choses ne sont plus nécessaires. Ensuite, il n'y a qu'une seule réponse et le langage de développement sera Java.

Java 8 VS Clojure

Eh bien, il existe de nombreux langages qui s'exécutent sur la JVM pour le développement en Java, il est donc possible de les écrire. J'ai commencé à écrire dans Clojure pour avoir une idée du prototype. Cependant, comme la vitesse de démarrage était trop lente, j'ai commencé à rendre la partie code de Clojure de plus en plus fine, et finalement Java a commencé à dépasser 90%, j'ai donc tout réécrit en Java.

L'option de démarrage commence également en mettant l'accent sur la vitesse de démarrage et la mémoire effacée.

-XX:+UseConcMarkSweepGC -XX:SoftRefLRUPolicyMSPerMB=50 -XX:+TieredCompilation -Xverify:none -Xms256m -Xmx2G

Coopération avec Build Tool

Le développement en Java nécessite la définition du chemin de classe. De nombreux projets utilisent un outil de construction existant. Il est très gentil de lire ces paramètres et de lier de manière transparente les paramètres du chemin de classe et les destinations de sortie.

Gradle Tooling API

Actuellement, les outils qui ne prennent pas en charge Gradle ne seront pas examinés. Gradle possède une API qui vous permet de vous connecter à Gradle Daemon en externe, d'envoyer des commandes et d'effectuer des tâches. Cette fois, nous nous connecterons à Gradle côté serveur et incorporerons des paramètres tels que la résolution des dépendances et le chemin de classe. La bonne chose à propos de cette API est que si vous avez un Gradle Wrapper, il téléchargera également le Gradle lui-même. Si vous validez le Wrapper de version Gradle utilisé dans le projet dans le projet, le corps principal sera téléchargé automatiquement et la dépendance sera résolue. Etc. peut être fait automatiquement.

Problème difficile de Maven

Maven est toujours très populaire. Maven peut également intégrer le Maven lui-même dans le serveur. J'ai en fait essayé cette méthode, mais je l'ai abandonnée car certaines fonctionnalités n'étaient pas bien injectées. (L'intérieur est DI, donc je ne sais pas quelle classe est dans quel Jar, et je ne suis pas sûr de la procédure d'initialisation.) Dans certains cas, le plug-in peut ne pas fonctionner selon la version de Maven. Par conséquent, nous adoptons une méthode qui exécute en interne une méthode hybride en analysant le POM et en exécutant la commande mvn et fonctionne ensemble.

Problème de chargeur de classe

Soit dit en passant, nous coopérons réellement avec le projet et effectuons des complémentations, etc., mais nous devons réfléchir à la manière d'acquérir les informations de la classe cible. Java a une API de réflexion, est-ce correct?

Problème lent de réflexion

On parle de lenteurs d'accès aux champs et d'appels de méthodes dans Reflection, mais qu'en est-il de l'énumération? Par exemple, je voudrais créer une liste de classes pour afficher une liste de classes à compléter. Guava a une classe appelée ClassPath qui accède aux informations de chemin de classe, mais appeler getAllClasses avec cela est ridiculement lent. C'est un facteur qui ralentit le chargement sur le chargeur de classe. Avec cela, une complémentation harmonieuse est difficile. En outre, le chargeur de classe est chargé avec des classes côté serveur. Par conséquent, les classes non liées qui ne sont pas dans le projet peuvent apparaître comme des candidats à l'achèvement.

ASM Reflector

J'ai créé mon propre réflecteur pour résoudre le problème ci-dessus. Puisqu'il n'est pas placé sur le chargeur de classe, il peut être un pur candidat pour être complété dans le projet. Nous adoptons également l'approche suivante pour résoudre le problème de vitesse.

  1. Détection de projets et compilation de projets
  2. Identifiez le chemin du fichier jar à partir du chemin de classe, scannez le fichier de classe du fichier jar en parallèle et créez une liste de classe.
  3. Notez quelle classe est dans quel bocal lors de la création de la liste de classe
  4. Identifiez le bocal de la classe spécifiée lors de l'émission des candidats à l'achèvement, recherchez à l'intérieur du bocal et lisez les membres, etc. avec ASM
  5. Placez le résultat de la lecture dans le cache mémoire et le cache fichier. S'il s'agit d'une classe du projet, enregistrez également la somme de contrôle de la source cible
  6. À partir de la prochaine fois, le candidat d'achèvement sera lu à partir du cache, et si la somme de contrôle est modifiée, il sera lu à nouveau par ASM.

Un grand nombre de classes

Mais combien y a-t-il de candidats complémentaires? Selon le JDK, il existe environ 9000 classes dans le seul standard Java. Et si vous ajoutez la dépendance de projet à cela, il indexera environ des dizaines de milliers de classes. Afin de traiter ces nombres énormes rapidement, il est nécessaire de paralléliser autant que possible.

Meghanada

Cependant, la mise en œuvre de ce qui précède est Meghanada []. Beaucoup sont implémentés sur des serveurs écrits en Java, mais le front elisp est également assez grand. Ce modèle client et serveur est fait en référence au mode ironie. En même temps que l'ouverture avec meghanada-mode [] dans Emacs, le serveur est démarré, connecté et les commandes sont échangées avec le serveur côté Emacs.

L'achèvement est implémenté en tant que backend pour l'entreprise. De plus, le système de vérification des erreurs de compilation est implémenté comme un vérificateur pour flycheck. Meghanada [] est conçu pour que la partie Emacs ne fasse pas autant que possible son propre traitement. Par conséquent, il peut être utilisé de la même manière que les autres paramètres de langue.

Les déclencheurs d'achèvement sont basés sur IntelliJ. Fondamentalement, pour ramasser des mots-clés, etc. Vous n'avez pas besoin d'appuyer sur une touche pour le terminer.

Facilité d'introduction

Meghanada [] considère également la facilité d'installation. Le fichier jar du serveur est transformé en un gros jar et téléchargé sur bintray. S'il n'y a pas de fichier jar de serveur localement du côté d'Elisp, il sera automatiquement téléchargé depuis bintray. Tout ce que vous avez à faire est de redémarrer Emacs et il semblera prêt à être utilisé. Vous n'êtes pas obligé de le compiler comme l'ironie.

Traitement asynchrone

Dans certains cas, il peut y avoir un traitement lourd. En prévision de cela, beaucoup sont conçus pour communiquer de manière asynchrone. Figer dans l'éditeur est un problème fatal. Le traitement d'achèvement utilise l'entreprise. L'entreprise dispose d'une API asynchrone, qui est utilisée pour l'empêcher de se figer.

Deux connexions

Meghanada [] colle deux connexions avec le serveur. L'un est pour l'envoi et la réception de commandes et l'autre pour les flux. Stream est une fonction permettant de recevoir la sortie en temps réel. Ceci est particulièrement utilisé pour vérifier les journaux de la console tels que l'exécution et la construction de JUnit.

Pour être honnête, je ne pense pas que ce serait possible sans flux, mais le LSP utilisé par VSCode n'a pas de flux.

finalement

J'ai brièvement présenté Meghanada []. J'ai commencé à écrire ce projet cette année et j'aurais aimé pouvoir le sortir dans l'année. Je l'ai réécrit plusieurs fois avant sa sortie, mais j'ai pu sortir la première édition vers octobre.

À l'heure actuelle, la partie analyse des sources a été implémentée indépendamment, mais cette partie est réécrite avec une API de compilateur interne comme ENSIME []. Je pense que cela nous permettra de prendre pleinement en charge les références lambda et de méthode.

Recommended Posts

L'histoire de l'écriture de Java dans Emacs
L'histoire de la comparaison de chaînes de bas niveau en Java
L'histoire de la fabrication d'un Othello ordinaire à Java
L'histoire de l'apprentissage de Java dans la première programmation
[Édition Java] Histoire de la sérialisation
Obtenez le résultat de POST en Java
Discussion continue sur l'écriture de Java avec Emacs @ 2018
[Java] Gestion des Java Beans dans la chaîne de méthodes
À propos de l'idée des classes anonymes en Java
Une histoire sur le JDK à l'ère de Java 11
Mesurer la taille d'un dossier avec Java
Ressentez le passage du temps même à Java
Importer des fichiers de la même hiérarchie en Java
L'histoire de l'oubli de fermer un fichier en Java et de l'échec
Obtenez l'URL de la destination de la redirection HTTP en Java
L'histoire d'une exception d'état illégale dans Jetty.
[Java] Récupère le fichier dans le fichier jar quel que soit l'environnement
[Java] Lors de l'écriture du source ... Mémorandum ①
Modifier la qualité de stockage des images JPEG en Java
Implémentation Java de tri-tree
L'histoire de la création de DTO, semblable à Dao avec Java, SQLite
Récapitulez les éléments supplémentaires de la classe Optional dans Java 9
L'histoire que .java est également construite dans Unity 2018
Une explication rapide des cinq types de statique Java
20190803_Java & k8s sur Azure L'histoire d'aller au festival
L'histoire du lancement de données BLOB depuis EXCEL dans DBUnit
Comptez le nombre de chiffres après la virgule décimale en Java
Éléments à prendre en compte lors de l'écriture de code en Java
Comment dériver le dernier jour du mois en Java
L'histoire de la transmission de Java à Heroku à l'aide du pipeline BitBucket
Accéder à l'interface réseau avec Java
Spécifiez l'emplacement Java dans eclipse.ini
Histoire du passage de Java Gold SE8
Décompressez le fichier zip en Java
L'histoire de @ViewScoped dévore la mémoire
Liste des membres ajoutés dans Java 9
Analyser l'analyse syntaxique de l'API COTOHA en Java
Liste des types ajoutés dans Java 9
Ordre de traitement dans le programme
L'origine des expressions Java lambda
Implémentation d'une fonction similaire en Java
Appelez la super méthode en Java
Examiner les informations système de l'environnement d'exploitation AWS Lambda en Java
L'histoire de ne pas connaître le comportement de String en passant par Java
Sortie de la différence entre chaque champ de deux objets en Java
Examiner la liste des polices disponibles dans AWS Lambda + Java
Une histoire sur l'utilisation de l'API League Of Legends avec JAVA
Examinez la liste des ID de fuseau horaire disponibles dans la classe Java ZoneId
Obtenez l'URL publique du fichier privé de Flickr en Java
Créons une application TODO en Java 5 Changer l'affichage de TODO
Comment obtenir la longueur d'un fichier audio avec Java
Comment incrémenter la valeur de Map sur une ligne en Java
L'histoire de la rencontre avec l'annotation personnalisée Spring
Implémentation de DBlayer en Java (RDB, MySQL)
Histoire de paiza.jp [solution d'entrée standard Java]
Référence Java à comprendre dans la figure
Différences d'écriture en Ruby, PHP, Java, JS