Skip to content

Commit 66a001c

Browse files
committed
Handle cursor positioning
1 parent 7e76bc5 commit 66a001c

File tree

1 file changed

+42
-10
lines changed

1 file changed

+42
-10
lines changed

app/src/cc/arduino/packages/formatter/clangformat/ClangFormat.java

+42-10
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@
4040
import org.apache.commons.compress.utils.IOUtils;
4141
import org.apache.logging.log4j.core.util.NullOutputStream;
4242

43+
import com.fasterxml.jackson.databind.DeserializationFeature;
44+
import com.fasterxml.jackson.databind.ObjectMapper;
45+
4346
import processing.app.Base;
4447
import processing.app.Editor;
4548
import processing.app.helpers.ProcessUtils;
@@ -57,14 +60,15 @@ public ClangFormat(Editor editor) {
5760
@Override
5861
public void run() {
5962
String originalText = editor.getCurrentTab().getText();
60-
63+
int cursorOffset = editor.getCurrentTab().getTextArea().getCaretPosition();
6164
try {
62-
String formattedText = runClangFormatOn(originalText);
63-
if (formattedText.equals(originalText)) {
65+
FormatResult result = runClangFormatOn(originalText, cursorOffset);
66+
if (result.FormattedText.equals(originalText)) {
6467
editor.statusNotice(tr("No changes necessary for Auto Format."));
6568
return;
6669
}
67-
editor.getCurrentTab().setText(formattedText);
70+
editor.getCurrentTab().setText(result.FormattedText);
71+
editor.getCurrentTab().getTextArea().setCaretPosition(result.Cursor);
6872
editor.statusNotice(tr("Auto Format finished."));
6973
} catch (IOException | InterruptedException e) {
7074
editor.statusError("Auto format error: " + e.getMessage());
@@ -94,21 +98,49 @@ private Thread copyAndClose(InputStream input, OutputStream output) {
9498
return t;
9599
}
96100

97-
String runClangFormatOn(String source)
101+
FormatResult runClangFormatOn(String source, int cursorOffset)
98102
throws IOException, InterruptedException {
99-
String cmd[] = new String[] { clangExecutable };
103+
String cmd[] = new String[] { clangExecutable, "--cursor=" + cursorOffset };
100104

101105
Process process = ProcessUtils.exec(cmd);
102-
ByteArrayOutputStream result = new ByteArrayOutputStream();
106+
ByteArrayOutputStream clangOutput = new ByteArrayOutputStream();
103107
ByteArrayInputStream dataOut = new ByteArrayInputStream(source.getBytes());
104-
Thread out = copyAndClose(process.getInputStream(), result);
108+
109+
Thread in = copyAndClose(dataOut, process.getOutputStream());
105110
Thread err = copyAndClose(process.getErrorStream(),
106111
NullOutputStream.getInstance());
107-
Thread in = copyAndClose(dataOut, process.getOutputStream());
112+
Thread out = copyAndClose(process.getInputStream(), clangOutput);
113+
108114
/* int r = */process.waitFor();
109115
in.join();
110116
out.join();
111117
err.join();
112-
return result.toString();
118+
119+
// clang-format will output first a JSON object with:
120+
// - the resulting cursor position and
121+
// - a flag teling if the conversion was successful
122+
// for example:
123+
//
124+
// { "Cursor": 34, "IncompleteFormat": false }
125+
ObjectMapper mapper = new ObjectMapper();
126+
mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
127+
mapper.configure(DeserializationFeature.EAGER_DESERIALIZER_FETCH, true);
128+
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
129+
FormatResult res = mapper.readValue(clangOutput.toByteArray(),
130+
FormatResult.class);
131+
132+
// After the JSON object above clang-format will output the formatted source
133+
// code in plain text
134+
String formattedText = clangOutput.toString();
135+
formattedText = formattedText.substring(formattedText.indexOf('}') + 1);
136+
// handle different line endings
137+
res.FormattedText = formattedText.replaceFirst("\\R", "");
138+
return res;
113139
}
114140
}
141+
142+
class FormatResult {
143+
public String FormattedText;
144+
public int Cursor;
145+
public boolean IncompleteFormat;
146+
}

0 commit comments

Comments
 (0)