mirror of
https://github.com/ilyakooo0/intellij-haskell.git
synced 2024-10-26 15:13:25 +03:00
Added reference contributor for variabels.
Various improvements.
This commit is contained in:
parent
1e73e47e1f
commit
b580287e2b
@ -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>
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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;
|
@ -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);
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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()))};
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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];
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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]
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
109
src/com/powertuple/intellij/haskell/util/HaskellFileIndex.scala
Normal file
109
src/com/powertuple/intellij/haskell/util/HaskellFileIndex.scala
Normal 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
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user