import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiFile;
import com.intellij.psi.codeStyle.CodeStyleManager;
+import com.intellij.util.containers.HashMap;
import com.intellij.util.containers.HashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
private int myStartLength;
private Project myProject;
+ private final Map<Object, MyCheckpoint> myCheckpoints = new HashMap<Object, MyCheckpoint>();
+
+ private static class MyCheckpoint {
+ int myFixedLength = -1;
+ int myFixedOffset;
+ }
+
public CustomTemplateCallback(Editor editor, PsiFile file) {
myEditor = editor;
myFile = file;
return startTemplate(template, predefinedVarValues, listener);
}
else if (listener != null) {
- listener.finished(false, false);
+ listener.finished(false);
}
return true;
}
if (brokenOff) return;
templateFinished[0] = true;
if (templateEnded[0] && listener != null) {
- listener.finished(true, true);
+ listener.finished(true);
}
}
});
templateEnded[0] = true;
if (templateFinished[0] && listener != null) {
- listener.finished(false, true);
+ listener.finished(false);
}
return templateFinished[0];
}
+ public void fixStartOfTemplate(@NotNull Object key) {
+ MyCheckpoint checkpoint = new MyCheckpoint();
+ checkpoint.myFixedOffset = myEditor.getCaretModel().getOffset();
+ checkpoint.myFixedLength = myEditor.getDocument().getTextLength();
+ myCheckpoints.put(key, checkpoint);
+ }
+
+ public void gotoEndOfTemplate(@NotNull Object key) {
+ MyCheckpoint checkpoint = myCheckpoints.get(key);
+ if (checkpoint == null) {
+ throw new IllegalArgumentException();
+ }
+ int length = myEditor.getDocument().getTextLength();
+ myEditor.getCaretModel().moveToOffset(checkpoint.myFixedOffset + length - checkpoint.myFixedLength);
+ }
+
private static List<TemplateImpl> getMatchingTemplates(@NotNull String templateKey) {
TemplateSettings settings = TemplateSettings.getInstance();
return settings.collectMatchingCandidates(templateKey, settings.getDefaultShortcutChar(), false);
public void execute(@NotNull String key, @NotNull CustomTemplateCallback callback, @Nullable TemplateInvokationListener listener) {
List<MyToken> tokens = parse(key, callback);
assert tokens != null;
- MyInterpreter interpreter = new MyInterpreter(tokens, 0, callback, MyState.WORD, listener);
- interpreter.iter();
+ MyInterpreter interpreter = new MyInterpreter(tokens, callback, MyState.WORD, listener);
+ interpreter.invoke(0);
}
private static void fail() {
}
}
- private class MyInterpreter extends Iteration {
+ private class MyInterpreter {
private final List<MyToken> myTokens;
private final CustomTemplateCallback myCallback;
private final TemplateInvokationListener myListener;
private int myEndOffset = -1;
private MyInterpreter(List<MyToken> tokens,
- int startIndex,
CustomTemplateCallback callback,
MyState initialState,
TemplateInvokationListener listener) {
- super(startIndex, tokens.size(), null);
myTokens = tokens;
myCallback = callback;
myListener = listener;
return myCallback.getEditor().getCaretModel().getOffset();
}
- private int getTextLength() {
- return myCallback.getEditor().getDocument().getCharsSequence().length();
- }
-
- private void moveCaret(int delta) {
- myCallback.getEditor().getCaretModel().moveToOffset(delta);
- }
-
private void finish(boolean inSeparateEvent) {
Editor editor = myCallback.getEditor();
if (myEndOffset >= 0) {
}
editor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE);
if (myListener != null) {
- myListener.finished(inSeparateEvent, true);
+ myListener.finished(inSeparateEvent);
}
}
- @Override
- protected void next() {
- if (myIndex == myMaxIndex - 1) {
- finish(true);
- }
- super.next();
- }
-
- @Override
- protected void iter() {
+ public boolean invoke(int startIndex) {
+ final int n = myTokens.size();
String templateKey = null;
int number = -1;
- for (; myIndex < myMaxIndex; myIndex++) {
- MyToken token = myTokens.get(myIndex);
+ for (int i = startIndex; i < n; i++) {
+ final int finalI = i;
+ MyToken token = myTokens.get(i);
switch (myState) {
case OPERATION:
if (templateKey != null) {
if (token instanceof MyMarkerToken || token instanceof MyOperationToken) {
final char sign = token instanceof MyOperationToken ? ((MyOperationToken)token).mySign : MARKER;
if (sign == MARKER || sign == '+') {
- final int offsetBefore = getOffset();
- final int lengthBefore = getTextLength();
+ final Object key = new Object();
+ myCallback.fixStartOfTemplate(key);
TemplateInvokationListener listener = new TemplateInvokationListener() {
- public void finished(boolean inSeparateEvent, boolean success) {
+ public void finished(boolean inSeparateEvent) {
myState = MyState.WORD;
fixEndOffset();
if (sign == '+') {
- moveCaret(offsetBefore + getTextLength() - lengthBefore);
+ myCallback.gotoEndOfTemplate(key);
}
if (inSeparateEvent) {
- next();
+ invoke(finalI + 1);
}
}
};
if (!invokeTemplate(templateKey, myCallback, listener)) {
- return;
+ return false;
}
templateKey = null;
}
else if (sign == '>') {
- if (!startTemplate(templateKey)) {
- return;
+ if (!startTemplate(templateKey, finalI)) {
+ return false;
}
templateKey = null;
}
if (token instanceof MyMarkerToken || token instanceof MyOperationToken) {
char sign = token instanceof MyOperationToken ? ((MyOperationToken)token).mySign : MARKER;
if (sign == MARKER || sign == '+') {
- ConsecutiveTemplateInvokation iteration = new ConsecutiveTemplateInvokation(templateKey, number);
- iteration.iter();
- if (!iteration.isFinished()) {
- return;
+ if (!invokeTemplateSeveralTimes(templateKey, number, finalI)) {
+ return false;
}
templateKey = null;
}
else if (number > 1) {
- ConsecutiveTemplateWithTailInvokation iteration =
- new ConsecutiveTemplateWithTailInvokation(templateKey, myTokens, myIndex + 1, number);
- iteration.iter();
- if (iteration.isFinished()) {
- myIndex = myMaxIndex;
- finish(false);
- }
- return;
+ return invokeTemplateAndProcessTail(templateKey, i + 1, number);
}
else {
assert number == 1;
- if (!startTemplate(templateKey)) {
- return;
+ if (!startTemplate(templateKey, finalI)) {
+ return false;
}
templateKey = null;
}
break;
}
}
- finish(false);
+ finish(startIndex == n);
+ return true;
}
- private boolean startTemplate(String templateKey) {
+ private boolean startTemplate(String templateKey, final int index) {
TemplateInvokationListener listener = new TemplateInvokationListener() {
- public void finished(boolean inSeparateEvent, boolean success) {
+ public void finished(boolean inSeparateEvent) {
myState = MyState.WORD;
if (inSeparateEvent) {
- next();
+ invoke(index + 1);
}
}
};
return true;
}
- private class ConsecutiveTemplateInvokation extends Iteration {
- private final String myTemplateKey;
-
- public ConsecutiveTemplateInvokation(String templateKey, int count) {
- super(0, count, MyInterpreter.this);
- myTemplateKey = templateKey;
- }
-
- @Override
- protected void iter() {
- final int offsetBefore = getOffset();
- final int lengthBefore = getTextLength();
- for (; myIndex < myMaxIndex; myIndex++) {
- TemplateInvokationListener listener = new TemplateInvokationListener() {
- public void finished(boolean inSeparateEvent, boolean success) {
- myState = MyState.WORD;
- fixEndOffset();
- moveCaret(offsetBefore + getTextLength() - lengthBefore);
- if (inSeparateEvent) {
- next();
+ private boolean invokeTemplateSeveralTimes(final String templateKey, final int count, final int index) {
+ final Object key = new Object();
+ myCallback.fixStartOfTemplate(key);
+ for (int i = 0; i < count; i++) {
+ final int finalI = i;
+ TemplateInvokationListener listener = new TemplateInvokationListener() {
+ public void finished(boolean inSeparateEvent) {
+ myState = MyState.WORD;
+ fixEndOffset();
+ myCallback.gotoEndOfTemplate(key);
+ if (inSeparateEvent) {
+ int newCount = count - finalI - 1;
+ if (newCount > 0) {
+ invokeTemplateSeveralTimes(templateKey, newCount, index);
+ }
+ else {
+ invoke(index + 1);
}
}
- };
- if (!invokeTemplate(myTemplateKey, myCallback, listener)) {
- return;
}
+ };
+ if (!invokeTemplate(templateKey, myCallback, listener)) {
+ return false;
}
}
+ return true;
}
- private class ConsecutiveTemplateWithTailInvokation extends Iteration {
- private final String myTemplateKey;
- private final List<MyToken> myTokens;
- private final int myTailStart;
-
- public ConsecutiveTemplateWithTailInvokation(String templateKey, List<MyToken> tokens, int tailStart, int count) {
- super(0, count, null);
- assert count > 1;
- assert tailStart < tokens.size();
- myTemplateKey = templateKey;
- myTailStart = tailStart;
- myTokens = tokens;
- }
-
- @Override
- protected void next() {
- if (myIndex == myMaxIndex - 1) {
- finish(true);
- }
- super.next();
- }
-
- @Override
- protected void iter() {
- final int offsetBefore = getOffset();
- final int lengthBefore = getTextLength();
- for (; myIndex < myMaxIndex; myIndex++) {
- final boolean[] flag = new boolean[]{false};
- TemplateInvokationListener listener = new TemplateInvokationListener() {
- public void finished(boolean inSeparateEvent, boolean success) {
- MyInterpreter interpreter =
- new MyInterpreter(myTokens, myTailStart, myCallback, MyState.WORD, new TemplateInvokationListener() {
- public void finished(boolean inSeparateEvent, boolean success) {
- fixEndOffset();
- moveCaret(offsetBefore + getTextLength() - lengthBefore);
- if (inSeparateEvent) {
- next();
- }
- }
- });
- interpreter.iter();
- if (inSeparateEvent && interpreter.isFinished()) {
- next();
+ private boolean invokeTemplateAndProcessTail(final String templateKey, final int tailStart, final int count) {
+ final Object key = new Object();
+ myCallback.fixStartOfTemplate(key);
+ for (int i = 0; i < count; i++) {
+ final boolean[] flag = new boolean[]{false};
+ final int finalI = i;
+ TemplateInvokationListener listener = new TemplateInvokationListener() {
+ public void finished(boolean inSeparateEvent) {
+ MyInterpreter interpreter = new MyInterpreter(myTokens, myCallback, MyState.WORD, new TemplateInvokationListener() {
+ public void finished(boolean inSeparateEvent) {
+ fixEndOffset();
+ myCallback.gotoEndOfTemplate(key);
+ if (inSeparateEvent) {
+ invokeTemplateAndProcessTail(templateKey, tailStart, count - finalI - 1);
+ }
}
- if (!interpreter.isFinished()) {
- flag[0] = true;
+ });
+ if (interpreter.invoke(tailStart)) {
+ if (inSeparateEvent) {
+ invokeTemplateAndProcessTail(templateKey, tailStart, count - finalI - 1);
}
}
- };
- if (!invokeTemplate(myTemplateKey, myCallback, listener) || flag[0]) {
- return;
+ else {
+ flag[0] = true;
+ }
}
+ };
+ if (!invokeTemplate(templateKey, myCallback, listener) || flag[0]) {
+ return false;
}
}
+ finish(count == 0);
+ return true;
}
}
-}
+}
\ No newline at end of file