diff --git a/app/.classpath b/app/.classpath index d556d28fe..77cb666a1 100644 --- a/app/.classpath +++ b/app/.classpath @@ -55,5 +55,6 @@ + diff --git a/app/src/cc/arduino/autocomplete/ArduinoCompletionsList.java b/app/src/cc/arduino/autocomplete/ArduinoCompletionsList.java new file mode 100644 index 000000000..8f8be5056 --- /dev/null +++ b/app/src/cc/arduino/autocomplete/ArduinoCompletionsList.java @@ -0,0 +1,61 @@ +package cc.arduino.autocomplete; + +import java.util.ArrayList; +import java.util.List; + +public class ArduinoCompletionsList extends ArrayList { +} + +class ArduinoCompletion { + ArduinoCompletionDetail completion; + String type; + + public ArduinoCompletionDetail getCompletion() { + return completion; + } + + public String getType() { + return type; + } +} + +class ArduinoCompletionDetail { + List chunks; + String brief; + + public List getChunks() { + return chunks; + } + + public String getBrief() { + return brief; + } +} + +class CompletionChunk { + String typedtext; + String t; + String placeholder; + String res; + ArduinoCompletionDetail optional; + + public String getTypedtext() { + return typedtext; + } + + public String getT() { + return t; + } + + public String getPlaceholder() { + return placeholder; + } + + public String getRes() { + return res; + } + + public ArduinoCompletionDetail getOptional() { + return optional; + } +} \ No newline at end of file diff --git a/app/src/cc/arduino/autocomplete/BaseCCompletionProvider.java b/app/src/cc/arduino/autocomplete/BaseCCompletionProvider.java index cf0b1dae1..7c10d256a 100755 --- a/app/src/cc/arduino/autocomplete/BaseCCompletionProvider.java +++ b/app/src/cc/arduino/autocomplete/BaseCCompletionProvider.java @@ -7,7 +7,7 @@ import org.fife.ui.autocomplete.DefaultCompletionProvider; * @author Ricardo JL Rufino (ricardo@criativasoft.com.br) * @date 28/04/2017 */ -public class BaseCCompletionProvider extends DefaultCompletionProvider{ +public class BaseCCompletionProvider extends DefaultCompletionProvider { @Override protected boolean isValidChar(char ch) { diff --git a/app/src/cc/arduino/autocomplete/ClangCompletionProvider.java b/app/src/cc/arduino/autocomplete/ClangCompletionProvider.java new file mode 100644 index 000000000..6a46d5ee6 --- /dev/null +++ b/app/src/cc/arduino/autocomplete/ClangCompletionProvider.java @@ -0,0 +1,88 @@ +package cc.arduino.autocomplete; + +import java.util.ArrayList; +import java.util.List; + +import javax.swing.text.BadLocationException; +import javax.swing.text.JTextComponent; + +import org.fife.ui.autocomplete.Completion; +import org.fife.ui.autocomplete.TemplateCompletion; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import processing.app.Editor; +import processing.app.EditorTab; + +public class ClangCompletionProvider extends BaseCCompletionProvider { + + private Editor editor; + + public ClangCompletionProvider(Editor e) { + super(); + editor = e; + } + + @Override + public List getCompletionByInputText(String inputText) { + System.out.println("INPUTTEXT: " + inputText); + return super.getCompletionByInputText(inputText); + } + + @Override + protected List getCompletionsImpl(JTextComponent textarea) { + + // Retrieve current line and column + EditorTab tab = editor.getCurrentTab(); + int line, col; + try { + int pos = tab.getTextArea().getCaretPosition(); + line = tab.getTextArea().getLineOfOffset(pos); + col = pos - tab.getTextArea().getLineStartOffset(line); + line++; + col++; + } catch (BadLocationException e1) { + // Should never happen... + e1.printStackTrace(); + return completions; + } + + try { + // Run codecompletion engine + String out = editor.getSketchController() + .codeComplete(tab.getSketchFile(), line, col); + + List res = new ArrayList<>(); + res.add(new TemplateCompletion(this, "for", "interate over array", + "for (int ${i} = 0; ${i} < ${array}.length; ${i}++) {\n ${cursor}\n}")); + + // Parse engine output and build code completions + ObjectMapper mapper = new ObjectMapper(); + ArduinoCompletionsList allCc; + allCc = mapper.readValue(out, ArduinoCompletionsList.class); + for (ArduinoCompletion cc : allCc) { + if (cc.type.equals("macro")) { + // for now skip macro + continue; + } + String returnType; + String typedText; + String template = ""; + for (CompletionChunk chunk : cc.completion.chunks) { + if (chunk.t != null) { + template += "t"; + } + if (chunk.res != null) { + returnType = chunk.res; + } + if (chunk.typedtext != null) { + typedText = chunk.typedtext; + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return completions; + } +} diff --git a/app/src/processing/app/EditorTab.java b/app/src/processing/app/EditorTab.java index 7fe2439cd..9765ba4f6 100644 --- a/app/src/processing/app/EditorTab.java +++ b/app/src/processing/app/EditorTab.java @@ -58,6 +58,7 @@ import java.nio.file.Path; import java.io.File; import org.apache.commons.lang3.StringUtils; +import org.fife.ui.autocomplete.AutoCompletion; import org.fife.ui.rsyntaxtextarea.RSyntaxDocument; import org.fife.ui.rsyntaxtextarea.RSyntaxTextAreaEditorKit; import org.fife.ui.rsyntaxtextarea.RSyntaxUtilities; @@ -65,7 +66,7 @@ import org.fife.ui.rtextarea.Gutter; import org.fife.ui.rtextarea.RTextScrollPane; import cc.arduino.UpdatableBoardsLibsFakeURLsHandler; -import cc.arduino.autocomplete.FakeCompletionProvider; +import cc.arduino.autocomplete.ClangCompletionProvider; import processing.app.helpers.DocumentTextChangeListener; import processing.app.syntax.ArduinoTokenMakerFactory; import processing.app.syntax.PdeKeywords; @@ -120,7 +121,17 @@ public class EditorTab extends JPanel implements SketchFile.TextStorage { applyPreferences(); add(scrollPane, BorderLayout.CENTER); editor.base.addEditorFontResizeMouseWheelListener(textarea); - textarea.setupAutoComplete(file.getSketch(), new FakeCompletionProvider()); +// SketchCompletionProvider completionProvider = new SketchCompletionProvider( +// editor.getSketch(), textarea, new ClangCompletionProvider(editor)); + + AutoCompletion ac = new AutoCompletion(new ClangCompletionProvider(editor)); + ac.setAutoActivationEnabled(true); + ac.setShowDescWindow(false); + ac.setAutoCompleteSingleChoices(true); + ac.setParameterAssistanceEnabled(true); + // ac.setParamChoicesRenderer(new CompletionsRenderer()); + // ac.setListCellRenderer(new CompletionsRenderer()); + ac.install(textarea); } private RSyntaxDocument createDocument(String contents) { diff --git a/app/src/processing/app/SketchController.java b/app/src/processing/app/SketchController.java index ce9e468cc..f4cbad0e2 100644 --- a/app/src/processing/app/SketchController.java +++ b/app/src/processing/app/SketchController.java @@ -680,6 +680,40 @@ public class SketchController { return Paths.get(tempFolder.getAbsolutePath(), sketch.getPrimaryFile().getFileName()).toFile(); } + /** + * Preprocess sketch and obtain code-completions. + * + * @return null if compilation failed, main class name if not + */ + public String codeComplete(SketchFile file, int line, int col) throws RunnerException, PreferencesMapException, IOException { + // run the preprocessor + for (CompilerProgressListener progressListener : editor.status.getCompilerProgressListeners()){ + progressListener.progress(20); + } + + ensureExistence(); + + boolean deleteTemp = false; + File pathToSketch = sketch.getPrimaryFile().getFile(); + File requestedFile = file.getFile(); + if (sketch.isModified()) { + // If any files are modified, make a copy of the sketch with the changes + // saved, so arduino-builder will see the modifications. + pathToSketch = saveSketchInTempFolder(); + // This takes into account when the sketch is copied into a temporary folder + requestedFile = new File(pathToSketch.getParent(), requestedFile.getName()); + deleteTemp = true; + } + + try { + return new Compiler(pathToSketch, sketch).codeComplete(editor.status.getCompilerProgressListeners(), requestedFile, line, col); + } finally { + // Make sure we clean up any temporary sketch copy + if (deleteTemp) + FileUtils.recursiveDelete(pathToSketch.getParentFile()); + } + } + /** * Handle export to applet. */ diff --git a/app/src/processing/app/syntax/SketchTextArea.java b/app/src/processing/app/syntax/SketchTextArea.java index 95a5ec7ab..060409ba5 100644 --- a/app/src/processing/app/syntax/SketchTextArea.java +++ b/app/src/processing/app/syntax/SketchTextArea.java @@ -135,22 +135,6 @@ public class SketchTextArea extends RSyntaxTextArea { setLinkGenerator(new DocLinkGenerator(pdeKeywords)); } - public void setupAutoComplete(Sketch sketch, CompletionProvider provider) { - - SketchCompletionProvider completionProvider = new SketchCompletionProvider(sketch, this, provider); - - AutoCompletion ac = new AutoCompletion( completionProvider ); - - ac.setAutoActivationEnabled(true); - ac.setShowDescWindow(false); - ac.setAutoCompleteSingleChoices(true); - ac.setParameterAssistanceEnabled(true); -// ac.setParamChoicesRenderer(new CompletionsRenderer()); -// ac.setListCellRenderer(new CompletionsRenderer()); - ac.install(this); - - } - private void installFeatures() throws IOException { setTheme(PreferencesData.get("editor.syntax_theme", "default")); diff --git a/arduino-core/src/cc/arduino/Compiler.java b/arduino-core/src/cc/arduino/Compiler.java index c2c5b0ff6..0b77cd3ed 100644 --- a/arduino-core/src/cc/arduino/Compiler.java +++ b/arduino-core/src/cc/arduino/Compiler.java @@ -125,7 +125,7 @@ public class Compiler implements MessageConsumer { } enum BuilderAction { - COMPILE("-compile"), DUMP_PREFS("-dump-prefs"); + COMPILE("-compile"), DUMP_PREFS("-dump-prefs"), CODE_COMPLETE("-code-complete-at"); final String value; @@ -143,6 +143,10 @@ public class Compiler implements MessageConsumer { private final boolean verbose; private RunnerException exception; + private File codeCompleteFile; + private int codeCompleteLine; + private int codeCompleteCol; + public Compiler(Sketch data) { this(data.getPrimaryFile().getFile(), data); } @@ -193,6 +197,34 @@ public class Compiler implements MessageConsumer { return sketch.getPrimaryFile().getFileName(); } + public String codeComplete(ArrayList progListeners, File file, int line, int col) throws RunnerException, PreferencesMapException, IOException { + this.buildPath = sketch.getBuildPath().getAbsolutePath(); + this.buildCache = BaseNoGui.getCachePath(); + + TargetBoard board = BaseNoGui.getTargetBoard(); + if (board == null) { + throw new RunnerException("Board is not selected"); + } + + TargetPlatform platform = board.getContainerPlatform(); + TargetPackage aPackage = platform.getContainerPackage(); + String vidpid = VIDPID(); + + ByteArrayOutputStream completions = new ByteArrayOutputStream(); + MessageConsumerOutputStream out = new MessageConsumerOutputStream(new ProgressAwareMessageConsumer(new I18NAwareMessageConsumer(new PrintStream(completions), System.err), progListeners), "\n"); + MessageConsumerOutputStream err = new MessageConsumerOutputStream(new I18NAwareMessageConsumer(System.err, Compiler.this), "\n"); + + codeCompleteFile = file; + codeCompleteLine = line; + codeCompleteCol = col; + callArduinoBuilder(board, platform, aPackage, vidpid, BuilderAction.CODE_COMPLETE, out, err); + + out.flush(); + err.flush(); + + return completions.toString(); + } + private String VIDPID() { BoardPort boardPort = BaseNoGui.getDiscoveryManager().find(PreferencesData.get("serial.port")); if (boardPort == null) { @@ -234,6 +266,9 @@ public class Compiler implements MessageConsumer { List cmd = new ArrayList<>(); cmd.add(BaseNoGui.getContentFile("arduino-builder").getAbsolutePath()); cmd.add(action.value); + if (action == BuilderAction.CODE_COMPLETE) { + cmd.add(codeCompleteFile.getAbsolutePath() + ":" + codeCompleteLine + ":" + codeCompleteCol); + } cmd.add("-logger=machine"); File installedPackagesFolder = new File(BaseNoGui.getSettingsFolder(), "packages"); @@ -280,9 +315,7 @@ public class Compiler implements MessageConsumer { } } - //commandLine.addArgument("-debug-level=10", false); - - if (verbose) { + if (verbose && action != BuilderAction.CODE_COMPLETE) { cmd.add("-verbose"); }