diff --git a/platform/lang-impl/src/com/intellij/lang/parser/GeneratedParserUtilBase.java b/platform/lang-impl/src/com/intellij/lang/parser/GeneratedParserUtilBase.java index 705c80888091..05e7cf8e8b09 100644 --- a/platform/lang-impl/src/com/intellij/lang/parser/GeneratedParserUtilBase.java +++ b/platform/lang-impl/src/com/intellij/lang/parser/GeneratedParserUtilBase.java @@ -39,6 +39,7 @@ import com.intellij.util.Function; import com.intellij.util.PairProcessor; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.LimitedPool; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -92,6 +93,59 @@ public class GeneratedParserUtilBase { } }; + public interface Hook { + + @Contract("_,null,_->null") + PsiBuilder.Marker run(PsiBuilder builder, PsiBuilder.Marker marker, T param); + + } + + public static final Hook LEFT_BINDER = + new Hook() { + @Override + public PsiBuilder.Marker run(PsiBuilder builder, + PsiBuilder.Marker marker, + WhitespacesAndCommentsBinder param) { + if (marker != null) marker.setCustomEdgeTokenBinders(param, null); + return marker; + } + }; + + public static final Hook RIGHT_BINDER = + new Hook() { + @Override + public PsiBuilder.Marker run(PsiBuilder builder, + PsiBuilder.Marker marker, + WhitespacesAndCommentsBinder param) { + if (marker != null) marker.setCustomEdgeTokenBinders(null, param); + return marker; + } + }; + + public static final Hook WS_BINDERS = + new Hook() { + @Override + public PsiBuilder.Marker run(PsiBuilder builder, + PsiBuilder.Marker marker, + WhitespacesAndCommentsBinder[] param) { + if (marker != null) marker.setCustomEdgeTokenBinders(param[0], param[1]); + return marker; + } + }; + + public static final Hook LOG_HOOK = new Hook() { + @Override + public PsiBuilder.Marker run(PsiBuilder builder, PsiBuilder.Marker marker, String param) { + PsiBuilderImpl.ProductionMarker m = (PsiBuilderImpl.ProductionMarker)marker; + int start = m == null ? builder.getCurrentOffset() : m.getStartOffset(); + int end = m == null ? start : m.getEndOffset(); + String prefix = "[" + start + ", " + end + "]" + (m == null ? "" : " " + m.getTokenType()); + builder.mark().error(prefix + ": " + param); + return marker; + } + }; + + public static boolean eof(PsiBuilder builder, int level) { return builder.eof(); } @@ -481,9 +535,35 @@ public class GeneratedParserUtilBase { close_frame_impl_(state, frame, builder, marker, elementType, result, pinned); exit_section_impl_(state, frame, builder, elementType, result, pinned, eatMore); } + if (state.hooks != null) { + run_hooks_impl_(builder, state, level, pinned || result ? elementType : null); + } state.FRAMES.recycle(frame); } + public static void register_hook_(PsiBuilder builder, int level, Hook hook, T param) { + ErrorState state = ErrorState.get(builder); + state.hooks = Hooks.concat(hook, param, level, state.hooks); + } + + public static void register_hook_(PsiBuilder builder, int level, Hook hook, T... param) { + ErrorState state = ErrorState.get(builder); + state.hooks = Hooks.concat(hook, param, level, state.hooks); + } + + private static void run_hooks_impl_(PsiBuilder builder, ErrorState state, int level, @Nullable IElementType elementType) { + PsiBuilder.Marker marker = elementType == null ? null : (PsiBuilder.Marker)builder.getLatestDoneMarker(); + if (elementType != null && marker == null) { + builder.error("No expected done marker at offset " + builder.getCurrentOffset()); + } + while (state.hooks != null && state.hooks.level >= level) { + if (state.hooks.level == level) { + marker = ((Hook)state.hooks.hook).run(builder, marker, state.hooks.param); + } + state.hooks = state.hooks.next; + } + } + private static void exit_section_impl_(ErrorState state, Frame frame, PsiBuilder builder, @@ -831,6 +911,7 @@ public class GeneratedParserUtilBase { int predicateCount; boolean predicateSign = true; boolean suppressErrors; + Hooks hooks; public Frame currentFrame; public CompletionState completionState; @@ -1042,6 +1123,24 @@ public class GeneratedParserUtilBase { } } + private static class Hooks { + final Hook hook; + final T param; + final int level; + final Hooks next; + + Hooks(Hook hook, T param, int level, Hooks next) { + this.hook = hook; + this.param = param; + this.level = level; + this.next = next; + } + + static Hooks concat(Hook hook, E param, int level, Hooks hooks) { + return new Hooks(hook, param, level, hooks); + } + } + private static final int MAX_CHILDREN_IN_TREE = 10; public static boolean parseAsTree(ErrorState state, final PsiBuilder builder, int level, final IElementType chunkType,