Die Geschichte des Schreibens von Java in Emacs

Die Geschichte des Schreibens von Java in Emacs

Hallo, ich bin Peter Russo. Ich werde mein Bestes tun, um den Gouverneur auszuwählen! !! Dieser Artikel ist der 21. Tagesartikel von Emacs Advent Calandar 2016. Übrigens konnte ich kürzlich Java in Emacs schreiben, und diesmal möchte ich darüber sprechen.

Emacs und Java Entwicklungsumgebung

Bei der Java-Entwicklung werden häufig IDEs verwendet. Derzeit entweder Eclipse oder IntelliJ? Ist es so

Die IDE ist sicherlich nützlich, hat aber immer noch ein schweres Image. Wenn Sie Emacs als Basishaupt verwenden, können Sie das Bild, das schwer zu sein scheint, nicht zerstreuen Viele Menschen haben den starken Wunsch, Java auch mit Emacs zu entwickeln.

Wie kann man Java in Emacs schreiben?

Um nur einige zu nennen, würden Sie die folgenden Pakete verwenden:

Alle hatten es vor langer Zeit schwer, sie zu installieren, aber jetzt sind sie alle sehr einfach zu testen, damit sie über MELPA installiert werden können.

Jetzt möchte ich sehen, welche Art von Paket jedes Paket ist.

JDEE

Seit der Antike hat Emacs ein riesiges CEDET-basiertes Paket namens JDEE []. Es gibt ziemlich viele Funktionen. Da es bei MELPA registriert wurde, kann gesagt werden, dass die Einführung erheblich geringer ist als zuvor.

Ich habe das Repository kürzlich auf GitHub verschoben und versuche, die Entwicklung fortzusetzen, aber es gibt immer noch viele nicht unterstützte Teile der Java 5-Syntax und höher, z. B. Generika und Erweiterungen für Anweisungen. Da es auf CEDET basiert, wird es auch schwierig sein, es weiter zu warten. Die Build-Tools unterstützen ursprünglich nur Ant, und die Funktionen, die grundsätzlich unterstützt werden, sind ebenfalls alt. (Freiwillige haben möglicherweise verschiedene Funktionen hinzugefügt, aber nicht alle wurden in die Haupteinheit integriert.)

JDEE [] hat alte Funktionen, ist aber zahlreich, so dass es scheint, dass einige Benutzer JDEE [] immer noch individuell angepasst verwenden.

Malabar-mode

[Malabar-Modus] [] unterstützt Maven 3 und Java 6. Es gibt viel Unterstützung für Maven, und dies ist das Paket, das vor einem Jahrzehnt am besten funktioniert hat.

Dies war auch für eine Weile tot und der Betreuer wurde ersetzt, um die Entwicklung fortzusetzen. Infolgedessen wurde die 1.x-Serie bei MELPA registriert, und die Einführung der 1.x-Serie war sehr gering.

Die nächste Version, 2.0, befand sich ebenfalls in der Entwicklung, aber [Malabar-Modus] [] selbst basiert auf CEDET, und 2.0 erfordert die neueste Version von CEDET, sodass die Einführung ziemlich schwierig war. Aus diesem Grund funktioniert es oft nicht stabil und die Entwicklung ist jetzt fast gestoppt.

Viele der CEDET-Parser funktionieren in einigen Fällen nicht. Dies liegt an der Spezifikation, dass CEDET die Leerlaufzeit für die Analyse und das Caching verwendet. Daher wird der Abschluss möglicherweise nicht sofort nach dem Öffnen der Datei korrekt angezeigt, oder der Abschluss kann lange dauern. (Wenn kein Analyseergebnis vorliegt, beginnt die Analyse vor Ort, ist jedoch extrem langsam.)

Eclim

Es war ursprünglich eine Portierung von Emacs zum Schreiben von Java in Vim. Dies ist auch bei MELPA registriert, so dass die Einführung erheblich zurückgegangen ist.

Viele der Funktionen, die im Vergleich zu den ursprünglichen Eclim -Funktionen nicht implementiert wurden, wurden implementiert. Sie können Eclipse-ähnliche Funktionen wie Importoptimierung, Refactoring und Ausführen von Ant- und Maven-Aufgaben sowie deren Abschluss nahtlos verwenden. Da es die Funktionen von Eclipse verwendet, unterstützt es sowohl Java 8 als auch Generika. Da Eclim [] jedoch weiterhin die Funktionen von Eclipse selbst in Emacs verwendet, gibt es möglicherweise einige Teile, die Sie möglicherweise nicht verstehen, ohne vorher über Eclipse Bescheid zu wissen.

Abgesehen davon basiert die Java-Integration in Visual Studio Code auch auf Eclipse (JDT).

ENSIME (ENJIME)

Dies ist auch einer der aktuellen Favoriten. ENSIME [] hat ein starkes Image als Scala-Entwicklungsumgebung, ist jedoch bereits Java-fähig und kann von Emacs profitieren. Eine weitere Stärke ist, dass viele Build-Tools unterstützt werden. Die Ersteinrichtung dauert jedoch einige Zeit. Dies ist keine Geschichte, die Zeit und Mühe kostet. Dies bedeutet, dass Sie nach Ausführung des Befehls zu einem entfernten Geschäft gehen, in dem Sie gelegentlich zum Mittagessen gehen möchten.

Der Java-fähige Teil heißt ENJIME und arbeitet mit Java-Dateien, indem einfach der ensime-Modus aktiviert wird. Die Ergänzung funktioniert sogar mit Lambda usw., und die Operation ist leicht. Obwohl die Grundlagen mit ENSIME [] identisch sind, gibt es derzeit überhaupt keine Dokumentation. Wenn Sie also versuchen, sie nur mit Java zu verwenden, gibt es möglicherweise einige Teile, die Sie nicht verstehen.

Ich selbst bin übrigens vom ENSIME-Ausschuss zur Zusammenarbeit aufgefordert worden.

Realistische Wahl

Wenn Sie sich entscheiden, haben Sie derzeit anscheinend zwei Möglichkeiten: Eclim [] oder ENSIME []. Eclim [] erfordert jedoch Eclipse und ist umständlich einzurichten. Es ähnelt im Wesentlichen dem Ausführen von Eclipse im Hintergrund. Warum also nicht einfach Eclipse verwenden? Ich fühle mich so.

Wenn es um ENJIME geht, sind die Anfangseinstellungen für jedes Build-Tool auch für ENJIME etwas problematisch. Ein weiterer negativer Eindruck ist, dass die Dokumentation zu mangelhaft ist und zu unklar ist, ob sie angepasst werden kann. Es kann ein Nachteil sein, dass die Einrichtung enorm viel Zeit in Anspruch nimmt.

Entwicklung einer neuen Entwicklungsumgebung

Letztendlich haben Sie also keine andere Wahl, als sie zu entwickeln, es sei denn, Sie haben eine Umgebung, die zu Ihnen passt (Vertreter Russo). Ich habe diesen Bereich nicht von Grund auf neu entwickelt, daher ist es möglicherweise eine gute Idee, ihn auszuprobieren. Ich dachte, ich erhöhte mein Gewicht und begann mit der Entwicklung. Ich bin übrigens neu darin, ein anständiges Elisp zu schreiben.

Informationen zur Designrichtlinie

Lassen Sie uns zunächst klären, was Sie (Vertreter Russo) wollen.

Wenn es so etwas wie das oben geschriebene gibt, scheint es praktisch zu sein. Also, welche Art von Design sollte gemacht werden, um es zu realisieren?

Client-Server-Modellmethode

Die erste wichtige Entwurfsrichtlinie ist das Client-Server-Modell, das die Verarbeitung auf dem Client reduziert (diesmal Emacs). Diese Methode ist das Ergebnis des Lernens aus den Fehlern unserer Vorgänger, und auch ENJIME und andere sind solche Methoden. Elisp wird so weit wie möglich mit der Politik entwickelt, sich nur auf den Anzeigeteil zu spezialisieren und kein großes Elisp zu schreiben.

Die Gründe dafür, nicht so viel wie möglich zu schreiben, sind folgende.

  1. Das Problem der armen Elisp
  2. Probleme mit der Verarbeitungsgeschwindigkeit
  3. Probleme mit der Parallelverarbeitung

Einige Verrückte versuchen es nur mit elisp umzusetzen, was ein Fehler ist. Wenn man nur elisp betrachtet, ist die Verarbeitungsgeschwindigkeit nicht so schlecht. Dies ist jedoch bei einer einfachen Verarbeitung der Fall. Wenn man sich die Sprachspezifikationen und Standardfunktionen von elisp ansieht, ist es schwer zu sagen, dass es reichhaltig ist, um die Verarbeitung zu beschreiben, die Sie realisieren möchten Sie müssen eine angemessene Menge an Code schreiben und so viel Code ausführen. Selbst wenn Sie das Paket als Bibliothek verwenden, wird die Codemenge reduziert, aber die Verarbeitung selbst wird nicht eliminiert. Bei einer moderaten Ausführungsgeschwindigkeit fühlen Sie sich immer noch langsam. Elisp kann auch nicht parallel verarbeitet werden. Jetzt, da Multi-Core zum Mainstream geworden ist, ist dies ein Schmerz. Daher schreibt elisp nicht viel Logik und implementiert die Hauptfunktion auf der Serverseite.

Natürlich hat dieses Verfahren auch seine Nachteile. Der Status des aktuell auf Emacs geschriebenen Puffers ist im Speicher von Emacs und vorhanden Der Punkt ist, dass es auf der Serverseite nicht sichtbar ist. Daher ist es nicht möglich, eine Syntaxanalyse in Echtzeit durchzuführen. Speichern Sie es einmal, spiegeln Sie es in einer Datei wider, analysieren Sie es vom Server und geben Sie das Ergebnis zurück. Sie müssen Schritte wie unternehmen. Es ist auch unklar, ob die gespeicherte Quelldatei eine syntaktisch korrekte Quelldatei ist. In einigen Fällen befindet sich die Quelldatei auf halbem Weg Es kann reflektiert werden. Ich denke, dieser Teil ist ein teilbarer Teil. Dieses Mal analysiert der Server beim Speichern der Datei die Quelldatei und verwaltet den Status der Quelldatei auf dem Server. Wenn die Quelldatei syntaktisch nicht korrekt ist, stoppen Sie die Analyse sofort und geben Sie das Analyseergebnis nicht wieder. Wenn Sie dagegen eine neue Variable hinzufügen, wird diese nur analysiert, wenn Sie sie speichern, und sie wird nicht ergänzt. Diese Methode ist für ENJIME usw. identisch. Es gibt jedoch einige schmerzhafte Teile. Es wird sehr schwierig zu schreiben sein, wenn Sie eine Methodenkette usw. ergänzen. Daher werden wir die Verarbeitung hinzufügen, um diese Probleme zu beheben.

Wenn Sie das Servermodell übernehmen, können Sie den Client frei wählen. Dies bedeutet, dass es mit weniger Aufwand auf Vim, Atom und VSCode portiert werden kann. ENSIME [], Eclim [] sind genau diese Methode, und es ist sehr einfach, verschiedene Editoren zu unterstützen.

Entwicklungssprache

In welcher Sprache sollte der Server entwickelt werden? Ich möchte darüber sprechen. Ursprünglich war eine der Motive für die Entwicklung dieses Themas die Veröffentlichung der Gradle Tooling API. Wie der Name schon sagt, können Sie Gradle direkt über die API bedienen. Die Ersteinrichtung von ENSIME [] usw. war ohne die Anwendung des Gradle-Plugins nicht möglich, aber solche Dinge sind nicht mehr erforderlich. Dann gibt es nur eine Antwort und die Entwicklungssprache wird Java sein.

Java 8 VS Clojure

Nun, es gibt viele Sprachen, die auf der JVM für die Entwicklung in Java ausgeführt werden, daher ist es möglicherweise in Ordnung, in ihnen zu schreiben. Ich begann in Clojure zu schreiben, um ein Gefühl für den Prototyp zu bekommen. Da die Startgeschwindigkeit jedoch zu langsam war, begann ich, den Code in Clojure immer dünner zu machen, und schließlich überschritt Java 90%, sodass ich alles in Java neu schrieb.

Die Startoption beginnt auch mit Schwerpunkt auf Startgeschwindigkeit und gelöschtem Speicher.

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

Zusammenarbeit mit Build Tool

Für die Entwicklung in Java muss der Klassenpfad festgelegt werden. Viele Projekte verwenden ein vorhandenes Build-Tool. Es ist sehr nett, diese Einstellungen zu lesen und die Klassenpfadeinstellungen und Ausgabeziele nahtlos zu verknüpfen.

Gradle Tooling API

Derzeit werden Tools, die Gradle nicht unterstützen, nicht betrachtet. Gradle verfügt über eine API, mit der Sie eine externe Verbindung zum Gradle Daemon herstellen, Befehle senden und Aufgaben ausführen können. Dieses Mal stellen wir eine Verbindung zu Gradle auf der Serverseite her und integrieren Einstellungen wie Abhängigkeitsauflösung und Klassenpfad. Das Schöne an dieser API ist, dass wenn Sie einen Gradle Wrapper haben, dieser auch den Gradle selbst herunterlädt. Wenn Sie den im Projekt verwendeten Gradle-Versions-Wrapper in das Projekt übernehmen, wird der Hauptteil automatisch heruntergeladen und die Abhängigkeit wird aufgelöst. Usw. kann automatisch erfolgen.

Maven schwieriges Problem

Maven ist immer noch sehr beliebt. Maven kann den Maven auch selbst in den Server einbetten. Ich habe diese Methode tatsächlich ausprobiert, aber ich habe sie aufgegeben, weil einige Funktionen nicht gut injiziert wurden. (Das Innere ist DI, daher bin ich mir nicht sicher, welche Klasse in welchem Jar ist, und ich bin mir nicht sicher über das Initialisierungsverfahren.) In einigen Fällen funktioniert das Plug-In je nach Version von Maven möglicherweise nicht. Daher verwenden wir eine Methode, die intern eine Hybridmethode ausführt, indem sie das POM analysiert und den Befehl mvn ausführt und zusammenarbeitet.

Problem mit dem Klassenlader

Übrigens kooperieren wir tatsächlich mit dem Projekt und führen Ergänzungen usw. durch, aber wir müssen darüber nachdenken, wie wir die Informationen der Zielklasse erhalten können. Java hat eine Reflection API, ist das in Ordnung?

Reflexion langsames Problem

In Reflection ist von langsamem Feldzugriff und Methodenaufrufen die Rede, aber was ist mit der Aufzählung? Zum Beispiel möchte ich eine Liste von Klassen erstellen, um eine Liste der zu vervollständigenden Klassen anzuzeigen. Guava hat eine Klasse namens ClassPath, die auf Klassenpfadinformationen zugreift, aber das Aufrufen von getAllClasses ist damit lächerlich langsam. Dies ist ein Faktor, der das Laden des Klassenladers verlangsamt. Damit ist eine reibungslose Ergänzung schwierig. Außerdem wird der Klassenlader mit serverseitigen Klassen geladen. Infolgedessen können nicht verwandte Klassen, die nicht im Projekt enthalten sind, als Abschlusskandidaten angezeigt werden.

ASM Reflector

Ich habe meinen eigenen Reflektor hergestellt, um das obige Problem zu lösen. Da es nicht auf dem Klassenladeprogramm platziert ist, kann es als Kandidat für die Fertigstellung innerhalb des Projekts verwendet werden. Wir verfolgen auch den folgenden Ansatz, um das Geschwindigkeitsproblem zu lösen.

  1. Projekterkennung und Projekterstellung
  2. Identifizieren Sie den Pfad der JAR-Datei aus dem Klassenpfad, scannen Sie parallel die Klassendatei der JAR-Datei und erstellen Sie eine Klassenliste.
  3. Notieren Sie sich beim Erstellen der Klassenliste, welche Klasse sich in welchem Glas befindet
  4. Identifizieren Sie das Glas der Klasse, die bei der Ausgabe von Abschlusskandidaten angegeben wurde, suchen Sie im Glas und lesen Sie Mitglieder usw. mit ASM
  5. Legen Sie das Leseergebnis in den Speicher- und Dateicache. Wenn es sich um eine Klasse im Projekt handelt, zeichnen Sie auch die Prüfsumme der Zielquelle auf
  6. Ab dem nächsten Mal wird der Abschlusskandidat aus dem Cache gelesen. Wenn die Prüfsumme geändert wird, wird sie von ASM erneut gelesen.

Riesige Anzahl von Klassen

Aber wie viele ergänzende Kandidaten gibt es? Abhängig vom JDK gibt es allein im Java-Standard etwa 9000 Klassen. Wenn Sie die Projektabhängigkeit hinzufügen, werden etwa Zehntausende von Klassen indiziert. Um diese großen Zahlen schnell verarbeiten zu können, ist es notwendig, so viel wie möglich zu parallelisieren.

Meghanada

Die Implementierung des oben genannten ist jedoch Meghanada []. Viele sind auf in Java geschriebenen Servern implementiert, aber das Front-Elisp ist auch ziemlich groß. Dieses Client- und Servermodell wurde unter Bezugnahme auf den Ironiemodus erstellt. Gleichzeitig mit dem Öffnen mit [Meghanada-Modus] [] in Emacs wird der Server gestartet, verbunden und Befehle werden mit dem Server auf der Emacs-Seite ausgetauscht.

Die Fertigstellung wird als Backend für das Unternehmen implementiert. Außerdem wird das Prüfsystem für Kompilierungsfehler als Prüfer für Flycheck implementiert. Meghanada [] ist so konzipiert, dass der Emacs-Teil nicht so viel wie möglich selbst verarbeitet. Daher kann es auf die gleiche Weise wie andere Spracheinstellungen verwendet werden.

Abschlussauslöser basieren auf IntelliJ. Grundsätzlich, um Schlüsselwörter usw. aufzunehmen und mit der Fertigstellung zu beginnen Sie müssen keine Taste drücken, um den Vorgang abzuschließen.

Einfache Einführung

Meghanada [] berücksichtigt auch die einfache Installation. Die Server-JAR-Datei wird in ein fettes JAR umgewandelt und in bintray hochgeladen. Wenn auf Elisp-Seite lokal kein Server-Jar vorhanden ist, wird es automatisch von bintray heruntergeladen. Alles was Sie tun müssen, ist Emacs neu zu starten und es klingt einsatzbereit. Sie müssen es nicht wie Ironie kompilieren.

Asynchrone Verarbeitung

In einigen Fällen kann es zu einer starken Verarbeitung kommen. In Erwartung dessen sind viele darauf ausgelegt, asynchron zu kommunizieren. Das Einfrieren im Editor ist ein schwerwiegendes Problem. Die Abschlussverarbeitung verwendet die Firma. Das Unternehmen verfügt über eine asynchrone API, mit der das Einfrieren verhindert wird.

Zwei Verbindungen

Meghanada [] fügt zwei Verbindungen mit dem Server ein. Eine dient zum Senden und Empfangen von Befehlen und eine zum Streamen. Stream ist eine Funktion zum Empfangen von Ausgaben in Echtzeit. Dies wird insbesondere verwendet, um Konsolenprotokolle wie das Ausführen und Erstellen von JUnit zu überprüfen.

Um ehrlich zu sein, denke ich nicht, dass es ohne einen Stream möglich wäre, aber der von VSCode verwendete LSP hat keinen Stream.

Schließlich

Ich stellte kurz Meghanada [] vor. Ich habe dieses Jahr angefangen, dieses Projekt zu schreiben und wünschte, ich könnte es innerhalb des Jahres veröffentlichen. Ich habe es mehrmals umgeschrieben, bevor es veröffentlicht wurde, aber ich konnte die erste Ausgabe um den Oktober herum veröffentlichen.

Gegenwärtig wurde der Teil der Quellanalyse unabhängig implementiert, aber dieser Teil wird mit einer internen Compiler-API wie ENSIME [] neu geschrieben. Ich denke, dies wird es uns ermöglichen, Lambda und Methodenreferenzen vollständig zu unterstützen.

Recommended Posts

Die Geschichte des Schreibens von Java in Emacs
Die Geschichte des einfachen String-Vergleichs in Java
Die Geschichte eines gewöhnlichen Othello in Java
Die Geschichte des Lernens von Java in der ersten Programmierung
[Java Edition] Geschichte der Serialisierung
Holen Sie sich das Ergebnis von POST in Java
Fortsetzung Sprechen Sie über das Schreiben von Java mit Emacs @ 2018
[Java] Behandlung von Java Beans in der Methodenkette
Über die Idee anonymer Klassen in Java
Eine Geschichte über das JDK in der Java 11-Ära
Messen Sie die Größe eines Ordners mit Java
Spüren Sie den Lauf der Zeit auch in Java
Importieren Sie Dateien derselben Hierarchie in Java
Die Geschichte, zu vergessen, eine Datei in Java zu schließen und zu scheitern
Rufen Sie die URL des HTTP-Umleitungsziels in Java ab
Die Geschichte einer illegalen staatlichen Ausnahme in Jetty.
[Java] Holen Sie sich die Datei unabhängig von der Umgebung in das JAR
[Java] Beim Schreiben der Quelle ... Memorandum ①
Ändern Sie die Speicherqualität von JPEG-Bildern in Java
Java-Implementierung von Tri-Tree
Die Geschichte von dto, dao-like mit Java, SQLite
Fassen Sie die zusätzlichen Elemente der optionalen Klasse in Java 9 zusammen
Die Geschichte, dass .java auch in Unity 2018 erstellt wurde
Eine kurze Erklärung der fünf Arten von Java Static
20190803_Java & k8s on Azure Die Geschichte vom Festivalbesuch
Die Geschichte des Werfens von BLOB-Daten von EXCEL in DBUnit
Zählen Sie die Anzahl der Stellen nach dem Dezimalpunkt in Java
Dinge, die Sie beim Schreiben von Code in Java beachten sollten
So leiten Sie den letzten Tag des Monats in Java ab
Die Geschichte, Java mithilfe der BitBucket-Pipeline nach Heroku zu bringen
Greifen Sie mit Java auf die Netzwerkschnittstelle zu
Geben Sie den Java-Speicherort in eclipse.ini an
Die Geschichte von Java Gold SE8
Entpacken Sie die Zip-Datei in Java
Die Geschichte von @ViewScoped, die Speicher verschlingt
Liste der in Java 9 hinzugefügten Mitglieder
Analysieren der COTOHA-API-Syntaxanalyse in Java
Liste der in Java 9 hinzugefügten Typen
Reihenfolge der Verarbeitung im Programm
Der Ursprung von Java-Lambda-Ausdrücken
Implementierung einer ähnlichen Funktion in Java
Rufen Sie die Super-Methode in Java auf
Untersuchen Sie die Systeminformationen der AWS Lambda-Betriebsumgebung in Java
Die Geschichte, das Verhalten von String durch Passieren von Java nicht zu kennen
Geben Sie die Differenz zwischen jedem Feld zweier Objekte in Java aus
Untersuchen Sie die Liste der in AWS Lambda + Java verfügbaren Schriftarten
Eine Geschichte über das Erreichen der League Of Legends-API mit JAVA
Untersuchen Sie die Liste der Zeitzonen-IDs, die in der Java ZoneId-Klasse verfügbar sind
Rufen Sie die öffentliche URL der privaten Datei von Flickr in Java ab
Lassen Sie uns eine TODO-App in Java 5 erstellen. Schalten Sie die Anzeige von TODO um
So ermitteln Sie die Länge einer Audiodatei mit Java
So erhöhen Sie den Wert von Map in einer Zeile in Java
Die Geschichte der Begegnung mit Spring Custom Annotation
Implementierung von DBlayer in Java (RDB, MySQL)
Geschichte von paiza.jp [Java Standard Input Solution]
Java-Referenz zum Verständnis in der Abbildung
Schriftliche Unterschiede in Ruby, PHP, Java, JS