Added: AS1/2 - underline errors in the code (also in edit mode)

This commit is contained in:
Jindra Petřík
2025-05-28 17:51:05 +02:00
parent d3dd8224d4
commit 479b61a7f1
10 changed files with 721 additions and 380 deletions
@@ -19,18 +19,25 @@ package com.jpexs.decompiler.flash.gui.action;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.action.parser.ActionParseException;
import com.jpexs.decompiler.flash.action.parser.script.variables.ActionScript2VariableParser;
import com.jpexs.decompiler.flash.action.parser.script.variables.ActionVariableParseException;
import com.jpexs.decompiler.flash.gui.editor.DebuggableEditorPane;
import com.jpexs.decompiler.flash.gui.editor.LineMarkedEditorPane;
import com.jpexs.decompiler.flash.gui.editor.LinkHandler;
import com.jpexs.decompiler.flash.gui.editor.WavyUnderLinePainter;
import java.awt.Color;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.logging.Logger;
import javax.swing.JEditorPane;
import javax.swing.event.CaretEvent;
@@ -54,16 +61,26 @@ public class ActionVariableMarker implements SyntaxComponent, CaretListener, Pro
public static final String DEFAULT_TOKENTYPES = "IDENTIFIER, KEYWORD, REGEX";
public static final String PROPERTY_COLOR = "ActionVariableMarker.Color";
public static final String PROPERTY_ERRORCOLOR = "ActionVariableMarker.ErrorColor";
public static final String PROPERTY_TOKENTYPES = "ActionVariableMarker.TokenTypes";
private static final Color DEFAULT_COLOR = new Color(0xf8e7d6);
private static final Color DEFAULT_COLOR = new Color(0xffeedd);
private static final Color DEFAULT_ERRORCOLOR = new Color(0xffaaaa);
private JEditorPane pane;
private final Set<TokenType> tokenTypes = new HashSet<>();
private Markers.SimpleMarker marker;
private Markers.SimpleMarker errorMarker;
private Status status;
private Map<Integer, String> errors = new LinkedHashMap<>();
private boolean errorsShown = false;
public static final long ERROR_DELAY = 2000;
private Timer errorsTimer;
private Map<Integer, List<Integer>> definitionPosToReferences = new LinkedHashMap<>();
private Map<Integer, Integer> referenceToDefinition = new LinkedHashMap<>();
private MouseMotionAdapter mouseMotionAdapter;
/**
* Constructs a new Token highlighter
*/
@@ -78,6 +95,12 @@ public class ActionVariableMarker implements SyntaxComponent, CaretListener, Pro
public void markTokenAt(int pos) {
SyntaxDocument doc = ActionUtils.getSyntaxDocument(pane);
if (doc != null) {
Token everyToken = getNearestTokenAt(doc, pos);
if (everyToken != null) {
if (errors.containsKey(everyToken.start)) {
//System.err.println(errors.get(everyToken.start));
}
}
Token token = getIdentifierTokenAt(doc, pos);
removeMarkers();
if (token != null && tokenTypes.contains(token.type)) {
@@ -93,6 +116,11 @@ public class ActionVariableMarker implements SyntaxComponent, CaretListener, Pro
Markers.removeMarkers(pane, marker);
}
public void removeErrorMarkers() {
Markers.removeMarkers(pane, errorMarker);
errorsShown = false;
}
private Token getIdentifierTokenAt(SyntaxDocument sDoc, int pos) {
Token thisToken = sDoc.getTokenAt(pos);
if (thisToken != null && (thisToken.type == TokenType.IDENTIFIER || thisToken.type == TokenType.REGEX || thisToken.type == TokenType.KEYWORD)) {
@@ -115,6 +143,42 @@ public class ActionVariableMarker implements SyntaxComponent, CaretListener, Pro
return null;
}
private Token getNearestTokenAt(SyntaxDocument sDoc, int pos) {
Token thisToken = sDoc.getTokenAt(pos);
if (thisToken != null) {
return thisToken;
}
Token token = sDoc.getTokenAt(pos - 1);
if (token != null
&& (token.start + token.length == pos)) {
return token;
}
token = sDoc.getTokenAt(pos + 1);
if (token != null
&& (token.start == pos)) {
return token;
}
return null;
}
void addErrorMarkers() {
SyntaxDocument doc = ActionUtils.getSyntaxDocument(pane);
if (doc == null) {
return;
}
doc.readLock();
for (int position : errors.keySet()) {
Token token = getNearestTokenAt(doc, position);
if (token != null) {
Markers.markToken(pane, token, errorMarker);
}
}
doc.readUnlock();
errorsShown = true;
}
/**
* add highlights for the given pattern
*
@@ -144,9 +208,10 @@ public class ActionVariableMarker implements SyntaxComponent, CaretListener, Pro
@Override
public void config(Configuration config) {
Color markerColor = config.getColor(
PROPERTY_COLOR, DEFAULT_COLOR);
Color markerColor = config.getColor(PROPERTY_COLOR, DEFAULT_COLOR);
Color errorColor = config.getColor(PROPERTY_ERRORCOLOR, DEFAULT_ERRORCOLOR);
this.marker = new Markers.SimpleMarker(markerColor);
this.errorMarker = new WavyUnderLinePainter(Color.red); //Markers.SimpleMarker(errorColor);
String types = config.getString(
PROPERTY_TOKENTYPES, DEFAULT_TOKENTYPES);
@@ -173,16 +238,43 @@ public class ActionVariableMarker implements SyntaxComponent, CaretListener, Pro
}
documentUpdated();
markTokenAt(editor.getCaretPosition());
mouseMotionAdapter = new MouseMotionAdapter() {
@Override
public void mouseMoved(MouseEvent e) {
editorMouseMoved(e);
}
};
editor.addMouseMotionListener(mouseMotionAdapter);
status = Status.INSTALLING;
}
private void editorMouseMoved(MouseEvent e) {
if (pane instanceof LineMarkedEditorPane) {
Token token = ((LineMarkedEditorPane) pane).getTokenUnderCursor();
if (token != null) {
String err = errors.get(token.start);
pane.setToolTipText(err);
} else {
pane.setToolTipText(null);
}
}
}
@Override
public void deinstall(JEditorPane editor) {
status = Status.DEINSTALLING;
removeMarkers();
removeErrorMarkers();
Timer tim = errorsTimer;
if (tim != null) {
tim.cancel();
errorsTimer = null;
}
pane.removePropertyChangeListener(this);
pane.getDocument().removeDocumentListener(this);
pane.removeCaretListener(this);
pane.removeMouseMotionListener(mouseMotionAdapter);
mouseMotionAdapter = null;
if (editor instanceof LineMarkedEditorPane) {
((LineMarkedEditorPane) editor).setLinkHandler(null);
}
@@ -205,6 +297,8 @@ public class ActionVariableMarker implements SyntaxComponent, CaretListener, Pro
}
private void documentUpdated() {
errors.clear();
removeErrorMarkers();
try {
SyntaxDocument sDoc = (SyntaxDocument) pane.getDocument();
@@ -222,15 +316,37 @@ public class ActionVariableMarker implements SyntaxComponent, CaretListener, Pro
Map<Integer, List<Integer>> newDefinitionPosToReferences = new LinkedHashMap<>();
Map<Integer, Integer> newReferenceToDefinition = new LinkedHashMap<>();
ActionScript2VariableParser varParser = new ActionScript2VariableParser(swf);
varParser.parse(fullText, newDefinitionPosToReferences, newReferenceToDefinition);
List<ActionVariableParseException> newErrors = new ArrayList<>();
varParser.parse(fullText, newDefinitionPosToReferences, newReferenceToDefinition, newErrors);
definitionPosToReferences = newDefinitionPosToReferences;
referenceToDefinition = newReferenceToDefinition;
markTokenAt(pane.getCaretPosition());
} catch (BadLocationException | ActionParseException | IOException | InterruptedException ex) {
for (ActionVariableParseException ex : newErrors) {
errors.put((int) ex.position, ex.getMessage());
}
} catch (BadLocationException | IOException | InterruptedException ex) {
definitionPosToReferences.clear();
referenceToDefinition.clear();
//ex.printStackTrace();
} catch (ActionParseException ex) {
definitionPosToReferences.clear();
referenceToDefinition.clear();
errors.put((int) ex.position, ex.getMessage());
}
Timer tim = errorsTimer;
if (tim != null) {
tim.cancel();
errorsTimer = null;
}
tim = new Timer();
tim.schedule(new TimerTask() {
@Override
public void run() {
removeErrorMarkers();
addErrorMarkers();
}
}, ERROR_DELAY);
errorsTimer = tim;
markTokenAt(pane.getCaretPosition());
}
@Override
@@ -78,6 +78,8 @@ public class LineMarkedEditorPane extends UndoFixedEditorPane implements LinkHan
private static final HighlightPainter underLinePainter = new UnderLinePainter(new Color(0, 0, 255));
private LinkHandler linkHandler = this;
private Point lastCursorPos = new Point(0, 0);
public static class LineMarker implements Comparable<LineMarker> {
@@ -381,11 +383,13 @@ public class LineMarkedEditorPane extends UndoFixedEditorPane implements LinkHan
}
return null;
}
public Token getTokenUnderCursor() {
return tokenAtPos(lastCursorPos);
}
private class LinkAdapter extends MouseAdapter implements KeyListener {
private Point lastPos = new Point(0, 0);
private boolean ctrlDown = false;
@Override
@@ -411,7 +415,7 @@ public class LineMarkedEditorPane extends UndoFixedEditorPane implements LinkHan
private void update() {
if (ctrlDown) {
Token t = tokenAtPos(lastPos);
Token t = tokenAtPos(lastCursorPos);
if (t != lastUnderlined) {
if (t == null || lastUnderlined == null || !t.equals(lastUnderlined)) {
@@ -446,7 +450,7 @@ public class LineMarkedEditorPane extends UndoFixedEditorPane implements LinkHan
@Override
public void mouseClicked(MouseEvent e) {
if (ctrlDown) {
Token t = tokenAtPos(lastPos);
Token t = tokenAtPos(lastCursorPos);
if (t != null && linkHandler.isLink(t)) {
linkHandler.handleLink(t);
}
@@ -457,7 +461,7 @@ public class LineMarkedEditorPane extends UndoFixedEditorPane implements LinkHan
public void mouseMoved(MouseEvent e) {
ctrlDown = (e.getModifiersEx() & InputEvent.CTRL_DOWN_MASK) != 0;
lastPos = e.getPoint();
lastCursorPos = e.getPoint();
update();
}
@@ -0,0 +1,120 @@
/*
* Copyright (C) 2010-2025 JPEXS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jpexs.decompiler.flash.gui.editor;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.Rectangle2D;
import javax.swing.plaf.TextUI;
import javax.swing.text.BadLocationException;
import javax.swing.text.JTextComponent;
import javax.swing.text.Position;
import javax.swing.text.View;
import jsyntaxpane.components.Markers;
/**
*
* @author JPEXS
*/
public class WavyUnderLinePainter extends Markers.SimpleMarker {
public WavyUnderLinePainter(Color color) {
super(color);
}
@Override
public void paint(Graphics g, int offs0, int offs1, Shape bounds, JTextComponent c) {
try {
// --- determine locations ---
TextUI mapper = c.getUI();
Color col = getColor();
if (col == null) {
col = Color.black;
}
g.setColor(col);
int waveHeight = 2;
for (int i = offs0; i < offs1; i++) {
Rectangle2D r = com.jpexs.decompiler.flash.gui.View.textUIModelToView(mapper, c, i, Position.Bias.Forward);
Rectangle2D r1 = com.jpexs.decompiler.flash.gui.View.textUIModelToView(mapper, c, i + 1, Position.Bias.Forward);
if (r1.getY() == r.getY()) {
int x = (int) r.getX();
int y = (int) (r.getY() + r1.getHeight());
int maxX = (int) r1.getX();
boolean up = true;
for (; x < maxX; x += 2) {
if (up) {
g.drawLine(x, y, x + 2, y - waveHeight);
} else {
g.drawLine(x, y - waveHeight, x + 2, y);
}
up = !up;
}
}
}
} catch (BadLocationException e) {
// can't render
}
}
@Override
public Shape paintLayer(Graphics g, int offs0, int offs1,
Shape bounds, JTextComponent c, View view) {
g.setColor(c.getSelectionColor());
Rectangle r;
if (offs0 == view.getStartOffset()
&& offs1 == view.getEndOffset()) {
// Contained in view, can just use bounds.
if (bounds instanceof Rectangle) {
r = (Rectangle) bounds;
} else {
r = bounds.getBounds();
}
} else {
// Should only render part of View.
try {
// --- determine locations ---
Shape shape = view.modelToView(offs0, Position.Bias.Forward,
offs1, Position.Bias.Backward,
bounds);
r = (shape instanceof Rectangle)
? (Rectangle) shape : shape.getBounds();
} catch (BadLocationException e) {
// can't render
r = null;
}
}
if (r != null) {
r.width = Math.max(r.width, 1);
paint(g, offs0, offs1, r, c);
}
return r;
}
}