From 3f1fbadc4a406ebcd67addaaa848d8fae7627de0 Mon Sep 17 00:00:00 2001 From: Tim Koers Date: Mon, 19 Mar 2018 21:37:43 +0100 Subject: [PATCH 1/4] First version of the auto board selector. Working! --- app/src/processing/app/Base.java | 6 +- app/src/processing/app/Editor.java | 17 ++++ app/src/processing/app/EditorTab.java | 49 +++++++-- arduino-core/src/processing/app/Sketch.java | 105 ++++++++++++++++++++ 4 files changed, 169 insertions(+), 8 deletions(-) diff --git a/app/src/processing/app/Base.java b/app/src/processing/app/Base.java index 1752e1dc824..468594c4f30 100644 --- a/app/src/processing/app/Base.java +++ b/app/src/processing/app/Base.java @@ -477,7 +477,10 @@ public Base(String[] args) throws Exception { contributionsSelfCheck = new ContributionsSelfCheck(this, new UpdatableBoardsLibsFakeURLsHandler(this), contributionInstaller, libraryInstaller); new Timer(false).schedule(contributionsSelfCheck, Constants.BOARDS_LIBS_UPDATABLE_CHECK_START_PERIOD); } - + // Load the build settings + for(Editor editor: editors){ + editor.findTab(editor.sketch.getPrimaryFile()).loadBuildSettings(this); + } } else if (parser.isNoOpMode()) { // Do nothing (intended for only changing preferences) System.exit(0); @@ -1496,6 +1499,7 @@ public void actionPerformed(ActionEvent actionevent) { } } + private JRadioButtonMenuItem createBoardMenusAndCustomMenus( final List boardsCustomMenus, List menuItemsToClickAfterStartup, Map buttonGroupsMap, diff --git a/app/src/processing/app/Editor.java b/app/src/processing/app/Editor.java index 99e53e488dc..102d652c737 100644 --- a/app/src/processing/app/Editor.java +++ b/app/src/processing/app/Editor.java @@ -65,6 +65,7 @@ import java.util.*; import java.util.List; import java.util.function.Predicate; +import java.util.stream.Collectors; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.ArrayList; @@ -339,6 +340,7 @@ public void windowDeactivated(WindowEvent e) { // Open the document that was passed in boolean loaded = handleOpenInternal(file); if (!loaded) sketchController = null; + } @@ -764,6 +766,10 @@ private JMenu buildToolsMenu() { item = new JMenuItem(tr("Get Board Info")); item.addActionListener(e -> handleBoardInfo()); toolsMenu.add(item); + + item = new JMenuItem(tr("Add build settings to .INO file")); + item.addActionListener(e -> handleAddBuildSettings()); + toolsMenu.add(item); toolsMenu.addSeparator(); base.rebuildProgrammerMenu(); @@ -2512,6 +2518,17 @@ public void handlePlotter() { } + public void handleAddBuildSettings(){ + final LinkedHashMap settingsMap = base.getBoardsCustomMenus().stream().filter(JMenu::isVisible).map((e)->{ + String setting = e.getText().substring(0, e.getText().indexOf(":")); + String value = e.getText().replace(setting + ":", "").replace("\"", "").trim(); + return new String[]{setting, value}; + }).collect(LinkedHashMap::new, (map, menu) -> map.put(menu[0], menu[1]), LinkedHashMap::putAll); + sketch.setBuildSettings(findTab(sketch.getPrimaryFile()), settingsMap); + handleSave(true); + System.out.println("Build settings header should be added"); + } + private void handleBurnBootloader() { console.clear(); statusNotice(tr("Burning bootloader to I/O Board (this may take a minute)...")); diff --git a/app/src/processing/app/EditorTab.java b/app/src/processing/app/EditorTab.java index 33dabdbbbc2..b190d9b6e34 100644 --- a/app/src/processing/app/EditorTab.java +++ b/app/src/processing/app/EditorTab.java @@ -34,13 +34,12 @@ import java.awt.event.MouseWheelEvent; import java.io.IOException; +import java.lang.annotation.Target; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.Optional; -import javax.swing.Action; -import javax.swing.BorderFactory; -import javax.swing.JMenuItem; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; -import javax.swing.ToolTipManager; +import javax.swing.*; import javax.swing.border.MatteBorder; import javax.swing.event.PopupMenuEvent; import javax.swing.event.PopupMenuListener; @@ -57,6 +56,8 @@ import org.fife.ui.rtextarea.RTextScrollPane; import cc.arduino.UpdatableBoardsLibsFakeURLsHandler; +import processing.app.debug.TargetBoard; +import processing.app.debug.TargetPackage; import processing.app.helpers.DocumentTextChangeListener; import processing.app.syntax.ArduinoTokenMakerFactory; import processing.app.syntax.PdeKeywords; @@ -109,7 +110,7 @@ public EditorTab(Editor editor, SketchFile file, String contents) file.setStorage(this); applyPreferences(); add(scrollPane, BorderLayout.CENTER); - textarea.addMouseWheelListener(this); + textarea.addMouseWheelListener(this); } private RSyntaxDocument createDocument(String contents) { @@ -433,6 +434,40 @@ public void setText(String what) { } } + /** + * This method loads the build settings from the main .INO file and sets the Arduino IDE accordingly + */ + public void loadBuildSettings(Base base){ + + LinkedHashMap buildSettings = getSketch().getSketch().getBuildSettingsFromProgram(getText()); + + Optional optionalTargetBoard = BaseNoGui.getTargetPlatform().getBoards().values().stream().filter(board -> board.getPreferences().get("name","").equals("Node32s")).findFirst(); + + TargetBoard targetBoard; + if(optionalTargetBoard.isPresent()){ + targetBoard = optionalTargetBoard.get(); + }else{ + return; + } + + for(String readableName : buildSettings.values()){ + base.getBoardsCustomMenus().forEach(menuItem -> { + Optional optionalBoardMenuItem = Arrays.stream(menuItem.getMenuComponents()).filter(subItem->{ + return subItem instanceof JRadioButtonMenuItem && ((JRadioButtonMenuItem)subItem).getText().equals(readableName) && (((JRadioButtonMenuItem) subItem).getAction().getValue("board") == null || (((JRadioButtonMenuItem) subItem).getAction().getValue("board").equals(targetBoard))); + } + ).map(subItem-> { + return subItem instanceof JRadioButtonMenuItem ? (JRadioButtonMenuItem)subItem : new JRadioButtonMenuItem(); + }).findFirst(); + if(optionalBoardMenuItem.isPresent()){ + optionalBoardMenuItem.get().setSelected(true); + optionalBoardMenuItem.get().getAction().actionPerformed(new ActionEvent(this, -1, "")); + }else{ + // TODO Ask the user which value should replace the current value + } + }); + } + } + /** * Is the text modified since the last save / load? */ diff --git a/arduino-core/src/processing/app/Sketch.java b/arduino-core/src/processing/app/Sketch.java index 6c417403ec9..c7ee9c9cbea 100644 --- a/arduino-core/src/processing/app/Sketch.java +++ b/arduino-core/src/processing/app/Sketch.java @@ -139,6 +139,111 @@ public void save() throws IOException { } } + private final String buildToolsHeader = "\n/** Arduino IDE Board Tool details\n"; + private final String buildToolsHeaderEnd = "*/"; + + /** + * Checks the code for a valid build tool header + * @param program The code to scan for the build tools + * @return True if the build tool header was found ONE time. Returns false if found MORE than one time, or not found at all. + */ + private boolean containsBuildSettings(String program){ + return program.contains(buildToolsHeader) && (program.indexOf(buildToolsHeader) == program.lastIndexOf(buildToolsHeader)); + } + + /** + * This function returns the index of the Nth occurrence of the substring in the specified string (http://programming.guide/java/nth-occurrence-in-string.html) + * @param str The string to find the Nth occurrence in + * @param substr The string to find + * @param n The occurrence number you'd like to find + * @return + */ + private static int ordinalIndexOf(String str, String substr, int n) { + int pos = str.indexOf(substr); + while (--n > 0 && pos != -1) + pos = str.indexOf(substr, pos + 1); + return pos; + } + + private void removeBuildSettingsHeader(EditorTab tab){ + if(tab.getText().contains(buildToolsHeader)) { + int headerStartIndex = tab.getText().indexOf(buildToolsHeader); + int headerStopIndex = tab.getText().indexOf(buildToolsHeaderEnd); + if (headerStartIndex > headerStopIndex) { + System.err.println("The build tool header is not the first comment block in your file! Please fix this."); + for (int i = 0; i < tab.getText().length(); i++) { + if (headerStartIndex < ordinalIndexOf(tab.getText(), buildToolsHeaderEnd, i)) { + headerStopIndex = ordinalIndexOf(tab.getText(), buildToolsHeaderEnd, i); + break; + } + } + } + String header = tab.getText().substring(headerStartIndex, headerStopIndex + buildToolsHeaderEnd.length()); + tab.setText(tab.getText().replace(header, "")); + // Run this method again so we are sure that there aren't any headers left + removeBuildSettingsHeader(tab); + } + } + + /** + * This checks the program code for a valid build tool settings header and returns the LinkedHashMap with the setting name and the value. + * The build tools header should not be changed or manipulated by the pre-processor as the pre-processors output may depend on the build tools. + * @param program The program code + * @return The {@code LinkedHashMap} with the settings and their values of the first header that was found in the program code + */ + public LinkedHashMap getBuildSettingsFromProgram(String program){ + LinkedHashMap buildSettings = new LinkedHashMap<>(); + if(containsBuildSettings(program)){ + int headerStartIndex = program.indexOf(buildToolsHeader); + int headerStopIndex = program.indexOf(buildToolsHeaderEnd); + if(headerStartIndex > headerStopIndex){ + System.err.println("The build tool header is not the first comment block in your file! Please fix this."); + for(int i = 0; i < program.length(); i++){ + if(headerStartIndex < ordinalIndexOf(program, buildToolsHeaderEnd, i)){ + headerStopIndex = ordinalIndexOf(program, buildToolsHeaderEnd, i); + break; + } + } + } + String header = program.substring(headerStartIndex + buildToolsHeader.length(), headerStopIndex); + + String[] headerLines = header.split("\n"); + + for(int line = 0; line < headerLines.length; line++){ + String[] setting = headerLines[line].replace("*","").trim().split(": "); + if(headerLines[line].indexOf(": ") != (headerLines[line].length() -1)){ + // The value of the setting is not empty + buildSettings.put(setting[0].trim(), setting[1].trim()); + }else{ + buildSettings.put(setting[0], ""); + } + } + }else{ + if(!program.contains(buildToolsHeader)){ + // There are multiple headers, remove them + // TODO Create a dialog asking the user to add a build header to the file + } + } + + return buildSettings; + } + + private boolean isBuildSettingsEqual(LinkedHashMap first, LinkedHashMap second){ + return first.keySet().containsAll(second.keySet()) && first.values().containsAll(second.values()); + } + + public void setBuildSettings(EditorTab tab, LinkedHashMap buildSettings){ + if(tab.getSketch().getSketch() != this){ + return; + } + + String customBoardSettingsHeader = buildSettings.entrySet().stream().map(entry-> String.format(" * %s: %s\n", entry.getKey(), entry.getValue())).collect(Collectors.joining("", buildToolsHeader, "*/")); + if(!isBuildSettingsEqual(getBuildSettingsFromProgram(tab.getText()),buildSettings)){ + removeBuildSettingsHeader(tab); + tab.setText(customBoardSettingsHeader + ((tab.getText().charAt(0) == '\n') ? "" : "\n") + tab.getText()); + } + } + public int getCodeCount() { return files.size(); } From 0b2982c73802cca30f96a3434f9dc557471f86d6 Mon Sep 17 00:00:00 2001 From: Tim Koers Date: Tue, 20 Mar 2018 15:06:01 +0100 Subject: [PATCH 2/4] Little formatting fix and added the Arduino/Genuino Uno as the default header for the sketch --- .gitignore | 1 + app/src/processing/app/Base.java | 2 -- app/src/processing/app/EditorTab.java | 1 + build/shared/examples/01.Basics/BareMinimum/BareMinimum.ino | 6 +++++- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 0ff213c4047..5f5d747e730 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ app/pde.jar build/macosx/work/ arduino-core/bin/ arduino-core/arduino-core.jar +lib/* hardware/arduino/bootloaders/caterina_LUFA/Descriptors.o hardware/arduino/bootloaders/caterina_LUFA/Descriptors.lst hardware/arduino/bootloaders/caterina_LUFA/Caterina.sym diff --git a/app/src/processing/app/Base.java b/app/src/processing/app/Base.java index 468594c4f30..5cfb8dc0f4c 100644 --- a/app/src/processing/app/Base.java +++ b/app/src/processing/app/Base.java @@ -680,10 +680,8 @@ protected int[] nextEditorLocation() { } } - // ................................................................. - boolean breakTime = false; String[] months = { "jan", "feb", "mar", "apr", "may", "jun", diff --git a/app/src/processing/app/EditorTab.java b/app/src/processing/app/EditorTab.java index b190d9b6e34..54d17f3a9e3 100644 --- a/app/src/processing/app/EditorTab.java +++ b/app/src/processing/app/EditorTab.java @@ -463,6 +463,7 @@ public void loadBuildSettings(Base base){ optionalBoardMenuItem.get().getAction().actionPerformed(new ActionEvent(this, -1, "")); }else{ // TODO Ask the user which value should replace the current value + } }); } diff --git a/build/shared/examples/01.Basics/BareMinimum/BareMinimum.ino b/build/shared/examples/01.Basics/BareMinimum/BareMinimum.ino index 95c2b6eb0a8..6586e6e69ab 100644 --- a/build/shared/examples/01.Basics/BareMinimum/BareMinimum.ino +++ b/build/shared/examples/01.Basics/BareMinimum/BareMinimum.ino @@ -1,3 +1,7 @@ +/** Arduino IDE Board Tool details + * Board: Arduino/Genuino Uno +*/ + void setup() { // put your setup code here, to run once: @@ -6,4 +10,4 @@ void setup() { void loop() { // put your main code here, to run repeatedly: -} +} \ No newline at end of file From 18b401660d804f1f9cde7ff1aa2302b1264d466b Mon Sep 17 00:00:00 2001 From: timkoers Date: Tue, 20 Mar 2018 16:40:54 +0100 Subject: [PATCH 3/4] Update Sketch.java This should fix the build error --- arduino-core/src/processing/app/Sketch.java | 1 + 1 file changed, 1 insertion(+) diff --git a/arduino-core/src/processing/app/Sketch.java b/arduino-core/src/processing/app/Sketch.java index c7ee9c9cbea..8c02ba50758 100644 --- a/arduino-core/src/processing/app/Sketch.java +++ b/arduino-core/src/processing/app/Sketch.java @@ -9,6 +9,7 @@ import cc.arduino.files.DeleteFilesOnShutdown; import processing.app.helpers.FileUtils; +import processing.app.EditorTab; import static processing.app.I18n.tr; From 9f1dcee2c58c6aea5732b365486125e2a215c3a0 Mon Sep 17 00:00:00 2001 From: Tim Koers Date: Tue, 20 Mar 2018 19:08:59 +0100 Subject: [PATCH 4/4] The Java compilation error has been fixed --- app/src/processing/app/Editor.java | 10 ++++-- app/src/processing/app/EditorTab.java | 1 + arduino-core/src/processing/app/Sketch.java | 35 ++++++++++----------- 3 files changed, 25 insertions(+), 21 deletions(-) diff --git a/app/src/processing/app/Editor.java b/app/src/processing/app/Editor.java index 102d652c737..cd9c49e9382 100644 --- a/app/src/processing/app/Editor.java +++ b/app/src/processing/app/Editor.java @@ -2524,9 +2524,13 @@ public void handleAddBuildSettings(){ String value = e.getText().replace(setting + ":", "").replace("\"", "").trim(); return new String[]{setting, value}; }).collect(LinkedHashMap::new, (map, menu) -> map.put(menu[0], menu[1]), LinkedHashMap::putAll); - sketch.setBuildSettings(findTab(sketch.getPrimaryFile()), settingsMap); - handleSave(true); - System.out.println("Build settings header should be added"); + handleSave(true); + Optional optionalEditorTab = tabs.stream().filter(tab -> tab.getSketch().getSketch().equals(sketch)).findFirst(); + if(optionalEditorTab.isPresent()){ + optionalEditorTab.get().setText(sketch.setBuildSettings(sketch, settingsMap)); + handleSave(true); + System.out.println("Build settings header should be added"); + } } private void handleBurnBootloader() { diff --git a/app/src/processing/app/EditorTab.java b/app/src/processing/app/EditorTab.java index 54d17f3a9e3..07ac0020aa0 100644 --- a/app/src/processing/app/EditorTab.java +++ b/app/src/processing/app/EditorTab.java @@ -434,6 +434,7 @@ public void setText(String what) { } } + /** * This method loads the build settings from the main .INO file and sets the Arduino IDE accordingly */ diff --git a/arduino-core/src/processing/app/Sketch.java b/arduino-core/src/processing/app/Sketch.java index 8c02ba50758..c9c42bc7f59 100644 --- a/arduino-core/src/processing/app/Sketch.java +++ b/arduino-core/src/processing/app/Sketch.java @@ -9,7 +9,6 @@ import cc.arduino.files.DeleteFilesOnShutdown; import processing.app.helpers.FileUtils; -import processing.app.EditorTab; import static processing.app.I18n.tr; @@ -166,24 +165,23 @@ private static int ordinalIndexOf(String str, String substr, int n) { return pos; } - private void removeBuildSettingsHeader(EditorTab tab){ - if(tab.getText().contains(buildToolsHeader)) { - int headerStartIndex = tab.getText().indexOf(buildToolsHeader); - int headerStopIndex = tab.getText().indexOf(buildToolsHeaderEnd); + private String removeBuildSettingsHeader(Sketch sketch){ + if(sketch.getPrimaryFile().getProgram().contains(buildToolsHeader)) { + int headerStartIndex = sketch.getPrimaryFile().getProgram().indexOf(buildToolsHeader); + int headerStopIndex = sketch.getPrimaryFile().getProgram().indexOf(buildToolsHeaderEnd); if (headerStartIndex > headerStopIndex) { System.err.println("The build tool header is not the first comment block in your file! Please fix this."); - for (int i = 0; i < tab.getText().length(); i++) { - if (headerStartIndex < ordinalIndexOf(tab.getText(), buildToolsHeaderEnd, i)) { - headerStopIndex = ordinalIndexOf(tab.getText(), buildToolsHeaderEnd, i); + for (int i = 0; i < sketch.getPrimaryFile().getProgram().length(); i++) { + if (headerStartIndex < ordinalIndexOf(sketch.getPrimaryFile().getProgram(), buildToolsHeaderEnd, i)) { + headerStopIndex = ordinalIndexOf(sketch.getPrimaryFile().getProgram(), buildToolsHeaderEnd, i); break; } } } - String header = tab.getText().substring(headerStartIndex, headerStopIndex + buildToolsHeaderEnd.length()); - tab.setText(tab.getText().replace(header, "")); - // Run this method again so we are sure that there aren't any headers left - removeBuildSettingsHeader(tab); + String header = sketch.getPrimaryFile().getProgram().substring(headerStartIndex, headerStopIndex + buildToolsHeaderEnd.length()); + return sketch.getPrimaryFile().getProgram().replace(header, ""); } + return sketch.getPrimaryFile().getProgram(); } /** @@ -233,16 +231,17 @@ private boolean isBuildSettingsEqual(LinkedHashMap first, LinkedH return first.keySet().containsAll(second.keySet()) && first.values().containsAll(second.values()); } - public void setBuildSettings(EditorTab tab, LinkedHashMap buildSettings){ - if(tab.getSketch().getSketch() != this){ - return; + public String setBuildSettings(Sketch sketch, LinkedHashMap buildSettings){ + if(sketch != this){ + return ""; } String customBoardSettingsHeader = buildSettings.entrySet().stream().map(entry-> String.format(" * %s: %s\n", entry.getKey(), entry.getValue())).collect(Collectors.joining("", buildToolsHeader, "*/")); - if(!isBuildSettingsEqual(getBuildSettingsFromProgram(tab.getText()),buildSettings)){ - removeBuildSettingsHeader(tab); - tab.setText(customBoardSettingsHeader + ((tab.getText().charAt(0) == '\n') ? "" : "\n") + tab.getText()); + if(!isBuildSettingsEqual(getBuildSettingsFromProgram(sketch.getPrimaryFile().getProgram()),buildSettings)){ + String headerLessProgram = removeBuildSettingsHeader(sketch); + return customBoardSettingsHeader + ((headerLessProgram.charAt(0) == '\n') ? "" : "\n") + headerLessProgram; } + return ""; } public int getCodeCount() {