Added reference contributor for variabels.

Various improvements.
This commit is contained in:
Rik 2014-07-23 11:43:37 +02:00
parent 1e73e47e1f
commit b580287e2b
27 changed files with 575 additions and 391 deletions

View File

@ -1,3 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4" />
<project version="4">
<component name="ScalaProjectSettings">
<option name="dontShowConversionDialog" value="true" />
</component>
</project>

View File

@ -47,7 +47,12 @@
<langCodeStyleSettingsProvider implementation="com.powertuple.intellij.haskell.formatter.settings.HaskellLanguageCodeStyleSettingsProvider"/>
<psi.referenceContributor implementation="com.powertuple.intellij.haskell.HaskellVaridReferenceContributor"/>
<psi.referenceContributor implementation="com.powertuple.intellij.haskell.HaskellReferenceContributor"/>
<lang.elementManipulator forClass="com.powertuple.intellij.haskell.psi.impl.HaskellVarImpl"
implementationClass="com.powertuple.intellij.haskell.psi.HaskellStringLiteralManipulator"/>
<fileBasedIndex implementation="com.powertuple.intellij.haskell.util.HaskellFileIndex"/>
</extensions>
<application-components>

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.powertuple.intellij.haskell.psi;
package com.powertuple.intellij.haskell;
import com.intellij.extapi.psi.PsiFileBase;
import com.intellij.openapi.fileTypes.FileType;

View File

@ -25,6 +25,8 @@ import javax.swing.*;
public class HaskellFileType extends LanguageFileType {
public static final HaskellFileType INSTANCE = new HaskellFileType();
public static HaskellFileType MODULE = new HaskellFileType();
private HaskellFileType() {
super(com.powertuple.intellij.haskell.HaskellLanguage.INSTANCE);
}

View File

@ -26,4 +26,8 @@ object HaskellNotificationGroup {
def notifyError(message: String) {
Group.createNotification(message, MessageType.ERROR).notify(null)
}
def notifyInfo(message: String) {
Group.createNotification(message, MessageType.INFO).notify(null)
}
}

View File

@ -29,7 +29,6 @@ import com.intellij.psi.TokenType;
import com.intellij.psi.tree.IFileElementType;
import com.intellij.psi.tree.TokenSet;
import com.powertuple.intellij.haskell.parser.HaskellParser;
import com.powertuple.intellij.haskell.psi.HaskellFile;
import com.powertuple.intellij.haskell.psi.HaskellTypes;
import org.jetbrains.annotations.NotNull;

View File

@ -0,0 +1,42 @@
/*
* Copyright 2014 Rik van der Kleij
* Licensed 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 com.powertuple.intellij.haskell;
import com.intellij.openapi.util.TextRange;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.psi.*;
import com.intellij.util.ProcessingContext;
import com.powertuple.intellij.haskell.psi.HaskellVar;
import com.powertuple.intellij.haskell.psi.HaskellVarReference;
import org.jetbrains.annotations.NotNull;
public class HaskellReferenceContributor extends PsiReferenceContributor {
@Override
public void registerReferenceProviders(PsiReferenceRegistrar registrar) {
registrar.registerReferenceProvider(PlatformPatterns.psiElement(HaskellVar.class),
new PsiReferenceProvider() {
@NotNull
@Override
public PsiReference[] getReferencesByElement(@NotNull PsiElement element, @NotNull ProcessingContext context) {
if (!(element instanceof HaskellVar)) {
return PsiReference.EMPTY_ARRAY;
}
return new PsiReference[]{new HaskellVarReference(element, TextRange.from(0, element.getTextLength()))};
}
});
}
}

View File

@ -1,57 +0,0 @@
package com.powertuple.intellij.haskell;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.search.FileTypeIndex;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.indexing.FileBasedIndex;
import com.powertuple.intellij.haskell.psi.HaskellFile;
import com.powertuple.intellij.haskell.psi.HaskellVarid;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
public class HaskellUtil {
public static List<HaskellVarid> findProperties(Project project, String key) {
List<HaskellVarid> result = null;
Collection<VirtualFile> virtualFiles = FileBasedIndex.getInstance().getContainingFiles(FileTypeIndex.NAME, HaskellFileType.INSTANCE,
GlobalSearchScope.allScope(project));
for (VirtualFile virtualFile : virtualFiles) {
HaskellFile simpleFile = (HaskellFile) PsiManager.getInstance(project).findFile(virtualFile);
if (simpleFile != null) {
HaskellVarid[] properties = PsiTreeUtil.getChildrenOfType(simpleFile, HaskellVarid.class);
if (properties != null) {
for (HaskellVarid property : properties) {
if (key.equals(property.getName())) {
if (result == null) {
result = new ArrayList<HaskellVarid>();
}
result.add(property);
}
}
}
}
}
return result != null ? result : Collections.<HaskellVarid>emptyList();
}
public static List<HaskellVarid> findProperties(Project project) {
List<HaskellVarid> result = new ArrayList<HaskellVarid>();
Collection<VirtualFile> virtualFiles = FileBasedIndex.getInstance().getContainingFiles(FileTypeIndex.NAME, HaskellFileType.INSTANCE,
GlobalSearchScope.allScope(project));
for (VirtualFile virtualFile : virtualFiles) {
HaskellFile simpleFile = (HaskellFile) PsiManager.getInstance(project).findFile(virtualFile);
if (simpleFile != null) {
HaskellVarid[] properties = PsiTreeUtil.getChildrenOfType(simpleFile, HaskellVarid.class);
if (properties != null) {
Collections.addAll(result, properties);
}
}
}
return result;
}
}

View File

@ -1,58 +0,0 @@
package com.powertuple.intellij.haskell;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.powertuple.intellij.haskell.psi.HaskellVarid;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
public class HaskellVaridReference extends PsiReferenceBase<PsiElement> implements PsiPolyVariantReference {
private String key;
public HaskellVaridReference(@NotNull PsiElement element, TextRange textRange) {
super(element, textRange);
key = element.getText().substring(textRange.getStartOffset(), textRange.getEndOffset());
}
@NotNull
@Override
public ResolveResult[] multiResolve(boolean incompleteCode) {
Project project = myElement.getProject();
final List<HaskellVarid> properties = HaskellUtil.findProperties(project, key);
List<ResolveResult> results = new ArrayList<ResolveResult>();
for (HaskellVarid property : properties) {
results.add(new PsiElementResolveResult(property));
}
return results.toArray(new ResolveResult[results.size()]);
}
@Nullable
@Override
public PsiElement resolve() {
ResolveResult[] resolveResults = multiResolve(false);
return resolveResults.length == 1 ? resolveResults[0].getElement() : null;
}
@NotNull
@Override
public Object[] getVariants() {
Project project = myElement.getProject();
List<HaskellVarid> properties = HaskellUtil.findProperties(project);
List<LookupElement> variants = new ArrayList<LookupElement>();
for (final HaskellVarid property : properties) {
if (property.getName() != null && property.getName().length() > 0) {
variants.add(LookupElementBuilder.create(property).
withIcon(HaskellIcons.HASKELL_SMALL_LOGO).
withTypeText(property.getContainingFile().getName())
);
}
}
return variants.toArray();
}
}

View File

@ -1,29 +0,0 @@
package com.powertuple.intellij.haskell;
import com.intellij.openapi.util.TextRange;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.psi.*;
import com.intellij.util.ProcessingContext;
import com.powertuple.intellij.haskell.psi.HaskellVarid;
import org.jetbrains.annotations.NotNull;
public class HaskellVaridReferenceContributor extends PsiReferenceContributor {
@Override
public void registerReferenceProviders(PsiReferenceRegistrar registrar) {
registrar.registerReferenceProvider(PlatformPatterns.psiElement(HaskellVarid.class),
new PsiReferenceProvider() {
@NotNull
@Override
public PsiReference[] getReferencesByElement(@NotNull PsiElement element, @NotNull ProcessingContext context) {
throw new RuntimeException("dada" );
// PsiLiteralExpression literalExpression = (PsiLiteralExpression) element;
// String text = (String) literalExpression.getValue();
// if (text != null) {
// return new PsiReference[]{new HaskellVaridReference(element, new TextRange(8, text.length() + 1))};
// }
// return new PsiReference[0];
}
});
}
}

View File

@ -16,7 +16,6 @@
package com.powertuple.intellij.haskell.actions
import java.awt.Point
import java.awt.event.{MouseEvent, MouseMotionAdapter}
import com.intellij.codeInsight.hint.{HintManager, HintManagerImpl, HintUtil}
@ -24,7 +23,7 @@ import com.intellij.openapi.actionSystem.{AnActionEvent, CommonDataKeys}
import com.intellij.openapi.editor.Editor
import com.intellij.ui.LightweightHint
import com.intellij.util.ui.UIUtil
import com.powertuple.intellij.haskell.psi.HaskellFile
import com.powertuple.intellij.haskell.HaskellFile
object HaskellActionUtil {

View File

@ -21,9 +21,8 @@ import com.intellij.openapi.editor.Editor
import com.intellij.psi.util.PsiUtilBase
import com.intellij.psi.{PsiElement, PsiFile}
import com.powertuple.intellij.haskell.external.GhciModManager
import com.powertuple.intellij.haskell.psi.HaskellFile
import com.powertuple.intellij.haskell.util.LineColumnPosition
import com.powertuple.intellij.haskell.{HaskellLanguage, HaskellNotificationGroup}
import com.powertuple.intellij.haskell.util.{FileUtil, LineColumnPosition}
import com.powertuple.intellij.haskell.{HaskellFile, HaskellLanguage, HaskellNotificationGroup}
import scala.util.{Failure, Success, Try}
@ -41,16 +40,20 @@ class ShowTypeAction extends AnAction {
val psiFile = PsiUtilBase.getPsiFileInEditor(editor, CommonDataKeys.PROJECT.getData(context))
if (psiFile.getLanguage != HaskellLanguage.INSTANCE) return
FileUtil.saveFile(psiFile)
val startExpression = findStartOfExpression(editor, psiFile)
val startPositionExpression = findStartOfExpression(editor, psiFile) match {
case Some(lcp) => lcp
case None => HaskellNotificationGroup.notifyError("Could not find start position of expression"); return
}
val ghcModi = GhciModManager.getGhcMod(psiFile.getProject)
val vFile = psiFile.getVirtualFile
val cmd = s"type ${vFile.getPath} ${startExpression.lineNr} ${startExpression.colunmNr}"
val cmd = s"type ${vFile.getPath} ${startPositionExpression.lineNr} ${startPositionExpression.colunmNr}"
val ghcModiOutput = ghcModi.execute(cmd)
val typeInfo = ghcModiOutputToTypeInfo(ghcModiOutput.outputLines) match {
case Success(typeInfos) => typeInfos.find(ty => ty.startLine == startExpression.lineNr && ty.startColumn == startExpression.colunmNr)
case Success(typeInfos) => typeInfos.find(ty => ty.startLine == startPositionExpression.lineNr && ty.startColumn == startPositionExpression.colunmNr)
case Failure(error) => HaskellNotificationGroup.notifyError(s"Could not determine type with $cmd. Error: $error.getMessage"); None
}
@ -60,7 +63,7 @@ class ShowTypeAction extends AnAction {
}
}
private def findStartOfExpression(editor: Editor, psiFile: PsiFile): LineColumnPosition = {
private def findStartOfExpression(editor: Editor, psiFile: PsiFile): Option[LineColumnPosition] = {
val offset = editor.getCaretModel.getOffset
val element = psiFile.findElementAt(offset)

View File

@ -19,12 +19,12 @@ package com.powertuple.intellij.haskell.annotator
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer
import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl
import com.intellij.lang.annotation.{AnnotationHolder, ExternalAnnotator}
import com.intellij.openapi.application.{ApplicationManager, ModalityState}
import com.intellij.openapi.fileEditor.FileDocumentManager
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiFile
import com.powertuple.intellij.haskell.HaskellFileType
import com.powertuple.intellij.haskell.external.GhciModManager
import com.powertuple.intellij.haskell.util.FileUtil
class GhcModiExternalAnnotator extends ExternalAnnotator[GhcModInitialInfo, GhcModiResult] {
@ -32,6 +32,8 @@ class GhcModiExternalAnnotator extends ExternalAnnotator[GhcModInitialInfo, GhcM
* Returning null will cause doAnnotate() not to be called by Intellij API.
*/
override def collectInformation(psiFile: PsiFile): GhcModInitialInfo = {
FileUtil.saveFile(psiFile)
val vFile = psiFile.getVirtualFile
vFile match {
case null => null // can be case if file is in memory only (just created file)
@ -42,12 +44,6 @@ class GhcModiExternalAnnotator extends ExternalAnnotator[GhcModInitialInfo, GhcM
}
override def doAnnotate(initialInfoGhcMod: GhcModInitialInfo): GhcModiResult = {
ApplicationManager.getApplication.invokeAndWait(new Runnable() {
override def run() {
FileDocumentManager.getInstance.saveDocument(FileDocumentManager.getInstance().getDocument(initialInfoGhcMod.psiFile.getVirtualFile))
}
}, ModalityState.any())
val ghcModi = GhciModManager.getGhcMod(initialInfoGhcMod.psiFile.getProject)
val ghcModiOutput = ghcModi.execute("check " + initialInfoGhcMod.filePath)
@ -73,7 +69,7 @@ class GhcModiExternalAnnotator extends ExternalAnnotator[GhcModInitialInfo, GhcM
private def markFileDirty(psiFile: PsiFile) {
val fileStatusMap = DaemonCodeAnalyzer.getInstance(psiFile.getProject).asInstanceOf[DaemonCodeAnalyzerImpl].getFileStatusMap
val document = FileDocumentManager.getInstance().getDocument(psiFile.getVirtualFile)
fileStatusMap.markFileScopeDirty(document, new TextRange(0, document.getTextLength), psiFile.getTextLength)
fileStatusMap.markFileScopeDirty(document, new TextRange(0, document.getTextLength), document.getTextLength)
}
private def startOffSetForProblem(lengthPerLine: Iterator[Int], problem: GhcModiProblem): Int = {
@ -84,7 +80,7 @@ class GhcModiExternalAnnotator extends ExternalAnnotator[GhcModInitialInfo, GhcM
val lengthPerLine = text.lines.map(_.size)
for (problem <- ghcModResult.problems) yield {
val startOffSet = startOffSetForProblem(lengthPerLine, problem)
val textRange = TextRange.create(startOffSet - 1, startOffSet)
val textRange = TextRange.create(startOffSet, startOffSet + 1)
if (problem.description.startsWith("Warning:")) {
WarningAnnotation(textRange, problem.description)
} else {

View File

@ -18,6 +18,9 @@ package com.powertuple.intellij.haskell.external
import com.intellij.openapi.project.Project
/**
* TODO: Create separate ghc-modi instances for libraries.
*/
object GhciModManager {
private var reinit = false

View File

@ -24,9 +24,7 @@ import com.intellij.psi.PsiFile;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
import com.powertuple.intellij.haskell.HaskellLanguage;
import com.powertuple.intellij.haskell.HaskellParserDefinition;
import com.powertuple.intellij.haskell.formatter.settings.HaskellCodeStyleSettings;
import com.powertuple.intellij.haskell.psi.HaskellElementType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

View File

@ -3,7 +3,8 @@
parserClass="com.powertuple.intellij.haskell.parser.HaskellParser"
parserUtilClass="com.powertuple.intellij.haskell.HaskellParserUtil"
extends="com.intellij.extapi.psi.ASTWrapperPsiElement"
implements="com.powertuple.intellij.haskell.psi.HaskellCompositeElement"
extends="com.powertuple.intellij.haskell.psi.impl.HaskellCompositeElementImpl"
psiClassPrefix="Haskell"
psiImplClassSuffix="Impl"
@ -11,7 +12,7 @@
psiImplPackage="com.powertuple.intellij.haskell.psi.impl"
elementTypeHolderClass="com.powertuple.intellij.haskell.psi.HaskellTypes"
elementTypeClass="com.powertuple.intellij.haskell.psi.HaskellElementType"
elementTypeClass="com.powertuple.intellij.haskell.psi.HaskellCompositeElementType"
tokenTypeClass="com.powertuple.intellij.haskell.psi.HaskellTokenType"
psiImplUtilClass="com.powertuple.intellij.haskell.psi.impl.HaskellPsiImplUtil"
@ -22,7 +23,7 @@
program ::= (lexeme | whitespace)*
private whitespace ::= (whitestuff)+
private whitestuff ::= WHITE_CHAR | COMMENT | NCOMMENT | NEWLINE
private lexeme ::= qvar | var | qcon | con | qconsym | consym | qvarsym | varsym | literal
private lexeme ::= start_type_signature | qvar | var | qcon | con | qconsym | consym | qvarsym | varsym | literal
| special | reservedop | reservedid | specialreservedid
literal ::= DECIMAL | HEXADECIMAL | OCTAL | FLOAT | CHARACTER_LITERAL | STRING_LITERAL
@ -39,9 +40,11 @@ private symbol ::= ascSymbol // ignoring unicode
varsym ::= (symbol (symbol | ':')*) // operator
consym ::= (':' (symbol | ':')*) // operator constructor
start_type_signature ::= VAR_ID COLON_COLON // start of type signature
var ::= VAR_ID // variables and type variables
{mixin="com.powertuple.intellij.haskell.psi.impl.HaskellNamedElementImpl"
implements="com.powertuple.intellij.haskell.psi.HaskellNamedElement" methods=[getName setName getNameIdentifier]}
implements="com.powertuple.intellij.haskell.psi.HaskellNamedElement" methods=[getName setName getNameIdentifier getReference]}
con ::= CON_ID // constructors, type constructors and type classes
@ -60,199 +63,3 @@ reservedop ::= DOT_DOT | COLON | COLON_COLON | DEFINED_BY | SLA
| RIGHT_ARROW | AT | TILDE | DOUBLE_RIGHT_ARROW
specialreservedid ::= AS | QUALIFIED | HIDING
// Context-Free Syntax (under construction because left-recursion is not supported by generator)
//module ::= 'module' modid (exports)? 'where' body
// | body
//
//body ::= impdecls *
// | topdecls *
// | (impdecls ';' topdecls) *
//
//impdecls ::= impdecl(';' impdecl)*
//
//exports ::= '(' export(',' export)* ')'
//
//export ::= qvar
// | qtycon (('..') | ((cname(',' cname)* )?))?
// | qtycls (('..') | ((qvar (',' qvar)* )?))?
// | module modid
//
//impdecl ::= import ('qualified')? modid ('as' modid)? (impspec)?
//
//impspec ::= '(' (import (',' import )* (',' )?)? ')'
// | 'hiding' ((import (',' import )* (',' )?)?)
//import ::= var
// | tycon (('..') | ((cname (',' cname )*)?))?
//cname ::= var | con
//topdecls ::= (topdecl (';' topdecl)*)?
//topdecl ::= 'type' simpletype DEFINED_BY type
// | 'data' ( context '=>' )? simpletype DEFINED_BY constrs ( deriving )?
// | 'newtype' ( context '=>' )? simpletype DEFINED_BY newconstr ( deriving )?
// | 'class' ( scontext '=>' )? tycls tyvar ('where' cdecls)?
// | 'instance' ( scontext '=>' )? qtycls inst ('where' idecls)?
// | 'default' (( type (',' type)* )? )
// | 'foreign' fdecl
// | decl
//decls ::= '{' (decl (';' decl )*)? '}'
//decl ::= gendecl
// | (funlhs | pat) rhs
//cdecls ::= '{' (cdecl (';' cdecl )*)? '}'
//cdecl ::= gendecl
// | (funlhs | var) rhs
//idecls ::= '{' (idecl ( ';' idecl)* )? '}'
//idecl ::= (funlhs | var ) rhs
//gendecl ::= vars '::' (context '=>')? type // type signature
// | fixity (integer)? ops // fixity signature
// | // empty declaration
//
//ops ::= op (',' op )*
//vars ::= var (',' var)*
//fixity ::= 'infixl' | 'infixr' | 'infix'
//
//type ::= btype ('->' type)? // function type
//
//btype ::= (btype)? atype // type application
//
//atype ::= gtycon
// | tyvar
// | (type(',' type )+) // tuple type
// | '['type']' // list type
// | '('type')' // parenthesized constructor
//
//gtycon ::= qtycon
// | '()' // unit type
// | '[]' // list constructor
// | '(->)' // function constructor
// | '(,' (',')* ')' // tupling constructor
//
//context ::= class
// | '(' (class (',' class)* )? ')'
//class ::= qtycls tyvar
// | qtycls (tyvar (atype)+ )
//scontext ::= simpleclass
// | '(' (simpleclass (',' simpleclass)* )? ')'
//simpleclass ::= qtycls tyvar
//
//simpletype ::= tycon (tyvar)*
//constrs ::= constr (| constr)*
//constr ::= con (('!')? atype)*
// | (btype | '!' atype) conop (btype | '!' atype)
// | con '{' (fielddecl (',' fielddecl)* )? '}'
//newconstr ::= con atype
// | con '{' var '::' type '}'
//fielddecl ::= vars '::' (type | '!' atype)
//deriving ::= 'deriving' (dclass | '(' (dclass (',' dclass)* )? ')' )
//dclass ::= qtycls
//
//inst ::= gtycon
// | '(' gtycon ( tyvar )* ')'
// | '(' tyvar ( ',' tyvar )+ ')'
// | '(' tyvar '->' tyvar ')'
// | (tyvar)?
//
//fdecl ::= 'import' callconv (safety)? impent var '::' ftype
// | 'export' callconv expent var '::' ftype
//callconv ::= 'ccall' | 'stdcall' | 'cplusplus' // (calling convention)
// | 'jvm' | 'dotnet' // | 'system-specific calling conventions'????
//impent ::= (string_)?
//expent ::= (string_)?
//safety ::= 'unsafe' | 'safe'
//
//ftype ::= frtype
// | fatype ::= ftype // do not know how to handle this
//
//frtype ::= fatype
// | '()'
//fatype ::= qtycon (atype)*
//
//funlhs ::= var (apat)+
// | pat varop pat
// | '(' funlhs ')' (apat)+
//rhs ::= DEFINED_BY exp ('where' decls)?
// | gdrhs ('where' decls)?
//gdrhs ::= guards DEFINED_BY exp (gdrhs)?
//guards ::= '|' guard+
//guard ::= pat '<-' infixexp
// | 'let' decls
// | infixexp
//
//exp ::= infixexp '::' (context '=>')? type
// | infixexp
//
//infixexp ::= lexp qop infixexp
// | '-' infixexp
// | lexp
//
//lexp ::= '\' (apat)+ '->' exp
// | 'let' decls 'in' exp
// | 'if' exp 'then' exp 'else' exp
// | 'case' exp 'of' '{' alts '}'
// | 'do' '{' stmts '}'
// | fexp
//
//fexp ::= (fexp)? aexp
//
//aexp ::= qvar
// | gcon
// | literal
// | '(' exp ')'
// | '(' exp (',' exp )+ ')'
// | '[' exp (',' exp )* ']'
// | '[' exp (',' exp )? '..' ( exp )? ']'
// | '[' exp | qual (',' qual )* ']'
// | '(' infixexp qop ')'
// | '(' qop infixexp ')'
// | qcon '{' (fbind ( ',' fbind)* )? '}'
// | aexp '{' fbind (',' fbind)* '}'
//
//qual ::= pat '<-' exp
// | 'let' decls
// | exp
//
//alts ::= (alt (';' alt )* )?
//alt ::= pat '->' exp ('where' decls)?
// | pat gdpat ('where' decls)?
// | // (empty alternative)
//gdpat ::= guards '->' exp (gdpat)?
//stmts ::= (stmt)* exp (';')?
//stmt ::= exp ';'
// | pat '<-' exp ';'
// | 'let' decls ';'
// | ';' // (empty statement)
//fbind ::= qvar DEFINED_BY exp
//pat ::= lpat qconop pat // (infix constructor)
// | lpat
//
//left lpat ::= apat
// | '-' (integer | float) // (negative literal)
// | gcon (apat)+
//
//apat ::= var ('@' apat)? // (as pattern)
// | gcon // (arity gcon = 0)
// | qcon (fpat)* // (labeled pattern, k ≥ 0)
// | literal
// | '_' // (wildcard)
// | '('pat')' // (parenthesized pattern)
// | '('pat (',' pat)+ ')' // (tuple pattern, k ≥ 2)
// | '['pat (',' pat)* ']' // (list pattern, k ≥ 1)
// | '~' apat
//
//fpat ::= qvar DEFINED_BY pat
//
//gcon ::= '()'
// | '[]'
// | '(' (',')* ')'
// | qcon
//
//var ::= varid | '(' varsym ')' // variable
//qvar ::= qvarid | '(' qvarsym ')'
//con ::= conid | '(' consym ')'
//qcon ::= qconid | '(' gconsym ')'
//varop ::= varsym | '`' varid '`'
//qvarop ::= qvarsym | '`' qvarid '`'
//conop ::= consym | '`' conid '`'
//qconop ::= gconsym | '`' qconid '`'
//op ::= varop | conop
//qop ::= qvarop | qconop
//gconsym ::= ':' | qconsym

View File

@ -0,0 +1,22 @@
/*
* Copyright 2014 Rik van der Kleij
* Licensed 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 com.powertuple.intellij.haskell.psi;
import com.intellij.psi.PsiElement;
public interface HaskellCompositeElement extends PsiElement {
}

View File

@ -21,8 +21,8 @@ import com.powertuple.intellij.haskell.HaskellLanguage;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
public class HaskellElementType extends IElementType {
public HaskellElementType(@NotNull @NonNls String debugName) {
public class HaskellCompositeElementType extends IElementType {
public HaskellCompositeElementType(@NotNull @NonNls String debugName) {
super(debugName, HaskellLanguage.INSTANCE);
}
}

View File

@ -1,18 +1,34 @@
/*
* Copyright 2014 Rik van der Kleij
* Licensed 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 com.powertuple.intellij.haskell.psi
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiFileFactory
import com.powertuple.intellij.haskell.HaskellFileType
import com.powertuple.intellij.haskell.{HaskellFile, HaskellFileType}
object HaskellElementFactory {
def createVar(project: Project, name: String): HaskellVarid = {
def createVar(project: Project, name: String): HaskellVar = {
val haskellFile = createFile(project, name)
haskellFile.getFirstChild.asInstanceOf[HaskellVarid]
haskellFile.getFirstChild.asInstanceOf[HaskellVar]
}
def createFile(project: Project, text: String): HaskellFile = {
val name: String = "dummy.hs"
private def createFile(project: Project, text: String): HaskellFile = {
val name = "dummy.hs"
PsiFileFactory.getInstance(project).createFileFromText(name, HaskellFileType.INSTANCE, text).asInstanceOf[HaskellFile]
}
}

View File

@ -1,6 +1,22 @@
/*
* Copyright 2014 Rik van der Kleij
* Licensed 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 com.powertuple.intellij.haskell.psi;
import com.intellij.psi.PsiNameIdentifierOwner;
public interface HaskellNamedElement extends PsiNameIdentifierOwner {
public interface HaskellNamedElement extends HaskellCompositeElement, PsiNameIdentifierOwner {
}

View File

@ -0,0 +1,41 @@
/*
* Copyright 2014 Rik van der Kleij
* Licensed 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 com.powertuple.intellij.haskell.psi;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.AbstractElementManipulator;
import com.powertuple.intellij.haskell.psi.HaskellVar;
import org.jetbrains.annotations.NotNull;
public class HaskellStringLiteralManipulator extends AbstractElementManipulator<HaskellVar> {
@Override
public HaskellVar handleContentChange(HaskellVar psi, TextRange range, String newContent) {
final String oldText = psi.getText();
final String newText = oldText.substring(0, range.getStartOffset()) + newContent + oldText.substring(range.getEndOffset());
return (HaskellVar) psi.setName(newText);
}
@NotNull
@Override
public TextRange getRangeInElement(final HaskellVar element) {
return getStringTokenRange(element);
}
public static TextRange getStringTokenRange(final HaskellVar element) {
return TextRange.from(1, element.getTextLength() - 2);
}
}

View File

@ -0,0 +1,136 @@
/*
* Copyright 2014 Rik van der Kleij
* Licensed 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 com.powertuple.intellij.haskell.psi
import com.intellij.codeInsight.lookup.LookupElementBuilder
import com.intellij.openapi.util.TextRange
import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.psi._
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.util.{PsiTreeUtil, PsiUtilCore}
import com.powertuple.intellij.haskell.external.GhciModManager
import com.powertuple.intellij.haskell.util.{FileUtil, HaskellFileIndex, LineColumnPosition}
import com.powertuple.intellij.haskell.{HaskellFile, HaskellIcons, HaskellNotificationGroup}
import scala.util.{Failure, Success, Try}
class HaskellVarReference(element: PsiElement, textRange: TextRange) extends PsiReferenceBase[PsiElement](element, textRange) {
override def resolve: PsiElement = {
FileUtil.saveAllFiles()
val psiFile = myElement.getContainingFile
val expression = myElement.getText.substring(textRange.getStartOffset, textRange.getEndOffset)
(for {
expressionInfo <- getExpressionInfo(psiFile, expression)
haskellFile <- Option(PsiManager.getInstance(myElement.getProject).
findFile(LocalFileSystem.getInstance().findFileByPath(expressionInfo.filePath)).asInstanceOf[HaskellFile])
typeSignature <- findTypeSignaturesFor(haskellFile, expression)
} yield typeSignature).orElse(for {
expressionInfo <- getLocalExpressionInfo(getExpressionInfo(psiFile, expression))
haskellFile <- Option(PsiManager.getInstance(myElement.getProject).
findFile(LocalFileSystem.getInstance().findFileByPath(expressionInfo.filePath)).asInstanceOf[HaskellFile])
startOffset <- LineColumnPosition.getOffset(haskellFile, LineColumnPosition(expressionInfo.lineNr, expressionInfo.colNr))
} yield PsiUtilCore.getElementAtOffset(haskellFile, startOffset)).orNull
}
/**
* Did not find solution to take scope into account, so currently it returns all vars of file :-(
*/
override def getVariants: Array[AnyRef] = {
val haskellFile = myElement.getContainingFile.asInstanceOf[HaskellFile]
val vars = PsiTreeUtil.getChildrenOfType(haskellFile, classOf[HaskellVar]).filter(v => v != null && v.getName != null && !v.getName.isEmpty).groupBy(_.getName).map(_._2.head).toArray
vars.map(v => LookupElementBuilder.create(v).withIcon(HaskellIcons.HASKELL_SMALL_LOGO).withTypeText(v.getName))
}
private def getLocalExpressionInfo(expressionInfo: Option[ExpressionInfo]) = {
expressionInfo match {
case Some(lei: LocalExpressionInfo) => Some(lei)
case _ => None
}
}
private def findTypeSignaturesFor(haskellFile: HaskellFile, expression: String) = {
PsiTreeUtil.getChildrenOfType(haskellFile, classOf[HaskellStartTypeSignature]).map(hts => hts.getFirstChild).find(p => p.getText == expression)
}
private def getOutputLine(outputLines: Seq[String]): Option[String] = {
outputLines match {
case Seq(ol) => Some(ol)
case _ => None
}
}
private def getExpressionInfo(psiFile: PsiFile, expression: String): Option[ExpressionInfo] = {
val ghcModi = GhciModManager.getGhcMod(psiFile.getProject)
val cmd = s"info ${psiFile.getVirtualFile.getPath} $expression"
val ghcModiOutput = ghcModi.execute(cmd)
for {
outputLine <- getOutputLine(ghcModiOutput.outputLines)
expressionInfo <- expressionInfoFrom(outputLine)
} yield expressionInfo
}
private def expressionInfoFrom(outputLine: String): Option[ExpressionInfo] = {
ghcModiOutputToExpressionInfo(outputLine) match {
case Success(ei) => Some(ei)
case Failure(error) => HaskellNotificationGroup.notifyInfo(error.getMessage); None
}
}
private def ghcModiOutputToExpressionInfo(ghcModiOutput: String): Try[ExpressionInfo] = Try {
val GhcModiInfoPattern = """(.+)-- Defined at (.+):([\d]+):([\d]+)""".r
val GhcModiInfoLibraryPattern = """(.+)-- Defined in (.+)""".r
ghcModiOutput match {
case GhcModiInfoPattern(typeSignature, filePath, lineNr, colNr) => LocalExpressionInfo(typeSignature.trim, filePath, lineNr.toInt, colNr.toInt)
case GhcModiInfoLibraryPattern(typeSignature, modulePath) => LibraryExpressionInfo(typeSignature, findLibraryFilePath(modulePath))
}
}
private def findLibraryFilePath(modulePath: String) = {
val ss = modulePath.split('.').toList.reverse
val (fileName, dirs) = ss match {
case fn :: d => (fn, d)
case _ => throw new Exception(s"Could not determine file and directory for $modulePath")
}
val files = HaskellFileIndex.getFilesByName(myElement.getProject, fileName, GlobalSearchScope.allScope(myElement.getProject))
val file = files.find(hf => getDirectories(hf.getContainingDirectory) == dirs).getOrElse(throw new Exception(s"Could not file path for $modulePath"))
file.getVirtualFile.getPath
}
private def getDirectories(dir: PsiDirectory, dirs: List[String] = List()): List[String] = {
if (dir != null && dir.getName != "src") {
getDirectories(dir.getParentDirectory, dir.getName :: dirs)
} else {
dirs
}
}
abstract class ExpressionInfo {
def typeSignature: String
def filePath: String
}
case class LocalExpressionInfo(typeSignature: String, filePath: String, lineNr: Int, colNr: Int) extends ExpressionInfo
case class LibraryExpressionInfo(typeSignature: String, filePath: String) extends ExpressionInfo
}

View File

@ -0,0 +1,50 @@
/*
* Copyright 2014 Rik van der Kleij
* Licensed 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 com.powertuple.intellij.haskell.psi.impl;
import com.intellij.extapi.psi.ASTWrapperPsiElement;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import com.intellij.psi.ResolveState;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.powertuple.intellij.haskell.psi.HaskellCompositeElement;
import org.jetbrains.annotations.NotNull;
public class HaskellCompositeElementImpl extends ASTWrapperPsiElement implements HaskellCompositeElement {
public HaskellCompositeElementImpl(ASTNode node) {
super(node);
}
@Override
public String toString() {
return getNode().getElementType().toString();
}
@Override
public boolean processDeclarations(@NotNull PsiScopeProcessor processor, @NotNull ResolveState state, PsiElement lastParent, @NotNull PsiElement place) {
return processDeclarations(this, processor, state, lastParent, place);
}
static boolean processDeclarations(@NotNull PsiElement element, @NotNull PsiScopeProcessor processor, @NotNull ResolveState state, PsiElement lastParent, @NotNull PsiElement place) {
if (!processor.execute(element, state)) {
return false;
} else {
return ResolveUtil.processChildren(element, processor, state, lastParent, place);
}
}
}

View File

@ -18,14 +18,16 @@ package com.powertuple.intellij.haskell.psi.impl;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry;
import com.intellij.util.ArrayUtil;
import com.powertuple.intellij.haskell.psi.HaskellElementFactory;
import com.powertuple.intellij.haskell.psi.HaskellTokenType;
import com.powertuple.intellij.haskell.psi.HaskellTypes;
import com.powertuple.intellij.haskell.psi.HaskellVarid;
import com.powertuple.intellij.haskell.psi.HaskellVar;
public class HaskellPsiImplUtil {
public static String getName(HaskellVarid element) {
public static String getName(HaskellVar element) {
ASTNode keyNode = element.getNode().findChildByType(HaskellTypes.HS_VAR_ID);
if (keyNode != null) {
return keyNode.getText();
@ -34,23 +36,26 @@ public class HaskellPsiImplUtil {
}
}
public static PsiElement setName(HaskellVarid element, String newName) {
public static PsiElement setName(HaskellVar element, String newName) {
ASTNode keyNode = element.getNode().findChildByType(HaskellTypes.HS_VAR_ID);
if (keyNode != null) {
HaskellVarid property = HaskellElementFactory.createVar(element.getProject(), newName);
HaskellVar property = HaskellElementFactory.createVar(element.getProject(), newName);
ASTNode newKeyNode = property.getFirstChild().getNode();
element.getNode().replaceChild(keyNode, newKeyNode);
}
return element;
}
public static PsiElement getNameIdentifier(HaskellVarid haskellVarid) {
ASTNode keyNode = haskellVarid.getNode().findChildByType(HaskellTypes.HS_VAR_ID);
public static PsiElement getNameIdentifier(HaskellVar haskellVar) {
ASTNode keyNode = haskellVar.getNode().findChildByType(HaskellTypes.HS_VAR_ID);
if (keyNode != null) {
return keyNode.getPsi();
} else {
return null;
}
}
public static PsiReference getReference(HaskellVar haskellVar) {
return ArrayUtil.getFirstElement(ReferenceProvidersRegistry.getReferencesFromProviders(haskellVar));
}
}

View File

@ -0,0 +1,44 @@
/*
* Copyright 2014 Rik van der Kleij
* Licensed 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 com.powertuple.intellij.haskell.psi.impl;
import com.intellij.psi.PsiElement;
import com.intellij.psi.ResolveState;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public abstract class ResolveUtil {
private ResolveUtil() {
}
public static boolean processChildren(@NotNull PsiElement element,
@NotNull PsiScopeProcessor processor,
@NotNull ResolveState substitutor,
@Nullable PsiElement lastParent,
@NotNull PsiElement place) {
PsiElement run = lastParent == null ? element.getLastChild() : lastParent.getPrevSibling();
while (run != null) {
if (PsiTreeUtil.findCommonParent(place, run) != run && !run.processDeclarations(processor, substitutor, null, place)) {
return false;
}
run = run.getPrevSibling();
}
return true;
}
}

View File

@ -18,6 +18,26 @@ package com.powertuple.intellij.haskell.util
import com.intellij.openapi.fileEditor.FileDocumentManager
import com.intellij.psi.PsiFile
import com.intellij.util.ui.UIUtil
object FileUtil {
def saveFile(psiFile: PsiFile) {
UIUtil.invokeLaterIfNeeded(new Runnable {
override def run() {
FileDocumentManager.getInstance.saveDocument(FileDocumentManager.getInstance().getDocument(psiFile.getVirtualFile))
}
})
}
def saveAllFiles() {
UIUtil.invokeLaterIfNeeded(new Runnable {
override def run() {
FileDocumentManager.getInstance.saveAllDocuments()
}
})
}
}
case class LineColumnPosition(lineNr: Int, colunmNr: Int) extends Ordered[LineColumnPosition] {
@ -33,14 +53,21 @@ case class LineColumnPosition(lineNr: Int, colunmNr: Int) extends Ordered[LineCo
object LineColumnPosition {
def fromOffset(psiFile: PsiFile, offset: Int): LineColumnPosition = {
val file = psiFile.getVirtualFile
if (file == null) return null
def fromOffset(psiFile: PsiFile, offset: Int): Option[LineColumnPosition] = {
val fdm = FileDocumentManager.getInstance
val doc = fdm.getCachedDocument(file)
if (doc == null) return null
val lineIndex = doc.getLineNumber(offset)
val startOffSet = offset - doc.getLineStartOffset(lineIndex)
LineColumnPosition(lineIndex + 1, startOffSet + 1)
for {
file <- Option(psiFile.getVirtualFile)
doc <- Option(fdm.getDocument(file))
li = doc.getLineNumber(offset)
} yield LineColumnPosition(li + 1, offset - doc.getLineStartOffset(li) + 1)
}
def getOffset(psiFile: PsiFile, lineCol: LineColumnPosition): Option[Int] = {
val fdm = FileDocumentManager.getInstance
for {
file <- Option(psiFile.getVirtualFile)
doc <- Option(fdm.getDocument(file))
startOffsetLine = doc.getLineStartOffset(lineCol.lineNr - 1)
} yield startOffsetLine + lineCol.colunmNr - 1
}
}

View File

@ -0,0 +1,109 @@
package com.powertuple.intellij.haskell.util
import java.io.{DataInput, DataOutput}
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiManager
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.util.CommonProcessors
import com.intellij.util.indexing.FileBasedIndex.InputFilter
import com.intellij.util.indexing._
import com.intellij.util.io.{DataExternalizer, EnumeratorStringDescriptor, KeyDescriptor}
import com.powertuple.intellij.haskell.{HaskellFile, HaskellFileType}
import gnu.trove.THashSet
import scala.collection.JavaConversions._
class HaskellFileIndex extends ScalaScalarIndexExtension[String] {
override def getName: ID[String, Unit] = HaskellFileIndex.HaskellFileIndex
override def getKeyDescriptor: KeyDescriptor[String] = HaskellFileIndex.Descriptor
override def dependsOnFileContent(): Boolean = false
override def getVersion: Int = HaskellFileIndex.IndexVersion
override def getInputFilter: InputFilter = {
HaskellFileIndex.HaskellModuleFilter
}
override def getIndexer: DataIndexer[String, Unit, FileContent] = haskellDataIndexer
private val haskellDataIndexer = new HaskellDataIndexer
private class HaskellDataIndexer extends DataIndexer[String, Unit, FileContent] {
def map(inputData: FileContent): java.util.Map[String, Unit] = {
Map(inputData.getFile.getNameWithoutExtension ->())
}
}
}
object HaskellFileIndex {
private final val HaskellFileIndex: ID[String, Unit] = ID.create("HaskellFileIndex")
private final val IndexVersion = 1
private final val Descriptor = new EnumeratorStringDescriptor
private final val HaskellModuleFilter = new FileBasedIndex.InputFilter {
def acceptInput(file: VirtualFile): Boolean = {
file.getFileType == HaskellFileType.INSTANCE
}
}
def getAllNames(project: Project, searchScope: GlobalSearchScope): Seq[String] = {
val allKeys: java.util.Set[String] = new THashSet[String]
FileBasedIndex.getInstance.processAllKeys(HaskellFileIndex, new CommonProcessors.CollectProcessor[String](allKeys), searchScope, null)
allKeys.toSeq
}
def getFilesByName(project: Project, name: String, searchScope: GlobalSearchScope): Seq[HaskellFile] = {
getByName(project, name, searchScope)
}
private def getByName(project: Project, name: String, searchScope: GlobalSearchScope): Seq[HaskellFile] = {
val psiManager = PsiManager.getInstance(project)
val virtualFiles = getVirtualFilesByName(project, name, searchScope)
virtualFiles.flatMap(convertToHaskellFile(_, psiManager))
}
private def convertToHaskellFile(virtualFile: VirtualFile, psiManager: PsiManager): Option[HaskellFile] = {
val psiFile = psiManager.findFile(virtualFile)
psiFile match {
case f: HaskellFile => Some(f)
case _ => None
}
}
private def getVirtualFilesByName(project: Project, name: String, searchScope: GlobalSearchScope): Seq[VirtualFile] = {
FileBasedIndex.getInstance.getContainingFiles(HaskellFileIndex, name, searchScope).toSeq
}
}
/**
* A specialization of FileBasedIndexExtension allowing to create a mapping [DataObject -> List of files containing this object]
*
*/
object ScalaScalarIndexExtension {
final val VoidDataExternalizer: DataExternalizer[Unit] = new ScalaScalarIndexExtension.UnitDataExternalizer
private class UnitDataExternalizer extends DataExternalizer[Unit] {
def save(out: DataOutput, value: Unit) {
}
def read(in: DataInput): Unit = {
()
}
}
}
abstract class ScalaScalarIndexExtension[K] extends FileBasedIndexExtension[K, Unit] {
final def getValueExternalizer: DataExternalizer[Unit] = {
ScalaScalarIndexExtension.VoidDataExternalizer
}
}