Bug 577760: Unify parser APIs and error handling

Follow-up-to: a5b730ae4fcfc88422a0ebf762cf1bd811ace567
Change-Id: Ie7a78e713d200de135d42e2bdda15c8aa4672468
diff --git a/docmlet/org.eclipse.statet.docmlet.tex.core/src/org/eclipse/statet/docmlet/tex/core/ast/LtxParser.java b/docmlet/org.eclipse.statet.docmlet.tex.core/src/org/eclipse/statet/docmlet/tex/core/ast/LtxParser.java
index 0e44ddd..07751c1 100644
--- a/docmlet/org.eclipse.statet.docmlet.tex.core/src/org/eclipse/statet/docmlet/tex/core/ast/LtxParser.java
+++ b/docmlet/org.eclipse.statet.docmlet.tex.core/src/org/eclipse/statet/docmlet/tex/core/ast/LtxParser.java
@@ -50,6 +50,7 @@
 import org.eclipse.statet.docmlet.tex.core.parser.CustomScanner;
 import org.eclipse.statet.docmlet.tex.core.parser.LtxLexer;
 import org.eclipse.statet.ltk.ast.core.AstNode;
+import org.eclipse.statet.ltk.core.source.StatusDetail;
 
 
 @NonNullByDefault
@@ -103,6 +104,7 @@
 	
 	private final LtxLexer lexer;
 	
+	private TextParserInput parseInput;
 	private int parseStartOffset;
 	private int parseEndOffset;
 	private @Nullable AstNode parseParent;
@@ -224,11 +226,25 @@
 	
 	
 	private void init0(final TextParserInput input, final @Nullable AstNode parent) {
+		this.parseInput= nonNullAssert(input);
 		this.parseStartOffset= input.getStartIndex();
 		this.parseEndOffset= input.getStopIndex();
 		this.parseParent= parent;
 	}
 	
+	@SuppressWarnings("null")
+	private void clear0() {
+		this.parseInput= null;
+		this.parseParent= null;
+	}
+	
+	private void logParseError(final Exception e) {
+		CommonsRuntime.log(new ErrorStatus(TexCore.BUNDLE_ID,
+				"An error occured while parsing TeX source code. Input:\n"
+						+ this.parseInput.toString(),
+				e ));
+	}
+	
 	private void initTask(final TextParserInput input, final TaskConfig config) {
 		if (this.commentsLevel != 0) {
 			this.comments.clear();
@@ -257,15 +273,19 @@
 		try {
 			initTask(input, new TaskConfig(commandSet));
 			
-			final SourceComponent node= new SourceComponent(null,
+			final SourceComponent sourceNode= new SourceComponent(null,
 					this.parseStartOffset, this.parseEndOffset );
 			
-			parseInput(node);
+			parseInput(sourceNode);
 			
-			return node;
+			return sourceNode;
 		}
 		catch (final Exception e) {
-			return onParseError(e);
+			logParseError(e);
+			return createErrorSourceComponent();
+		}
+		finally {
+			clear0();
 		}
 	}
 	
@@ -276,15 +296,19 @@
 		try {
 			initTask(input, new TaskConfig(commandSet));
 			
-			final SourceComponent node= new SourceComponent(parent,
+			final SourceComponent sourceNode= new SourceComponent(parent,
 					this.parseStartOffset, this.parseEndOffset );
 			
-			parseInput(node);
+			parseInput(sourceNode);
 			
-			return node;
+			return sourceNode;
 		}
 		catch (final Exception e) {
-			return onParseError(e);
+			logParseError(e);
+			return createErrorSourceComponent();
+		}
+		finally {
+			clear0();
 		}
 	}
 	
@@ -300,10 +324,7 @@
 		node.endOffset= this.lexer.getStopOffset();
 	}
 	
-	private SourceComponent onParseError(final Exception e) {
-		CommonsRuntime.log(new ErrorStatus(TexCore.BUNDLE_ID,
-				"An error occured while parsing LaTeX source code.",
-				e ));
+	private SourceComponent createErrorSourceComponent() {
 		final int startOffset= this.parseStartOffset;
 		int endOffset= this.parseEndOffset;
 		if (endOffset < startOffset) {
@@ -317,23 +338,29 @@
 		return dummy;
 	}
 	
-	public ControlNode scanControlWordArgs(final TextParserInput input, final TexCommand command,
+	public ControlNode parseControlWordArgs(final TextParserInput input, final TexCommand command,
 			final boolean expand,
 			final TaskConfig config) {
-		initTask(input, config);
-		
-		this.wasLinebreak= false;
-		final ControlNode.Word controlNode= new ControlNode.Word(command.getControlWord());
-		controlNode.startOffset= input.getStartIndex();
-		controlNode.endOffset= input.getStartIndex();
-		this.lexer.consume();
-		
-		parseWord(controlNode, command, false);
-		
-		if (expand && this.lexer.getOffset() > controlNode.endOffset) {
-			controlNode.endOffset= this.lexer.getOffset();
+		init0(input, null);
+		try {
+			initTask(input, config);
+			
+			this.wasLinebreak= false;
+			final ControlNode.Word controlNode= new ControlNode.Word(command.getControlWord());
+			controlNode.startOffset= this.parseStartOffset;
+			controlNode.endOffset= this.parseStartOffset;
+			this.lexer.consume();
+			
+			parseWord(controlNode, command, false);
+			
+			if (expand && this.lexer.getOffset() > controlNode.endOffset) {
+				controlNode.endOffset= this.lexer.getOffset();
+			}
+			return controlNode;
 		}
-		return controlNode;
+		finally {
+			clear0();
+		}
 	}
 	
 	
@@ -874,6 +901,7 @@
 			if (this.lexer.next() == LtxLexer.VERBATIM_TEXT) {
 				this.wasLinebreak= false;
 				final TexAstNode verbatimNode;
+				final StatusDetail statusDetail;
 				switch (this.lexer.getFlags()) {
 				case LtxLexer.SUB_OPEN_MISSING:
 //					verbatimNode= new Dummy();
@@ -889,16 +917,18 @@
 					verbatimNode.texParent= controlNode;
 					verbatimNode.status= TYPE123_VERBATIM_INLINE_NOT_CLOSED;
 					verbatimNode.startOffset= this.lexer.getOffset() + 1;
-					verbatimNode.endOffset= controlNode.endOffset=
-							this.lexer.getStopOffset();
+					verbatimNode.endOffset= controlNode.endOffset= this.lexer.getStopOffset();
+					statusDetail= this.lexer.getStatusDetail();
+					if (statusDetail != null) {
+						verbatimNode.addAttachment(statusDetail);
+					}
 					this.lexer.consume();
 					break;
 				default:
 					verbatimNode= new Verbatim();
 					verbatimNode.texParent= controlNode;
 					verbatimNode.startOffset= this.lexer.getOffset() + 1;
-					verbatimNode.endOffset= controlNode.endOffset=
-							this.lexer.getStopOffset() - 1;
+					verbatimNode.endOffset= controlNode.endOffset= this.lexer.getStopOffset() - 1;
 					this.lexer.consume();
 					break;
 				}
diff --git a/docmlet/org.eclipse.statet.docmlet.tex.core/src/org/eclipse/statet/docmlet/tex/core/model/build/LtxIssueReporter.java b/docmlet/org.eclipse.statet.docmlet.tex.core/src/org/eclipse/statet/docmlet/tex/core/model/build/LtxIssueReporter.java
index 03caa46..462526f 100644
--- a/docmlet/org.eclipse.statet.docmlet.tex.core/src/org/eclipse/statet/docmlet/tex/core/model/build/LtxIssueReporter.java
+++ b/docmlet/org.eclipse.statet.docmlet.tex.core/src/org/eclipse/statet/docmlet/tex/core/model/build/LtxIssueReporter.java
@@ -26,7 +26,7 @@
 import org.eclipse.statet.docmlet.tex.core.model.TexSourceUnit;
 import org.eclipse.statet.docmlet.tex.core.project.TexProject;
 import org.eclipse.statet.internal.docmlet.tex.core.builder.TexTaskTagReporter;
-import org.eclipse.statet.internal.docmlet.tex.core.model.LtxProblemAstVisitor;
+import org.eclipse.statet.internal.docmlet.tex.core.model.AstProblemReporter;
 import org.eclipse.statet.internal.docmlet.tex.core.model.LtxProblemModelCheck;
 import org.eclipse.statet.ltk.core.source.SourceContent;
 import org.eclipse.statet.ltk.issues.core.IssueRequestor;
@@ -37,7 +37,7 @@
 public class LtxIssueReporter {
 	
 	
-	private final LtxProblemAstVisitor astVisitor= new LtxProblemAstVisitor();
+	private final AstProblemReporter astVisitor= new AstProblemReporter();
 	private final LtxProblemModelCheck modelCheck= new LtxProblemModelCheck();
 	
 	private final TexTaskTagReporter taskReporter= new TexTaskTagReporter();
diff --git a/docmlet/org.eclipse.statet.docmlet.tex.core/src/org/eclipse/statet/docmlet/tex/core/parser/LtxLexer.java b/docmlet/org.eclipse.statet.docmlet.tex.core/src/org/eclipse/statet/docmlet/tex/core/parser/LtxLexer.java
index ddc96e0..d69e3b2 100644
--- a/docmlet/org.eclipse.statet.docmlet.tex.core/src/org/eclipse/statet/docmlet/tex/core/parser/LtxLexer.java
+++ b/docmlet/org.eclipse.statet.docmlet.tex.core/src/org/eclipse/statet/docmlet/tex/core/parser/LtxLexer.java
@@ -14,9 +14,13 @@
 
 package org.eclipse.statet.docmlet.tex.core.parser;
 
+import org.eclipse.statet.jcommons.lang.Nullable;
+import org.eclipse.statet.jcommons.string.BasicStringFactory;
 import org.eclipse.statet.jcommons.string.StringFactory;
 import org.eclipse.statet.jcommons.text.core.input.TextParserInput;
 
+import org.eclipse.statet.ltk.core.source.StatusDetail;
+
 
 public class LtxLexer {
 	
@@ -70,11 +74,19 @@
 	
 /*=============================================================================*/
 	
+	private static StatusDetail createDetail(final TextParserInput in,
+			final int startOffset, final int endOffset, final @Nullable String text) {
+		final int beginIndex= in.getIndex(startOffset);
+		return new StatusDetail(beginIndex, in.getIndex() + in.getLengthInSource(endOffset),
+				text );
+	}
+	
 	
 	private TextParserInput input;
 	
 	private byte foundType;
 	private int foundFlags;
+	private @Nullable StatusDetail foundDetail;
 	private int foundOffset;
 	private int foundNum;
 	private int foundLength;
@@ -199,6 +211,10 @@
 		return this.foundFlags;
 	}
 	
+	public final @Nullable StatusDetail getStatusDetail() {
+		return this.foundDetail;
+	}
+	
 	public final int getOffset() {
 		return this.foundOffset;
 	}
@@ -267,18 +283,21 @@
 	private void foundEOF(final TextParserInput in) {
 		this.foundType= EOF;
 		this.foundFlags= 0;
+		this.foundDetail= null;
 		this.foundLength= in.getLengthInSource(this.foundNum= 0);
 	}
 	
 	private void foundLineComment(final TextParserInput in, final int n) {
 		this.foundType= LINE_COMMENT;
 		this.foundFlags= 0;
+		this.foundDetail= null;
 		this.foundLength= in.getLengthInSource(this.foundNum= n);
 	}
 	
 	private void foundLinebreak(final TextParserInput in, final int n) {
 		this.foundType= LINEBREAK;
 		this.foundFlags= 0;
+		this.foundDetail= null;
 		this.foundLength= in.getLengthInSource(this.foundNum= n);
 		this.wasLinebreak= true;
 	}
@@ -286,12 +305,14 @@
 	private void foundWhitespace(final TextParserInput in, final int n) {
 		this.foundType= WHITESPACE;
 		this.foundFlags= 0;
+		this.foundDetail= null;
 		this.foundLength= in.getLengthInSource(this.foundNum= n);
 	}
 	
 	private void foundControlLinebreak(final TextParserInput in, final int n) {
 		this.foundType= CONTROL_CHAR;
 		this.foundFlags= 0;
+		this.foundDetail= null;
 		this.foundLength= in.getLengthInSource(this.foundNum= n);
 		this.wasLinebreak= true;
 	}
@@ -299,18 +320,21 @@
 	private void found1(final TextParserInput in, final byte type) {
 		this.foundType= type;
 		this.foundFlags= 0;
+		this.foundDetail= null;
 		this.foundLength= in.getLengthInSource(this.foundNum= 1);
 	}
 	
 	private void found2(final TextParserInput in, final byte type) {
 		this.foundType= type;
 		this.foundFlags= 0;
+		this.foundDetail= null;
 		this.foundLength= in.getLengthInSource(this.foundNum= 2);
 	}
 	
 	private void found(final TextParserInput in, final byte type, final int n) {
 		this.foundType= type;
 		this.foundFlags= 0;
+		this.foundDetail= null;
 		this.foundLength= in.getLengthInSource(this.foundNum= n);
 	}
 	
@@ -318,14 +342,18 @@
 			final byte newState) {
 		this.foundType= VERBATIM_TEXT;
 		this.foundFlags= 0;
+		this.foundDetail= null;
 		this.foundLength= in.getLengthInSource(this.foundNum= n);
 		this.state= newState;
 	}
 	
-	private void foundVerbatimText(final TextParserInput in, final byte flags, final int n,
+	private void foundVerbatimText(final TextParserInput in,
+			final byte flags, final @Nullable StatusDetail detail,
+			final int n,
 			final byte newState) {
 		this.foundType= VERBATIM_TEXT;
 		this.foundFlags= flags;
+		this.foundDetail= detail;
 		this.foundLength= in.getLengthInSource(this.foundNum= n);
 		this.state= newState;
 	}
@@ -675,7 +703,9 @@
 		ITER_CN: while (true) {
 			switch (in.get(n++)) {
 			case TextParserInput.EOF:
-				foundVerbatimText(in, SUB_CLOSE_MISSING, n - 1, S_DEFAULT);
+				foundVerbatimText(in, SUB_CLOSE_MISSING,
+						null,
+						n - 1, S_DEFAULT );
 				return;
 			case '\r':
 				if (in.get(n) == '\n') {
@@ -706,7 +736,7 @@
 		final TextParserInput in= this.input;
 		final int end= in.get(0);
 		if (end < 0x20) {
-			foundVerbatimText(in, SUB_OPEN_MISSING, 0, this.savedVerbatimState);
+			foundVerbatimText(in, SUB_OPEN_MISSING, null, 0, this.savedVerbatimState);
 			return;
 		}
 		int n= 1;
@@ -716,7 +746,9 @@
 			case TextParserInput.EOF:
 			case '\r':
 			case '\n':
-				foundVerbatimText(in, SUB_CLOSE_MISSING, n - 1, this.savedVerbatimState);
+				foundVerbatimText(in, SUB_CLOSE_MISSING,
+						createDetail(in, 0, n - 1, BasicStringFactory.INSTANCE.get(end)),
+						n - 1, this.savedVerbatimState );
 				return;
 			default:
 				if (c == end) {
diff --git a/docmlet/org.eclipse.statet.docmlet.tex.core/src/org/eclipse/statet/internal/docmlet/tex/core/model/AstProblemReporter.java b/docmlet/org.eclipse.statet.docmlet.tex.core/src/org/eclipse/statet/internal/docmlet/tex/core/model/AstProblemReporter.java
new file mode 100644
index 0000000..e13a9c7
--- /dev/null
+++ b/docmlet/org.eclipse.statet.docmlet.tex.core/src/org/eclipse/statet/internal/docmlet/tex/core/model/AstProblemReporter.java
@@ -0,0 +1,380 @@
+/*=============================================================================#
+ # Copyright (c) 2012, 2021 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.internal.docmlet.tex.core.model;
+
+import static org.eclipse.statet.docmlet.tex.core.ast.TexAstStatusConstants.TYPE123_ENV_MISSING_NAME;
+import static org.eclipse.statet.docmlet.tex.core.ast.TexAstStatusConstants.TYPE123_ENV_NOT_CLOSED;
+import static org.eclipse.statet.docmlet.tex.core.ast.TexAstStatusConstants.TYPE123_ENV_NOT_OPENED;
+import static org.eclipse.statet.docmlet.tex.core.ast.TexAstStatusConstants.TYPE123_GROUP_NOT_CLOSED;
+import static org.eclipse.statet.docmlet.tex.core.ast.TexAstStatusConstants.TYPE123_GROUP_NOT_OPENED;
+import static org.eclipse.statet.docmlet.tex.core.ast.TexAstStatusConstants.TYPE123_MATH_NOT_CLOSED;
+import static org.eclipse.statet.docmlet.tex.core.ast.TexAstStatusConstants.TYPE123_VERBATIM_INLINE_C_MISSING;
+import static org.eclipse.statet.docmlet.tex.core.ast.TexAstStatusConstants.TYPE123_VERBATIM_INLINE_NOT_CLOSED;
+import static org.eclipse.statet.ltk.core.StatusCodes.TYPE123;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.jface.text.BadLocationException;
+
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
+import org.eclipse.statet.docmlet.tex.core.ast.Comment;
+import org.eclipse.statet.docmlet.tex.core.ast.ControlNode;
+import org.eclipse.statet.docmlet.tex.core.ast.Dummy;
+import org.eclipse.statet.docmlet.tex.core.ast.Embedded;
+import org.eclipse.statet.docmlet.tex.core.ast.Environment;
+import org.eclipse.statet.docmlet.tex.core.ast.Group;
+import org.eclipse.statet.docmlet.tex.core.ast.Label;
+import org.eclipse.statet.docmlet.tex.core.ast.Math;
+import org.eclipse.statet.docmlet.tex.core.ast.SourceComponent;
+import org.eclipse.statet.docmlet.tex.core.ast.TexAstNode;
+import org.eclipse.statet.docmlet.tex.core.ast.TexAstVisitor;
+import org.eclipse.statet.docmlet.tex.core.ast.Text;
+import org.eclipse.statet.docmlet.tex.core.ast.Verbatim;
+import org.eclipse.statet.docmlet.tex.core.model.TexModel;
+import org.eclipse.statet.docmlet.tex.core.model.TexSourceUnit;
+import org.eclipse.statet.ltk.ast.core.util.AbstractAstProblemReporter;
+import org.eclipse.statet.ltk.core.source.SourceContent;
+import org.eclipse.statet.ltk.core.source.StatusDetail;
+import org.eclipse.statet.ltk.issues.core.IssueRequestor;
+import org.eclipse.statet.ltk.issues.core.Problem;
+
+
+@NonNullByDefault
+public class AstProblemReporter extends AbstractAstProblemReporter {
+	
+	
+	private static final int ENV_LABEL_LIMIT= 20;
+	
+	
+	private final Visitor visitor= new Visitor();
+	
+	
+	public AstProblemReporter() {
+		super(TexModel.LTX_TYPE_ID);
+	}
+	
+	
+	public void run(final TexSourceUnit sourceUnit, final TexAstNode node,
+			final SourceContent content,
+			final IssueRequestor requestor) {
+		try {
+			init(content, requestor);
+			
+			node.acceptInTex(this.visitor);
+			
+			flush();
+		}
+		catch (final InvocationTargetException e) {}
+		finally {
+			clear();
+		}
+	}
+	
+	
+	protected void handleCommonCodes(final TexAstNode node, final int code)
+			throws BadLocationException, InvocationTargetException {
+		super.handleCommonCodes(node, code);
+	}
+	
+	
+	private class Visitor extends TexAstVisitor {
+		
+		
+		@Override
+		public void visit(final SourceComponent node) throws InvocationTargetException {
+			final int code= (node.getStatusCode() & MASK);
+			if (requiredCheck(code)) {
+				STATUS: try {
+					handleCommonCodes(node, code);
+					break STATUS;
+				}
+				catch (final BadLocationException e) {
+					throw new InvocationTargetException(e);
+				}
+			}
+			
+			node.acceptInTexChildren(this);
+		}
+		
+		@Override
+		public void visit(final Group node) throws InvocationTargetException {
+			final int code= (node.getStatusCode() & MASK);
+			if (requiredCheck(code)) {
+				STATUS: try {
+					switch (code & TYPE123) {
+					
+					case TYPE123_GROUP_NOT_CLOSED:
+						if (node.getParent() instanceof ControlNode) {
+							addProblem(Problem.SEVERITY_ERROR, code,
+									(node.getText() == "{") ? //$NON-NLS-1$
+											ProblemMessages.Ast_ReqArgument_NotClosed_message :
+											ProblemMessages.Ast_OptArgument_NotClosed_Opt_message,
+									node.getStartOffset(), node.getStartOffset() + 1 );
+							break STATUS;
+						}
+						else {
+							addProblem(Problem.SEVERITY_ERROR, code,
+									(node.getText() == "{") ? //$NON-NLS-1$
+											ProblemMessages.Ast_CurlyBracket_NotClosed_message :
+											ProblemMessages.Ast_SquareBracket_NotClosed_message,
+									node.getStartOffset(), node.getStartOffset() + 1 );
+							break STATUS;
+						}
+					
+					default:
+						handleCommonCodes(node, code);
+						break STATUS;
+					}
+				}
+				catch (final BadLocationException e) {
+					throw new InvocationTargetException(e);
+				}
+			}
+			
+			node.acceptInTexChildren(this);
+		}
+		
+		@Override
+		public void visit(final Environment node) throws InvocationTargetException {
+			final int code= (node.getStatusCode() & MASK);
+			if (requiredCheck(code)) {
+				STATUS: try {
+					switch (code & TYPE123) {
+					
+					case TYPE123_ENV_NOT_CLOSED: {
+							final TexAstNode beginNode= node.getBeginNode();
+							addProblem(Problem.SEVERITY_ERROR, code,
+									getMessageBuilder().bind(ProblemMessages.Ast_Env_NotClosed_message,
+											limit(node.getText(), ENV_LABEL_LIMIT) ),
+									beginNode.getStartOffset(), beginNode.getEndOffset() );
+							break STATUS;
+						}
+					case TYPE123_MATH_NOT_CLOSED: {
+							final TexAstNode beginNode= node.getBeginNode();
+							String c= node.getText();
+							if (c == "[") { //$NON-NLS-1$
+								c= "\\]"; //$NON-NLS-1$
+							}
+							else if (c == "(") { //$NON-NLS-1$
+								c= "\\)"; //$NON-NLS-1$
+							}
+							else {
+								c= null;
+							}
+							if (c != null) {
+								addProblem(Problem.SEVERITY_ERROR, code,
+										getMessageBuilder().bind(
+												ProblemMessages.Ast_Math_NotClosed_message,
+												c ),
+										beginNode.getStartOffset(), beginNode.getEndOffset() );
+							}
+							break STATUS;
+						}
+					
+					default:
+						handleCommonCodes(node, code);
+						break STATUS;
+					}
+				}
+				catch (final BadLocationException e) {
+					throw new InvocationTargetException(e);
+				}
+			}
+			
+			node.acceptInTexChildren(this);
+		}
+		
+		@Override
+		public void visit(final ControlNode node) throws InvocationTargetException {
+			final int code= (node.getStatusCode() & MASK);
+			if (requiredCheck(code)) {
+				STATUS: try {
+					switch (code & TYPE123) {
+					
+					case TYPE123_ENV_MISSING_NAME:
+						addProblem(Problem.SEVERITY_ERROR, code,
+								(node.getText().equals("begin")) ? //$NON-NLS-1$
+										ProblemMessages.Ast_Env_MissingName_Begin_message :
+										ProblemMessages.Ast_Env_MissingName_End_message,
+								node.getStartOffset(), node.getEndOffset() );
+						break STATUS;
+					case TYPE123_ENV_NOT_OPENED:
+						addProblem(Problem.SEVERITY_ERROR, code,
+								getMessageBuilder().bind(
+										ProblemMessages.Ast_Env_NotOpened_message,
+										limit(node.getChild(0).getChild(0).getText(), ENV_LABEL_LIMIT) ),
+								node.getStartOffset(), node.getEndOffset() );
+						break STATUS;
+					
+					case TYPE123_VERBATIM_INLINE_C_MISSING:
+						addProblem(Problem.SEVERITY_ERROR, code,
+								ProblemMessages.Ast_Verbatim_MissingSep_message,
+								node.getEndOffset()-1, node.getEndOffset() );
+						break STATUS;
+					
+					default:
+						handleCommonCodes(node, code);
+						break STATUS;
+					}
+				}
+				catch (final BadLocationException e) {
+					throw new InvocationTargetException(e);
+				}
+			}
+			
+			node.acceptInTexChildren(this);
+		}
+		
+		@Override
+		public void visit(final Text node) throws InvocationTargetException {
+			final int code= (node.getStatusCode() & MASK);
+			if (requiredCheck(code)) {
+				STATUS: try {
+					handleCommonCodes(node, code);
+					break STATUS;
+				}
+				catch (final BadLocationException e) {
+					throw new InvocationTargetException(e);
+				}
+			}
+		}
+		
+		@Override
+		public void visit(final Label node) throws InvocationTargetException {
+			final int code= (node.getStatusCode() & MASK);
+			if (requiredCheck(code)) {
+				STATUS: try {
+					handleCommonCodes(node, code);
+					break STATUS;
+				}
+				catch (final BadLocationException e) {
+					throw new InvocationTargetException(e);
+				}
+			}
+		}
+		
+		@Override
+		public void visit(final Math node) throws InvocationTargetException {
+			final int code= (node.getStatusCode() & MASK);
+			if (requiredCheck(code)) {
+				STATUS: try {
+					switch (code & TYPE123) {
+					
+					case TYPE123_MATH_NOT_CLOSED:
+						addProblem(Problem.SEVERITY_ERROR, code,
+								getMessageBuilder().bind(
+										ProblemMessages.Ast_Math_NotClosed_message,
+										node.getText() ),
+								node.getStartOffset(), node.getStartOffset() + node.getText().length() );
+						break STATUS;
+					
+					default:
+						handleCommonCodes(node, code);
+						break;
+					}
+				}
+				catch (final BadLocationException e) {
+					throw new InvocationTargetException(e);
+				}
+			}
+			
+			node.acceptInTexChildren(this);
+		}
+		
+		@Override
+		public void visit(final Verbatim node) throws InvocationTargetException {
+			final int code= (node.getStatusCode() & MASK);
+			if (requiredCheck(code)) {
+				STATUS: try {
+					switch (code & TYPE123) {
+					
+					case TYPE123_VERBATIM_INLINE_NOT_CLOSED:
+						addProblem(Problem.SEVERITY_ERROR, code,
+								getMessageBuilder().bind(
+										ProblemMessages.Ast_Verbatim_NotClosed_message,
+										StatusDetail.getStatusDetail(node).getText() ),
+								node.getEndOffset() - 1, node.getEndOffset() );
+						break STATUS;
+					
+					default:
+						handleCommonCodes(node, code);
+						break STATUS;
+					}
+				}
+				catch (final BadLocationException e) {
+					throw new InvocationTargetException(e);
+				}
+			}
+		}
+		
+		@Override
+		public void visit(final Comment node) throws InvocationTargetException {
+		}
+		
+		@Override
+		public void visit(final Dummy node) throws InvocationTargetException {
+			final int code= (node.getStatusCode() & MASK);
+			if (requiredCheck(code)) {
+				STATUS: try {
+					switch (code & TYPE123) {
+					
+					case TYPE123_GROUP_NOT_OPENED:
+						addProblem(Problem.SEVERITY_ERROR, code,
+								(node.getText() == "{") ? //$NON-NLS-1$
+										ProblemMessages.Ast_CurlyBracket_NotOpened_message :
+										ProblemMessages.Ast_SquareBracket_NotOpened_message,
+								node.getStartOffset(), node.getStartOffset() + 1 );
+						break STATUS;
+					
+					default:
+						handleCommonCodes(node, code);
+						break STATUS;
+					}
+				}
+				catch (final BadLocationException e) {
+					throw new InvocationTargetException(e);
+				}
+			}
+			
+			node.acceptInTexChildren(this);
+		}
+		
+		@Override
+		public void visit(final Embedded node) throws InvocationTargetException {
+			final int code= (node.getStatusCode() & MASK);
+			if (requiredCheck(code)) {
+				STATUS: try {
+					handleCommonCodes(node, code);
+					break STATUS;
+				}
+				catch (final BadLocationException e) {
+					throw new InvocationTargetException(e);
+				}
+			}
+		}
+		
+	}
+	
+	
+	protected static final @Nullable String limit(final @Nullable String label, final int limit) {
+		if (label != null && label.length() > limit) {
+			return label.substring(0, limit) + '…';
+		}
+		return label;
+	}
+	
+}
diff --git a/docmlet/org.eclipse.statet.docmlet.tex.core/src/org/eclipse/statet/internal/docmlet/tex/core/model/LtxProblemAstVisitor.java b/docmlet/org.eclipse.statet.docmlet.tex.core/src/org/eclipse/statet/internal/docmlet/tex/core/model/LtxProblemAstVisitor.java
deleted file mode 100644
index 9c51b51..0000000
--- a/docmlet/org.eclipse.statet.docmlet.tex.core/src/org/eclipse/statet/internal/docmlet/tex/core/model/LtxProblemAstVisitor.java
+++ /dev/null
@@ -1,276 +0,0 @@
-/*=============================================================================#
- # Copyright (c) 2012, 2021 Stephan Wahlbrink and others.
- # 
- # This program and the accompanying materials are made available under the
- # terms of the Eclipse Public License 2.0 which is available at
- # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
- # which is available at https://www.apache.org/licenses/LICENSE-2.0.
- # 
- # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
- # 
- # Contributors:
- #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
- #=============================================================================*/
-
-package org.eclipse.statet.internal.docmlet.tex.core.model;
-
-import static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullAssert;
-import static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullLateInit;
-
-import static org.eclipse.statet.docmlet.tex.core.ast.TexAstStatusConstants.TYPE123_ENV_MISSING_NAME;
-import static org.eclipse.statet.docmlet.tex.core.ast.TexAstStatusConstants.TYPE123_ENV_NOT_CLOSED;
-import static org.eclipse.statet.docmlet.tex.core.ast.TexAstStatusConstants.TYPE123_ENV_NOT_OPENED;
-import static org.eclipse.statet.docmlet.tex.core.ast.TexAstStatusConstants.TYPE123_GROUP_NOT_CLOSED;
-import static org.eclipse.statet.docmlet.tex.core.ast.TexAstStatusConstants.TYPE123_GROUP_NOT_OPENED;
-import static org.eclipse.statet.docmlet.tex.core.ast.TexAstStatusConstants.TYPE123_MATH_NOT_CLOSED;
-import static org.eclipse.statet.docmlet.tex.core.ast.TexAstStatusConstants.TYPE123_VERBATIM_INLINE_C_MISSING;
-import static org.eclipse.statet.docmlet.tex.core.ast.TexAstStatusConstants.TYPE123_VERBATIM_INLINE_NOT_CLOSED;
-import static org.eclipse.statet.ltk.core.StatusCodes.TYPE123;
-
-import java.lang.reflect.InvocationTargetException;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.eclipse.statet.jcommons.lang.NonNullByDefault;
-import org.eclipse.statet.jcommons.lang.Nullable;
-
-import org.eclipse.statet.ecommons.runtime.core.util.MessageBuilder;
-
-import org.eclipse.statet.docmlet.tex.core.ast.Comment;
-import org.eclipse.statet.docmlet.tex.core.ast.ControlNode;
-import org.eclipse.statet.docmlet.tex.core.ast.Dummy;
-import org.eclipse.statet.docmlet.tex.core.ast.Embedded;
-import org.eclipse.statet.docmlet.tex.core.ast.Environment;
-import org.eclipse.statet.docmlet.tex.core.ast.Group;
-import org.eclipse.statet.docmlet.tex.core.ast.Label;
-import org.eclipse.statet.docmlet.tex.core.ast.Math;
-import org.eclipse.statet.docmlet.tex.core.ast.SourceComponent;
-import org.eclipse.statet.docmlet.tex.core.ast.TexAstNode;
-import org.eclipse.statet.docmlet.tex.core.ast.TexAstVisitor;
-import org.eclipse.statet.docmlet.tex.core.ast.Text;
-import org.eclipse.statet.docmlet.tex.core.ast.Verbatim;
-import org.eclipse.statet.docmlet.tex.core.model.TexModel;
-import org.eclipse.statet.docmlet.tex.core.model.TexSourceUnit;
-import org.eclipse.statet.ltk.core.source.SourceContent;
-import org.eclipse.statet.ltk.issues.core.IssueRequestor;
-import org.eclipse.statet.ltk.issues.core.Problem;
-import org.eclipse.statet.ltk.issues.core.impl.BasicProblem;
-import org.eclipse.statet.ltk.model.core.element.SourceUnit;
-
-
-@NonNullByDefault
-public class LtxProblemAstVisitor extends TexAstVisitor {
-	
-	
-	private static final int ENV_LABEL_LIMIT= 20;
-	private static final int BUFFER_SIZE= 100;
-	
-	
-	private SourceUnit sourceUnit= nonNullLateInit();
-	private SourceContent sourceContent= nonNullLateInit();
-	private IssueRequestor requestor= nonNullLateInit();
-	
-	private final MessageBuilder messageBuilder= new MessageBuilder();
-	private final List<Problem> problemBuffer= new ArrayList<>(BUFFER_SIZE);
-	
-	
-	public LtxProblemAstVisitor() {
-	}
-	
-	
-	public void run(final TexSourceUnit sourceUnit, final TexAstNode node,
-			final SourceContent content,
-			final IssueRequestor requestor) {
-		try {
-			this.sourceUnit= nonNullAssert(sourceUnit);
-			this.sourceContent= nonNullAssert(content);
-			this.requestor= nonNullAssert(requestor);
-			
-			node.acceptInTex(this);
-			
-			if (this.problemBuffer.size() > 0) {
-				this.requestor.acceptProblems(TexModel.LTX_TYPE_ID, this.problemBuffer);
-			}
-		}
-		catch (final InvocationTargetException e) {}
-		finally {
-			clear();
-		}
-	}
-	
-	@SuppressWarnings("null")
-	private void clear() {
-		this.problemBuffer.clear();
-		this.sourceUnit= null;
-		this.sourceContent= null;
-		this.requestor= null;
-	}
-	
-	
-	@Override
-	public void visit(final SourceComponent node) throws InvocationTargetException {
-		node.acceptInTexChildren(this);
-	}
-	
-	@Override
-	public void visit(final Group node) throws InvocationTargetException {
-		final int code= (node.getStatusCode() & TYPE123);
-		if (code == TYPE123_GROUP_NOT_CLOSED) {
-			if (node.getParent() instanceof ControlNode) {
-				addProblem(Problem.SEVERITY_ERROR, code, (node.getText() == "{") ? //$NON-NLS-1$
-						ProblemMessages.Ast_ReqArgument_NotClosed_message :
-						ProblemMessages.Ast_OptArgument_NotClosed_Opt_message,
-						node.getStartOffset(), node.getStartOffset() + 1 );
-			}
-			else {
-				addProblem(Problem.SEVERITY_ERROR, code, (node.getText() == "{") ? //$NON-NLS-1$
-						ProblemMessages.Ast_CurlyBracket_NotClosed_message :
-						ProblemMessages.Ast_SquareBracket_NotClosed_message,
-						node.getStartOffset(), node.getStartOffset() + 1 );
-			}
-		}
-		
-		node.acceptInTexChildren(this);
-	}
-	
-	@Override
-	public void visit(final Environment node) throws InvocationTargetException {
-		final int code= (node.getStatusCode() & TYPE123);
-		switch (code) {
-		case TYPE123_ENV_NOT_CLOSED: {
-			final TexAstNode beginNode= node.getBeginNode();
-			addProblem(Problem.SEVERITY_ERROR, code,
-					this.messageBuilder.bind(ProblemMessages.Ast_Env_NotClosed_message,
-							limit(node.getText(), ENV_LABEL_LIMIT) ),
-					beginNode.getStartOffset(), beginNode.getEndOffset() );
-			break; }
-		case TYPE123_MATH_NOT_CLOSED: {
-			final TexAstNode beginNode= node.getBeginNode();
-			String c= node.getText();
-			if (c == "[") { //$NON-NLS-1$
-				c= "\\]"; //$NON-NLS-1$
-			}
-			else if (c == "(") { //$NON-NLS-1$
-				c= "\\)"; //$NON-NLS-1$
-			}
-			else {
-				c= null;
-			}
-			if (c != null) {
-				addProblem(Problem.SEVERITY_ERROR, code,
-						this.messageBuilder.bind(ProblemMessages.Ast_Math_NotClosed_message, c),
-						beginNode.getStartOffset(), beginNode.getEndOffset() );
-			}
-			break; }
-		}
-		
-		node.acceptInTexChildren(this);
-	}
-	
-	@Override
-	public void visit(final ControlNode node) throws InvocationTargetException {
-		final int code= (node.getStatusCode() & TYPE123);
-		switch (code) {
-		case TYPE123_ENV_MISSING_NAME:
-			addProblem(Problem.SEVERITY_ERROR, code, (node.getText().equals("begin")) ? //$NON-NLS-1$
-					ProblemMessages.Ast_Env_MissingName_Begin_message :
-					ProblemMessages.Ast_Env_MissingName_End_message,
-					node.getStartOffset(), node.getEndOffset() );
-			break;
-		case TYPE123_ENV_NOT_OPENED:
-			addProblem(Problem.SEVERITY_ERROR, code,
-					this.messageBuilder.bind(ProblemMessages.Ast_Env_NotOpened_message,
-							limit(node.getChild(0).getChild(0).getText(), ENV_LABEL_LIMIT) ),
-					node.getStartOffset(), node.getEndOffset() );
-			break;
-		case TYPE123_VERBATIM_INLINE_C_MISSING:
-			addProblem(Problem.SEVERITY_ERROR, code,
-					ProblemMessages.Ast_Verbatim_MissingSep_message,
-					node.getEndOffset()-1, node.getEndOffset() );
-			break;
-		}
-		
-		node.acceptInTexChildren(this);
-	}
-	
-	@Override
-	public void visit(final Text node) throws InvocationTargetException {
-	}
-	
-	@Override
-	public void visit(final Label node) throws InvocationTargetException {
-	}
-	
-	@Override
-	public void visit(final Math node) throws InvocationTargetException {
-		final int code= (node.getStatusCode() & TYPE123);
-		switch (code) {
-		case TYPE123_MATH_NOT_CLOSED:
-			addProblem(Problem.SEVERITY_ERROR, code,
-					this.messageBuilder.bind(ProblemMessages.Ast_Math_NotClosed_message,
-							node.getText() ),
-					node.getStartOffset(), node.getStartOffset() + node.getText().length() );
-			break;
-		}
-		node.acceptInTexChildren(this);
-	}
-	
-	@Override
-	public void visit(final Verbatim node) throws InvocationTargetException {
-		final int code= (node.getStatusCode() & TYPE123);
-		switch (code) {
-		case TYPE123_VERBATIM_INLINE_NOT_CLOSED:
-			addProblem(Problem.SEVERITY_ERROR, code,
-					this.messageBuilder.bind(ProblemMessages.Ast_Verbatim_NotClosed_message,
-							this.sourceContent.getString(node.getStartOffset() - 1, node.getStartOffset()) ),
-					node.getEndOffset() - 1, node.getEndOffset() );
-			break;
-		}
-	}
-	
-	@Override
-	public void visit(final Comment node) throws InvocationTargetException {
-	}
-	
-	@Override
-	public void visit(final Dummy node) throws InvocationTargetException {
-		final int code= (node.getStatusCode() & TYPE123);
-		switch (code) {
-		case TYPE123_GROUP_NOT_OPENED:
-			addProblem(Problem.SEVERITY_ERROR, code, (node.getText() == "{") ? //$NON-NLS-1$
-					ProblemMessages.Ast_CurlyBracket_NotOpened_message :
-					ProblemMessages.Ast_SquareBracket_NotOpened_message,
-					node.getStartOffset(), node.getStartOffset() + 1 );
-			break;
-		}
-	}
-	
-	@Override
-	public void visit(final Embedded node) throws InvocationTargetException {
-	}
-	
-	
-	protected final @Nullable String limit(final @Nullable String label, final int limit) {
-		if (label != null && label.length() > limit) {
-			return label.substring(0, limit) + '…';
-		}
-		return label;
-	}
-	
-	protected final void addProblem(final int severity, final int code, final String message,
-			int startOffset, int endOffset) {
-		if (startOffset < this.sourceContent.getStartOffset()) {
-			startOffset= this.sourceContent.getStartOffset();
-		}
-		if (endOffset > this.sourceContent.getEndOffset()) {
-			endOffset= this.sourceContent.getEndOffset();
-		}
-		this.problemBuffer.add(new BasicProblem(TexModel.LTX_TYPE_ID, severity, code, message,
-				startOffset, endOffset ));
-		if (this.problemBuffer.size() >= BUFFER_SIZE) {
-			this.requestor.acceptProblems(TexModel.LTX_TYPE_ID, this.problemBuffer);
-			this.problemBuffer.clear();
-		}
-	}
-	
-}
diff --git a/docmlet/org.eclipse.statet.docmlet.tex.ui/src/org/eclipse/statet/internal/docmlet/tex/ui/sourceediting/LtxContextInformationValidator.java b/docmlet/org.eclipse.statet.docmlet.tex.ui/src/org/eclipse/statet/internal/docmlet/tex/ui/sourceediting/LtxContextInformationValidator.java
index 366a248..ccd783d 100644
--- a/docmlet/org.eclipse.statet.docmlet.tex.ui/src/org/eclipse/statet/internal/docmlet/tex/ui/sourceediting/LtxContextInformationValidator.java
+++ b/docmlet/org.eclipse.statet.docmlet.tex.ui/src/org/eclipse/statet/internal/docmlet/tex/ui/sourceediting/LtxContextInformationValidator.java
@@ -178,7 +178,7 @@
 					final int docEndOffset= Math.min(0x800, rRootNode.getEndOffset() - docStartOffset);
 					final String text= document.get(docStartOffset, docEndOffset);
 					final LtxParser parser= new LtxParser(new NowebLtxLexer(), null);
-					args= parser.scanControlWordArgs(new OffsetStringParserInput(text, startOffset)
+					args= parser.parseControlWordArgs(new OffsetStringParserInput(text, startOffset)
 									.init(startOffset, startOffset + text.length()),
 							info.getCommand(), true, getParserTaskConfig());
 				}
diff --git a/yaml/org.eclipse.statet.yaml.core-tests/src/org/eclipse/statet/internal/yaml/core/model/SyntaxProblemReporterTest.java b/yaml/org.eclipse.statet.yaml.core-tests/src/org/eclipse/statet/internal/yaml/core/model/AstProblemReporterTest.java
similarity index 98%
rename from yaml/org.eclipse.statet.yaml.core-tests/src/org/eclipse/statet/internal/yaml/core/model/SyntaxProblemReporterTest.java
rename to yaml/org.eclipse.statet.yaml.core-tests/src/org/eclipse/statet/internal/yaml/core/model/AstProblemReporterTest.java
index 8256ef0..b98cfd7 100644
--- a/yaml/org.eclipse.statet.yaml.core-tests/src/org/eclipse/statet/internal/yaml/core/model/SyntaxProblemReporterTest.java
+++ b/yaml/org.eclipse.statet.yaml.core-tests/src/org/eclipse/statet/internal/yaml/core/model/AstProblemReporterTest.java
@@ -36,15 +36,15 @@
 
 
 @NonNullByDefault
-public class SyntaxProblemReporterTest {
+public class AstProblemReporterTest {
 	
 	
 	private final YamlParser yamlParser= new YamlParser();
 	
-	private final SyntaxProblemReporter syntaxProblemReporter= new SyntaxProblemReporter();
+	private final AstProblemReporter syntaxProblemReporter= new AstProblemReporter();
 	
 	
-	public SyntaxProblemReporterTest() {
+	public AstProblemReporterTest() {
 	}
 	
 	
diff --git a/yaml/org.eclipse.statet.yaml.core/src/org/eclipse/statet/internal/yaml/core/model/AstProblemReporter.java b/yaml/org.eclipse.statet.yaml.core/src/org/eclipse/statet/internal/yaml/core/model/AstProblemReporter.java
new file mode 100644
index 0000000..5d9b916
--- /dev/null
+++ b/yaml/org.eclipse.statet.yaml.core/src/org/eclipse/statet/internal/yaml/core/model/AstProblemReporter.java
@@ -0,0 +1,465 @@
+/*=============================================================================#
+ # Copyright (c) 2012, 2021 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.internal.yaml.core.model;
+
+import static org.eclipse.statet.ltk.ast.core.AstNode.NA_OFFSET;
+import static org.eclipse.statet.ltk.core.StatusCodes.CTX12;
+import static org.eclipse.statet.ltk.core.StatusCodes.TYPE12;
+import static org.eclipse.statet.ltk.core.StatusCodes.TYPE123;
+import static org.eclipse.statet.yaml.core.source.YamlSourceConstants.CTX12_DIRECTIVES_END_MARKER;
+import static org.eclipse.statet.yaml.core.source.YamlSourceConstants.CTX12_DOC_CONTENT;
+import static org.eclipse.statet.yaml.core.source.YamlSourceConstants.CTX12_MAP_ENTRY;
+import static org.eclipse.statet.yaml.core.source.YamlSourceConstants.CTX12_MAP_FLOW;
+import static org.eclipse.statet.yaml.core.source.YamlSourceConstants.CTX12_MAP_KEY;
+import static org.eclipse.statet.yaml.core.source.YamlSourceConstants.CTX12_MAP_VALUE;
+import static org.eclipse.statet.yaml.core.source.YamlSourceConstants.CTX12_SCALAR_FLOW;
+import static org.eclipse.statet.yaml.core.source.YamlSourceConstants.CTX12_SEQ_ENTRY;
+import static org.eclipse.statet.yaml.core.source.YamlSourceConstants.CTX12_SEQ_FLOW;
+import static org.eclipse.statet.yaml.core.source.YamlSourceConstants.CTX12_TAG_VERBATIM;
+import static org.eclipse.statet.yaml.core.source.YamlSourceConstants.TYPE123_SYNTAX_CHOMPING_INDICATOR_MULTIPLE;
+import static org.eclipse.statet.yaml.core.source.YamlSourceConstants.TYPE123_SYNTAX_INDENTATION_INDICATOR_INVALID;
+import static org.eclipse.statet.yaml.core.source.YamlSourceConstants.TYPE123_SYNTAX_LINE_BREAK_MISSING;
+import static org.eclipse.statet.yaml.core.source.YamlSourceConstants.TYPE123_SYNTAX_SPACE_BEFORE_COMMENT_MISSING;
+import static org.eclipse.statet.yaml.core.source.YamlSourceConstants.TYPE12_SYNTAX_CHAR_INVALID;
+import static org.eclipse.statet.yaml.core.source.YamlSourceConstants.TYPE12_SYNTAX_COLLECTION_NOT_CLOSED;
+import static org.eclipse.statet.yaml.core.source.YamlSourceConstants.TYPE12_SYNTAX_ESCAPE_INVALID;
+import static org.eclipse.statet.yaml.core.source.YamlSourceConstants.TYPE12_SYNTAX_MISSING_INDICATOR;
+import static org.eclipse.statet.yaml.core.source.YamlSourceConstants.TYPE12_SYNTAX_MISSING_MARKER;
+import static org.eclipse.statet.yaml.core.source.YamlSourceConstants.TYPE12_SYNTAX_MISSING_NODE;
+import static org.eclipse.statet.yaml.core.source.YamlSourceConstants.TYPE12_SYNTAX_SEP_COMMA_MISSING;
+import static org.eclipse.statet.yaml.core.source.YamlSourceConstants.TYPE12_SYNTAX_TOKEN_NOT_CLOSED;
+import static org.eclipse.statet.yaml.core.source.YamlSourceConstants.TYPE12_SYNTAX_TOKEN_UNEXPECTED;
+import static org.eclipse.statet.yaml.core.source.YamlSourceConstants.TYPE12_SYNTAX_TOKEN_UNKNOWN;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.jface.text.BadLocationException;
+
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+
+import org.eclipse.statet.ltk.ast.core.util.AbstractAstProblemReporter;
+import org.eclipse.statet.ltk.core.source.SourceContent;
+import org.eclipse.statet.ltk.core.source.StatusDetail;
+import org.eclipse.statet.ltk.issues.core.IssueRequestor;
+import org.eclipse.statet.ltk.issues.core.Problem;
+import org.eclipse.statet.yaml.core.model.YamlModel;
+import org.eclipse.statet.yaml.core.source.ast.Alias;
+import org.eclipse.statet.yaml.core.source.ast.Anchor;
+import org.eclipse.statet.yaml.core.source.ast.Collection;
+import org.eclipse.statet.yaml.core.source.ast.Directive;
+import org.eclipse.statet.yaml.core.source.ast.Dummy;
+import org.eclipse.statet.yaml.core.source.ast.MapEntry;
+import org.eclipse.statet.yaml.core.source.ast.Marker;
+import org.eclipse.statet.yaml.core.source.ast.Scalar;
+import org.eclipse.statet.yaml.core.source.ast.SeqEntry;
+import org.eclipse.statet.yaml.core.source.ast.Tag;
+import org.eclipse.statet.yaml.core.source.ast.YamlAstNode;
+import org.eclipse.statet.yaml.core.source.ast.YamlAstVisitor;
+
+
+@NonNullByDefault
+public class AstProblemReporter extends AbstractAstProblemReporter {
+	
+	
+	private final Visitor visitor= new Visitor();
+	
+	
+	public AstProblemReporter() {
+		super(YamlModel.YAML_TYPE_ID);
+	}
+	
+	
+	public void run(final YamlAstNode node,
+			final SourceContent content,
+			final IssueRequestor requestor) {
+		try {
+			init(content, requestor);
+			
+			node.acceptInYaml(this.visitor);
+			
+			flush();
+		}
+		catch (final OperationCanceledException | InvocationTargetException e) {}
+		finally {
+			clear();
+		}
+	}
+	
+	
+	protected void handleCommonCodes(final YamlAstNode node, final int code)
+			throws BadLocationException, InvocationTargetException {
+		final StatusDetail detail;
+		TYPE12: switch (code & TYPE12) {
+		
+		case TYPE12_SYNTAX_TOKEN_UNKNOWN:
+			addProblem(Problem.SEVERITY_ERROR, code, getMessageBuilder().bind(
+					ProblemMessages.Syntax_Gen_TokenUnknown_message,
+							getMessageUtil().getStartText(node, 0) ),
+					node.getStartOffset(), node.getEndOffset() );
+			return;
+			
+		case TYPE12_SYNTAX_TOKEN_UNEXPECTED:
+			addProblem(Problem.SEVERITY_ERROR, code, getMessageBuilder().bind(
+					ProblemMessages.Syntax_Gen_TokenUnexpected_message,
+							getMessageUtil().getStartText(node, 0) ),
+					node.getStartOffset(), node.getEndOffset() );
+			return;
+			
+		case TYPE12_SYNTAX_MISSING_NODE:
+//			addProblem(Problem.SEVERITY_ERROR, code,
+//					ProblemMessages.Syntax_Gen_NodeMissing_message,
+//					node.getStartOffset(), node.getEndOffset() );
+			return;
+			
+		case TYPE12_SYNTAX_CHAR_INVALID:
+			detail= StatusDetail.getStatusDetail(node);
+			if (detail != null) {
+				switch (code & TYPE123) {
+				case TYPE123_SYNTAX_SPACE_BEFORE_COMMENT_MISSING:
+					addProblem(Problem.SEVERITY_ERROR, code,
+							ProblemMessages.Syntax_Comment_SpaceBeforeMissing_message,
+							detail.getStartOffset(), detail.getEndOffset() );
+					return;
+				case TYPE123_SYNTAX_LINE_BREAK_MISSING:
+					addProblem(Problem.SEVERITY_ERROR, code,
+							ProblemMessages.Syntax_Gen_LineBreakMissing_message,
+							detail.getStartOffset(), detail.getEndOffset() );
+					return;
+				default:
+					addProblem(Problem.SEVERITY_ERROR, code, getMessageBuilder().bind(
+							ProblemMessages.Syntax_Gen_TokenUnexpected_message,
+							getMessageUtil().getStartText(node, 0) ),
+							node.getStartOffset(), node.getEndOffset() );
+					return;
+				}
+			}
+			break TYPE12;
+		
+		default:
+			break TYPE12;
+		}
+		
+		super.handleCommonCodes(node, code);
+	}
+	
+	
+	private class Visitor extends YamlAstVisitor {
+		
+		
+		//	@Override
+		//	public void visit(SourceComponent node) throws InvocationTargetException {
+		//		super.visit(node);
+		//	}
+			
+		//	@Override
+		//	public void visit(final DocumentNode node) throws InvocationTargetException {
+		//		super.visit(node);
+		//	}
+		
+		@Override
+		public void visit(final Marker node) throws InvocationTargetException {
+			final int code= (node.getStatusCode() & MASK);
+			if (requiredCheck(code)) {
+				STATUS: try {
+					{	handleCommonCodes(node, code);
+						break STATUS;
+					}
+				}
+				catch (final BadLocationException e) {
+					throw new InvocationTargetException(e);
+				}
+			}
+		}
+		
+		@Override
+		public void visit(final Directive node) throws InvocationTargetException {
+			final int code= (node.getStatusCode() & MASK);
+			if (requiredCheck(code)) {
+				STATUS: try {
+					{	handleCommonCodes(node, code);
+						break STATUS;
+					}
+				}
+				catch (final BadLocationException e) {
+					throw new InvocationTargetException(e);
+				}
+			}
+		}
+		
+	//	@Override
+	//	public void visit(final NodeWithProperties node) throws InvocationTargetException {
+	//		super.visit(node);
+	//	}
+		
+		@Override
+		public void visit(final Tag node) throws InvocationTargetException {
+			final int code= (node.getStatusCode() & MASK);
+			if (requiredCheck(code)) {
+				STATUS: try {
+					switch (code & (TYPE12 | CTX12)) {
+					case TYPE12_SYNTAX_TOKEN_NOT_CLOSED | CTX12_TAG_VERBATIM:
+						addProblem(Problem.SEVERITY_ERROR, code, getMessageBuilder().bind(
+								ProblemMessages.Syntax_Tag_VerbatimTag_NotClosed_message,
+										getMessageUtil().getStartText(node, 0) ),
+								node.getEndOffset(), node.getEndOffset() + 1);
+						break STATUS;
+					
+					default:
+						handleCommonCodes(node, code);
+						break STATUS;
+					}
+				}
+				catch (final BadLocationException e) {
+					throw new InvocationTargetException(e);
+				}
+			}
+		}
+		
+		@Override
+		public void visit(final Anchor node) throws InvocationTargetException {
+			final int code= (node.getStatusCode() & MASK);
+			if (requiredCheck(code)) {
+				STATUS: try {
+					{	handleCommonCodes(node, code);
+						break STATUS;
+					}
+				}
+				catch (final BadLocationException e) {
+					throw new InvocationTargetException(e);
+				}
+			}
+		}
+		
+		@Override
+		public void visit(final Collection node) throws InvocationTargetException {
+			final int code= (node.getStatusCode() & MASK);
+			if (requiredCheck(code)) {
+				STATUS: try {
+					final int offset;
+					switch (code & (TYPE12 | CTX12)) {
+					case TYPE12_SYNTAX_COLLECTION_NOT_CLOSED | CTX12_SEQ_FLOW:
+						addProblem(Problem.SEVERITY_ERROR, code,
+								ProblemMessages.Syntax_FlowSeq_NotClosed_message,
+								offset= node.getStartOffset(), offset + 1 );
+						break STATUS;
+					case TYPE12_SYNTAX_COLLECTION_NOT_CLOSED | CTX12_MAP_FLOW:
+						addProblem(Problem.SEVERITY_ERROR, code,
+								ProblemMessages.Syntax_FlowMap_NotClosed_message,
+								offset= node.getStartOffset(), offset + 1 );
+						break STATUS;
+					
+					case TYPE12_SYNTAX_SEP_COMMA_MISSING | CTX12_SEQ_FLOW:
+						addProblem(Problem.SEVERITY_ERROR, code,
+								ProblemMessages.Syntax_FlowSeq_SeqEntrySeparatorMissing_message,
+								offset= getMissingSepOffset((Collection.FlowCollection)node), offset + 1 );
+						break STATUS;
+					case TYPE12_SYNTAX_SEP_COMMA_MISSING | CTX12_MAP_FLOW:
+						addProblem(Problem.SEVERITY_ERROR, code,
+								ProblemMessages.Syntax_FlowMap_MapEntrySeparatorMissing_message,
+								offset= getMissingSepOffset((Collection.FlowCollection)node), offset + 1 );
+						break STATUS;
+					
+					default:
+						handleCommonCodes(node, code);
+						break STATUS;
+					}
+				}
+				catch (final BadLocationException e) {
+					throw new InvocationTargetException(e);
+				}
+			}
+			node.acceptInYamlChildren(this);
+		}
+		
+		@Override
+		public void visit(final SeqEntry node) throws InvocationTargetException {
+			final int code= (node.getStatusCode() & MASK);
+			if (requiredCheck(code)) {
+				STATUS: try {
+					switch (code & (TYPE12 | CTX12)) {
+					case TYPE12_SYNTAX_MISSING_INDICATOR | CTX12_SEQ_ENTRY:
+						addProblem(Problem.SEVERITY_ERROR, code,
+								ProblemMessages.Syntax_Seq_SeqEntryIndicatorMissing_message,
+								node.getStartOffset(), node.getStartOffset() + 1 );
+						break STATUS;
+					
+					default:
+						handleCommonCodes(node, code);
+						break STATUS;
+					}
+				}
+				catch (final BadLocationException e) {
+					throw new InvocationTargetException(e);
+				}
+			}
+			node.acceptInYamlChildren(this);
+		}
+		
+		@Override
+		public void visit(final MapEntry node) throws InvocationTargetException {
+			final int code= (node.getStatusCode() & MASK);
+			if (requiredCheck(code)) {
+				try {
+					final int offset;
+					TYPE: switch (code & (TYPE12 | CTX12)) {
+					case TYPE12_SYNTAX_MISSING_INDICATOR | CTX12_MAP_KEY:
+						addProblem(Problem.SEVERITY_ERROR, code,
+								ProblemMessages.Syntax_Map_MapKeyIndicatorMissing_messsage,
+								offset= node.getStartOffset(), offset + 1 );
+						break TYPE;
+					case TYPE12_SYNTAX_MISSING_INDICATOR | CTX12_MAP_VALUE:
+						addProblem(Problem.SEVERITY_ERROR, code,
+								ProblemMessages.Syntax_Map_MapValueIndicatorMissing_message,
+								node.getKey().getEndOffset() - 1, node.getKey().getEndOffset() + 1 );
+						break TYPE;
+						
+					default:
+						handleCommonCodes(node, code);
+						break TYPE;
+					}
+				}
+				catch (final BadLocationException e) {
+					throw new InvocationTargetException(e);
+				}
+			}
+			node.acceptInYamlChildren(this);
+		}
+		
+		@Override
+		public void visit(final Scalar node) throws InvocationTargetException {
+			final int code= (node.getStatusCode() & MASK);
+			if (requiredCheck(code)) {
+				STATUS: try {
+					final StatusDetail detail;
+					switch (code & (TYPE123 | CTX12)) {
+					case TYPE12_SYNTAX_TOKEN_NOT_CLOSED | CTX12_SCALAR_FLOW:
+						addProblem(Problem.SEVERITY_ERROR, code, getMessageBuilder().bind(
+								ProblemMessages.Syntax_Scalar_QuotedScalar_NotClosed_message,
+										getMessageUtil().getStartText(node, 1),
+										String.valueOf(node.getOperator()) ),
+								node.getEndOffset() - 1, node.getEndOffset() + 1 );
+						break STATUS;
+					
+					case TYPE123_SYNTAX_INDENTATION_INDICATOR_INVALID:
+						detail= StatusDetail.getStatusDetail(node);
+						if (detail != null) {
+							addProblem(Problem.SEVERITY_ERROR, code, getMessageBuilder().bind(
+									ProblemMessages.Syntax_Scalar_BlockScalar_IndentIndicatorInvalid_message,
+									detail.getText() ),
+									detail.getStartOffset(), detail.getEndOffset() );
+							break STATUS;
+						}
+						handleCommonCodes(node, code);
+						break STATUS;
+					case TYPE123_SYNTAX_CHOMPING_INDICATOR_MULTIPLE:
+						detail= StatusDetail.getStatusDetail(node);
+						if (detail != null) {
+							addProblem(Problem.SEVERITY_ERROR, code,
+									ProblemMessages.Syntax_Scalar_BlockScalar_ChompingIndicatorMultiple_message,
+									detail.getStartOffset(), detail.getEndOffset() );
+							break STATUS;
+						}
+						handleCommonCodes(node, code);
+						break STATUS;
+					
+					case TYPE12_SYNTAX_ESCAPE_INVALID | CTX12_SCALAR_FLOW:
+						detail= StatusDetail.getStatusDetail(node);
+						if (detail != null) {
+							addProblem(Problem.SEVERITY_ERROR, code, getMessageBuilder().bind(
+									ProblemMessages.Syntax_Scalar_QuotedScalar_EscapeSequenceInvalid_messsage,
+											detail.getText() ),
+									detail.getStartOffset(), detail.getEndOffset() );
+							break STATUS;
+						}
+						handleCommonCodes(node, code);
+						break STATUS;
+					
+					default:
+						handleCommonCodes(node, code);
+						break STATUS;
+					}
+				}
+				catch (final BadLocationException e) {
+					throw new InvocationTargetException(e);
+				}
+			}
+		}
+		
+		@Override
+		public void visit(final Dummy node) throws InvocationTargetException {
+			final int code= (node.getStatusCode() & MASK);
+			if (requiredCheck(code)) {
+				STATUS: try {
+					switch (code & (TYPE12 | CTX12)) {
+					case TYPE12_SYNTAX_MISSING_MARKER | CTX12_DIRECTIVES_END_MARKER:
+						addProblem(Problem.SEVERITY_ERROR, code,
+								ProblemMessages.Syntax_Doc_DirectiveEndMarkerMissing_message,
+								node.getStartOffset(), node.getEndOffset() );
+						break STATUS;
+					
+					case TYPE12_SYNTAX_MISSING_NODE | CTX12_DOC_CONTENT:
+						addProblem(Problem.SEVERITY_ERROR, code,
+								ProblemMessages.Syntax_Doc_ContentMissing_message,
+								node.getStartOffset(), node.getEndOffset() );
+						break STATUS;
+					case TYPE12_SYNTAX_MISSING_NODE | CTX12_SEQ_ENTRY:
+						addProblem(Problem.SEVERITY_ERROR, code,
+								ProblemMessages.Syntax_FlowSeq_SeqEntryMissing_message,
+								node.getStartOffset(), node.getEndOffset() );
+						break STATUS;
+					case TYPE12_SYNTAX_MISSING_NODE | CTX12_MAP_ENTRY:
+						addProblem(Problem.SEVERITY_ERROR, code,
+								ProblemMessages.Syntax_FlowMap_MapEntryMissing_message,
+								node.getStartOffset(), node.getEndOffset() );
+						break STATUS;
+					
+					default:
+						handleCommonCodes(node, code);
+						break STATUS;
+					}
+				}
+				catch (final BadLocationException e) {
+					throw new InvocationTargetException(e);
+				}
+			}
+		}
+		
+		@Override
+		public void visit(final Alias node) throws InvocationTargetException {
+			final int code= (node.getStatusCode() & MASK);
+			if (requiredCheck(code)) {
+				STATUS: try {
+					{	handleCommonCodes(node, code);
+						break STATUS;
+					}
+				}
+				catch (final BadLocationException e) {
+					throw new InvocationTargetException(e);
+				}
+			}
+		}
+		
+		
+		
+	}
+	
+	private static int getMissingSepOffset(final Collection.FlowCollection node) {
+		final int idx= node.getSepOffsets().indexOf(NA_OFFSET);
+		final YamlAstNode child= node.getChild(idx + 1);
+		return child.getStartOffset();
+	}
+	
+}
diff --git a/yaml/org.eclipse.statet.yaml.core/src/org/eclipse/statet/internal/yaml/core/model/SyntaxProblemReporter.java b/yaml/org.eclipse.statet.yaml.core/src/org/eclipse/statet/internal/yaml/core/model/SyntaxProblemReporter.java
deleted file mode 100644
index ea45596..0000000
--- a/yaml/org.eclipse.statet.yaml.core/src/org/eclipse/statet/internal/yaml/core/model/SyntaxProblemReporter.java
+++ /dev/null
@@ -1,515 +0,0 @@
-/*=============================================================================#
- # Copyright (c) 2012, 2021 Stephan Wahlbrink and others.
- # 
- # This program and the accompanying materials are made available under the
- # terms of the Eclipse Public License 2.0 which is available at
- # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
- # which is available at https://www.apache.org/licenses/LICENSE-2.0.
- # 
- # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
- # 
- # Contributors:
- #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
- #=============================================================================*/
-
-package org.eclipse.statet.internal.yaml.core.model;
-
-import static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullAssert;
-import static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullLateInit;
-
-import static org.eclipse.statet.ltk.ast.core.AstNode.NA_OFFSET;
-import static org.eclipse.statet.ltk.core.StatusCodes.CTX12;
-import static org.eclipse.statet.ltk.core.StatusCodes.SUBSEQUENT;
-import static org.eclipse.statet.ltk.core.StatusCodes.TYPE12;
-import static org.eclipse.statet.ltk.core.StatusCodes.TYPE123;
-import static org.eclipse.statet.yaml.core.source.YamlSourceConstants.CTX12_DIRECTIVES_END_MARKER;
-import static org.eclipse.statet.yaml.core.source.YamlSourceConstants.CTX12_DOC_CONTENT;
-import static org.eclipse.statet.yaml.core.source.YamlSourceConstants.CTX12_MAP_ENTRY;
-import static org.eclipse.statet.yaml.core.source.YamlSourceConstants.CTX12_MAP_FLOW;
-import static org.eclipse.statet.yaml.core.source.YamlSourceConstants.CTX12_MAP_KEY;
-import static org.eclipse.statet.yaml.core.source.YamlSourceConstants.CTX12_MAP_VALUE;
-import static org.eclipse.statet.yaml.core.source.YamlSourceConstants.CTX12_SCALAR_FLOW;
-import static org.eclipse.statet.yaml.core.source.YamlSourceConstants.CTX12_SEQ_ENTRY;
-import static org.eclipse.statet.yaml.core.source.YamlSourceConstants.CTX12_SEQ_FLOW;
-import static org.eclipse.statet.yaml.core.source.YamlSourceConstants.CTX12_TAG_VERBATIM;
-import static org.eclipse.statet.yaml.core.source.YamlSourceConstants.TYPE123_SYNTAX_CHOMPING_INDICATOR_MULTIPLE;
-import static org.eclipse.statet.yaml.core.source.YamlSourceConstants.TYPE123_SYNTAX_INDENTATION_INDICATOR_INVALID;
-import static org.eclipse.statet.yaml.core.source.YamlSourceConstants.TYPE123_SYNTAX_LINE_BREAK_MISSING;
-import static org.eclipse.statet.yaml.core.source.YamlSourceConstants.TYPE123_SYNTAX_SPACE_BEFORE_COMMENT_MISSING;
-import static org.eclipse.statet.yaml.core.source.YamlSourceConstants.TYPE12_SYNTAX_CHAR_INVALID;
-import static org.eclipse.statet.yaml.core.source.YamlSourceConstants.TYPE12_SYNTAX_COLLECTION_NOT_CLOSED;
-import static org.eclipse.statet.yaml.core.source.YamlSourceConstants.TYPE12_SYNTAX_ESCAPE_INVALID;
-import static org.eclipse.statet.yaml.core.source.YamlSourceConstants.TYPE12_SYNTAX_MISSING_INDICATOR;
-import static org.eclipse.statet.yaml.core.source.YamlSourceConstants.TYPE12_SYNTAX_MISSING_MARKER;
-import static org.eclipse.statet.yaml.core.source.YamlSourceConstants.TYPE12_SYNTAX_MISSING_NODE;
-import static org.eclipse.statet.yaml.core.source.YamlSourceConstants.TYPE12_SYNTAX_SEP_COMMA_MISSING;
-import static org.eclipse.statet.yaml.core.source.YamlSourceConstants.TYPE12_SYNTAX_TOKEN_NOT_CLOSED;
-import static org.eclipse.statet.yaml.core.source.YamlSourceConstants.TYPE12_SYNTAX_TOKEN_UNEXPECTED;
-import static org.eclipse.statet.yaml.core.source.YamlSourceConstants.TYPE12_SYNTAX_TOKEN_UNKNOWN;
-
-import java.lang.reflect.InvocationTargetException;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.eclipse.core.runtime.OperationCanceledException;
-import org.eclipse.jface.text.BadLocationException;
-
-import org.eclipse.statet.jcommons.lang.NonNullByDefault;
-
-import org.eclipse.statet.ecommons.runtime.core.util.MessageBuilder;
-
-import org.eclipse.statet.ltk.core.StatusCodes;
-import org.eclipse.statet.ltk.core.source.SourceContent;
-import org.eclipse.statet.ltk.core.source.StatusDetail;
-import org.eclipse.statet.ltk.core.util.SourceMessageUtil;
-import org.eclipse.statet.ltk.issues.core.IssueRequestor;
-import org.eclipse.statet.ltk.issues.core.Problem;
-import org.eclipse.statet.ltk.issues.core.impl.BasicProblem;
-import org.eclipse.statet.yaml.core.model.YamlModel;
-import org.eclipse.statet.yaml.core.source.ast.Alias;
-import org.eclipse.statet.yaml.core.source.ast.Anchor;
-import org.eclipse.statet.yaml.core.source.ast.Collection;
-import org.eclipse.statet.yaml.core.source.ast.Directive;
-import org.eclipse.statet.yaml.core.source.ast.Dummy;
-import org.eclipse.statet.yaml.core.source.ast.MapEntry;
-import org.eclipse.statet.yaml.core.source.ast.Marker;
-import org.eclipse.statet.yaml.core.source.ast.Scalar;
-import org.eclipse.statet.yaml.core.source.ast.SeqEntry;
-import org.eclipse.statet.yaml.core.source.ast.Tag;
-import org.eclipse.statet.yaml.core.source.ast.YamlAstNode;
-import org.eclipse.statet.yaml.core.source.ast.YamlAstVisitor;
-
-
-@NonNullByDefault
-public class SyntaxProblemReporter extends YamlAstVisitor {
-	
-	
-	private static final int BUFFER_SIZE= 64;
-	
-	private static final int MASK= TYPE123 | CTX12 | SUBSEQUENT;
-	
-	
-	private final boolean reportSubsequent= false;
-	
-	private SourceContent sourceContent= nonNullLateInit();
-	private IssueRequestor requestor= nonNullLateInit();
-	
-	private final SourceMessageUtil messageUtil= new SourceMessageUtil();
-	private final MessageBuilder messageBuilder= new MessageBuilder();
-	private final List<Problem> problemBuffer= new ArrayList<>(BUFFER_SIZE);
-	
-	
-	public SyntaxProblemReporter() {
-	}
-	
-	
-	public void run(final YamlAstNode node,
-			final SourceContent content,
-			final IssueRequestor requestor) {
-		try {
-			this.sourceContent= nonNullAssert(content);
-			this.messageUtil.setSourceContent(content);
-			this.requestor= requestor;
-			
-			node.acceptInYaml(this);
-			
-			if (!this.problemBuffer.isEmpty()) {
-				this.requestor.acceptProblems(YamlModel.YAML_TYPE_ID, this.problemBuffer);
-			}
-		}
-		catch (final OperationCanceledException | InvocationTargetException e) {}
-		finally {
-			clear();
-		}
-	}
-	
-	@SuppressWarnings("null")
-	private void clear() {
-		this.problemBuffer.clear();
-		this.sourceContent= null;
-		this.requestor= null;
-	}
-	
-	
-	private boolean requiredCheck(final int code) {
-		return code != StatusCodes.TYPE1_OK &&
-				(this.reportSubsequent || ((code & SUBSEQUENT) == 0));
-	}
-	
-	protected final void addProblem(final int severity, final int code, final String message,
-			int startOffset, int endOffset) {
-		if (startOffset < this.sourceContent.getStartOffset()) {
-			startOffset= this.sourceContent.getStartOffset();
-		}
-		if (endOffset <= startOffset) {
-			endOffset= startOffset + 1;
-		}
-		if (endOffset > this.sourceContent.getEndOffset()) {
-			endOffset= this.sourceContent.getEndOffset();
-		}
-		
-		this.problemBuffer.add(new BasicProblem(YamlModel.YAML_TYPE_ID, severity, code, message,
-				startOffset, endOffset ));
-		
-		if (this.problemBuffer.size() >= BUFFER_SIZE) {
-			this.requestor.acceptProblems(YamlModel.YAML_TYPE_ID, this.problemBuffer);
-			this.problemBuffer.clear();
-		}
-	}
-	
-	protected void handleCommonCodes(final YamlAstNode node, final int code)
-			throws BadLocationException, InvocationTargetException {
-		final StatusDetail detail;
-		switch (code & TYPE12) {
-		case TYPE12_SYNTAX_TOKEN_UNKNOWN:
-			addProblem(Problem.SEVERITY_ERROR, code, this.messageBuilder.bind(
-					ProblemMessages.Syntax_Gen_TokenUnknown_message,
-							this.messageUtil.getStartText(node, 0) ),
-					node.getStartOffset(), node.getEndOffset() );
-			return;
-			
-		case TYPE12_SYNTAX_TOKEN_UNEXPECTED:
-			addProblem(Problem.SEVERITY_ERROR, code, this.messageBuilder.bind(
-					ProblemMessages.Syntax_Gen_TokenUnexpected_message,
-							this.messageUtil.getStartText(node, 0) ),
-					node.getStartOffset(), node.getEndOffset() );
-			return;
-			
-		case TYPE12_SYNTAX_MISSING_NODE:
-//			addProblem(Problem.SEVERITY_ERROR, code,
-//					ProblemMessages.Syntax_Gen_NodeMissing_message,
-//					node.getStartOffset(), node.getEndOffset() );
-			return;
-			
-		case TYPE12_SYNTAX_CHAR_INVALID:
-			detail= StatusDetail.getStatusDetail(node);
-			if (detail != null) {
-				switch (code & TYPE123) {
-				case TYPE123_SYNTAX_SPACE_BEFORE_COMMENT_MISSING:
-					addProblem(Problem.SEVERITY_ERROR, code,
-							ProblemMessages.Syntax_Comment_SpaceBeforeMissing_message,
-							detail.getStartOffset(), detail.getEndOffset() );
-					return;
-				case TYPE123_SYNTAX_LINE_BREAK_MISSING:
-					addProblem(Problem.SEVERITY_ERROR, code,
-							ProblemMessages.Syntax_Gen_LineBreakMissing_message,
-							detail.getStartOffset(), detail.getEndOffset() );
-					return;
-				default:
-					addProblem(Problem.SEVERITY_ERROR, code, this.messageBuilder.bind(
-							ProblemMessages.Syntax_Gen_TokenUnexpected_message,
-							this.messageUtil.getStartText(node, 0) ),
-							node.getStartOffset(), node.getEndOffset() );
-					return;
-				}
-			}
-			break;
-		
-		default:
-			// TODO
-			return;
-		}
-	}
-	
-	
-//	@Override
-//	public void visit(SourceComponent node) throws InvocationTargetException {
-//		super.visit(node);
-//	}
-	
-//	@Override
-//	public void visit(final DocumentNode node) throws InvocationTargetException {
-//		super.visit(node);
-//	}
-	
-	@Override
-	public void visit(final Marker node) throws InvocationTargetException {
-		final int code= (node.getStatusCode() & MASK);
-		if (requiredCheck(code)) {
-			STATUS: try {
-				{	handleCommonCodes(node, code);
-					break STATUS;
-				}
-			}
-			catch (final BadLocationException e) {
-				throw new InvocationTargetException(e);
-			}
-		}
-	}
-	
-	@Override
-	public void visit(final Directive node) throws InvocationTargetException {
-		final int code= (node.getStatusCode() & MASK);
-		if (requiredCheck(code)) {
-			STATUS: try {
-				{	handleCommonCodes(node, code);
-					break STATUS;
-				}
-			}
-			catch (final BadLocationException e) {
-				throw new InvocationTargetException(e);
-			}
-		}
-	}
-	
-//	@Override
-//	public void visit(final NodeWithProperties node) throws InvocationTargetException {
-//		super.visit(node);
-//	}
-	
-	@Override
-	public void visit(final Tag node) throws InvocationTargetException {
-		final int code= (node.getStatusCode() & MASK);
-		if (requiredCheck(code)) {
-			STATUS: try {
-				switch (code & (TYPE12 | CTX12)) {
-				case TYPE12_SYNTAX_TOKEN_NOT_CLOSED | CTX12_TAG_VERBATIM:
-					addProblem(Problem.SEVERITY_ERROR, code, this.messageBuilder.bind(
-							ProblemMessages.Syntax_Tag_VerbatimTag_NotClosed_message,
-									this.messageUtil.getStartText(node, 0) ),
-							node.getEndOffset(), node.getEndOffset() + 1);
-					break STATUS;
-				
-				default:
-					handleCommonCodes(node, code);
-					break STATUS;
-				}
-			}
-			catch (final BadLocationException e) {
-				throw new InvocationTargetException(e);
-			}
-		}
-	}
-	
-	@Override
-	public void visit(final Anchor node) throws InvocationTargetException {
-		final int code= (node.getStatusCode() & MASK);
-		if (requiredCheck(code)) {
-			STATUS: try {
-				{	handleCommonCodes(node, code);
-					break STATUS;
-				}
-			}
-			catch (final BadLocationException e) {
-				throw new InvocationTargetException(e);
-			}
-		}
-	}
-	
-	@Override
-	public void visit(final Collection node) throws InvocationTargetException {
-		final int code= (node.getStatusCode() & MASK);
-		if (requiredCheck(code)) {
-			STATUS: try {
-				final int offset;
-				switch (code & (TYPE12 | CTX12)) {
-				case TYPE12_SYNTAX_COLLECTION_NOT_CLOSED | CTX12_SEQ_FLOW:
-					addProblem(Problem.SEVERITY_ERROR, code,
-							ProblemMessages.Syntax_FlowSeq_NotClosed_message,
-							offset= node.getStartOffset(), offset + 1 );
-					break STATUS;
-				case TYPE12_SYNTAX_COLLECTION_NOT_CLOSED | CTX12_MAP_FLOW:
-					addProblem(Problem.SEVERITY_ERROR, code,
-							ProblemMessages.Syntax_FlowMap_NotClosed_message,
-							offset= node.getStartOffset(), offset + 1 );
-					break STATUS;
-				
-				case TYPE12_SYNTAX_SEP_COMMA_MISSING | CTX12_SEQ_FLOW:
-					addProblem(Problem.SEVERITY_ERROR, code,
-							ProblemMessages.Syntax_FlowSeq_SeqEntrySeparatorMissing_message,
-							offset= getMissingSepOffset((Collection.FlowCollection)node), offset + 1 );
-					break STATUS;
-				case TYPE12_SYNTAX_SEP_COMMA_MISSING | CTX12_MAP_FLOW:
-					addProblem(Problem.SEVERITY_ERROR, code,
-							ProblemMessages.Syntax_FlowMap_MapEntrySeparatorMissing_message,
-							offset= getMissingSepOffset((Collection.FlowCollection)node), offset + 1 );
-					break STATUS;
-				
-				default:
-					handleCommonCodes(node, code);
-					break STATUS;
-				}
-			}
-			catch (final BadLocationException e) {
-				throw new InvocationTargetException(e);
-			}
-		}
-		node.acceptInYamlChildren(this);
-	}
-	
-	@Override
-	public void visit(final SeqEntry node) throws InvocationTargetException {
-		final int code= (node.getStatusCode() & MASK);
-		if (requiredCheck(code)) {
-			STATUS: try {
-				switch (code & (TYPE12 | CTX12)) {
-				case TYPE12_SYNTAX_MISSING_INDICATOR | CTX12_SEQ_ENTRY:
-					addProblem(Problem.SEVERITY_ERROR, code,
-							ProblemMessages.Syntax_Seq_SeqEntryIndicatorMissing_message,
-							node.getStartOffset(), node.getStartOffset() + 1 );
-					break STATUS;
-				
-				default:
-					handleCommonCodes(node, code);
-					break STATUS;
-				}
-			}
-			catch (final BadLocationException e) {
-				throw new InvocationTargetException(e);
-			}
-		}
-		node.acceptInYamlChildren(this);
-	}
-	
-	@Override
-	public void visit(final MapEntry node) throws InvocationTargetException {
-		final int code= (node.getStatusCode() & MASK);
-		if (requiredCheck(code)) {
-			try {
-				final int offset;
-				TYPE: switch (code & (TYPE12 | CTX12)) {
-				case TYPE12_SYNTAX_MISSING_INDICATOR | CTX12_MAP_KEY:
-					addProblem(Problem.SEVERITY_ERROR, code,
-							ProblemMessages.Syntax_Map_MapKeyIndicatorMissing_messsage,
-							offset= node.getStartOffset(), offset + 1 );
-					break TYPE;
-				case TYPE12_SYNTAX_MISSING_INDICATOR | CTX12_MAP_VALUE:
-					addProblem(Problem.SEVERITY_ERROR, code,
-							ProblemMessages.Syntax_Map_MapValueIndicatorMissing_message,
-							node.getKey().getEndOffset() - 1, node.getKey().getEndOffset() + 1 );
-					break TYPE;
-					
-				default:
-					handleCommonCodes(node, code);
-					break TYPE;
-				}
-			}
-			catch (final BadLocationException e) {
-				throw new InvocationTargetException(e);
-			}
-		}
-		node.acceptInYamlChildren(this);
-	}
-	
-	@Override
-	public void visit(final Scalar node) throws InvocationTargetException {
-		final int code= (node.getStatusCode() & MASK);
-		if (requiredCheck(code)) {
-			STATUS: try {
-				final StatusDetail detail;
-				switch (code & (TYPE123 | CTX12)) {
-				case TYPE12_SYNTAX_TOKEN_NOT_CLOSED | CTX12_SCALAR_FLOW:
-					addProblem(Problem.SEVERITY_ERROR, code, this.messageBuilder.bind(
-							ProblemMessages.Syntax_Scalar_QuotedScalar_NotClosed_message,
-									this.messageUtil.getStartText(node, 1),
-									String.valueOf(node.getOperator()) ),
-							node.getEndOffset() - 1, node.getEndOffset() + 1 );
-					break STATUS;
-				
-				case TYPE123_SYNTAX_INDENTATION_INDICATOR_INVALID:
-					detail= StatusDetail.getStatusDetail(node);
-					if (detail != null) {
-						addProblem(Problem.SEVERITY_ERROR, code, this.messageBuilder.bind(
-								ProblemMessages.Syntax_Scalar_BlockScalar_IndentIndicatorInvalid_message,
-								detail.getText() ),
-								detail.getStartOffset(), detail.getEndOffset() );
-						break STATUS;
-					}
-					handleCommonCodes(node, code);
-					break STATUS;
-				case TYPE123_SYNTAX_CHOMPING_INDICATOR_MULTIPLE:
-					detail= StatusDetail.getStatusDetail(node);
-					if (detail != null) {
-						addProblem(Problem.SEVERITY_ERROR, code,
-								ProblemMessages.Syntax_Scalar_BlockScalar_ChompingIndicatorMultiple_message,
-								detail.getStartOffset(), detail.getEndOffset() );
-						break STATUS;
-					}
-					handleCommonCodes(node, code);
-					break STATUS;
-				
-				case TYPE12_SYNTAX_ESCAPE_INVALID | CTX12_SCALAR_FLOW:
-					detail= StatusDetail.getStatusDetail(node);
-					if (detail != null) {
-						addProblem(Problem.SEVERITY_ERROR, code, this.messageBuilder.bind(
-								ProblemMessages.Syntax_Scalar_QuotedScalar_EscapeSequenceInvalid_messsage,
-										detail.getText() ),
-								detail.getStartOffset(), detail.getEndOffset() );
-						break STATUS;
-					}
-					handleCommonCodes(node, code);
-					break STATUS;
-				
-				default:
-					handleCommonCodes(node, code);
-					break STATUS;
-				}
-			}
-			catch (final BadLocationException e) {
-				throw new InvocationTargetException(e);
-			}
-		}
-	}
-	
-	@Override
-	public void visit(final Dummy node) throws InvocationTargetException {
-		final int code= (node.getStatusCode() & MASK);
-		if (requiredCheck(code)) {
-			STATUS: try {
-				switch (code & (TYPE12 | CTX12)) {
-				case TYPE12_SYNTAX_MISSING_MARKER | CTX12_DIRECTIVES_END_MARKER:
-					addProblem(Problem.SEVERITY_ERROR, code,
-							ProblemMessages.Syntax_Doc_DirectiveEndMarkerMissing_message,
-							node.getStartOffset(), node.getEndOffset() );
-					break STATUS;
-				
-				case TYPE12_SYNTAX_MISSING_NODE | CTX12_DOC_CONTENT:
-					addProblem(Problem.SEVERITY_ERROR, code,
-							ProblemMessages.Syntax_Doc_ContentMissing_message,
-							node.getStartOffset(), node.getEndOffset() );
-					break STATUS;
-				case TYPE12_SYNTAX_MISSING_NODE | CTX12_SEQ_ENTRY:
-					addProblem(Problem.SEVERITY_ERROR, code,
-							ProblemMessages.Syntax_FlowSeq_SeqEntryMissing_message,
-							node.getStartOffset(), node.getEndOffset() );
-					break STATUS;
-				case TYPE12_SYNTAX_MISSING_NODE | CTX12_MAP_ENTRY:
-					addProblem(Problem.SEVERITY_ERROR, code,
-							ProblemMessages.Syntax_FlowMap_MapEntryMissing_message,
-							node.getStartOffset(), node.getEndOffset() );
-					break STATUS;
-				
-				default:
-					handleCommonCodes(node, code);
-					break STATUS;
-				}
-			}
-			catch (final BadLocationException e) {
-				throw new InvocationTargetException(e);
-			}
-		}
-	}
-	
-	@Override
-	public void visit(final Alias node) throws InvocationTargetException {
-		final int code= (node.getStatusCode() & MASK);
-		if (requiredCheck(code)) {
-			STATUS: try {
-				{	handleCommonCodes(node, code);
-					break STATUS;
-				}
-			}
-			catch (final BadLocationException e) {
-				throw new InvocationTargetException(e);
-			}
-		}
-	}
-	
-	
-	private static int getMissingSepOffset(final Collection.FlowCollection node) {
-		final int idx= node.getSepOffsets().indexOf(NA_OFFSET);
-		final YamlAstNode child= node.getChild(idx + 1);
-		return child.getStartOffset();
-	}
-	
-}
diff --git a/yaml/org.eclipse.statet.yaml.core/src/org/eclipse/statet/yaml/core/model/build/YamlIssueReporter.java b/yaml/org.eclipse.statet.yaml.core/src/org/eclipse/statet/yaml/core/model/build/YamlIssueReporter.java
index e70bd01..658176d 100644
--- a/yaml/org.eclipse.statet.yaml.core/src/org/eclipse/statet/yaml/core/model/build/YamlIssueReporter.java
+++ b/yaml/org.eclipse.statet.yaml.core/src/org/eclipse/statet/yaml/core/model/build/YamlIssueReporter.java
@@ -19,7 +19,7 @@
 
 import org.eclipse.statet.ecommons.preferences.core.EPreferences;
 
-import org.eclipse.statet.internal.yaml.core.model.SyntaxProblemReporter;
+import org.eclipse.statet.internal.yaml.core.model.AstProblemReporter;
 import org.eclipse.statet.ltk.ast.core.AstNode;
 import org.eclipse.statet.ltk.core.source.SourceContent;
 import org.eclipse.statet.ltk.issues.core.IssueRequestor;
@@ -36,7 +36,7 @@
 public class YamlIssueReporter {
 	
 	
-	private final SyntaxProblemReporter syntaxProblemReporter= new SyntaxProblemReporter();
+	private final AstProblemReporter syntaxProblemReporter= new AstProblemReporter();
 	
 	private final YamlTaskTagReporter taskReporter= new YamlTaskTagReporter();
 	
diff --git a/yaml/org.eclipse.statet.yaml.core/src/org/eclipse/statet/yaml/core/source/ast/YamlParser.java b/yaml/org.eclipse.statet.yaml.core/src/org/eclipse/statet/yaml/core/source/ast/YamlParser.java
index 4f21dad..f864d33 100644
--- a/yaml/org.eclipse.statet.yaml.core/src/org/eclipse/statet/yaml/core/source/ast/YamlParser.java
+++ b/yaml/org.eclipse.statet.yaml.core/src/org/eclipse/statet/yaml/core/source/ast/YamlParser.java
@@ -89,6 +89,7 @@
 		}
 	};
 	
+	private String parseInput;
 	private int parseStartOffset;
 	private int parseEndOffset;
 	private @Nullable AstNode parseParent;
@@ -122,12 +123,26 @@
 	}
 	
 	
-	private void init0(final int startOffset, final int endOffset, final @Nullable AstNode parent) {
-		this.parseStartOffset= startOffset;
-		this.parseEndOffset= endOffset;
+	private void init0(final String input, final int inputOffset, final @Nullable AstNode parent) {
+		this.parseInput= nonNullAssert(input);
+		this.parseStartOffset= inputOffset;
+		this.parseEndOffset= inputOffset + input.length();
 		this.parseParent= parent;
 	}
 	
+	@SuppressWarnings("null")
+	private void clear0() {
+		this.parseInput= null;
+		this.parseParent= null;
+	}
+	
+	private void logParseError(final Exception e) {
+		CommonsRuntime.log(new ErrorStatus(YamlCore.BUNDLE_ID,
+				"An error occured while parsing R source code. Input:\n"
+						+ this.parseInput.toString(),
+				e ));
+	}
+	
 	
 	private void init() {
 		this.depth= -1;
@@ -137,16 +152,16 @@
 		this.docState= DOC_NONE;
 	}
 	
-	public SourceComponent parseSourceUnit(final String text, final int offset,
+	public SourceComponent parseSourceUnit(final String input, final int inputOffset,
 			final @Nullable AstNode parent) {
-		init0(offset, offset + text.length(), parent);
+		init0(input, inputOffset, parent);
 		try {
 			init();
-			this.lexer.reset(text, offset);
+			this.lexer.reset(input, inputOffset);
 			
-			final SourceComponent node= new SourceComponent(parent,
+			final SourceComponent sourceNode= new SourceComponent(parent,
 					this.parseStartOffset, this.parseEndOffset );
-			enterNode(node);
+			enterNode(sourceNode);
 			
 			processTokens();
 			
@@ -155,15 +170,17 @@
 			}
 			
 			if (this.commentsLevel != 0) {
-				node.comments= ImCollections.toList(this.comments);
+				sourceNode.comments= ImCollections.toList(this.comments);
 			}
 			
-			return node;
+			return sourceNode;
 		}
 		catch (final Exception e) {
-			return onParseError(e);
+			logParseError(e);
+			return createErrorSourceComponent();
 		}
 		finally {
+			clear0();
 			while (this.depth >= 0) {
 				if (this.depth < this.containerStack.size()) {
 					final var builder= this.containerStack.get(this.depth);
@@ -178,10 +195,7 @@
 		return parseSourceUnit(text, 0, null);
 	}
 	
-	private SourceComponent onParseError(final Exception e) {
-		CommonsRuntime.log(new ErrorStatus(YamlCore.BUNDLE_ID,
-				"An error occured while parsing YAML source code.",
-				e ));
+	private SourceComponent createErrorSourceComponent() {
 		final int startOffset= this.parseStartOffset;
 		int endOffset= this.parseEndOffset;
 		if (endOffset < startOffset) {