blob: b1f60bab386a7a1df7f0b1d50dfe3c9ae1b5414b [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2012, 2018 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.ITexProblemConstants.MASK_12;
import static org.eclipse.statet.docmlet.tex.core.ast.ITexAstStatusConstants.STATUS2_ENV_MISSING_NAME;
import static org.eclipse.statet.docmlet.tex.core.ast.ITexAstStatusConstants.STATUS2_ENV_NOT_CLOSED;
import static org.eclipse.statet.docmlet.tex.core.ast.ITexAstStatusConstants.STATUS2_ENV_NOT_OPENED;
import static org.eclipse.statet.docmlet.tex.core.ast.ITexAstStatusConstants.STATUS2_GROUP_NOT_CLOSED;
import static org.eclipse.statet.docmlet.tex.core.ast.ITexAstStatusConstants.STATUS2_GROUP_NOT_OPENED;
import static org.eclipse.statet.docmlet.tex.core.ast.ITexAstStatusConstants.STATUS2_MATH_NOT_CLOSED;
import static org.eclipse.statet.docmlet.tex.core.ast.ITexAstStatusConstants.STATUS2_VERBATIM_INLINE_C_MISSING;
import static org.eclipse.statet.docmlet.tex.core.ast.ITexAstStatusConstants.STATUS2_VERBATIM_INLINE_NOT_CLOSED;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.statet.jcommons.text.core.TextLineInformation;
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.ITexSourceUnit;
import org.eclipse.statet.docmlet.tex.core.model.TexModel;
import org.eclipse.statet.ltk.core.SourceContent;
import org.eclipse.statet.ltk.issues.core.BasicProblem;
import org.eclipse.statet.ltk.issues.core.Problem;
import org.eclipse.statet.ltk.issues.core.ProblemRequestor;
import org.eclipse.statet.ltk.model.core.elements.ISourceUnit;
public class LtxProblemAstVisitor extends TexAstVisitor {
private static final int ENV_LABEL_LIMIT= 20;
private static final int BUFFER_SIZE= 100;
private ISourceUnit currentUnit;
private String currentText;
private TextLineInformation currentLines;
private ProblemRequestor currentRequestor;
private final MessageBuilder messageBuilder= new MessageBuilder();
private final List<Problem> problemBuffer= new ArrayList<>(BUFFER_SIZE);
public void run(final ITexSourceUnit su, final SourceContent content,
final TexAstNode node, final ProblemRequestor requestor) {
try {
this.currentUnit= su;
this.currentText= content.getText();
this.currentLines= content.getLines();
this.currentRequestor= requestor;
node.acceptInTex(this);
if (this.problemBuffer.size() > 0) {
this.currentRequestor.acceptProblems(TexModel.LTX_TYPE_ID, this.problemBuffer);
}
}
catch (final InvocationTargetException e) {}
finally {
this.problemBuffer.clear();
this.currentUnit= null;
this.currentRequestor= 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(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() & MASK_12);
switch (code) {
case STATUS2_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 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(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() & MASK_12);
switch (code) {
case STATUS2_ENV_MISSING_NAME:
addProblem(Problem.SEVERITY_ERROR, code, (node.getText() == "begin") ? //$NON-NLS-1$
ProblemMessages.Ast_Env_MissingName_Begin_message :
ProblemMessages.Ast_Env_MissingName_End_message,
node.getStartOffset(), node.getEndOffset() );
break;
case STATUS2_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 STATUS2_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() & MASK_12);
switch (code) {
case STATUS2_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() & MASK_12);
switch (code) {
case STATUS2_VERBATIM_INLINE_NOT_CLOSED:
addProblem(Problem.SEVERITY_ERROR, code,
this.messageBuilder.bind(ProblemMessages.Ast_Verbatim_NotClosed_message,
new String(this.currentText.substring(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() & MASK_12);
switch (code) {
case STATUS2_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 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 endOffset) {
if (startOffset < 0) {
startOffset= 0;
}
if (endOffset > this.currentText.length()) {
endOffset= this.currentText.length();
}
this.problemBuffer.add(new BasicProblem(TexModel.LTX_TYPE_ID, severity, code, message,
startOffset, endOffset ));
if (this.problemBuffer.size() >= BUFFER_SIZE) {
this.currentRequestor.acceptProblems(TexModel.LTX_TYPE_ID, this.problemBuffer);
this.problemBuffer.clear();
}
}
}