diff --git a/.gitignore b/.gitignore
index e3b98dcf5f..c57a023c7c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -92,4 +92,7 @@ bin-test
# VS Code Java project files
.project
-.vscode/
\ No newline at end of file
+.vscode/
+
+# ANTLR intermediate files
+java/src/processing/mode/java/preproc/.antlr
\ No newline at end of file
diff --git a/app/build.xml b/app/build.xml
index c48adeb02d..b8cec68c86 100644
--- a/app/build.xml
+++ b/app/build.xml
@@ -160,7 +160,9 @@
lib/ant-launcher.jar;
lib/flatlaf.jar;
lib/jna.jar;
- lib/jna-platform.jar"
+ lib/jna-platform.jar;
+ lib/build-flexmark-all.jar;
+ lib/ST-4.3.4.jar"
debug="on"
nowarn="true">
diff --git a/app/lib/build-flexmark-all.jar b/app/lib/build-flexmark-all.jar
new file mode 100644
index 0000000000..9e17821c0c
Binary files /dev/null and b/app/lib/build-flexmark-all.jar differ
diff --git a/app/src/processing/app/ui/EditorStatus.java b/app/src/processing/app/ui/EditorStatus.java
index 02a85f10e1..348f27551d 100644
--- a/app/src/processing/app/ui/EditorStatus.java
+++ b/app/src/processing/app/ui/EditorStatus.java
@@ -35,15 +35,30 @@
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.awt.event.*;
+import java.awt.BorderLayout;
+import java.awt.Desktop;
import javax.swing.*;
import javax.swing.plaf.basic.BasicSplitPaneDivider;
import javax.swing.plaf.basic.BasicSplitPaneUI;
+import javax.swing.JScrollPane;
+import javax.swing.event.*;
+import javax.swing.border.Border;
+
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+import java.net.URI;
+
+import java.io.IOException;
import processing.app.Platform;
import processing.app.Preferences;
import processing.core.PApplet;
+import com.vladsch.flexmark.html.HtmlRenderer;
+import com.vladsch.flexmark.parser.Parser;
+import com.vladsch.flexmark.util.ast.Node;
+import com.vladsch.flexmark.util.data.MutableDataSet;
/**
* Panel just below the editing area that contains status messages.
@@ -68,6 +83,7 @@ public class EditorStatus extends BasicSplitPaneDivider {
int mode;
String message = "";
+ String friendlyMessage = "";
int messageRight;
String url;
@@ -79,6 +95,8 @@ public class EditorStatus extends BasicSplitPaneDivider {
static final int COLLAPSE_PRESSED = 4;
static final int CLIPBOARD_ROLLOVER = 5;
static final int CLIPBOARD_PRESSED = 6;
+ static final int MORE_INFO_ROLLOVER = 7;
+ static final int MORE_INFO_PRESSED = 8;
int mouseState;
Font font;
@@ -91,6 +109,8 @@ public class EditorStatus extends BasicSplitPaneDivider {
ImageIcon[] searchIcon;
ImageIcon[] collapseIcon;
ImageIcon[] expandIcon;
+ ImageIcon[] moreInfoIcon;
+
float btnEnabledAlpha;
float btnRolloverAlpha;
@@ -108,7 +128,121 @@ public class EditorStatus extends BasicSplitPaneDivider {
boolean indeterminate;
Thread thread;
+ /**
+ * The friendly instance variable that determines whether a given error passed in is friendly (has additional information to be shown in a popup).
+ */
+ public boolean friendly = false;
+
+
+ /*
+ * Popup that shows additional info about the error message that shows up in the status bar.
+ */
+
+ public class FriendlyErrorPopup{
+
+ private JFrame popupFrame;
+ final int PROCESSING_SAYS_OFFSET = 6;
+
+ private String markdownToHtml(String target) {
+ MutableDataSet options = new MutableDataSet();
+ Parser parser = Parser.builder(options).build();
+ HtmlRenderer renderer = HtmlRenderer.builder(options).build();
+ Node document = parser.parse(target);
+ String html = renderer.render(document);
+ html = "
π΅Processing says:" + html + "
" ;
+ return html;
+ }
+
+ /**
+ * Constructor for FriendlyErrorPopup class.
+ * @param messageText a String containing the full message to be simplified.
+ */
+ public FriendlyErrorPopup(String messageText){
+
+ int firstLineIndex = messageText.indexOf(" ");
+ int lineCounter = 0;
+ int newLineCount = 0;
+
+ String firstSentence = messageText.substring(0,firstLineIndex);
+ String pureText = messageText;
+
+ Pattern newLineCounter = Pattern.compile(" ");
+ Pattern linkSkipper = Pattern.compile("\\[([^\\]]+)\\]\\([^\\)]+\\)");
+ Matcher newLineMatcher = newLineCounter.matcher(pureText);
+ Matcher linkSkipperMatcher = linkSkipper.matcher(pureText);
+
+ // allows for error messages with markdown links in the first line although there currently are none
+ while (linkSkipperMatcher.find()){
+ pureText = pureText.replace(linkSkipperMatcher.group(0),linkSkipperMatcher.group(1));
+ }
+
+ firstSentence = pureText.substring(0,pureText.indexOf(" "));
+ firstLineIndex = firstSentence.length();
+
+ int index = 0;
+
+ while (index < pureText.length()) {
+ lineCounter++;
+ int nextBreakIndex = pureText.indexOf(" ", index);
+ index = (((nextBreakIndex - index) <= firstLineIndex) && nextBreakIndex != -1) ? nextBreakIndex + 4 : index + firstLineIndex;
+ }
+
+ pureText = pureText.replaceAll(" ","");
+ messageText = markdownToHtml(messageText);
+
+ JEditorPane errorPane = new JEditorPane();
+ errorPane.setContentType("text/html");
+
+ JScrollPane scrollPane = new JScrollPane(errorPane);
+
+ errorPane.setFont(new Font("Source Code PRO", Font.PLAIN, 13)); //not actually necessary but it allows the window resizing to work
+ errorPane.setText(messageText);
+ errorPane.setBackground(Color.decode("#fafcff"));
+ errorPane.setEditable(false);
+
+ java.awt.FontMetrics fontMetrics = errorPane.getFontMetrics(errorPane.getFont());
+
+ int popupWidth = fontMetrics.stringWidth(firstSentence) - 25;
+ int popupHeight = (lineCounter + PROCESSING_SAYS_OFFSET) * fontMetrics.getHeight();
+
+ errorPane.addHyperlinkListener((event) -> {
+ HyperlinkEvent.EventType eventType = event.getEventType();
+ boolean linkClicked = eventType == HyperlinkEvent.EventType.ACTIVATED;
+ if (linkClicked) {
+ String url = event.getURL().toString();
+ URI targetUri = URI.create(url);
+ try {
+ Desktop.getDesktop().browse(targetUri);
+ }
+ catch(IOException e) {
+ }
+ }
+ });
+
+ JFrame frame = new JFrame("Error Details");
+
+ Border paddingBorder = BorderFactory.createEmptyBorder(0, 20, 0, 0);
+ Border border = BorderFactory.createLineBorder(java.awt.Color.decode("#58a2d1"), 10);
+ Border compoundBorder = BorderFactory.createCompoundBorder(border, paddingBorder);
+
+ errorPane.setBorder(compoundBorder);
+ scrollPane.setBorder(null);
+ frame.setSize(popupWidth, popupHeight);
+
+ JPanel containerPanel = new JPanel(new BorderLayout());
+ containerPanel.add(scrollPane, BorderLayout.CENTER);
+ frame.setContentPane(containerPanel);
+ frame.setVisible(true);
+ }
+ }
+ /**
+ * A constructor for the EditorStatus class. It interacts with the BasicSplitPaneUI and Editor objects
+ * to handle mouse events for opening URLs, copying text to the clipboard, and toggling collapse.
+ *
+ * @param ui an instance of the BasicSplitPaneUI class.
+ * @param editor the editor panel associated with this status.
+ */
public EditorStatus(BasicSplitPaneUI ui, Editor editor) {
super(ui);
this.editor = editor;
@@ -222,18 +356,26 @@ void updateMouse(MouseEvent e, boolean pressed) {
} else if (url != null && mouseX > LEFT_MARGIN && mouseX < messageRight) {
mouseState = pressed ? URL_PRESSED : URL_ROLLOVER;
}
+ else if (mouseX > sizeW - (buttonEach * 3) && mouseX < sizeW - (2 * buttonEach)) {
+ mouseState = pressed ? MORE_INFO_PRESSED : MORE_INFO_ROLLOVER;
+ }
}
}
// only change on the rollover, no need to update on press
switch (mouseState) {
case CLIPBOARD_ROLLOVER:
+ setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
+ break;
case URL_ROLLOVER:
setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
break;
case COLLAPSE_ROLLOVER:
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
break;
+ case MORE_INFO_ROLLOVER:
+ setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
+ break;
case NONE:
setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
break;
@@ -250,7 +392,6 @@ static String findURL(String message) {
return null;
}
-
protected void updateTheme() {
urlEnabledAlpha = 255 * Theme.getInteger("status.url.enabled.alpha") / 100;
urlRolloverAlpha = 255 * Theme.getInteger("status.url.rollover.alpha") / 100;
@@ -268,6 +409,8 @@ protected void updateTheme() {
searchIcon = renderIcons("status/search", stateColors);
collapseIcon = renderIcons("status/console-collapse", stateColors);
expandIcon = renderIcons("status/console-expand", stateColors);
+ moreInfoIcon = renderIcons("status/more-info", stateColors);
+
btnEnabledAlpha = Theme.getInteger("status.button.enabled.alpha") / 100f;
btnRolloverAlpha = Theme.getInteger("status.button.rollover.alpha") / 100f;
@@ -310,12 +453,23 @@ public void empty() {
repaint();
}
-
- public void message(String message, int mode) {
- this.message = message;
+ /**
+ * The message method updates the message and mode instance variables and determines whether the error message being passed in is friendly or not.
+ * @param message the error message being passed in.
+ * @param mode represents whether the message is friendly or not.
+ */
+ public void message(String message, int mode) {
+
+ String newMessage = message;
+ int indexOfNewLine = message.indexOf(" ");
+ if (indexOfNewLine != -1) {
+ this.friendly = true;
+ this.friendlyMessage = message;
+ newMessage = message.substring(0,indexOfNewLine);
+ }
+ this.message = newMessage;
this.mode = mode;
-
- url = findURL(message);
+ url = findURL(newMessage);
repaint();
}
@@ -449,9 +603,24 @@ public void paint(Graphics g) {
alpha = btnEnabledAlpha;
}
drawButton(g, 0, glyph, alpha);
+
+ // draw more info button
+ if (friendly) {
+ ImageIcon glyph2;
+ glyph2 = moreInfoIcon[mode];
+ if (mouseState == MORE_INFO_ROLLOVER) {
+ alpha = btnRolloverAlpha;
+ } else if (mouseState == MORE_INFO_PRESSED) {
+ alpha = btnPressedAlpha;
+ FriendlyErrorPopup friendlyPopup = new FriendlyErrorPopup(friendlyMessage);
+ }
+ else {
+ alpha = btnEnabledAlpha;
+ }
+ drawButton(g,2,moreInfoIcon[mode],alpha);
+ }
}
-
/**
* @param pos A zero-based button index with 0 as the rightmost button
*/
diff --git a/build/build.xml b/build/build.xml
index b20fa8357d..4035f6b0c0 100644
--- a/build/build.xml
+++ b/build/build.xml
@@ -186,6 +186,8 @@
+
+
@@ -534,6 +536,9 @@
+
+
+
@@ -1636,4 +1641,4 @@
-
+
\ No newline at end of file
diff --git a/build/shared/lib/languages/PDE.properties b/build/shared/lib/languages/PDE.properties
index fb2042f225..01a931b024 100644
--- a/build/shared/lib/languages/PDE.properties
+++ b/build/shared/lib/languages/PDE.properties
@@ -390,19 +390,21 @@ editor.status.archiver.cancel = Archive sketch canceled.
# Errors
editor.status.warning = Warning
editor.status.error = Error
-editor.status.error.syntax = Syntax Error - %s
+editor.status.error.syntax = Syntax Error - $statement$
+editor.status.error.syntaxdefault = Issue near $statement$
editor.status.error_on = Error on β%sβ
-editor.status.missing.default = Missing β%cβ
+editor.status.missing.default = Missing β$character$β
+
editor.status.missing.semicolon = Missing a semicolon β;β
editor.status.missing.left_sq_bracket = Missing left square bracket β[β
editor.status.missing.right_sq_bracket = Missing right square bracket β]β
editor.status.missing.left_paren = Missing left parenthesis β(β
editor.status.missing.right_paren = Missing right parenthesis β)β
-editor.status.missing.left_curly_bracket = Missing left curly bracket β{β
-editor.status.missing.right_curly_bracket = Missing right curly bracket β}β
+editor.status.missing.left_curly_bracket = It looks like there is a missing left curly bracket β{β somewhere near line β$linenumber$β.
Hint: [Curly brackets](https://processing.org/reference/curlybraces.html) are used to group lines of code that should be executed together. These groups are often known as code blocks. It may be helpful to think of a code block as a set of instructions that Processing follows in the order that they are written. If a left curly bracket is missing, Processing doesnβt know where the code block begins.
Suggestion: Try adding the missing left curly bracket before $statement$. Double check that you are adding it before the first line of code that you want to include in the code block.
For more: [Variable Scope Example](https://processing.org/examples/variablescope.html) [Methods / Functions Example](https://processing.org/examples/functions.html)
+editor.status.missing.right_curly_bracket = It looks like there is a missing right curly bracket β}β somewhere near line β$linenumber$β.
Hint: [Curly bracket](https://processing.org/reference/curlybraces.html) are used to group lines of code that should be executed together. These groups are often known as code blocks. It may be helpful to think of a code block as a set of instructions that Processing follows in the order that they are written. If a right curly bracket is missing, Processing doesnβt know where the code block ends.
Suggestion: Try adding the missing right curly bracket after $statement$. Double check that you are adding it after the last line of code that you want to include in the code block.
For more: [Variable Scope Example](https://processing.org/examples/variablescope.html) [Methods / Functions Example](https://processing.org/examples/functions.html)
editor.status.missing.add = Consider adding β%sβ
editor.status.bad_curly_quote = Curly quotes like %s donβt work. Use straight quotes. Ctrl-T to autocorrect.
-editor.status.reserved_words = βcolorβ and βintβ are reserved words & cannot be used as variable names
+editor.status.reserved_words = β%sβ and β%sβ are reserved words & cannot be used as variable names
editor.status.undefined_method = The function β%s(%s)β does not exist
editor.status.undefined_constructor = The constructor β%s(%s)β does not exist
editor.status.empty_param = The function β%s()β does not expect any parameters
@@ -411,23 +413,27 @@ editor.status.undef_global_var = The global variable β%sβ does not exist
editor.status.undef_class = The class β%sβ does not exist
editor.status.undef_var = The variable β%sβ does not exist
editor.status.undef_name = The name β%sβ cannot be recognized
-editor.status.unterm_string_curly = String literal is not closed by a straight double quote. Curly quotes like %s wonβt help.
+editor.status.unterm_string_curly = String literal is not closed by a straight double quote. Curly quotes like $statement$ wonβt help.
editor.status.type_mismatch = Type mismatch, β%sβ does not match with β%sβ
editor.status.unused_variable = The value of the local variable β%sβ is not used
editor.status.uninitialized_variable = The local variable β%sβ may not have been initialized
editor.status.no_effect_assignment = The assignment to variable β%sβ has no effect
editor.status.hiding_enclosing_type = The class β%sβ cannot have the same name as your sketch or its enclosing class
-editor.status.bad.assignment = Possible error on variable assignment near β%sβ?
-editor.status.bad.generic = Possibly missing type in generic near β%sβ?
-editor.status.bad.identifier = Bad identifier? Did you forget a variable or start an identifier with digits near β%sβ?
-editor.status.bad.parameter = Error on parameter or method declaration near β%sβ?
+editor.status.bad.assignment = There seems to be some trouble with how a variable is being assigned near line $linenumber$!
Hint: There is an issue with how a variable is being set to a certain value. This is also known as variable [assignment](https://processing.org/reference/assign.html). We suspect that the problem is near β$statement$β.
Suggestion: Itβs important to double check that the variable is named properly (see the variable example below) and that you have included all of the necessary parts of a variable assignment. You should also make sure that you chose an appropriate [datatype](https://processing.org/reference/#data) for your variable so that it can hold the value that you are assigning to it.
Example:To assign variables you need the type, name, assignment operator (=), the value you want to assign the variable to and a semicolon. Hereβs the basic format: [data type] [name] = [value]; Example: int num = 4;
For more: [Variable Example](https://processing.org/examples/variables.html)
+editor.status.bad.generic = It looks like something (a generic) was not given a type to hold near line $linenumber$ near $statement$.
Hint: When we're working with things like lists or arrays in Processing, you need to tell it what type of data it will hold. It seems like you forgot to specify the type of data you're using somewhere near line$linenumber$.
Suggestion: The syntax for informing Processing about the intended data type of a data structure involves the use of angle brackets ('<>') referred to as generics. Typically, these angle brackets are placed after the name of the data structure.
Example: If you want to store integers in a list, you would declare it like this: List myList = new ArrayList();
For More: [Example of Data Structure ArrayList that Uses Generics](https://processing.org/examples/arraylistclass.html)
+editor.status.bad.identifier = There is an issue with how something in the code was named near line $linenumber$.
Hint: Itβs likely that $statement$ is not a valid name. In Processing, identifiers (names of variables, methods, classes, etc.) cannot start with a number and should only include letters, numbers, dollar signs ($) or underscores (_).
Suggestion: Double check that your name starts with a letter, dollar sign or an underscore and that the name is not a keyword that is part of the language like "true", βclassβ or βsetupβ.
For more: [Variable Examples](https://processing.org/examples/variables.html) [Function Examples](https://processing.org/examples/functions.html)
+editor.status.bad.parameter = It looks like the wrong type of parameter was given to a method near line $linenumber$.
Hint: This likely means that a variable of the wrong [type](https://processing.org/reference/#data) was given to the method near β$statement$β.
Suggestion:Make sure that the data type that the method is expecting is the one that is being passed in.
Example: βint count(int num){ return num;} The above method βcountβ accepts an [integer](https://processing.org/reference/int.html) as a parameter. This means it is expecting an integer to be given to count to use in the code that it runs. If instead of getting an integer, it getsa [String](https://processing.org/reference/String.html): βcount(βoneβ);β Then the method cannot run properly.
For more: [Methods / Functions Example](https://processing.org/examples/functions.html)
editor.status.bad.import = Import not allowed here.
editor.status.bad.mixed_mode = You may be mixing active and static modes.
-editor.status.extraneous = Incomplete statement or extra code near β%sβ?
-editor.status.mismatched = Missing operator, semicolon, or β}β near β%sβ?
-editor.status.missing.name = Missing name or ; near β%sβ?
-editor.status.missing.type = Missing name or ; or type near β%sβ?
+editor.status.extraneous = Incomplete statement or extra code near β$statement$β?
+editor.status.mismatched = Missing operator, semicolon, or β}β near β$statement$β?
+
+# turn into 3 separate error codes for MissingIdentifierSimplifierStrategy, MissingClassNameStrategy & MethodMissingNameStrategy
+editor.status.missing.name = Missing name or ; near β$statement$β?
+
+
+editor.status.missing.type = There seems to be some trouble with how a variable is being declared near line $linenumber$!
Hint: Somewhere along the line there is an issue with how a variable is being created (also known as being declared). We suspect that the problem is near β$statement$β.
Suggestion: Itβs important to double check that the variable is named properly (see the variable example below) and that you have included all of the necessary parts of a variable declaration. It is also possible that you forgot a [semicolon(https://processing.org/reference/semicolon.html).
Example:To declare variables you need the type, name and a semicolon. Hereβs the basic format: Β [data type] [name] = [value]; Example: int num;