/* * Copyright (c) 2012 Sam Harwell, Tunnel Vision Laboratories LLC * All rights reserved. * * The source code of this document is proprietary work, and is not licensed for * distribution. For information about licensing, contact Sam Harwell at: * sam@tunnelvisionlabs.com */ package org.tvl.goworks.editor.go.highlighter; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.text.AttributeSet; import javax.swing.text.StyledDocument; import org.antlr.netbeans.editor.text.DocumentSnapshot; import org.antlr.netbeans.editor.text.OffsetRegion; import org.antlr.netbeans.parsing.spi.ParserData; import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.misc.Tuple2; import org.antlr.v4.runtime.tree.ParseTree; import org.antlr.v4.runtime.tree.TerminalNode; import org.antlr.works.editor.antlr4.semantics.AbstractParseTreeSemanticHighlighter; import org.antlr.works.editor.antlr4.semantics.AbstractSemanticHighlighter; import org.netbeans.api.annotations.common.NonNull; import org.netbeans.api.editor.mimelookup.MimeLookup; import org.netbeans.api.editor.mimelookup.MimePath; import org.netbeans.api.editor.mimelookup.MimeRegistration; import org.netbeans.api.editor.settings.FontColorSettings; import org.netbeans.spi.editor.highlighting.HighlightsLayerFactory; import org.netbeans.spi.editor.highlighting.support.OffsetsBag; import org.openide.util.Lookup; import org.openide.util.Parameters; import org.tvl.goworks.editor.GoEditorKit; import org.tvl.goworks.editor.go.GoParserDataDefinitions; import org.tvl.goworks.editor.go.codemodel.FileModel; import org.tvl.goworks.editor.go.codemodel.TypeKind; import org.tvl.goworks.editor.go.codemodel.VarKind; import org.tvl.goworks.editor.go.parser.GoParser; import org.tvl.goworks.editor.go.parser.generated.GoParserBaseListener; import org.tvl.goworks.editor.go.semantics.GoAnnotatedParseTree; import org.tvl.goworks.editor.go.semantics.NodeType; /** * * @author Sam Harwell */ public class SemanticHighlighter extends AbstractParseTreeSemanticHighlighter<SemanticHighlighter.SemanticAnalyzerListener, GoAnnotatedParseTree> { // -J-Dorg.tvl.goworks.editor.go.highlighter.SemanticHighlighter.level=FINE private static final Logger LOGGER = Logger.getLogger(SemanticHighlighter.class.getName()); public static final Set<String> PREDEFINED_TYPES = new HashSet<String>() {{ add("bool"); add("byte"); add("complex64"); add("complex128"); add("error"); add("float32"); add("float64"); add("int"); add("int8"); add("int16"); add("int32"); add("int64"); add("rune"); add("string"); add("uint"); add("uint8"); add("uint16"); add("uint32"); add("uint64"); add("uintptr"); }}; public static final Set<String> PREDEFINED_CONSTANTS = new HashSet<String>() {{ add("true"); add("false"); add("iota"); add("nil"); }}; public static final Set<String> PREDEFINED_FUNCTIONS = new HashSet<String>() {{ add("append"); add("cap"); add("close"); add("complex"); add("copy"); add("delete"); add("imag"); add("len"); add("make"); add("new"); add("panic"); add("print"); add("println"); add("real"); add("recover"); }}; private final AttributeSet packageDeclarationAttributes; private final AttributeSet packageUseAttributes; private final AttributeSet globalConstDeclarationAttributes; private final AttributeSet globalConstUseAttributes; private final AttributeSet localConstDeclarationAttributes; private final AttributeSet localConstUseAttributes; private final AttributeSet funcDeclarationAttributes; private final AttributeSet funcUseAttributes; private final AttributeSet methodDeclarationAttributes; private final AttributeSet methodUseAttributes; private final AttributeSet typeDeclarationAttributes; private final AttributeSet typeUseAttributes; private final AttributeSet structDeclarationAttributes; private final AttributeSet structUseAttributes; private final AttributeSet interfaceDeclarationAttributes; private final AttributeSet interfaceUseAttributes; private final AttributeSet builtinTypeUseAttributes; private final AttributeSet builtinConstUseAttributes; private final AttributeSet builtinFunctionUseAttributes; private final AttributeSet parameterDeclarationAttributes; private final AttributeSet parameterUseAttributes; private final AttributeSet returnParameterDeclarationAttributes; private final AttributeSet returnParameterUseAttributes; private final AttributeSet varUseAttributes; private final AttributeSet globalVarDeclarationAttributes; private final AttributeSet globalVarUseAttributes; private final AttributeSet fieldVarDeclarationAttributes; private final AttributeSet fieldVarUseAttributes; private final AttributeSet localVarDeclarationAttributes; private final AttributeSet localVarUseAttributes; private final AttributeSet labelDeclarationAttributes; private final AttributeSet labelUseAttributes; private final AttributeSet unresolvedIdentifierAttributes; private SemanticHighlighter(@NonNull StyledDocument document) { super(document, GoParserDataDefinitions.ANNOTATED_PARSE_TREE); Lookup lookup = MimeLookup.getLookup(MimePath.parse(GoEditorKit.GO_MIME_TYPE)); FontColorSettings settings = lookup.lookup(FontColorSettings.class); this.packageDeclarationAttributes = getFontAndColors(settings, "packageDeclaration"); this.packageUseAttributes = getFontAndColors(settings, "packageUse"); this.globalConstDeclarationAttributes = getFontAndColors(settings, "globalConstDeclaration"); this.globalConstUseAttributes = getFontAndColors(settings, "globalConstUse"); this.localConstDeclarationAttributes = getFontAndColors(settings, "localConstDeclaration"); this.localConstUseAttributes = getFontAndColors(settings, "localConstUse"); this.funcDeclarationAttributes = getFontAndColors(settings, "funcDeclaration"); this.funcUseAttributes = getFontAndColors(settings, "funcUse"); this.methodDeclarationAttributes = getFontAndColors(settings, "methodDeclaration"); this.methodUseAttributes = getFontAndColors(settings, "methodUse"); this.typeDeclarationAttributes = getFontAndColors(settings, "typeDeclaration"); this.typeUseAttributes = getFontAndColors(settings, "typeUse"); this.structDeclarationAttributes = getFontAndColors(settings, "structDeclaration"); this.structUseAttributes = getFontAndColors(settings, "structUse"); this.interfaceDeclarationAttributes = getFontAndColors(settings, "interfaceDeclaration"); this.interfaceUseAttributes = getFontAndColors(settings, "interfaceUse"); this.builtinTypeUseAttributes = getFontAndColors(settings, "builtinTypeUse"); this.builtinConstUseAttributes = getFontAndColors(settings, "builtinConstUse"); this.builtinFunctionUseAttributes = getFontAndColors(settings, "builtinFunctionUse"); this.parameterDeclarationAttributes = getFontAndColors(settings, "parameterDeclaration"); this.parameterUseAttributes = getFontAndColors(settings, "parameterUse"); this.returnParameterDeclarationAttributes = getFontAndColors(settings, "returnParameterDeclaration"); this.returnParameterUseAttributes = getFontAndColors(settings, "returnParameterUse"); this.varUseAttributes = getFontAndColors(settings, "varUse"); this.globalVarDeclarationAttributes = getFontAndColors(settings, "globalVarDeclaration"); this.globalVarUseAttributes = getFontAndColors(settings, "globalVarUse"); this.fieldVarDeclarationAttributes = getFontAndColors(settings, "fieldVarDeclaration"); this.fieldVarUseAttributes = getFontAndColors(settings, "fieldVarUse"); this.localVarDeclarationAttributes = getFontAndColors(settings, "localVarDeclaration"); this.localVarUseAttributes = getFontAndColors(settings, "localVarUse"); this.labelDeclarationAttributes = getFontAndColors(settings, "labelDeclaration"); this.labelUseAttributes = getFontAndColors(settings, "labelUse"); this.unresolvedIdentifierAttributes = getFontAndColors(settings, "unresolvedIdentifier"); } @Override protected SemanticAnalyzerListener createListener(ParserData<? extends GoAnnotatedParseTree> parserData) { FileModel fileModel = null; GoAnnotatedParseTree annotatedParseTree = null; try { Future<ParserData<FileModel>> futureFileModelData = getTaskManager().getData(parserData.getSnapshot(), GoParserDataDefinitions.FILE_MODEL); ParserData<FileModel> fileModelData = futureFileModelData != null ? futureFileModelData.get() : null; fileModel = fileModelData != null ? fileModelData.getData() : null; annotatedParseTree = parserData != null ? parserData.getData() : null; } catch (InterruptedException | ExecutionException ex) { LOGGER.log(Level.WARNING, "An exception occurred while getting the file model.", ex); } if (fileModel == null || annotatedParseTree == null) { return null; } return new SemanticAnalyzerListener(fileModel, annotatedParseTree); } @Override protected ParseTree getParseTree(ParserData<? extends GoAnnotatedParseTree> parserData) { GoAnnotatedParseTree annotatedParseTree = parserData != null ? parserData.getData() : null; return annotatedParseTree != null ? annotatedParseTree.getParseTree() : null; } private final Set<Token> resolvedTokens = new HashSet<>(); @Override protected void addHighlights(List<Tuple2<OffsetRegion, AttributeSet>> intermediateContainer, DocumentSnapshot sourceSnapshot, DocumentSnapshot currentSnapshot, Collection<Token> tokens, AttributeSet attributes) { if (LOGGER.isLoggable(Level.FINE)) { resolvedTokens.addAll(tokens); } super.addHighlights(intermediateContainer, sourceSnapshot, currentSnapshot, tokens, attributes); } @Override protected void updateHighlights(OffsetsBag targetContainer, DocumentSnapshot sourceSnapshot, DocumentSnapshot currentSnapshot, SemanticAnalyzerListener listener) { List<Tuple2<OffsetRegion, AttributeSet>> intermediateContainer = new ArrayList<>(); resolvedTokens.clear(); addHighlights(intermediateContainer, sourceSnapshot, currentSnapshot, listener.getPackageDeclarations(), packageDeclarationAttributes); addHighlights(intermediateContainer, sourceSnapshot, currentSnapshot, listener.getPackageUses(), packageUseAttributes); addHighlights(intermediateContainer, sourceSnapshot, currentSnapshot, listener.getGlobalConstDeclarations(), globalConstDeclarationAttributes); addHighlights(intermediateContainer, sourceSnapshot, currentSnapshot, listener.getGlobalConstUses(), globalConstUseAttributes); addHighlights(intermediateContainer, sourceSnapshot, currentSnapshot, listener.getLocalConstDeclarations(), localConstDeclarationAttributes); addHighlights(intermediateContainer, sourceSnapshot, currentSnapshot, listener.getLocalConstUses(), localConstUseAttributes); addHighlights(intermediateContainer, sourceSnapshot, currentSnapshot, listener.getFuncDeclarations(), funcDeclarationAttributes); addHighlights(intermediateContainer, sourceSnapshot, currentSnapshot, listener.getFuncUses(), funcUseAttributes); addHighlights(intermediateContainer, sourceSnapshot, currentSnapshot, listener.getMethodDeclarations(), methodDeclarationAttributes); addHighlights(intermediateContainer, sourceSnapshot, currentSnapshot, listener.getMethodUses(), methodUseAttributes); addHighlights(intermediateContainer, sourceSnapshot, currentSnapshot, listener.getTypeDeclarations(), typeDeclarationAttributes); addHighlights(intermediateContainer, sourceSnapshot, currentSnapshot, listener.getTypeUses(), typeUseAttributes); addHighlights(intermediateContainer, sourceSnapshot, currentSnapshot, listener.getStructDeclarations(), structDeclarationAttributes); addHighlights(intermediateContainer, sourceSnapshot, currentSnapshot, listener.getStructUses(), structUseAttributes); addHighlights(intermediateContainer, sourceSnapshot, currentSnapshot, listener.getInterfaceDeclarations(), interfaceDeclarationAttributes); addHighlights(intermediateContainer, sourceSnapshot, currentSnapshot, listener.getInterfaceUses(), interfaceUseAttributes); addHighlights(intermediateContainer, sourceSnapshot, currentSnapshot, listener.getBuiltinTypeUses(), builtinTypeUseAttributes); addHighlights(intermediateContainer, sourceSnapshot, currentSnapshot, listener.getBuiltinConstUses(), builtinConstUseAttributes); addHighlights(intermediateContainer, sourceSnapshot, currentSnapshot, listener.getBuiltinFunctionUses(), builtinFunctionUseAttributes); addHighlights(intermediateContainer, sourceSnapshot, currentSnapshot, listener.getParameterDeclarations(), parameterDeclarationAttributes); addHighlights(intermediateContainer, sourceSnapshot, currentSnapshot, listener.getParameterUses(), parameterUseAttributes); addHighlights(intermediateContainer, sourceSnapshot, currentSnapshot, listener.getReturnParameterDeclarations(), returnParameterDeclarationAttributes); addHighlights(intermediateContainer, sourceSnapshot, currentSnapshot, listener.getReturnParameterUses(), returnParameterUseAttributes); addHighlights(intermediateContainer, sourceSnapshot, currentSnapshot, listener.getVarUses(), varUseAttributes); addHighlights(intermediateContainer, sourceSnapshot, currentSnapshot, listener.getGlobalVarDeclarations(), globalVarDeclarationAttributes); addHighlights(intermediateContainer, sourceSnapshot, currentSnapshot, listener.getGlobalVarUses(), globalVarUseAttributes); addHighlights(intermediateContainer, sourceSnapshot, currentSnapshot, listener.getFieldVarDeclarations(), fieldVarDeclarationAttributes); addHighlights(intermediateContainer, sourceSnapshot, currentSnapshot, listener.getFieldVarUses(), fieldVarUseAttributes); addHighlights(intermediateContainer, sourceSnapshot, currentSnapshot, listener.getLocalVarDeclarations(), localVarDeclarationAttributes); addHighlights(intermediateContainer, sourceSnapshot, currentSnapshot, listener.getLocalVarUses(), localVarUseAttributes); addHighlights(intermediateContainer, sourceSnapshot, currentSnapshot, listener.getLabelDeclarations(), labelDeclarationAttributes); addHighlights(intermediateContainer, sourceSnapshot, currentSnapshot, listener.getLabelUses(), labelUseAttributes); if (LOGGER.isLoggable(Level.FINE)) { Collection<Token> unresolved = listener.getUnresolvedIdentifiers(); unresolved.removeAll(resolvedTokens); addHighlights(intermediateContainer, sourceSnapshot, currentSnapshot, unresolved, unresolvedIdentifierAttributes); } OffsetsBag container = new OffsetsBag(currentSnapshot.getVersionedDocument().getDocument()); fillHighlights(container, intermediateContainer); targetContainer.setHighlights(container); } @MimeRegistration(mimeType=GoEditorKit.GO_MIME_TYPE, service=HighlightsLayerFactory.class) public static class LayerFactory extends AbstractLayerFactory { public LayerFactory() { super(SemanticHighlighter.class); } @Override protected AbstractSemanticHighlighter<?> createHighlighter(Context context) { return new SemanticHighlighter((StyledDocument)context.getDocument()); } } public static class SemanticAnalyzerListener extends GoParserBaseListener { private final FileModel fileModel; private final GoAnnotatedParseTree annotatedTree; private final List<Token> packageDeclarations = new ArrayList<>(); private final List<Token> packageUses = new ArrayList<>(); private final List<Token> globalConstDeclarations = new ArrayList<>(); private final List<Token> globalConstUses = new ArrayList<>(); private final List<Token> localConstDeclarations = new ArrayList<>(); private final List<Token> localConstUses = new ArrayList<>(); private final List<Token> funcDeclarations = new ArrayList<>(); private final List<Token> funcUses = new ArrayList<>(); private final List<Token> methodDeclarations = new ArrayList<>(); private final List<Token> methodUses = new ArrayList<>(); private final List<Token> typeDeclarations = new ArrayList<>(); private final List<Token> typeUses = new ArrayList<>(); private final List<Token> structDeclarations = new ArrayList<>(); private final List<Token> structUses = new ArrayList<>(); private final List<Token> interfaceDeclarations = new ArrayList<>(); private final List<Token> interfaceUses = new ArrayList<>(); private final List<Token> builtinTypeUses = new ArrayList<>(); private final List<Token> builtinConstUses = new ArrayList<>(); private final List<Token> builtinFunctionUses = new ArrayList<>(); private final List<Token> parameterDeclarations = new ArrayList<>(); private final List<Token> parameterUses = new ArrayList<>(); private final List<Token> returnParameterDeclarations = new ArrayList<>(); private final List<Token> returnParameterUses = new ArrayList<>(); private final List<Token> varUses = new ArrayList<>(); private final List<Token> globalVarDeclarations = new ArrayList<>(); private final List<Token> globalVarUses = new ArrayList<>(); private final List<Token> fieldVarDeclarations = new ArrayList<>(); private final List<Token> fieldVarUses = new ArrayList<>(); private final List<Token> localVarDeclarations = new ArrayList<>(); private final List<Token> localVarUses = new ArrayList<>(); private final List<Token> labelDeclarations = new ArrayList<>(); private final List<Token> labelUses = new ArrayList<>(); private final Set<Token> unresolvedIdentifiers = new HashSet<>(); public SemanticAnalyzerListener(@NonNull FileModel fileModel, @NonNull GoAnnotatedParseTree annotatedTree) { Parameters.notNull("fileModel", fileModel); Parameters.notNull("annotatedTree", annotatedTree); this.fileModel = fileModel; this.annotatedTree = annotatedTree; } public List<Token> getPackageDeclarations() { return packageDeclarations; } public List<Token> getPackageUses() { return packageUses; } public List<Token> getGlobalConstDeclarations() { return globalConstDeclarations; } public List<Token> getGlobalConstUses() { return globalConstUses; } public List<Token> getLocalConstDeclarations() { return localConstDeclarations; } public List<Token> getLocalConstUses() { return localConstUses; } public List<Token> getFuncDeclarations() { return funcDeclarations; } public List<Token> getFuncUses() { return funcUses; } public List<Token> getMethodDeclarations() { return methodDeclarations; } public List<Token> getMethodUses() { return methodUses; } public List<Token> getTypeDeclarations() { return typeDeclarations; } public List<Token> getTypeUses() { return typeUses; } public List<Token> getStructDeclarations() { return structDeclarations; } public List<Token> getStructUses() { return structUses; } public List<Token> getInterfaceDeclarations() { return interfaceDeclarations; } public List<Token> getInterfaceUses() { return interfaceUses; } public List<Token> getBuiltinTypeUses() { return builtinTypeUses; } public List<Token> getBuiltinConstUses() { return builtinConstUses; } public List<Token> getBuiltinFunctionUses() { return builtinFunctionUses; } public List<Token> getParameterDeclarations() { return parameterDeclarations; } public List<Token> getParameterUses() { return parameterUses; } public List<Token> getReturnParameterDeclarations() { return returnParameterDeclarations; } public List<Token> getReturnParameterUses() { return returnParameterUses; } public List<Token> getVarUses() { return varUses; } public List<Token> getGlobalVarDeclarations() { return globalVarDeclarations; } public List<Token> getGlobalVarUses() { return globalVarUses; } public List<Token> getFieldVarDeclarations() { return fieldVarDeclarations; } public List<Token> getFieldVarUses() { return fieldVarUses; } public List<Token> getLocalVarDeclarations() { return localVarDeclarations; } public List<Token> getLocalVarUses() { return localVarUses; } public List<Token> getLabelDeclarations() { return labelDeclarations; } public List<Token> getLabelUses() { return labelUses; } public Collection<Token> getUnresolvedIdentifiers() { return unresolvedIdentifiers; } @Override public void visitTerminal(TerminalNode node) { Token symbol = node.getSymbol(); if (symbol.getType() != GoParser.IDENTIFIER) { return; } boolean isDeclaration = annotatedTree.isDeclaration(node); boolean resolved = annotatedTree.isResolved(node); if (!resolved) { unresolvedIdentifiers.add(symbol); return; } NodeType nodeType = annotatedTree.getNodeType(node); switch (nodeType) { case PACKAGE_DECL: packageDeclarations.add(symbol); break; case PACKAGE_REF: packageUses.add(symbol); break; case CONST_DECL: if (annotatedTree.isGlobal(node)) { this.globalConstDeclarations.add(symbol); } else { this.localConstDeclarations.add(symbol); } break; case CONST_REF: if (annotatedTree.isGlobal(node)) { this.globalConstUses.add(symbol); } else { this.localConstUses.add(symbol); } break; case VAR_DECL: case VAR_REF: VarKind varType = annotatedTree.getVarType(node); Collection<Token> varCollection; switch (varType) { case GLOBAL: varCollection = isDeclaration ? globalVarDeclarations : globalVarUses; break; case FIELD: varCollection = isDeclaration ? fieldVarDeclarations : fieldVarUses; break; case LOCAL: varCollection = isDeclaration ? localVarDeclarations : localVarUses; break; case RECEIVER: case PARAMETER: varCollection = isDeclaration ? parameterDeclarations : parameterUses; break; case RETURN: varCollection = isDeclaration ? returnParameterDeclarations : returnParameterUses; break; default: varCollection = null; break; } if (varCollection != null) { varCollection.add(symbol); } break; case FUNC_DECL: this.funcDeclarations.add(symbol); break; case FUNC_REF: if (annotatedTree.isBuiltin(node)) { this.builtinFunctionUses.add(symbol); } else { this.funcUses.add(symbol); } break; case METHOD_DECL: this.methodDeclarations.add(symbol); break; case METHOD_REF: this.methodUses.add(symbol); break; case TYPE_DECL: case TYPE_REF: TypeKind kind = annotatedTree.getTypeKind(node); isDeclaration = annotatedTree.isDeclaration(node); Collection<Token> typeCollection; switch (kind) { case INTERFACE: typeCollection = isDeclaration ? interfaceDeclarations : interfaceUses; break; case STRUCT: typeCollection = isDeclaration ? structDeclarations : structUses; break; case INTRINSIC: typeCollection = builtinTypeUses; break; case ALIAS: case ARRAY: case CHANNEL: case FUNCTION: case MAP: case POINTER: case SLICE: case UNKNOWN: case UNDEFINED: default: typeCollection = isDeclaration ? typeDeclarations : typeUses; break; } if (typeCollection != null) { typeCollection.add(symbol); } break; case LABEL_DECL: this.labelDeclarations.add(symbol); break; case LABEL_REF: this.labelUses.add(symbol); break; case UNKNOWN: case UNDEFINED: default: if (symbol.getType() == GoParser.IDENTIFIER) { unresolvedIdentifiers.add(symbol); } break; } } } }