/*=============================================================================# # Copyright (c) 2012-2016 Stephan Wahlbrink (WalWare.de) and others. # All rights reserved. This program and the accompanying materials # are made available under the terms of the Eclipse Public License v1.0 # which accompanies this distribution, and is available at # http://www.eclipse.org/legal/epl-v10.html # # Contributors: # Stephan Wahlbrink - initial API and implementation #=============================================================================*/ package de.walware.docmlet.tex.internal.core.model; import static de.walware.docmlet.tex.core.ITexProblemConstants.MASK_12; import static de.walware.docmlet.tex.core.ast.ITexAstStatusConstants.STATUS2_ENV_MISSING_NAME; import static de.walware.docmlet.tex.core.ast.ITexAstStatusConstants.STATUS2_ENV_NOT_CLOSED; import static de.walware.docmlet.tex.core.ast.ITexAstStatusConstants.STATUS2_ENV_NOT_OPENED; import static de.walware.docmlet.tex.core.ast.ITexAstStatusConstants.STATUS2_GROUP_NOT_CLOSED; import static de.walware.docmlet.tex.core.ast.ITexAstStatusConstants.STATUS2_GROUP_NOT_OPENED; import static de.walware.docmlet.tex.core.ast.ITexAstStatusConstants.STATUS2_MATH_NOT_CLOSED; import static de.walware.docmlet.tex.core.ast.ITexAstStatusConstants.STATUS2_VERBATIM_INLINE_C_MISSING; import static de.walware.docmlet.tex.core.ast.ITexAstStatusConstants.STATUS2_VERBATIM_INLINE_NOT_CLOSED; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.List; import de.walware.ecommons.MessageBuilder; import de.walware.ecommons.ltk.IProblem; import de.walware.ecommons.ltk.IProblemRequestor; import de.walware.ecommons.ltk.core.SourceContent; import de.walware.ecommons.ltk.core.impl.Problem; import de.walware.ecommons.ltk.core.model.ISourceUnit; import de.walware.ecommons.text.core.ILineInformation; import de.walware.docmlet.tex.core.ast.Comment; import de.walware.docmlet.tex.core.ast.ControlNode; import de.walware.docmlet.tex.core.ast.Dummy; import de.walware.docmlet.tex.core.ast.Embedded; import de.walware.docmlet.tex.core.ast.Environment; import de.walware.docmlet.tex.core.ast.Group; import de.walware.docmlet.tex.core.ast.Label; import de.walware.docmlet.tex.core.ast.Math; import de.walware.docmlet.tex.core.ast.SourceComponent; import de.walware.docmlet.tex.core.ast.TexAstNode; import de.walware.docmlet.tex.core.ast.TexAstVisitor; import de.walware.docmlet.tex.core.ast.Text; import de.walware.docmlet.tex.core.ast.Verbatim; import de.walware.docmlet.tex.core.model.ITexSourceUnit; import de.walware.docmlet.tex.core.model.TexModel; public class LtxProblemAstVisitor extends TexAstVisitor { private static final int ENV_LABEL_LIMIT = 20; private static final int BUFFER_SIZE = 100; private ISourceUnit fCurrentUnit; private String fCurrentText; private ILineInformation fCurrentLines; private IProblemRequestor fCurrentRequestor; private final MessageBuilder fMessageBuilder = new MessageBuilder(); private final List<IProblem> fProblemBuffer = new ArrayList<>(BUFFER_SIZE); public void run(final ITexSourceUnit su, final SourceContent content, final TexAstNode node, final IProblemRequestor requestor) { try { fCurrentUnit = su; fCurrentText = content.getText(); fCurrentLines = content.getLines(); fCurrentRequestor = requestor; node.acceptInTex(this); if (fProblemBuffer.size() > 0) { fCurrentRequestor.acceptProblems(TexModel.LTX_TYPE_ID, fProblemBuffer); } } catch (final InvocationTargetException e) {} finally { fProblemBuffer.clear(); fCurrentUnit = null; fCurrentRequestor = 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() & MASK_12); if (code == STATUS2_GROUP_NOT_CLOSED) { if (node.getParent() instanceof ControlNode) { addProblem(IProblem.SEVERITY_ERROR, code, (node.getText() == "{") ? //$NON-NLS-1$ ProblemMessages.Ast_ReqArgument_NotClosed_message : ProblemMessages.Ast_OptArgument_NotClosed_Opt_message, node.getOffset(), node.getOffset()+1 ); } else { addProblem(IProblem.SEVERITY_ERROR, code, (node.getText() == "{") ? //$NON-NLS-1$ ProblemMessages.Ast_CurlyBracket_NotClosed_message : ProblemMessages.Ast_SquareBracket_NotClosed_message, node.getOffset(), node.getOffset()+1 ); } } node.acceptInTexChildren(this); } @Override public void visit(final Environment node) throws InvocationTargetException { final int code = (node.getStatusCode() & MASK_12); switch (code) { case STATUS2_ENV_NOT_CLOSED: { final TexAstNode beginNode = node.getBeginNode(); addProblem(IProblem.SEVERITY_ERROR, code, fMessageBuilder.bind(ProblemMessages.Ast_Env_NotClosed_message, limit(node.getText(), ENV_LABEL_LIMIT) ), beginNode.getOffset(), beginNode.getEndOffset() ); break; } case STATUS2_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(IProblem.SEVERITY_ERROR, code, fMessageBuilder.bind(ProblemMessages.Ast_Math_NotClosed_message, c), beginNode.getOffset(), beginNode.getEndOffset() ); } break; } } node.acceptInTexChildren(this); } @Override public void visit(final ControlNode node) throws InvocationTargetException { final int code = (node.getStatusCode() & MASK_12); switch (code) { case STATUS2_ENV_MISSING_NAME: addProblem(IProblem.SEVERITY_ERROR, code, (node.getText() == "begin") ? //$NON-NLS-1$ ProblemMessages.Ast_Env_MissingName_Begin_message : ProblemMessages.Ast_Env_MissingName_End_message, node.getOffset(), node.getEndOffset() ); break; case STATUS2_ENV_NOT_OPENED: addProblem(IProblem.SEVERITY_ERROR, code, fMessageBuilder.bind(ProblemMessages.Ast_Env_NotOpened_message, limit(node.getChild(0).getChild(0).getText(), ENV_LABEL_LIMIT) ), node.getOffset(), node.getEndOffset() ); break; case STATUS2_VERBATIM_INLINE_C_MISSING: addProblem(IProblem.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() & MASK_12); switch (code) { case STATUS2_MATH_NOT_CLOSED: addProblem(IProblem.SEVERITY_ERROR, code, fMessageBuilder.bind(ProblemMessages.Ast_Math_NotClosed_message, node.getText() ), node.getOffset(), node.getOffset() + node.getText().length() ); break; } node.acceptInTexChildren(this); } @Override public void visit(final Verbatim node) throws InvocationTargetException { final int code = (node.getStatusCode() & MASK_12); switch (code) { case STATUS2_VERBATIM_INLINE_NOT_CLOSED: addProblem(IProblem.SEVERITY_ERROR, code, fMessageBuilder.bind(ProblemMessages.Ast_Verbatim_NotClosed_message, new String(fCurrentText.substring(node.getOffset()-1, node.getOffset())) ), 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() & MASK_12); switch (code) { case STATUS2_GROUP_NOT_OPENED: addProblem(IProblem.SEVERITY_ERROR, code, (node.getText() == "{") ? //$NON-NLS-1$ ProblemMessages.Ast_CurlyBracket_NotOpened_message : ProblemMessages.Ast_SquareBracket_NotOpened_message, node.getOffset(), node.getOffset()+1 ); break; } } @Override public void visit(final Embedded node) throws InvocationTargetException { } protected final String limit(final 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 stopOffset) { if (startOffset < 0) { startOffset = 0; } if (stopOffset > fCurrentText.length()) { stopOffset = fCurrentText.length(); } fProblemBuffer.add(new Problem(TexModel.LTX_TYPE_ID, severity, code, message, fCurrentUnit, startOffset, stopOffset )); if (fProblemBuffer.size() >= BUFFER_SIZE) { fCurrentRequestor.acceptProblems(TexModel.LTX_TYPE_ID, fProblemBuffer); fProblemBuffer.clear(); } } }