blob: e3843f5e0f20c6cdfb955c77f8649a58dd04c796 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.javascript2.editor;
import com.oracle.js.parser.TokenType;
import com.oracle.js.parser.ir.ClassNode;
import com.oracle.js.parser.ir.ExportSpecifierNode;
import com.oracle.js.parser.ir.FromNode;
import com.oracle.js.parser.ir.FunctionNode;
import com.oracle.js.parser.ir.ImportSpecifierNode;
import com.oracle.js.parser.ir.LexicalContext;
import com.oracle.js.parser.ir.NameSpaceImportNode;
import com.oracle.js.parser.ir.ObjectNode;
import com.oracle.js.parser.ir.PropertyNode;
import com.oracle.js.parser.ir.UnaryNode;
import com.oracle.js.parser.ir.VarNode;
import com.oracle.js.parser.ir.visitor.NodeVisitor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.netbeans.api.lexer.Token;
import org.netbeans.api.lexer.TokenSequence;
import org.netbeans.modules.csl.api.ColoringAttributes;
import org.netbeans.modules.csl.api.Modifier;
import org.netbeans.modules.csl.api.OffsetRange;
import org.netbeans.modules.csl.api.SemanticAnalyzer;
import org.netbeans.modules.javascript2.doc.api.JsDocumentationSupport;
import org.netbeans.modules.javascript2.doc.spi.JsComment;
import org.netbeans.modules.javascript2.lexer.api.JsTokenId;
import org.netbeans.modules.javascript2.lexer.api.LexUtilities;
import org.netbeans.modules.javascript2.types.api.Identifier;
import org.netbeans.modules.javascript2.model.api.JsFunction;
import org.netbeans.modules.javascript2.model.api.JsObject;
import org.netbeans.modules.javascript2.model.api.Model;
import org.netbeans.modules.javascript2.model.api.Occurrence;
import org.netbeans.modules.javascript2.types.api.Type;
import org.netbeans.modules.javascript2.model.api.ModelUtils;
import org.netbeans.modules.javascript2.editor.parser.JsParserResult;
import org.netbeans.modules.javascript2.model.api.JsReference;
import org.netbeans.modules.parsing.spi.Scheduler;
import org.netbeans.modules.parsing.spi.SchedulerEvent;
/**
*
* @author Petr Pisl
*/
public class JsSemanticAnalyzer extends SemanticAnalyzer<JsParserResult> {
//public static final EnumSet<ColoringAttributes> UNUSED_VARIABLE_SET = EnumSet.of(ColoringAttributes.UNUSED, ColoringAttributes.VA);
public static final EnumSet<ColoringAttributes> UNUSED_OBJECT_SET = EnumSet.of( ColoringAttributes.UNUSED, ColoringAttributes.CLASS);
public static final EnumSet<ColoringAttributes> UNUSED_METHOD_SET = EnumSet.of( ColoringAttributes.UNUSED, ColoringAttributes.METHOD);
public static final EnumSet<ColoringAttributes> LOCAL_VARIABLE_DECLARATION = EnumSet.of(ColoringAttributes.LOCAL_VARIABLE_DECLARATION);
public static final EnumSet<ColoringAttributes> LOCAL_VARIABLE_DECLARATION_UNUSED = EnumSet.of(ColoringAttributes.LOCAL_VARIABLE_DECLARATION, ColoringAttributes.UNUSED);
public static final EnumSet<ColoringAttributes> LOCAL_VARIABLE_USE = EnumSet.of(ColoringAttributes.LOCAL_VARIABLE);
public static final EnumSet<ColoringAttributes> GLOBAL_DEFINITION = EnumSet.of(ColoringAttributes.GLOBAL, ColoringAttributes.CLASS);
public static final EnumSet<ColoringAttributes> NUMBER_OXB_CHAR = EnumSet.of(ColoringAttributes.CUSTOM1);
public static final EnumSet<ColoringAttributes> SEMANTIC_KEYWORD = EnumSet.of(ColoringAttributes.CUSTOM2);
private boolean cancelled;
private Map<OffsetRange, Set<ColoringAttributes>> semanticHighlights;
private static final List<String> GLOBAL_TYPES = Arrays.asList(Type.ARRAY, Type.STRING, Type.BOOLEAN, Type.NUMBER);
private Collection<OffsetRange> globalJsHintInlines = new ArrayList<OffsetRange>();
public JsSemanticAnalyzer() {
this.cancelled = false;
this.semanticHighlights = null;
}
@Override
public Map<OffsetRange, Set<ColoringAttributes>> getHighlights() {
return semanticHighlights;
}
@Override
public void run(JsParserResult result, SchedulerEvent event) {
resume();
if (isCancelled()) {
return;
}
Map<OffsetRange, Set<ColoringAttributes>> highlights =
new HashMap<OffsetRange, Set<ColoringAttributes>>(100);
Model model = Model.getModel(result, false);
JsObject global = model.getGlobalObject();
Collection<Identifier> definedGlobal = ModelUtils.getDefinedGlobal(result.getSnapshot(), -1);
for (Identifier iden: definedGlobal) {
globalJsHintInlines.add(iden.getOffsetRange());
}
highlights = count(result, global, highlights, new HashSet<String>());
highlights = processSemanticKeywords(result, highlights);
highlights = processNumbers(result, highlights);
if (highlights != null && highlights.size() > 0) {
semanticHighlights = highlights;
} else {
semanticHighlights = null;
}
}
private Map<OffsetRange, Set<ColoringAttributes>> count (JsParserResult result, JsObject parent, Map<OffsetRange, Set<ColoringAttributes>> highlights, Set<String> processedObjects) {
if (ModelUtils.wasProcessed(parent, processedObjects)) {
return highlights;
}
for (Iterator<? extends JsObject> it = parent.getProperties().values().iterator(); it.hasNext();) {
JsObject object = it.next();
if (object.getDeclarationName() != null) {
switch (object.getJSKind()) {
case CONSTRUCTOR:
case METHOD:
case FUNCTION:
case GENERATOR:
if(object.isDeclared() && !object.isAnonymous() && !object.getDeclarationName().getOffsetRange().isEmpty()) {
EnumSet<ColoringAttributes> coloring = ColoringAttributes.METHOD_SET;
if (object.getModifiers().contains(Modifier.PRIVATE)) {
if (object.getOccurrences().isEmpty()) {
coloring = UNUSED_METHOD_SET;
} else if (object.getOccurrences().size() == 1) {
OffsetRange orDeclaration = object.getDeclarationName().getOffsetRange();
OffsetRange orOccurrence = object.getOccurrences().get(0).getOffsetRange();
if (orDeclaration.equals(orOccurrence)) {
coloring = UNUSED_METHOD_SET;
}
}
}
addColoring(result, highlights, object.getDeclarationName().getOffsetRange(), coloring);
}
for(JsObject param: ((JsFunction)object).getParameters()) {
if (!(object instanceof JsReference && !((JsReference)object).getOriginal().isAnonymous())) {
count(result, param, highlights, processedObjects);
}
if (!hasSourceOccurences(result, param)) {
OffsetRange range = LexUtilities.getLexerOffsets(result, param.getDeclarationName().getOffsetRange());
if (range.getStart() < range.getEnd()) {
// only for declared parameters
highlights.put(range, ColoringAttributes.UNUSED_SET);
}
}
}
break;
case PROPERTY_GETTER:
case PROPERTY_SETTER:
int offset = LexUtilities.getLexerOffset(result, object.getDeclarationName().getOffsetRange().getStart());
TokenSequence<? extends JsTokenId> ts = LexUtilities.getJsTokenSequence(result.getSnapshot(), offset);
if (ts != null) {
ts.move(offset);
if (ts.moveNext() && ts.movePrevious()) {
Token token = LexUtilities.findPrevious(ts, Arrays.asList(JsTokenId.WHITESPACE, JsTokenId.BLOCK_COMMENT, JsTokenId.DOC_COMMENT));
if (token.id() == JsTokenId.IDENTIFIER && token.length() == 3) {
highlights.put(new OffsetRange(ts.offset(), ts.offset() + token.length()), ColoringAttributes.METHOD_SET);
}
}
highlights.put(LexUtilities.getLexerOffsets(result, object.getDeclarationName().getOffsetRange()), ColoringAttributes.FIELD_SET);
}
break;
case OBJECT:
case OBJECT_LITERAL:
case CLASS:
if(!"UNKNOWN".equals(object.getName())) {
if (parent.getParent() == null && !GLOBAL_TYPES.contains(object.getName())) {
addColoring(result, highlights, object.getDeclarationName().getOffsetRange(), GLOBAL_DEFINITION);
for (Occurrence occurence : object.getOccurrences()) {
addColoring(result, highlights, occurence.getOffsetRange(), ColoringAttributes.GLOBAL_SET);
}
} else if (object.isDeclared() && !ModelUtils.PROTOTYPE.equals(object.getName()) && !object.isAnonymous()) {
if((object.getOccurrences().isEmpty()
|| (object.getOccurrences().size() == 1 && object.getOccurrences().get(0).getOffsetRange().equals(object.getDeclarationName().getOffsetRange())))
&& object.getModifiers().contains(Modifier.PRIVATE)) {
highlights.put(LexUtilities.getLexerOffsets(result, object.getDeclarationName().getOffsetRange()), UNUSED_OBJECT_SET);
} else {
highlights.put(LexUtilities.getLexerOffsets(result, object.getDeclarationName().getOffsetRange()), ColoringAttributes.CLASS_SET);
TokenSequence<? extends JsTokenId> cts = LexUtilities.getJsTokenSequence(result.getSnapshot(), object.getDeclarationName().getOffsetRange().getStart());
for (Occurrence occurrence: object.getOccurrences()) {
cts.move(occurrence.getOffsetRange().getStart());
if (cts.moveNext() && cts.token().id() == JsTokenId.STRING && !occurrence.getOffsetRange().equals(object.getDeclarationName().getOffsetRange())) {
highlights.put(LexUtilities.getLexerOffsets(result, occurrence.getOffsetRange()), ColoringAttributes.CLASS_SET);
}
}
}
}
}
break;
case PROPERTY:
case FIELD:
if(object.isDeclared()) {
addColoring(result, highlights, object.getDeclarationName().getOffsetRange(), ColoringAttributes.FIELD_SET);
for(Occurrence occurence: object.getOccurrences()) {
addColoring(result, highlights, occurence.getOffsetRange(), ColoringAttributes.FIELD_SET);
}
} else {
// we need to check whether the fiels is not used in aa["bb"], then bb color with black
TokenSequence<? extends JsTokenId> cts = LexUtilities.getJsTokenSequence(result.getSnapshot(), object.getOffset());
cts.move(object.getOffsetRange().getStart());
if (cts.moveNext() && cts.token().id() == JsTokenId.STRING) {
addColoring(result, highlights, object.getOffsetRange(), ColoringAttributes.FIELD_SET);
}
for (Occurrence occurrence : object.getOccurrences()) {
cts.move(occurrence.getOffsetRange().getStart());
if (cts.moveNext() && cts.token().id() == JsTokenId.STRING) {
addColoring(result, highlights, occurrence.getOffsetRange(), ColoringAttributes.FIELD_SET);
}
}
}
break;
case VARIABLE:
if (parent.getParent() == null && !GLOBAL_TYPES.contains(object.getName())) {
addColoring(result, highlights, object.getDeclarationName().getOffsetRange(), ColoringAttributes.GLOBAL_SET);
for(Occurrence occurence: object.getOccurrences()) {
addColoring(result, highlights, occurence.getOffsetRange(), ColoringAttributes.GLOBAL_SET);
}
} else {
if ((object.getOccurrences().isEmpty()
|| (object.getOccurrences().size() == 1 && object.getOccurrences().get(0).getOffsetRange().equals(object.getDeclarationName().getOffsetRange())))
&& !GLOBAL_TYPES.contains(object.getName())) {
OffsetRange range = object.getDeclarationName().getOffsetRange();
if (range.getStart() < range.getEnd()) {
// some virtual variables (like arguments) doesn't have to be declared, but are in the model
if (object.getModifiers().contains(Modifier.PRIVATE) || object.getModifiers().contains(Modifier.PROTECTED)) {
highlights.put(LexUtilities.getLexerOffsets(result, object.getDeclarationName().getOffsetRange()), LOCAL_VARIABLE_DECLARATION_UNUSED);
} else {
highlights.put(LexUtilities.getLexerOffsets(result, object.getDeclarationName().getOffsetRange()), ColoringAttributes.UNUSED_SET);
}
}
} else if (object instanceof JsObject && !ModelUtils.ARGUMENTS.equals(object.getName())) { // NOI18N
if (object.getOccurrences().size() <= ((JsObject)object).getAssignmentCount()) {
// probably is used only on the left site => is unused
if (object.getDeclarationName().getOffsetRange().getLength() > 0) {
highlights.put(LexUtilities.getLexerOffsets(result, object.getDeclarationName().getOffsetRange()), ColoringAttributes.UNUSED_SET);
}
for(Occurrence occurence: object.getOccurrences()) {
if (occurence.getOffsetRange().getLength() > 0) {
highlights.put(LexUtilities.getLexerOffsets(result, occurence.getOffsetRange()), ColoringAttributes.UNUSED_SET);
}
}
} else if (object.getModifiers().contains(Modifier.PRIVATE) || object.getModifiers().contains(Modifier.PROTECTED)) {
OffsetRange decOffset = object.getDeclarationName().getOffsetRange();
addColoring(result, highlights, decOffset, LOCAL_VARIABLE_DECLARATION);
for(Occurrence occurence: object.getOccurrences()) {
if (occurence.getOffsetRange().getLength() > 0 && !occurence.getOffsetRange().equals(decOffset)) {
addColoring(result, highlights, occurence.getOffsetRange(), LOCAL_VARIABLE_USE);
}
}
}
}
}
}
}
if (isCancelled()) {
highlights = null;
break;
}
if (!(object instanceof JsReference && ModelUtils.isDescendant(object, ((JsReference)object).getOriginal()))) {
highlights = count(result, object, highlights, processedObjects);
}
}
return highlights;
}
private Map<OffsetRange, Set<ColoringAttributes>> processSemanticKeywords(final JsParserResult result,
final Map<OffsetRange, Set<ColoringAttributes>> highlights) {
FunctionNode root = result.getRoot();
if (root == null) {
return highlights;
}
NodeVisitor visitor = new NodeVisitor(new LexicalContext()) {
@Override
public boolean enterFunctionNode(FunctionNode functionNode) {
if (functionNode.isModule()) {
functionNode.visitImports(this);
functionNode.visitExports(this);
}
if (functionNode.isAsync() && !functionNode.isMethod()) {
int pos = com.oracle.js.parser.Token.descPosition(functionNode.getFirstToken());
if (functionNode.getKind() != FunctionNode.Kind.ARROW) {
// in arrow function async is the first token
pos--;
}
TokenSequence<? extends JsTokenId> ts = LexUtilities.getJsPositionedSequence(result.getSnapshot(), pos);
if (ts != null) {
Token<? extends JsTokenId> token = LexUtilities.findPreviousNonWsNonComment(ts);
if (token != null && token.id() == JsTokenId.IDENTIFIER && "async".equals(token.text().toString())) {
highlights.put(LexUtilities.getLexerOffsets(result,
new OffsetRange(ts.offset(), ts.offset() + token.length())), SEMANTIC_KEYWORD);
}
}
}
return super.enterFunctionNode(functionNode);
}
@Override
public boolean enterImportSpecifierNode(ImportSpecifierNode importSpecifierNode) {
if (importSpecifierNode.getIdentifier() != null) {
int start = importSpecifierNode.getIdentifier().getFinish();
TokenSequence<? extends JsTokenId> ts = LexUtilities.getJsPositionedSequence(result.getSnapshot(), start);
if (ts != null) {
Token<? extends JsTokenId> token = LexUtilities.findNextNonWsNonComment(ts);
if (token != null && token.id() == JsTokenId.IDENTIFIER && ts.offset() < importSpecifierNode.getBindingIdentifier().getStart()) {
// it has to be "as"
highlights.put(LexUtilities.getLexerOffsets(result,
new OffsetRange(ts.offset(), ts.offset() + token.length())), SEMANTIC_KEYWORD);
}
}
}
return false;
}
@Override
public boolean enterExportSpecifierNode(ExportSpecifierNode exportSpecifierNode) {
if (exportSpecifierNode.getExportIdentifier() != null) {
int start = exportSpecifierNode.getIdentifier().getFinish();
TokenSequence<? extends JsTokenId> ts = LexUtilities.getJsPositionedSequence(result.getSnapshot(), start);
if (ts != null) {
Token<? extends JsTokenId> token = LexUtilities.findNextNonWsNonComment(ts);
if (token != null && token.id() == JsTokenId.IDENTIFIER && ts.offset() < exportSpecifierNode.getExportIdentifier().getStart()) {
// it has to be "as"
highlights.put(LexUtilities.getLexerOffsets(result,
new OffsetRange(ts.offset(), ts.offset() + token.length())), SEMANTIC_KEYWORD);
}
}
}
return false;
}
@Override
public boolean enterNameSpaceImportNode(NameSpaceImportNode nameSpaceImportNode) {
int start = nameSpaceImportNode.getBindingIdentifier().getStart();
if (start <= 0) {
return false;
}
TokenSequence<? extends JsTokenId> ts = LexUtilities.getJsPositionedSequence(result.getSnapshot(), start - 1);
if (ts != null) {
Token<? extends JsTokenId> token = LexUtilities.findPreviousNonWsNonComment(ts);
if (token != null && token.id() == JsTokenId.IDENTIFIER && ts.token().length() > 1) {
// it has to be "as"
highlights.put(LexUtilities.getLexerOffsets(result,
new OffsetRange(ts.offset(), ts.offset() + token.length())), SEMANTIC_KEYWORD);
}
}
return false;
}
@Override
public boolean enterFromNode(FromNode fromNode) {
highlights.put(LexUtilities.getLexerOffsets(result,
new OffsetRange(fromNode.getStart(), fromNode.getStart() + "from".length())), SEMANTIC_KEYWORD); // NOI18N
return false;
}
@Override
public boolean enterClassNode(ClassNode classNode) {
for (PropertyNode p : classNode.getClassElements()) {
handleProperty(p, true);
}
return super.enterClassNode(classNode);
}
@Override
public boolean enterObjectNode(ObjectNode objectNode) {
for (PropertyNode p : objectNode.getElements()) {
handleProperty(p, false);
}
return super.enterObjectNode(objectNode);
}
@Override
public boolean enterVarNode(VarNode varNode) {
if (varNode.isLet()) {
TokenSequence<? extends JsTokenId> ts = LexUtilities.getJsPositionedSequence(result.getSnapshot(), varNode.getStart() - 1);
if (ts != null) {
Token<? extends JsTokenId> token = LexUtilities.findPreviousNonWsNonComment(ts);
if (token != null && token.id() == JsTokenId.RESERVED_LET) {
highlights.put(LexUtilities.getLexerOffsets(result,
new OffsetRange(ts.offset(), ts.offset() + token.length())), SEMANTIC_KEYWORD);
}
}
}
return super.enterVarNode(varNode);
}
@Override
public boolean enterUnaryNode(UnaryNode unaryNode) {
if (unaryNode.isTokenType(TokenType.AWAIT)) {
TokenSequence<? extends JsTokenId> ts = LexUtilities.getJsPositionedSequence(result.getSnapshot(), unaryNode.getStart());
if (ts != null) {
Token<? extends JsTokenId> token = LexUtilities.findPreviousNonWsNonComment(ts);
if (token != null && token.id() == JsTokenId.RESERVED_AWAIT) {
highlights.put(LexUtilities.getLexerOffsets(result,
new OffsetRange(ts.offset(), ts.offset() + token.length())), SEMANTIC_KEYWORD);
}
}
}
return super.enterUnaryNode(unaryNode);
}
private void handleProperty(PropertyNode p, boolean classElement) {
int offset = -1;
if ((p.getValue() instanceof FunctionNode) && ((FunctionNode) p.getValue()).isAsync()) {
TokenSequence<? extends JsTokenId> ts = LexUtilities.getJsPositionedSequence(result.getSnapshot(), p.getStart() - 1);
if (ts != null) {
Token<? extends JsTokenId> token = LexUtilities.findPreviousNonWsNonComment(ts);
if (token != null && token.id() == JsTokenId.IDENTIFIER && "async".equals(token.text().toString())) {
offset = ts.offset();
highlights.put(LexUtilities.getLexerOffsets(result,
new OffsetRange(ts.offset(), ts.offset() + token.length())), SEMANTIC_KEYWORD);
}
}
}
if (classElement && p.isStatic()) {
TokenSequence<? extends JsTokenId> ts = LexUtilities.getJsPositionedSequence(result.getSnapshot(), offset >= 0 ? offset - 1 : p.getStart() - 1);
if (ts != null) {
Token<? extends JsTokenId> token = LexUtilities.findPreviousNonWsNonComment(ts);
if (token != null && token.id() == JsTokenId.RESERVED_STATIC) {
highlights.put(LexUtilities.getLexerOffsets(result,
new OffsetRange(ts.offset(), ts.offset() + token.length())), SEMANTIC_KEYWORD);
}
}
}
}
};
root.accept(visitor);
return highlights;
}
private Map<OffsetRange, Set<ColoringAttributes>> processNumbers(JsParserResult result, Map<OffsetRange, Set<ColoringAttributes>> highlights) {
TokenSequence<? extends JsTokenId> ts = LexUtilities.getJsTokenSequence(result.getSnapshot(), 0);
if (ts != null) {
ts.move(0);
List<JsTokenId> lookFor = new ArrayList<JsTokenId>(3);
lookFor.add(JsTokenId.NUMBER);
Token<? extends JsTokenId> token;
while (ts.moveNext() && (token = LexUtilities.findNextToken(ts, lookFor)) != null) {
if (token.id() == JsTokenId.NUMBER) {
String number = token.text().toString().toLowerCase(Locale.ENGLISH);
if (number.startsWith("0b") || number.startsWith("0x") || number.startsWith("0o")) { //NOI18N
highlights.put(LexUtilities.getLexerOffsets(result, new OffsetRange(ts.offset() + 1, ts.offset() + 2)), NUMBER_OXB_CHAR);
}
}
}
}
return highlights;
}
private void addColoring(JsParserResult result, Map<OffsetRange, Set<ColoringAttributes>> highlights, OffsetRange astRange, Set<ColoringAttributes> coloring) {
int start = result.getSnapshot().getOriginalOffset(astRange.getStart());
int end = result.getSnapshot().getOriginalOffset(astRange.getEnd());
if (start > -1 && end > -1 && start < end && !isInComment(result, astRange)) {
OffsetRange range = start == astRange.getStart() ? astRange : new OffsetRange(start, end);
highlights.put(range, coloring);
}
}
@Override
public int getPriority() {
return 0;
}
@Override
public Class<? extends Scheduler> getSchedulerClass() {
return Scheduler.EDITOR_SENSITIVE_TASK_SCHEDULER;
}
@Override
public synchronized void cancel() {
cancelled = true;
}
protected final synchronized boolean isCancelled() {
return cancelled;
}
protected final synchronized void resume() {
cancelled = false;
}
private boolean hasSourceOccurences(JsParserResult result, JsObject param) {
if (param.getOccurrences().isEmpty()) {
return false;
}
if (param.getOccurrences().size() == 1 && param.getOccurrences().get(0).getOffsetRange().equals(param.getDeclarationName().getOffsetRange())) {
return false;
}
int sourceOccurenceCount = 0;
for (Occurrence occurrence : param.getOccurrences()) {
if (!isInComment(result, occurrence.getOffsetRange())) {
sourceOccurenceCount++;
}
if (sourceOccurenceCount > 1) {
return true;
}
}
return false;
}
private boolean isInComment(JsParserResult result, OffsetRange range) {
for (JsComment comment : JsDocumentationSupport.getDocumentationHolder(result).getCommentBlocks().values()) {
if (comment.getOffsetRange().containsInclusive(range.getStart())) {
return true;
}
}
if (globalJsHintInlines.contains(range)) {
return true;
}
return false;
}
}