Fixed IllegalStateException when using smart-semicolon to add a new
value to an enum with a "Next Id" comment, e.g:
message SomeMessage {
// Next id: 3
enum SomeEnum {
FIRST = 1;
SECOND = 2;
NEW<insert semicolon>
}
}
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/commands/semicolon/SmartSemicolonHandler.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/commands/semicolon/SmartSemicolonHandler.java
index f3d607a..5c21247 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/commands/semicolon/SmartSemicolonHandler.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/commands/semicolon/SmartSemicolonHandler.java
@@ -8,32 +8,11 @@
*/
package com.google.eclipse.protobuf.ui.commands.semicolon;
+import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.LITERAL__INDEX;
import static java.util.regex.Pattern.compile;
-
import static org.eclipse.xtext.util.Strings.isEmpty;
-import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.LITERAL__INDEX;
-import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.MESSAGE_FIELD__INDEX;
-
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.apache.log4j.Logger;
-import org.eclipse.emf.ecore.EAttribute;
-import org.eclipse.emf.ecore.EObject;
-import org.eclipse.jface.text.BadLocationException;
-import org.eclipse.swt.custom.StyledText;
-import org.eclipse.xtext.nodemodel.INode;
-import org.eclipse.xtext.resource.XtextResource;
-import org.eclipse.xtext.ui.editor.XtextEditor;
-import org.eclipse.xtext.ui.editor.contentassist.ContentAssistContext;
-import org.eclipse.xtext.ui.editor.contentassist.antlr.ParserBasedContentAssistContextFactory;
-import org.eclipse.xtext.ui.editor.model.IXtextDocument;
-import org.eclipse.xtext.ui.editor.preferences.IPreferenceStoreAccess;
-import org.eclipse.xtext.util.Pair;
-import org.eclipse.xtext.util.concurrent.IUnitOfWork;
-
+import com.google.common.collect.Lists;
import com.google.eclipse.protobuf.grammar.CommonKeyword;
import com.google.eclipse.protobuf.model.util.INodes;
import com.google.eclipse.protobuf.model.util.IndexedElements;
@@ -49,12 +28,31 @@
import com.google.eclipse.protobuf.ui.preferences.editor.numerictag.NumericTagPreferences;
import com.google.inject.Inject;
+import org.apache.log4j.Logger;
+import org.eclipse.emf.ecore.EAttribute;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.xtext.nodemodel.INode;
+import org.eclipse.xtext.resource.XtextResource;
+import org.eclipse.xtext.ui.editor.XtextEditor;
+import org.eclipse.xtext.ui.editor.contentassist.ContentAssistContext;
+import org.eclipse.xtext.ui.editor.contentassist.antlr.ParserBasedContentAssistContextFactory;
+import org.eclipse.xtext.ui.editor.model.IXtextDocument;
+import org.eclipse.xtext.ui.editor.preferences.IPreferenceStoreAccess;
+import org.eclipse.xtext.util.Pair;
+import org.eclipse.xtext.util.Tuples;
+import org.eclipse.xtext.util.concurrent.IUnitOfWork;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
/**
* Inserts a semicolon at the end of a line, regardless of the current position of the caret in the editor. If the line
* of code being edited is a field or enum literal and if it does not have an index yet, this handler will insert an
* index with a proper value as well.
- *
- * @author alruiz@google.com (Alex Ruiz)
*/
public class SmartSemicolonHandler extends SmartInsertHandler {
private static final Pattern NUMBERS_PATTERN = compile("[\\d]+");
@@ -90,8 +88,15 @@
private void insertContent(final XtextEditor editor, final StyledTextAccess styledTextAccess) {
final AtomicBoolean shouldInsertSemicolon = new AtomicBoolean(true);
final IXtextDocument document = editor.getDocument();
+ final List<Pair<EObject, Long>> commentsToUpdate = Lists.newLinkedList();
+
document.readOnly(NULL_UNIT_OF_WORK); // wait for reconciler to finish its work.
try {
+ /*
+ * Textual and semantic updates cannot be done in the same IUnitOfWork (throws an
+ * IllegalStateException), so index updates (semantic) are done first and tracked in the
+ * commentsToUpdate list, then a 2nd IUnitOfWork processes the comment updates (textual).
+ */
document.modify(new IUnitOfWork.Void<XtextResource>() {
@Override public void process(XtextResource resource) {
Protobuf root = resources.rootOf(resource);
@@ -114,7 +119,7 @@
if (shouldCalculateIndex(literal, LITERAL__INDEX)) {
long index = literals.calculateNewIndexOf(literal);
literal.setIndex(index);
- updateIndexInCommentOfParent(literal, index, document);
+ commentsToUpdate.add(Tuples.create(model, index));
shouldInsertSemicolon.set(false);
}
}
@@ -123,13 +128,23 @@
if (shouldCalculateIndex(field)) {
long index = indexedElements.calculateNewIndexFor(field);
field.setIndex(index);
- updateIndexInCommentOfParent(field, index, document);
+ commentsToUpdate.add(Tuples.create(model, index));
shouldInsertSemicolon.set(false);
}
}
}
}
});
+
+ if (!commentsToUpdate.isEmpty()) {
+ document.modify(new IUnitOfWork.Void<XtextResource>() {
+ @Override public void process(XtextResource resource) {
+ for (Pair<EObject, Long> updateInfo : commentsToUpdate) {
+ updateIndexInCommentOfParent(updateInfo.getFirst(), updateInfo.getSecond(), document);
+ }
+ }
+ });
+ }
} catch (Throwable t) {
shouldInsertSemicolon.set(true);
logger.error("Unable to generate tag number", t);