Swing JTextPane Formatierungen SyntaxHighlighting

knoppers

Bekanntes Mitglied
Hallo,

ich will einen kleinen Java Editor schreiben der Code formatiert. Da bin ich auf einige Probleme gestoßen die ich einfach nicht verstehe von der Logic her.

Ich habe auch schon min. 2 Tage lang im Internet nach Ähnlichen Problemen und Lösungen gesucht aber nichts passendes gefunden. Ich hoffe Ihr könnt mir ein bisschen helfen. Danke.

Ich habe ein TextPane darauf habe ich ein Keylistener gesetzt. Wenn der User etwas eingibt soll der Code überprüft und dann dementsprechend angepasst werden (Syntax Highlighting usw.).

Problem 1:
Der User schreibt einen einzeiligen Kommentar. Ich laufe über den gesamten Text und überprüfe jedes Zeichen, wenn das Zeichen ein / ist und das nächste ein / ist, will ich die gesamte Zeile in grüner Farbe darstellen. Die Funktioniert auch sollange der user kein \n oder \r (\r verstehe ich sowieso nicht) eingibt.
Auf jeden Fall wenn er dieses Zeichen eingibt und bei der neuen Zeile wieder ein Kommentar schreibt werden die ersten beiden Zeichen nicht mehr formatiert. Warum nicht? Nachfolgend mal die Methode die jedesmal nach einen ausgelösten KeyListener ausgeführt wird.

Java:
		try {

			if (getDocument() == null) {
				return;
			}

			// no text to check
			if (isDocumentEmpty()) {
				return;
			}

			final int startOffset = getDocument().getStartPosition().getOffset();

			// loop over all signs by the text
			int currPos = startOffset;
			while (currPos < getDocumentLength()) {

				if (isDocumentEmpty()) {
					return;
				}

				char currChar = getText().charAt(currPos);		

				// check of commentare on the position
				if (currChar == '/' && currPos + 1 < getDocumentLength()) {
					try {
						if (getText().charAt(currPos + 1) == '/') {

							// Loop from the actually pos to the pos by end of
							// the line and set
							while (currChar != '\n' || currChar != '\r') {
								actionRefreshStyleOnPlace(currPos, 1, lineComment);
								currChar = getText().charAt(currPos);
								currPos++;
							}
						}
					} catch (Exception exc) {

					}
				}

				currPos++;

			}

		} catch (Exception exception) {
			exception.printStackTrace();
		}
	/**
	 * @Author jkonopka
	 * @Date 20.06.2010
	 */
	private void actionRefreshStyleOnPlace(final int begin, final int length, final SimpleAttributeSet set) {
		final StyledDocument styledDoc = getStyledDocument();
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				styledDoc.setCharacterAttributes(begin, length, set, false);
			}
		});
	}
 
Ich versuch mal ein wenig zu helfen, auch wenn ich nicht verstehe was genau das Problem ist.

\r steht für Carriage Return: Es bewegt den den Cursor zurück in die erste Position der aktuellen Zeile.
\n Linfeed, neue Zeile

Sind noch Artefakte auf der guten alten Zeit 🙂

Nach einem Zeilensprung wird alles grün geschrieben, ausser die beiden // -Zeichen ? Verstehe ich das richtig?
 
Okay, \r und \n weiss ich was das macht.

Du hast das nicht richtig verstanden. "//" = Beginn Kommentare für eine, bzw. Rest bis zum Ende der ganzen Zeile. Also mit den "//" wird alles Grün und in einer anderen Schriftart geschrieben. (Halt Java Code Zeilen Kommentar)

Für das erste Problem habe ich eine Lösung gefunden. Ich setze am Anfang die Entertaste als "\n" Zeichen genommen werden soll. Problem hierbei ist ich verstehe demnoch nicht wie ich das mit dem "\r" machen hätten sollen. Wenn ich nicht am Anfang die Enter Taste mit den Zeichen "\n" besetzt hätte. Wenn ich das ganze debug unter Windows dann setzt Java automatisch (ausser wenn ich am Anfang sage das es "\n" nehmen soll) bei der Entertaste = "\n\r", unter Linux setzt Java = "\n". Mit meiner Lösung sage ich ja jetzt nur, das die Linux/bzw. UNIX Art genommen werden soll. Aber wie würde ich das ganze lösen wenn ich "\n\r" lasse. Java fügt unter Windows dann automatisch ein nicht lesbares Zeichen ein, Was aber auch nicht beim durchlaufen und vergleichen aller Zeichen beachtet, bzw. lessbar ist.
 
also ich hab nen Syntaxhighlighting für meinen Formeleditor gebastelt .. der arbeitet entweder auf festen Strings (Variablen, Schlüsselwörter ..) oder eben regulären Ausdrücken für Kommentare (Block-/Zeilenkommentare) und Zahlendarstellungen.

Ich geb den Text in einen Scanner der mir dann ne Liste von Token liefert, diese enthalten dann den jeweiligen Typ des Textteils und den entsprechenden Text. Wie wärs wenn du das nach diesem Konzept umsetzen würdest? Somit kannst du später den Scanner beliebig anlernen sozusagen.

Durch reguläre Ausdrücke kannst du das mit den Kommentaren auch ganz easy erledigen:
Zeilenkommentar:
Code:
//.*
Blockkommentar:
Code:
(?s)/\\*((.*?\\*/)|(.*))
--> /* ... */
 
Zuletzt bearbeitet:
Da hast du vollkommen Recht mit dem Scanner, dies ist auch geplant. Das ganze soll später in ein großes Projekt einfließen. Jetzt im Moment teste ich erst einmal was überhaupt, und wie es in Java möglich ist. Später sollte das ganze schon von einander unabhängig getrennt werden. Aber erst einmal testen, dann den Prototypen und dann kommt die richtige Entwicklung. Abgeprüft wird später natürlich auch anhand von Schlüsselwörtern und Regex.

Das Problem ist nur dass ich manche Eigenheiten von JEditPane nicht ganz verstehe, bzw. mir etwas Probleme machen. Ersteres Problem hatte ich ja schon beschrieben. Weiteres Problem ist die aktuelle Position in den Text zu finden. Ich dachte wenn ich immer mit getText().charAt(position) arbeite geht dies. Wo ich mich leider sehr getäuscht habe.
 
was genau war nochmal das erste problem, das mit dem Erkennen?

Bei mir scanne und manipuliere ich momentan immer den ganzen Text, wenn sich die Eingabe ändert .. das wird noch optimiert auf notwendige Abschnitte und da arbeite ich an sich nur mit einem Offset x, welches sich daraus ergibt wo der zu scannende Text anfängt. Ich manipuliere auch in dem Sinne ganze "Wörter" und nicht einzelne Zeichen. Vielleicht kannst ja son rudimentären Scanner bauen, das sollte die Sache leichter machen.
 
Mache ich auch genau so. Ich durchlaufe auch bei jeden Änderung der ganzen Text und setze die Syntax dementsprechend.

Gut fangen wir mal kurz mit einem anderen Problem oder Sache an.
Wieso brauche ich bei jeder Änderung im Text das "SwingUtilities.invokeLater(....". Zumindestens brauche ich, sowie ich das bis jetzt bemerkt habe bei unteren Beispiel und bei insert... und remove...
Das ganze ohne dies bleibt mir das Programm hängen. Aber mit diesen ist es ganz schön Performance lastig und geht auf die Geschwindigkeit. Wenn jemand eine andere Lösung hat, habe ich immer ein offenes Ohr dafür.

Java:
    /**
     * @Author jkonopka
     * @Date 20.06.2010
     */
    private void actionRefreshStyleOnPlace(final int begin, final int length, final SimpleAttributeSet set) {
        final StyledDocument styledDoc = getStyledDocument();
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                styledDoc.setCharacterAttributes(begin, length, set, false);
            }
        });
    }
 
lustigerweise habe ich auch dieses invokeLater in meinem Document drin, ich weiss allerdings nicht mehr warum ich das getan hab ^^ .. ich vermute es lag daran, dass bei removes oder dergleichen nicht richtig gehighlightet wurde, bin mir aber nicht mehr sicher ob das der grund war.

jedenfalls hat mir das irgendwie geholfen 🙂 .. das wichtige ist wirklich, das highlighting auf den nötigen teil zu beschränken. Es lässt sich ja nicht vermeiden bei remove eines zeichens x Zeilen zu checken, wenn es denn im zweifelsfall eine Schlüsselposition (wie Ende-Markierung eines Blockkommentares) ist.

Wieviel performanter man es noch machen kann ist ne gute Frage, bin auch gern für Vorschläge offen.

Ich haben mein invokeLater allerdings noch vor dem Scannerdurchlauf ..
Java:
private void invokeHighlight(final int startOffset, final int endOffset) {
		SwingUtilities.invokeLater(new Thread() {
			
			@Override
			public void run() {
				try {
					highlightString(startOffset, endOffset);
    			} catch (BadLocationException e) {
    				SimpleLog.print("INTERNAL ERROR - while highlighting string (SyntaxHighlightStyledDocument\n"
    						+ e.getMessage());
    			}//try
			}//run
		});
	}//invokeHighlighting

highlightString jagt dann den Text in den Scanner, erhält die Token und manipuliert entsprechend der Einstellungen den Text.
 
so hatte ich das auch mal, aber da ist er mir immer zu sehr hängengeblieben. Da konnte mein beim Tippen nebenbei Kaffeetrinken. Ich habe mehrere Unterschiedliche Varianten jetzt getestet und bin jetzt dabei gelandet, das ich bei "insert..." "remove...." "setCharacter..." usw. immer einen neuen invoke... mache. Leider ist mir dies immer noch zu Performance verlustreich. Da der (vorerst nur) Editor (später mehr als nur ein Editor) sehr viele Funktionen bekommen wird.
 
Also ich habe jetzt gemerkt das ich mit dem was ich erreichen möchte mit JTextPane nicht weit komme. Ich werde mir ein eigenes TextPane von JComponent ableiten müssen.
 
Es soll wie gesagt vorerst ein Editor werden. Funktionen sollen von Haus aus sehr mächtig sein. Aktuelle Zeilenmarkierung in der ich mich befinde, Anzeige Zeilennummer, Ausblenden Zeilennummer, Lesezeichen für Zeile setzen, cut, copy, paste, undo, redo, undo * max 100, redo * max 100, bestimmte Teilbereiche ausblenden, und noch viel, viel mehr. Bei den Erweiterten abgeleiteten Editoren kommt z.B. noch dann das Syntax Highlighting, automatische Formatierung, Kommentare setzen, Checkstyle usw. dazu. Dies ist nur ein kleiner Auszug. Ich habe schon nach ähnlichen Projekten im Netz gesucht, einiges auch gefunden. Leider fehlt bei denen die ich gefunden habe, Dinge auf die ich nicht verzichten möchte.

Grundsätzlich sollen alle Komponenten wiederverwertbar, erweiterbar und soweit wie möglich unabhängig voneinander einsetzbar sein.
 

Zurück
Oben