mirror of
https://github.com/ilyakooo0/intellij-haskell.git
synced 2024-09-11 14:56:19 +03:00
Various improvements/fixes including changes to support -fobject-code (issue #386) and changes to make renaming more solid (issues #384 and #385)
This commit is contained in:
parent
3f73155359
commit
a15653f1bd
@ -47,6 +47,8 @@ public class HaskellParser implements PsiParser, LightPsiParser {
|
||||
r = conid(b, 0);
|
||||
} else if (t == HS_CONOP) {
|
||||
r = conop(b, 0);
|
||||
} else if (t == HS_CONSTR) {
|
||||
r = constr(b, 0);
|
||||
} else if (t == HS_CONSTR_1) {
|
||||
r = constr1(b, 0);
|
||||
} else if (t == HS_CONSTR_2) {
|
||||
@ -1547,13 +1549,15 @@ public class HaskellParser implements PsiParser, LightPsiParser {
|
||||
|
||||
/* ********************************************************** */
|
||||
// type_signature | constr1 | constr2 | constr3
|
||||
static boolean constr(PsiBuilder b, int l) {
|
||||
public static boolean constr(PsiBuilder b, int l) {
|
||||
if (!recursion_guard_(b, l, "constr")) return false;
|
||||
boolean r;
|
||||
Marker m = enter_section_(b, l, _NONE_, HS_CONSTR, "<constr>");
|
||||
r = type_signature(b, l + 1);
|
||||
if (!r) r = constr1(b, l + 1);
|
||||
if (!r) r = constr2(b, l + 1);
|
||||
if (!r) r = constr3(b, l + 1);
|
||||
exit_section_(b, l, m, r, false, null);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
20
gen/intellij/haskell/psi/HaskellConstr.java
Normal file
20
gen/intellij/haskell/psi/HaskellConstr.java
Normal file
@ -0,0 +1,20 @@
|
||||
// This is a generated file. Not intended for manual editing.
|
||||
package intellij.haskell.psi;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public interface HaskellConstr extends HaskellCompositeElement {
|
||||
|
||||
@Nullable
|
||||
HaskellConstr1 getConstr1();
|
||||
|
||||
@Nullable
|
||||
HaskellConstr2 getConstr2();
|
||||
|
||||
@Nullable
|
||||
HaskellConstr3 getConstr3();
|
||||
|
||||
@Nullable
|
||||
HaskellTypeSignature getTypeSignature();
|
||||
|
||||
}
|
@ -15,13 +15,7 @@ public interface HaskellDataDeclaration extends HaskellDataConstructorDeclaratio
|
||||
HaskellCcontext getCcontext();
|
||||
|
||||
@NotNull
|
||||
List<HaskellConstr1> getConstr1List();
|
||||
|
||||
@NotNull
|
||||
List<HaskellConstr2> getConstr2List();
|
||||
|
||||
@NotNull
|
||||
List<HaskellConstr3> getConstr3List();
|
||||
List<HaskellConstr> getConstrList();
|
||||
|
||||
@Nullable
|
||||
HaskellDataDeclarationDeriving getDataDeclarationDeriving();
|
||||
|
@ -6,7 +6,7 @@ import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiReference;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public interface HaskellQConQualifier1 extends HaskellQualifierElement {
|
||||
public interface HaskellQConQualifier1 extends HaskellQualifierElement, HaskellNamedElement {
|
||||
|
||||
@NotNull
|
||||
HaskellConid getConid();
|
||||
|
@ -8,7 +8,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface HaskellQConQualifier2 extends HaskellQualifierElement {
|
||||
public interface HaskellQConQualifier2 extends HaskellQualifierElement, HaskellNamedElement {
|
||||
|
||||
@NotNull
|
||||
List<HaskellConid> getConidList();
|
||||
|
@ -8,7 +8,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface HaskellQConQualifier3 extends HaskellQualifierElement {
|
||||
public interface HaskellQConQualifier3 extends HaskellQualifierElement, HaskellNamedElement {
|
||||
|
||||
@NotNull
|
||||
List<HaskellConid> getConidList();
|
||||
|
@ -8,7 +8,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface HaskellQConQualifier4 extends HaskellQualifierElement {
|
||||
public interface HaskellQConQualifier4 extends HaskellQualifierElement, HaskellNamedElement {
|
||||
|
||||
@NotNull
|
||||
List<HaskellConid> getConidList();
|
||||
|
@ -8,7 +8,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface HaskellQualifier extends HaskellQualifierElement {
|
||||
public interface HaskellQualifier extends HaskellQualifierElement, HaskellNamedElement {
|
||||
|
||||
@NotNull
|
||||
List<HaskellConid> getConidList();
|
||||
|
@ -20,6 +20,7 @@ public interface HaskellTypes {
|
||||
IElementType HS_CON = new HaskellCompositeElementType("HS_CON");
|
||||
IElementType HS_CONID = HaskellElementTypeFactory.factory("HS_CONID");
|
||||
IElementType HS_CONOP = new HaskellCompositeElementType("HS_CONOP");
|
||||
IElementType HS_CONSTR = new HaskellCompositeElementType("HS_CONSTR");
|
||||
IElementType HS_CONSTR_1 = new HaskellCompositeElementType("HS_CONSTR_1");
|
||||
IElementType HS_CONSTR_2 = new HaskellCompositeElementType("HS_CONSTR_2");
|
||||
IElementType HS_CONSTR_3 = new HaskellCompositeElementType("HS_CONSTR_3");
|
||||
@ -193,6 +194,8 @@ public interface HaskellTypes {
|
||||
return new HaskellConidImpl(node);
|
||||
} else if (type == HS_CONOP) {
|
||||
return new HaskellConopImpl(node);
|
||||
} else if (type == HS_CONSTR) {
|
||||
return new HaskellConstrImpl(node);
|
||||
} else if (type == HS_CONSTR_1) {
|
||||
return new HaskellConstr1Impl(node);
|
||||
} else if (type == HS_CONSTR_2) {
|
||||
|
@ -54,6 +54,10 @@ public class HaskellVisitor extends PsiElementVisitor {
|
||||
visitCNameElement(o);
|
||||
}
|
||||
|
||||
public void visitConstr(@NotNull HaskellConstr o) {
|
||||
visitCompositeElement(o);
|
||||
}
|
||||
|
||||
public void visitConstr1(@NotNull HaskellConstr1 o) {
|
||||
visitCompositeElement(o);
|
||||
}
|
||||
@ -229,18 +233,22 @@ public class HaskellVisitor extends PsiElementVisitor {
|
||||
|
||||
public void visitQConQualifier1(@NotNull HaskellQConQualifier1 o) {
|
||||
visitQualifierElement(o);
|
||||
// visitNamedElement(o);
|
||||
}
|
||||
|
||||
public void visitQConQualifier2(@NotNull HaskellQConQualifier2 o) {
|
||||
visitQualifierElement(o);
|
||||
// visitNamedElement(o);
|
||||
}
|
||||
|
||||
public void visitQConQualifier3(@NotNull HaskellQConQualifier3 o) {
|
||||
visitQualifierElement(o);
|
||||
// visitNamedElement(o);
|
||||
}
|
||||
|
||||
public void visitQConQualifier4(@NotNull HaskellQConQualifier4 o) {
|
||||
visitQualifierElement(o);
|
||||
// visitNamedElement(o);
|
||||
}
|
||||
|
||||
public void visitQName(@NotNull HaskellQName o) {
|
||||
@ -257,6 +265,7 @@ public class HaskellVisitor extends PsiElementVisitor {
|
||||
|
||||
public void visitQualifier(@NotNull HaskellQualifier o) {
|
||||
visitQualifierElement(o);
|
||||
// visitNamedElement(o);
|
||||
}
|
||||
|
||||
public void visitReservedId(@NotNull HaskellReservedId o) {
|
||||
|
50
gen/intellij/haskell/psi/impl/HaskellConstrImpl.java
Normal file
50
gen/intellij/haskell/psi/impl/HaskellConstrImpl.java
Normal file
@ -0,0 +1,50 @@
|
||||
// This is a generated file. Not intended for manual editing.
|
||||
package intellij.haskell.psi.impl;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.psi.PsiElementVisitor;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import intellij.haskell.psi.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class HaskellConstrImpl extends HaskellCompositeElementImpl implements HaskellConstr {
|
||||
|
||||
public HaskellConstrImpl(ASTNode node) {
|
||||
super(node);
|
||||
}
|
||||
|
||||
public void accept(@NotNull HaskellVisitor visitor) {
|
||||
visitor.visitConstr(this);
|
||||
}
|
||||
|
||||
public void accept(@NotNull PsiElementVisitor visitor) {
|
||||
if (visitor instanceof HaskellVisitor) accept((HaskellVisitor) visitor);
|
||||
else super.accept(visitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public HaskellConstr1 getConstr1() {
|
||||
return PsiTreeUtil.getChildOfType(this, HaskellConstr1.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public HaskellConstr2 getConstr2() {
|
||||
return PsiTreeUtil.getChildOfType(this, HaskellConstr2.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public HaskellConstr3 getConstr3() {
|
||||
return PsiTreeUtil.getChildOfType(this, HaskellConstr3.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public HaskellTypeSignature getTypeSignature() {
|
||||
return PsiTreeUtil.getChildOfType(this, HaskellTypeSignature.class);
|
||||
}
|
||||
|
||||
}
|
@ -36,20 +36,8 @@ public class HaskellDataDeclarationImpl extends HaskellCompositeElementImpl impl
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public List<HaskellConstr1> getConstr1List() {
|
||||
return PsiTreeUtil.getChildrenOfTypeAsList(this, HaskellConstr1.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public List<HaskellConstr2> getConstr2List() {
|
||||
return PsiTreeUtil.getChildrenOfTypeAsList(this, HaskellConstr2.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public List<HaskellConstr3> getConstr3List() {
|
||||
return PsiTreeUtil.getChildrenOfTypeAsList(this, HaskellConstr3.class);
|
||||
public List<HaskellConstr> getConstrList() {
|
||||
return PsiTreeUtil.getChildrenOfTypeAsList(this, HaskellConstr.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -21,8 +21,7 @@ import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.{PsiElement, PsiFile}
|
||||
import intellij.haskell.HaskellNotificationGroup
|
||||
import intellij.haskell.editor.HaskellCompletionContributor
|
||||
import intellij.haskell.external.component.{HaskellComponentsManager, StackProjectManager}
|
||||
import intellij.haskell.external.component.{FileModuleIdentifiers, HaskellComponentsManager, StackProjectManager}
|
||||
import intellij.haskell.psi._
|
||||
import intellij.haskell.util.{HaskellEditorUtil, StringUtil}
|
||||
|
||||
@ -93,23 +92,16 @@ object ShowTypeAction {
|
||||
}
|
||||
|
||||
private def findTypeSignatureFromScope(psiFile: PsiFile, psiElement: PsiElement) = {
|
||||
if (HaskellPsiUtil.findExpressionParent(psiElement).isDefined) {
|
||||
HaskellPsiUtil.findQualifiedNameParent(psiElement).flatMap(qualifiedNameElement => {
|
||||
if (HaskellPsiUtil.findExpression(psiElement).isDefined) {
|
||||
HaskellPsiUtil.findQualifiedName(psiElement).flatMap(qualifiedNameElement => {
|
||||
val definedInFile = HaskellComponentsManager.findDefinitionLocation(psiFile, qualifiedNameElement, None).toOption.map(_.namedElement.getContainingFile)
|
||||
if (definedInFile.contains(psiFile)) {
|
||||
// To prevent stale type info while compilation errors
|
||||
None
|
||||
} else {
|
||||
val name = qualifiedNameElement.getName
|
||||
val result = for {
|
||||
moduleName <- HaskellPsiUtil.findModuleName(psiFile)
|
||||
stackInfo <- HaskellComponentsManager.findStackComponentInfo(psiFile)
|
||||
globalInfo <- HaskellComponentsManager.findStackComponentGlobalInfo(stackInfo)
|
||||
declaration <- HaskellCompletionContributor.getAvailableModuleIdentifiers(globalInfo, stackInfo, psiFile, Some(moduleName)).find(_.name == name).map(_.declaration)
|
||||
} yield {
|
||||
declaration
|
||||
}
|
||||
result.orElse(HaskellPsiUtil.findHaskellDeclarationElements(psiFile).find(_.getIdentifierElements.exists(_.getName == name)).map(_.getText.replaceAll("""\s+""", " ")))
|
||||
val declaration = FileModuleIdentifiers.findAvailableModuleIdentifiers(psiFile).find(_.name == name).map(_.declaration)
|
||||
declaration.orElse(HaskellPsiUtil.findHaskellDeclarationElements(psiFile).find(_.getIdentifierElements.exists(_.getName == name)).map(_.getText.replaceAll("""\s+""", " ")))
|
||||
}
|
||||
})
|
||||
} else {
|
||||
|
@ -43,7 +43,7 @@ class ShowTypeStickyAction extends AnAction {
|
||||
private def untilNameElementBackwards(element: Option[PsiElement]): Option[HaskellQualifiedNameElement] = {
|
||||
element match {
|
||||
case Some(e) =>
|
||||
HaskellPsiUtil.findQualifiedNameParent(e) match {
|
||||
HaskellPsiUtil.findQualifiedName(e) match {
|
||||
case None => untilNameElementBackwards(Option(e.getPrevSibling))
|
||||
case qualifiedName => qualifiedName
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ class HaskellAnnotator extends ExternalAnnotator[(PsiFile, Option[PsiElement]),
|
||||
case (_, Some(_)) if !psiFile.isValid => null
|
||||
case (_, Some(_)) =>
|
||||
val currentElement = Option(psiFile.findElementAt(editor.getCaretModel.getOffset)).
|
||||
find(e => HaskellPsiUtil.findExpressionParent(e).isDefined).
|
||||
find(e => HaskellPsiUtil.findExpression(e).isDefined).
|
||||
flatMap(e => Option(PsiTreeUtil.prevVisibleLeaf(e))).filter(_.isValid)
|
||||
(psiFile, currentElement)
|
||||
}
|
||||
@ -381,7 +381,7 @@ class DeprecatedUseAction(name: String, suggestion: String) extends HaskellBaseI
|
||||
private object IntentionHelper {
|
||||
def replace(project: Project, editor: Editor, file: PsiFile, newName: String): Unit = {
|
||||
val offset = editor.getCaretModel.getOffset
|
||||
Option(file.findElementAt(offset)).flatMap(HaskellPsiUtil.findQualifiedNameParent) match {
|
||||
Option(file.findElementAt(offset)).flatMap(HaskellPsiUtil.findQualifiedName) match {
|
||||
case Some(e) =>
|
||||
if (e.getText.startsWith("`") && e.getText.endsWith("`")) {
|
||||
e.replace(HaskellElementFactory.createQualifiedNameElement(project, s"`$newName`"))
|
||||
@ -494,8 +494,8 @@ class ImportAloneInstancesAction(importDecl: String) extends HaskellBaseIntentio
|
||||
Option(file.findElementAt(offset)) match {
|
||||
case Some(e) =>
|
||||
for {
|
||||
importDeclarations <- HaskellPsiUtil.findImportDeclarationsParent(e)
|
||||
importDeclaration <- HaskellPsiUtil.findImportDeclarationParent(e)
|
||||
importDeclarations <- HaskellPsiUtil.findImportDeclarations(e)
|
||||
importDeclaration <- HaskellPsiUtil.findImportDeclaration(e)
|
||||
importDeclElement <- HaskellElementFactory.createImportDeclaration(project, importDecl)
|
||||
} yield importDeclarations.getNode.replaceChild(importDeclaration.getNode, importDeclElement.getNode)
|
||||
case None => ()
|
||||
|
@ -78,7 +78,7 @@ trait ModulePartImpl extends CabalNamedElementImpl {
|
||||
case Some(m) => GlobalSearchScope.moduleScope(m)
|
||||
case None => GlobalSearchScopesCore.projectProductionScope(getProject)
|
||||
}
|
||||
val haskellFile = HaskellModuleNameIndex.findFileByModuleName(getProject, moduleName).toOption.flatMap(_.headOption)
|
||||
val haskellFile = HaskellModuleNameIndex.findFilesByModuleName(getProject, moduleName).toOption.flatMap(_.headOption)
|
||||
haskellFile.flatMap(f => HaskellPsiUtil.findModuleDeclaration(f).find(_.getModuleName.contains(moduleName)).flatMap(_.getIdentifierElements.headOption))
|
||||
}
|
||||
}
|
||||
|
@ -16,10 +16,6 @@
|
||||
|
||||
package intellij.haskell.editor
|
||||
|
||||
import java.util
|
||||
import java.util.concurrent.TimeoutException
|
||||
|
||||
import com.github.blemale.scaffeine.{LoadingCache, Scaffeine}
|
||||
import com.intellij.codeInsight.completion._
|
||||
import com.intellij.codeInsight.lookup.LookupElementBuilder
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
@ -28,15 +24,12 @@ import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.patterns.PlatformPatterns
|
||||
import com.intellij.psi.impl.source.tree.TreeUtil
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.intellij.psi.{PsiElement, PsiFile, TokenType}
|
||||
import com.intellij.util.{ProcessingContext, WaitFor}
|
||||
import icons.HaskellIcons
|
||||
import intellij.haskell.annotator.HaskellAnnotator
|
||||
import intellij.haskell.external.component.HaskellComponentsManager.StackComponentInfo
|
||||
import intellij.haskell.external.component._
|
||||
import intellij.haskell.psi.HaskellElementCondition._
|
||||
import intellij.haskell.psi.HaskellPsiUtil._
|
||||
import intellij.haskell.psi.HaskellTypes._
|
||||
import intellij.haskell.psi._
|
||||
import intellij.haskell.runconfig.console.{HaskellConsoleView, HaskellConsoleViewMap}
|
||||
@ -81,23 +74,23 @@ class HaskellCompletionContributor extends CompletionContributor {
|
||||
private final val CommentIds = Stream("{-", "-}", "--")
|
||||
private final val HaddockIds = Stream("{-|", "-- |", "-- ^")
|
||||
|
||||
def findQualifiedNamedElementToComplete(element: PsiElement): Option[HaskellQualifiedNameElement] = {
|
||||
private def findQualifiedNamedElementToComplete(element: PsiElement): Option[HaskellQualifiedNameElement] = {
|
||||
val elementType = Option(element.getNode.getElementType)
|
||||
val psiFile = element.getContainingFile.getOriginalFile
|
||||
(for {
|
||||
et <- elementType
|
||||
if et == HS_DOT
|
||||
e <- Option(psiFile.findElementAt(element.getTextOffset - 1))
|
||||
p <- HaskellPsiUtil.findQualifiedNameParent(e)
|
||||
p <- HaskellPsiUtil.findQualifiedName(e)
|
||||
} yield p).orElse(for {
|
||||
et <- elementType
|
||||
if et == HS_NEWLINE || et == TokenType.WHITE_SPACE
|
||||
d <- Option(psiFile.findElementAt(element.getTextOffset - 1))
|
||||
if d.getNode.getElementType == HS_DOT
|
||||
e <- Option(psiFile.findElementAt(element.getTextOffset - 2))
|
||||
p <- HaskellPsiUtil.findQualifiedNameParent(e)
|
||||
p <- HaskellPsiUtil.findQualifiedName(e)
|
||||
} yield p).
|
||||
orElse(HaskellPsiUtil.findQualifiedNameParent(element))
|
||||
orElse(HaskellPsiUtil.findQualifiedName(element))
|
||||
}
|
||||
|
||||
|
||||
@ -210,12 +203,9 @@ class HaskellCompletionContributor extends CompletionContributor {
|
||||
|
||||
ProgressManager.checkCanceled()
|
||||
|
||||
for {
|
||||
file <- projectFile
|
||||
info <- stackComponentInfo
|
||||
gInfo <- globalInfo
|
||||
} yield
|
||||
resultSet.addAllElements(getAvailableLookupElements(gInfo, info, file).asJava)
|
||||
val currentElement = op.flatMap(HaskellPsiUtil.findNamedElement)
|
||||
|
||||
projectFile.foreach(f => resultSet.addAllElements(getAvailableLookupElements(f, currentElement).asJava))
|
||||
|
||||
ProgressManager.checkCanceled()
|
||||
|
||||
@ -227,19 +217,14 @@ class HaskellCompletionContributor extends CompletionContributor {
|
||||
|
||||
ProgressManager.checkCanceled()
|
||||
|
||||
op.foreach(element => {
|
||||
val localElements = HaskellPsiUtil.findNamedElement(element) match {
|
||||
case Some(ne) => findLocalElements(element).filterNot(_ == ne)
|
||||
case None => findLocalElements(element)
|
||||
}
|
||||
resultSet.addAllElements(localElements.map(HaskellCompletionContributor.createLocalLookupElement).asJavaCollection)
|
||||
})
|
||||
val localLookupElements = currentElement.map(ce => findLocalElements(ce).filterNot(_ == ce).map(createLocalLookupElement)).getOrElse(Stream())
|
||||
resultSet.addAllElements(localLookupElements.asJavaCollection)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val smartProvider: CompletionProvider[CompletionParameters] = new CompletionProvider[CompletionParameters] {
|
||||
private val smartProvider: CompletionProvider[CompletionParameters] = new CompletionProvider[CompletionParameters] {
|
||||
|
||||
import intellij.haskell.external.execution.HaskellCompilationResultHelper.LayoutSpaceChar
|
||||
|
||||
@ -252,7 +237,7 @@ class HaskellCompletionContributor extends CompletionContributor {
|
||||
HaskellEditorUtil.showHaskellSupportIsNotAvailableWhileInitializing(project)
|
||||
} else {
|
||||
Option(parameters.getOriginalPosition) match {
|
||||
case Some(position) if HaskellPsiUtil.findExpressionParent(position).isDefined & position.getNode.getElementType == HaskellTypes.HS_UNDERSCORE =>
|
||||
case Some(position) if HaskellPsiUtil.findExpression(position).isDefined & position.getNode.getElementType == HaskellTypes.HS_UNDERSCORE =>
|
||||
val offset = position.getTextOffset
|
||||
val editor = parameters.getEditor
|
||||
HaskellAnnotator.findHighlightInfo(project, offset, editor) match {
|
||||
@ -318,10 +303,8 @@ class HaskellCompletionContributor extends CompletionContributor {
|
||||
}
|
||||
}
|
||||
|
||||
import HaskellCompletionContributor._
|
||||
|
||||
private def findLocalElements(element: PsiElement) = {
|
||||
HaskellPsiUtil.findExpressionParent(element).toStream.flatMap(e => HaskellPsiUtil.findNamedElements(e)).filter {
|
||||
HaskellPsiUtil.findExpression(element).toStream.flatMap(e => HaskellPsiUtil.findNamedElements(e)).filter {
|
||||
case _: HaskellVarid => true
|
||||
case _: HaskellVarsym => true
|
||||
case _: HaskellConsym => true
|
||||
@ -330,11 +313,11 @@ class HaskellCompletionContributor extends CompletionContributor {
|
||||
}
|
||||
|
||||
private def isImportSpecInProgress(element: PsiElement): Boolean = {
|
||||
Option(PsiTreeUtil.findFirstParent(element, ImportSpecCondition)).isDefined
|
||||
Option(TreeUtil.findParent(element.getNode, HaskellTypes.HS_IMPORT_SPEC)).isDefined
|
||||
}
|
||||
|
||||
private def isFileHeaderPragmaInProgress(element: PsiElement): Boolean = {
|
||||
Option(PsiTreeUtil.findFirstParent(element, FileHeaderCondition)).isDefined
|
||||
Option(TreeUtil.findParent(element.getNode, HaskellTypes.HS_FILE_HEADER)).isDefined
|
||||
}
|
||||
|
||||
private def isPragmaInProgress(element: PsiElement): Boolean = {
|
||||
@ -342,14 +325,14 @@ class HaskellCompletionContributor extends CompletionContributor {
|
||||
}
|
||||
|
||||
private def isImportModuleDeclarationInProgress(element: PsiElement): Boolean = {
|
||||
Option(PsiTreeUtil.findFirstParent(element, ImportDeclarationCondition)).isDefined ||
|
||||
HaskellPsiUtil.findImportDeclaration(element).isDefined ||
|
||||
Option(TreeUtil.findSiblingBackward(element.getNode, HS_IMPORT)).isDefined ||
|
||||
isImportIdInProgressInsideImportModuleDeclaration(element)
|
||||
}
|
||||
|
||||
private def isImportIdInProgressInsideImportModuleDeclaration(element: PsiElement): Boolean = {
|
||||
val prevNode = Option(TreeUtil.prevLeaf(element.getNode))
|
||||
prevNode.exists(node => Option(PsiTreeUtil.findFirstParent(node.getPsi, ImportDeclarationCondition)).isDefined)
|
||||
prevNode.exists(node => HaskellPsiUtil.findImportDeclaration(element).isDefined)
|
||||
}
|
||||
|
||||
private def isNCommentInProgress(element: PsiElement): Boolean = {
|
||||
@ -357,7 +340,7 @@ class HaskellCompletionContributor extends CompletionContributor {
|
||||
}
|
||||
|
||||
private def getGlobalInfo(psiFile: PsiFile): Option[(StackComponentInfo, StackComponentGlobalInfo)] = {
|
||||
val globalInfo = ApplicationManager.getApplication.executeOnPooledThread(ScalaUtil.callable {
|
||||
val infos = ApplicationManager.getApplication.executeOnPooledThread(ScalaUtil.callable {
|
||||
for {
|
||||
info <- HaskellComponentsManager.findStackComponentInfo(psiFile)
|
||||
globalInfo <- HaskellComponentsManager.findStackComponentGlobalInfo(info)
|
||||
@ -367,12 +350,12 @@ class HaskellCompletionContributor extends CompletionContributor {
|
||||
new WaitFor(ApplicationUtil.timeout, 1) {
|
||||
override def condition(): Boolean = {
|
||||
ProgressManager.checkCanceled()
|
||||
globalInfo.isDone
|
||||
infos.isDone
|
||||
}
|
||||
}
|
||||
|
||||
if (globalInfo.isDone) {
|
||||
globalInfo.get()
|
||||
if (infos.isDone) {
|
||||
infos.get()
|
||||
} else {
|
||||
HaskellNotificationGroup.logInfoEvent(psiFile.getProject, "Timeout in getGlobalInfo for " + psiFile.getName)
|
||||
None
|
||||
@ -382,9 +365,9 @@ class HaskellCompletionContributor extends CompletionContributor {
|
||||
private def findAvailableIdsForImportModuleSpec(stackComponentGlobalInfo: StackComponentGlobalInfo, psiFile: PsiFile, element: PsiElement) = {
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
|
||||
HaskellPsiUtil.findImportDeclarationParent(element).flatMap(_.getModuleName) match {
|
||||
HaskellPsiUtil.findImportDeclaration(element).flatMap(_.getModuleName) match {
|
||||
case Some(moduleName) =>
|
||||
val ids = HaskellComponentsManager.findExportedModuleIdentifiers(psiFile, moduleName).map(_.map(i => i.map(x => createLookupElement(x, addParens = true))).getOrElse(Iterable()))
|
||||
val ids = HaskellComponentsManager.findModuleIdentifiers(psiFile.getProject, moduleName).map(_.map(i => i.map(x => createLookupElement(x, addParens = true))).getOrElse(Iterable()))
|
||||
|
||||
new WaitFor(ApplicationUtil.timeout, 1) {
|
||||
override def condition(): Boolean = {
|
||||
@ -436,11 +419,10 @@ class HaskellCompletionContributor extends CompletionContributor {
|
||||
HaddockIds.map(p => LookupElementBuilder.create(p).withIcon(HaskellIcons.HaskellSmallBlueLogo).withTailText(" haddock", true))
|
||||
}
|
||||
|
||||
private def getAvailableLookupElements(globalInfo: StackComponentGlobalInfo, info: StackComponentInfo, psiFile: PsiFile): Iterable[LookupElementBuilder] = {
|
||||
private def getAvailableLookupElements(psiFile: PsiFile, currentElement: Option[HaskellNamedElement]): Iterable[LookupElementBuilder] = {
|
||||
val moduleName = HaskellPsiUtil.findModuleName(psiFile)
|
||||
ProgressManager.checkCanceled()
|
||||
|
||||
useAvailableModuleIdentifiers(globalInfo, info, psiFile, moduleName, (f1, f2) => f1.map(mi => createLookupElement(mi)) ++ getLocalTopLevelLookupElements(psiFile, moduleName, f2))
|
||||
FileModuleIdentifiers.findAvailableModuleIdentifiers(psiFile).map(createLookupElement(_)) ++ findTopLeveLookupElements(psiFile, moduleName, currentElement)
|
||||
}
|
||||
|
||||
private def getKeywordLookupElements = {
|
||||
@ -454,244 +436,48 @@ class HaskellCompletionContributor extends CompletionContributor {
|
||||
private def getSpecialReservedIdLookupElements = {
|
||||
SpecialReservedIds.map(sr => LookupElementBuilder.create(sr).withIcon(HaskellIcons.HaskellSmallBlueLogo).withTailText(" special keyword", true))
|
||||
}
|
||||
}
|
||||
|
||||
object FileModuleIdentifiers {
|
||||
|
||||
private case class Key(psiFile: PsiFile)
|
||||
|
||||
private type Result = Option[ModuleIdentifiers]
|
||||
|
||||
// TODO Maybe use the buildAsyncFuture
|
||||
private final val Cache: LoadingCache[Key, Result] = Scaffeine().build((k: Key) => findModuleIdentifiers(k))
|
||||
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
|
||||
def invalidate(psiFile: PsiFile): Unit = {
|
||||
Cache.invalidate(Key(psiFile))
|
||||
}
|
||||
|
||||
def refresh(psiFile: PsiFile): Unit = {
|
||||
Cache.refresh(Key(psiFile))
|
||||
}
|
||||
|
||||
// Invalidate files which have imported this module
|
||||
def invalidate(moduleName: String): Unit = {
|
||||
val keys = Cache.asMap().filter { case (_, v) => v.exists(_.exists(_.exists(_.exists(_.moduleName == moduleName)))) }.keys
|
||||
Cache.invalidateAll(keys)
|
||||
}
|
||||
|
||||
def invalidateAll(project: Project): Unit = {
|
||||
Cache.asMap().filter(_._1.psiFile.getProject == project).keys.foreach(Cache.invalidate)
|
||||
}
|
||||
|
||||
def getModuleIdentifiers(psiFile: PsiFile): Option[Iterable[ModuleIdentifier]] = {
|
||||
val key = Key(psiFile)
|
||||
Cache.getIfPresent(key) match {
|
||||
case Some(Some(x)) =>
|
||||
if (x.toSeq.contains(None)) {
|
||||
Cache.invalidate(key)
|
||||
}
|
||||
Some(x.flatten.flatten)
|
||||
case Some(None) =>
|
||||
Cache.invalidate(key)
|
||||
None
|
||||
case None =>
|
||||
if (ApplicationManager.getApplication.isReadAccessAllowed) {
|
||||
val f = Future(Cache.get(key))
|
||||
ScalaFutureUtil.waitWithCheckCancelled(psiFile.getProject, f, "getModuleIdentifiers", 5.seconds).flatten match {
|
||||
case Some(x) =>
|
||||
if (x.toSeq.contains(None)) {
|
||||
Cache.invalidate(key)
|
||||
}
|
||||
Some(x.flatten.flatten)
|
||||
case None =>
|
||||
Cache.invalidate(key)
|
||||
None
|
||||
}
|
||||
} else {
|
||||
Cache.get(key) match {
|
||||
case Some(x) =>
|
||||
if (x.toSeq.contains(None)) {
|
||||
Cache.invalidate(key)
|
||||
}
|
||||
Some(x.flatten.flatten)
|
||||
case None =>
|
||||
Cache.invalidate(key)
|
||||
None
|
||||
}
|
||||
}
|
||||
private def findTopLeveLookupElements(psiFile: PsiFile, moduleName: Option[String], currentElement: Option[HaskellNamedElement]): Iterable[LookupElementBuilder] = {
|
||||
def createTopLevelDeclarationLookupElement(e: HaskellNamedElement, d: HaskellDeclarationElement) = {
|
||||
createLocalTopLevelLookupElement(ApplicationUtil.runReadAction(e.getName), ApplicationUtil.runReadAction(d.getPresentation.getPresentableText), moduleName.getOrElse("-"))
|
||||
}
|
||||
|
||||
val expressionLookupElements = HaskellPsiUtil.findTopLevelExpressions(psiFile).flatMap(_.getQNameList.asScala.headOption.map(_.getIdentifierElement)).filterNot(e => currentElement.contains(e)).map(createLocalLookupElement)
|
||||
|
||||
ApplicationUtil.runReadAction(HaskellPsiUtil.findTopLevelDeclarations(psiFile)).
|
||||
flatMap(d => getIdentifiers(d).map { e =>
|
||||
d match {
|
||||
case dd: HaskellDataDeclaration =>
|
||||
val dataType = dd.getSimpletype.getText
|
||||
val constrType = HaskellComponentsManager.findTypeInfoForElement(e).toOption.filterNot(_.withFailure).getOrElse(
|
||||
HaskellPsiUtil.findDataFieldDecl(e).orElse(HaskellPsiUtil.findDataConstr(e)) match {
|
||||
case Some(ce) => ce.getText
|
||||
case None => createTopLevelDeclarationLookupElement(e, d)
|
||||
})
|
||||
LookupElementBuilder.create(e.getName).withTypeText(s"$constrType -> $dataType")
|
||||
case _ => createTopLevelDeclarationLookupElement(e, d)
|
||||
}
|
||||
}) ++ expressionLookupElements
|
||||
}
|
||||
|
||||
private type ModuleIdentifiers = Iterable[Option[Iterable[ModuleIdentifier]]]
|
||||
|
||||
private def findModuleIdentifiers(k: Key): Option[ModuleIdentifiers] = {
|
||||
val project = k.psiFile.getProject
|
||||
val psiFile = k.psiFile
|
||||
|
||||
ApplicationUtil.runInReadActionWithWriteActionPriority(project, findImportDeclarations(psiFile), "In findModuleIdentifiers") match {
|
||||
case Right(importDeclarations) =>
|
||||
val noImplicitPrelude = if (HaskellProjectUtil.isSourceFile(psiFile)) {
|
||||
HaskellComponentsManager.findStackComponentInfo(psiFile).exists(info => HaskellCompletionContributor.isNoImplicitPreludeActive(info, psiFile))
|
||||
} else {
|
||||
false
|
||||
}
|
||||
val idsF1 = HaskellCompletionContributor.getModuleIdentifiersFromFullImportedModules(noImplicitPrelude, psiFile, importDeclarations)
|
||||
|
||||
val idsF2 = HaskellCompletionContributor.getModuleIdentifiersFromHidingIdsImportedModules(psiFile, importDeclarations)
|
||||
|
||||
val idsF3 = HaskellCompletionContributor.getModuleIdentifiersFromSpecIdsImportedModules(psiFile, importDeclarations)
|
||||
|
||||
val f = for {
|
||||
f1 <- idsF1
|
||||
f2 <- idsF2
|
||||
f3 <- idsF3
|
||||
} yield (f1, f2, f3)
|
||||
|
||||
try {
|
||||
val (x, y, z) = Await.result(f, 5.seconds)
|
||||
Some(x ++ y ++ z)
|
||||
} catch {
|
||||
case _: TimeoutException =>
|
||||
HaskellNotificationGroup.logInfoEvent(project, s"Timeout while find module identifiers for file ${k.psiFile.getName}")
|
||||
None
|
||||
}
|
||||
case Left(noInfo) =>
|
||||
HaskellNotificationGroup.logInfoEvent(project, s"Timeout while find import declarations in findModuleIdentifiers for file ${psiFile.getName}: ${noInfo.message}")
|
||||
None
|
||||
}
|
||||
private def getIdentifiers(declarationElement: HaskellDeclarationElement) = {
|
||||
ApplicationUtil.runReadAction(declarationElement.getIdentifierElements)
|
||||
}
|
||||
}
|
||||
|
||||
object HaskellCompletionContributor {
|
||||
private def createLookupElement(moduleIdentifier: ModuleIdentifier, addParens: Boolean = false): LookupElementBuilder = {
|
||||
addWiths(LookupElementBuilder.create(
|
||||
if (moduleIdentifier.isOperator && addParens)
|
||||
s"""(${moduleIdentifier.name})"""
|
||||
else
|
||||
moduleIdentifier.name
|
||||
), moduleIdentifier)
|
||||
}
|
||||
|
||||
private def createLocalLookupElement(namedElement: HaskellNamedElement): LookupElementBuilder = {
|
||||
def createLocalLookupElement(namedElement: HaskellNamedElement): LookupElementBuilder = {
|
||||
val typeSignature = HaskellComponentsManager.findTypeInfoForElement(namedElement).map(_.typeSignature)
|
||||
LookupElementBuilder.create(namedElement.getName).withTypeText(typeSignature.map(StringUtil.unescapeXml).getOrElse("")).withIcon(HaskellIcons.HaskellSmallBlueLogo)
|
||||
}
|
||||
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
|
||||
// TODO Create cache for this one instead of the downstream one???
|
||||
private def useAvailableModuleIdentifiers[A](globalInfo: StackComponentGlobalInfo, info: StackComponentInfo, psiFile: PsiFile, moduleName: Option[String],
|
||||
doIt: (Iterable[ModuleIdentifier], Iterable[ModuleIdentifier]) => Iterable[A]): Iterable[A] = {
|
||||
|
||||
val fileModuleIdentifiers = Future(FileModuleIdentifiers.getModuleIdentifiers(psiFile))
|
||||
val moduleIdentifiers = Future.successful(Some(Iterable()))
|
||||
|
||||
val result = for {
|
||||
r1 <- fileModuleIdentifiers
|
||||
r2 <- moduleIdentifiers
|
||||
} yield {
|
||||
(r1, r2)
|
||||
}
|
||||
|
||||
ScalaFutureUtil.waitWithCheckCancelled(psiFile.getProject, result, s"In useAvailableModuleIdentifiers wait for all module identifiers for ${psiFile.getName}") match {
|
||||
case Some((result1, result2)) =>
|
||||
(result1, result2) match {
|
||||
case (Some(x), Some(y)) => doIt(x, y)
|
||||
case (_, Some(y)) => doIt(Iterable(), y)
|
||||
case (Some(x), _) => doIt(x, Iterable())
|
||||
case (_, _) => Iterable()
|
||||
}
|
||||
case _ => Iterable()
|
||||
}
|
||||
}
|
||||
|
||||
def getAvailableModuleIdentifiers(globalInfo: StackComponentGlobalInfo, info: StackComponentInfo, psiFile: PsiFile, moduleName: Option[String]): Iterable[ModuleIdentifier] = {
|
||||
useAvailableModuleIdentifiers(globalInfo, info, psiFile, moduleName, (f1, f2) => f1 ++ f2)
|
||||
}
|
||||
|
||||
private sealed trait ImportInfo {
|
||||
def moduleName: String
|
||||
|
||||
def qualified: Boolean
|
||||
|
||||
def as: Option[String]
|
||||
}
|
||||
|
||||
private case class ImportFull(moduleName: String, qualified: Boolean, as: Option[String]) extends ImportInfo
|
||||
|
||||
private case class ImportWithHiding(moduleName: String, ids: Iterable[String], qualified: Boolean, as: Option[String]) extends ImportInfo
|
||||
|
||||
private case class ImportWithIds(moduleName: String, ids: Iterable[String], qualified: Boolean, as: Option[String]) extends ImportInfo
|
||||
|
||||
def isNoImplicitPreludeActive(info: StackComponentInfo, psiFile: PsiFile): Boolean = {
|
||||
info.isImplicitPreludeActive || ApplicationUtil.runReadAction(HaskellPsiUtil.findLanguageExtensions(psiFile)).exists(p => ApplicationUtil.runReadAction(p.getText).contains("NoImplicitPrelude"))
|
||||
}
|
||||
|
||||
private def getFullImportedModules(implicitPreludeActive: Boolean, psiFile: PsiFile, importDeclarations: Iterable[HaskellImportDeclaration]): Iterable[ImportFull] = {
|
||||
val moduleNames = for {
|
||||
id <- importDeclarations
|
||||
if Option(id.getImportSpec).isEmpty
|
||||
mn <- ApplicationUtil.runReadAction(id.getModuleName)
|
||||
} yield ImportFull(mn, Option(id.getImportQualified).isDefined, Option(id.getImportQualifiedAs).map(qa => ApplicationUtil.runReadAction(qa.getQualifier.getName)))
|
||||
|
||||
if (moduleNames.map(_.moduleName).toSeq.contains(HaskellProjectUtil.Prelude) || implicitPreludeActive) {
|
||||
moduleNames
|
||||
} else {
|
||||
Iterable(ImportFull(HaskellProjectUtil.Prelude, qualified = false, None)) ++ moduleNames
|
||||
}
|
||||
}
|
||||
|
||||
private def getImportedModulesWithHidingIdsSpec(psiFile: PsiFile, importDeclarations: Iterable[HaskellImportDeclaration]): Iterable[ImportWithHiding] = {
|
||||
for {
|
||||
importDeclaration <- importDeclarations.filter(i => Option(i.getImportSpec).flatMap(is => Option(is.getImportHidingSpec)).isDefined)
|
||||
importIdList = importDeclaration.getImportSpec.getImportHidingSpec.getImportIdList
|
||||
mn <- ApplicationUtil.runReadAction(importDeclaration.getModuleName)
|
||||
} yield ImportWithHiding(
|
||||
mn,
|
||||
findImportIds(importIdList),
|
||||
Option(importDeclaration.getImportQualified).isDefined,
|
||||
Option(importDeclaration.getImportQualifiedAs).map(_.getQualifier).map(q => ApplicationUtil.runReadAction(q.getName))
|
||||
)
|
||||
}
|
||||
|
||||
private def getImportedModulesWithSpecIds(psiFile: PsiFile, importDeclarations: Iterable[HaskellImportDeclaration]): Iterable[ImportWithIds] = {
|
||||
for {
|
||||
importDeclaration <- importDeclarations.filter(i => Option(i.getImportSpec).flatMap(is => Option(is.getImportIdsSpec)).isDefined)
|
||||
importIdList = importDeclaration.getImportSpec.getImportIdsSpec.getImportIdList
|
||||
mn <- ApplicationUtil.runReadAction(importDeclaration.getModuleName)
|
||||
} yield ImportWithIds(
|
||||
mn,
|
||||
findImportIds(importIdList),
|
||||
Option(importDeclaration.getImportQualified).isDefined,
|
||||
Option(importDeclaration.getImportQualifiedAs).map(_.getQualifier).map(q => ApplicationUtil.runReadAction(q.getName))
|
||||
)
|
||||
}
|
||||
|
||||
def getModuleIdentifiersFromFullImportedModules(impliciPrelueActive: Boolean, psiFile: PsiFile, importDeclarations: Iterable[HaskellImportDeclaration]): Future[Iterable[Option[Iterable[ModuleIdentifier]]]] = {
|
||||
val importInfos = getFullImportedModules(impliciPrelueActive, psiFile, importDeclarations)
|
||||
|
||||
Future.sequence(importInfos.map(importInfo => {
|
||||
val allModuleIdentifiers = HaskellComponentsManager.findExportedModuleIdentifiers(psiFile, importInfo.moduleName)
|
||||
allModuleIdentifiers.map(mi => mi.map(i => createQualifiedModuleIdentifiers(importInfo, i)))
|
||||
}))
|
||||
}
|
||||
|
||||
def getModuleIdentifiersFromHidingIdsImportedModules(psiFile: PsiFile, importDeclarations: Iterable[HaskellImportDeclaration]): Future[Iterable[Option[Iterable[ModuleIdentifier]]]] = {
|
||||
val importInfos = getImportedModulesWithHidingIdsSpec(psiFile, importDeclarations)
|
||||
|
||||
Future.sequence(importInfos.map(importInfo => {
|
||||
val allModuleIdentifiers = HaskellComponentsManager.findExportedModuleIdentifiers(psiFile, importInfo.moduleName)
|
||||
allModuleIdentifiers.map(ids => ids.map(is => createQualifiedModuleIdentifiers(importInfo, is.filterNot(mi => importInfo.ids.exists(_ == mi.name)))))
|
||||
}))
|
||||
}
|
||||
|
||||
def getModuleIdentifiersFromSpecIdsImportedModules(psiFile: PsiFile, importDeclarations: Iterable[HaskellImportDeclaration]): Future[Iterable[Option[Iterable[ModuleIdentifier]]]] = {
|
||||
val importInfos = getImportedModulesWithSpecIds(psiFile, importDeclarations)
|
||||
|
||||
Future.sequence(importInfos.map(importInfo => {
|
||||
val allModuleIdentifiers = HaskellComponentsManager.findExportedModuleIdentifiers(psiFile, importInfo.moduleName)
|
||||
allModuleIdentifiers.map(ids => ids.map(is => createQualifiedModuleIdentifiers(importInfo, is.filter(mi => importInfo.ids.exists(id => if (mi.isOperator) s"(${mi.name})" == id else id == mi.name)))))
|
||||
}))
|
||||
}
|
||||
|
||||
private def createLocalTopLevelLookupElement(moduleIdentifier: ModuleIdentifier): LookupElementBuilder = {
|
||||
createLocalTopLevelLookupElement(moduleIdentifier.name, moduleIdentifier.declaration, moduleIdentifier.moduleName)
|
||||
}
|
||||
|
||||
private def createLocalTopLevelLookupElement(name: String, declaration: String, module: String): LookupElementBuilder = {
|
||||
LookupElementBuilder.create(name).withTypeText(declaration).withIcon(findIcon(declaration))
|
||||
}
|
||||
@ -719,76 +505,4 @@ object HaskellCompletionContributor {
|
||||
case _ => HaskellSmallBlueLogo
|
||||
}
|
||||
}
|
||||
|
||||
private def createLookupElement(moduleIdentifier: ModuleIdentifier, addParens: Boolean = false) = {
|
||||
addWiths(LookupElementBuilder.create(
|
||||
if (moduleIdentifier.isOperator && addParens)
|
||||
s"""(${moduleIdentifier.name})"""
|
||||
else
|
||||
moduleIdentifier.name
|
||||
), moduleIdentifier)
|
||||
}
|
||||
|
||||
private def getLocalTopLevelLookupElements(psiFile: PsiFile, moduleName: Option[String], localIdentifiers: Iterable[ModuleIdentifier]): Iterable[LookupElementBuilder] = {
|
||||
moduleName match {
|
||||
case Some(_) =>
|
||||
ProgressManager.checkCanceled()
|
||||
|
||||
val importDeclarations = ApplicationUtil.runReadAction(HaskellPsiUtil.findImportDeclarations(psiFile))
|
||||
val localLookupElements = localIdentifiers.map(mi =>
|
||||
if (moduleName.contains(mi.moduleName)) {
|
||||
createLocalTopLevelLookupElement(mi)
|
||||
} else {
|
||||
val qualifier = importDeclarations.find(id => ApplicationUtil.runReadAction(id.getModuleName).contains(mi.moduleName)).flatMap(id => Option(id.getImportQualifiedAs).map(q => ApplicationUtil.runReadAction(q.getQualifier.getName)))
|
||||
qualifier match {
|
||||
case Some(q) => createLookupElement(mi.copy(name = s"$q.${mi.name}"))
|
||||
case None => createLookupElement(mi)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
localLookupElements ++ findRemainingTopLeveLookupElements(psiFile, moduleName, localIdentifiers)
|
||||
case None =>
|
||||
HaskellNotificationGroup.logWarningEvent(psiFile.getProject, s"No REPL support for suggesting local top level identifiers because no module defined in `${psiFile.getName}`")
|
||||
findRemainingTopLeveLookupElements(psiFile, None, Iterable())
|
||||
}
|
||||
}
|
||||
|
||||
private def findRemainingTopLeveLookupElements(psiFile: PsiFile, moduleName: Option[String], localIdentifiers: Iterable[ModuleIdentifier]) = {
|
||||
def createTopLevelDeclarationLookupElement(e: HaskellNamedElement, d: HaskellDeclarationElement) = {
|
||||
createLocalTopLevelLookupElement(ApplicationUtil.runReadAction(e.getName), ApplicationUtil.runReadAction(d.getPresentation.getPresentableText), moduleName.getOrElse("-"))
|
||||
}
|
||||
|
||||
ApplicationUtil.runReadAction(HaskellPsiUtil.findTopLevelDeclarations(psiFile)).filterNot(_.isInstanceOf[HaskellModuleDeclaration]).
|
||||
flatMap(d => getIdentifiers(d).map { e =>
|
||||
if (HaskellPsiUtil.findDataDeclarationElementParent(e).isDefined | HaskellPsiUtil.findNewTypeDeclarationElementParent(e).isDefined) {
|
||||
d match {
|
||||
case _: HaskellDataDeclaration => createTopLevelDeclarationLookupElement(e, d)
|
||||
case _: HaskellNewtypeDeclaration => createTopLevelDeclarationLookupElement(e, d)
|
||||
case _ => createLocalLookupElement(e)
|
||||
}
|
||||
} else {
|
||||
createTopLevelDeclarationLookupElement(e, d)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private def getIdentifiers(declarationElement: HaskellDeclarationElement) = {
|
||||
ApplicationUtil.runReadAction(declarationElement.getIdentifierElements)
|
||||
}
|
||||
|
||||
private def createQualifiedModuleIdentifiers(importInfo: ImportInfo, moduleIdentifiers: Iterable[ModuleIdentifier]): Iterable[ModuleIdentifier] = {
|
||||
moduleIdentifiers.flatMap(mi => {
|
||||
(importInfo.as, importInfo.qualified) match {
|
||||
case (None, false) => Iterable(mi, mi.copy(name = mi.moduleName + "." + mi.name))
|
||||
case (None, true) => Iterable(mi.copy(name = mi.moduleName + "." + mi.name))
|
||||
case (Some(q), false) => Iterable(mi, mi.copy(name = q + "." + mi.name))
|
||||
case (Some(q), true) => Iterable(mi.copy(name = q + "." + mi.name))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private def findImportIds(importIdList: util.List[HaskellImportId]): Iterable[String] = {
|
||||
importIdList.asScala.flatMap(importId => Iterable(ApplicationUtil.runReadAction(importId.getCname.getName)) ++ importId.getCnameDotDotList.asScala.flatMap(cndd => Option(cndd.getCname).map(cn => ApplicationUtil.runReadAction(cn.getName))))
|
||||
}
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ class HaskellExtendWordSelectioner extends ExtendWordSelectionHandler {
|
||||
val startOffset = e.getTextRange.getStartOffset
|
||||
val nextEndOffsets = getOffsets(Some(e), ListBuffer.empty[Int], (e: PsiElement) => e.getNextSibling, (e: PsiElement) => e.getTextRange.getEndOffset)
|
||||
|
||||
val prevStartOffsets = getOffsets(HaskellPsiUtil.findQualifiedNameParent(e).flatMap(qe => Option(qe.getPrevSibling)), ListBuffer.empty[Int], (e: PsiElement) => e.getPrevSibling, (e: PsiElement) => e.getTextRange.getStartOffset)
|
||||
val prevStartOffsets = getOffsets(HaskellPsiUtil.findQualifiedName(e).flatMap(qe => Option(qe.getPrevSibling)), ListBuffer.empty[Int], (e: PsiElement) => e.getPrevSibling, (e: PsiElement) => e.getTextRange.getStartOffset)
|
||||
val lastEndOffset = nextEndOffsets.lastOption.getOrElse(e.getTextRange.getEndOffset)
|
||||
|
||||
(nextEndOffsets.map(eo => new TextRange(startOffset, eo)) ++ prevStartOffsets.map(so => new TextRange(so, lastEndOffset))).asJava
|
||||
@ -52,7 +52,7 @@ class HaskellExtendWordSelectioner extends ExtendWordSelectionHandler {
|
||||
|
||||
element match {
|
||||
case Some(e) =>
|
||||
HaskellPsiUtil.findQualifiedNameParent(e) match {
|
||||
HaskellPsiUtil.findQualifiedName(e) match {
|
||||
case None => e match {
|
||||
case e: PsiWhiteSpace => recur(e)
|
||||
case e: PsiElement if e.getNode.getElementType == HS_COMMA => recur(e)
|
||||
|
@ -82,7 +82,7 @@ private[component] object AvailableModuleNamesComponent {
|
||||
private def findModuleNamesInModule(project: Project, currentModule: Module, modules: Seq[Module], includeTests: Boolean): Iterable[String] = {
|
||||
for {
|
||||
vf <- findHaskellFiles(project, currentModule, modules, includeTests)
|
||||
hf <- HaskellFileUtil.convertToHaskellFileInReadAction(project, vf).toOption.flatten
|
||||
hf <- HaskellFileUtil.convertToHaskellFileInReadAction(project, vf).toSeq
|
||||
mn <- HaskellPsiUtil.findModuleName(hf)
|
||||
} yield mn
|
||||
}
|
||||
|
@ -17,17 +17,18 @@
|
||||
package intellij.haskell.external.component
|
||||
|
||||
import com.github.blemale.scaffeine.{AsyncLoadingCache, Scaffeine}
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiFile
|
||||
import intellij.haskell.external.repl._
|
||||
import intellij.haskell.util.index.HaskellModuleNameIndex
|
||||
import intellij.haskell.util.{HaskellProjectUtil, ScalaFutureUtil, StringUtil}
|
||||
import intellij.haskell.util.{HaskellFileUtil, StringUtil}
|
||||
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
|
||||
private[component] object BrowseModuleComponent {
|
||||
|
||||
private case class Key(project: Project, moduleName: String, psiFile: Option[PsiFile], exported: Boolean)
|
||||
private case class Key(project: Project, moduleName: String)
|
||||
|
||||
type BrowseModuleResult = Iterable[ModuleIdentifier]
|
||||
private type BrowseModuleInternalResult = Either[NoInfo, Iterable[ModuleIdentifier]]
|
||||
@ -45,82 +46,36 @@ private[component] object BrowseModuleComponent {
|
||||
case Right(ids) => Some(ids)
|
||||
case Left(NoInfoAvailable(_, _)) =>
|
||||
None
|
||||
case Left(ReplNotAvailable) | Left(ReplIsBusy) | Left(IndexNotReady) | Left(ModuleNotLoaded(_)) | Left(ReadActionTimeout(_)) =>
|
||||
case Left(ReplNotAvailable) | Left(ReplIsBusy) | Left(IndexNotReady) | Left(ModuleNotAvailable(_)) | Left(ReadActionTimeout(_)) =>
|
||||
Cache.synchronous().invalidate(key)
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
def findLibraryModuleIdentifiers(project: Project, moduleName: String)(implicit ec: ExecutionContext): Future[Option[Iterable[ModuleIdentifier]]] = {
|
||||
val key = Key(project, moduleName, None, exported = true)
|
||||
def findModuleIdentifiers(project: Project, moduleName: String)(implicit ec: ExecutionContext): Future[Option[Iterable[ModuleIdentifier]]] = {
|
||||
val key = Key(project, moduleName)
|
||||
matchResult(key, Cache.get(key))
|
||||
}
|
||||
|
||||
def findLibraryModuleIdentifiersSync(project: Project, moduleName: String): BrowseModuleInternalResult = {
|
||||
val key = Key(project, moduleName, None, exported = true)
|
||||
def findModuleIdentifiersSync(project: Project, moduleName: String): BrowseModuleInternalResult = {
|
||||
val key = Key(project, moduleName)
|
||||
Cache.synchronous().get(key)
|
||||
}
|
||||
|
||||
def loadExportedIdentifiersSync(project: Project, psiFile: PsiFile, moduleName: String): Unit = {
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
val result = findExportedIdentifiers(psiFile, moduleName)
|
||||
ScalaFutureUtil.waitForValue(project, result, s"loading exported identifiers in findExportedIdentifiersSync for module $moduleName")
|
||||
}
|
||||
|
||||
def findTopLevelIdentifiers(psiFile: PsiFile, moduleName: String)(implicit ec: ExecutionContext): Future[Option[Iterable[ModuleIdentifier]]] = {
|
||||
val key = Key(psiFile.getProject, moduleName, Some(psiFile), exported = false)
|
||||
|
||||
if (LoadComponent.isFileLoaded(psiFile)) {
|
||||
matchResult(key, Cache.get(key))
|
||||
} else {
|
||||
matchResult(key, Cache.getIfPresent(key).getOrElse(Future.successful(Left(ModuleNotLoaded(key.moduleName)))))
|
||||
}
|
||||
}
|
||||
|
||||
def findExportedIdentifiers(psiFile: PsiFile, moduleName: String)(implicit ec: ExecutionContext): Future[Option[Iterable[ModuleIdentifier]]] = {
|
||||
val moduleFiles = HaskellModuleNameIndex.findFileByModuleName(psiFile.getProject, moduleName)
|
||||
val projectFile = moduleFiles.toOption.exists(_.headOption.exists(HaskellProjectUtil.isSourceFile))
|
||||
val project = psiFile.getProject
|
||||
|
||||
if (projectFile) {
|
||||
val key = Key(project, moduleName, Some(psiFile), exported = true)
|
||||
val result = matchResult(key, Cache.get(key))
|
||||
result.map {
|
||||
case r@Some(_) => r
|
||||
case None =>
|
||||
val result = Cache.synchronous().asMap().find { case (k, _) => k.project == project && k.moduleName == moduleName }.map(_._2).flatMap(_.toOption)
|
||||
result.foreach(r => Cache.synchronous().put(key, Right(r)))
|
||||
result
|
||||
}
|
||||
} else {
|
||||
findLibraryModuleIdentifiers(psiFile.getProject, moduleName)
|
||||
}
|
||||
}
|
||||
|
||||
def findModuleIdentifiersInCache(project: Project): Iterable[ModuleIdentifier] = {
|
||||
Cache.synchronous().asMap().filter(_._1.project == project).values.flatMap(_.toSeq).flatten
|
||||
}
|
||||
|
||||
def refreshTopLevel(project: Project, moduleName: String, psiFile: PsiFile): Unit = {
|
||||
val key = Key(project, moduleName, Some(psiFile), exported = false)
|
||||
Cache.synchronous().refresh(key)
|
||||
}
|
||||
|
||||
def invalidateTopLevel(project: Project, moduleName: String, psiFile: PsiFile): Unit = {
|
||||
val key = Key(project, moduleName, Some(psiFile), exported = false)
|
||||
Cache.synchronous().invalidate(key)
|
||||
}
|
||||
|
||||
def invalidateExportedModuleName(project: Project, moduleName: String): Unit = {
|
||||
def invalidateModuleName(project: Project, moduleName: String): Unit = {
|
||||
val synchronousCache = Cache.synchronous
|
||||
val key = synchronousCache.asMap().keys.filter(k => k.moduleName == moduleName && k.exported) // Can be more than one for a module name if file of module can not be found
|
||||
val key = synchronousCache.asMap().keys.filter(k => k.moduleName == moduleName) // Can be more than one for a module name if file of module can not be found
|
||||
key.foreach(synchronousCache.invalidate)
|
||||
}
|
||||
|
||||
def refreshExportedModuleNames(project: Project, moduleNames: Seq[String]): Unit = {
|
||||
def invalidateModuleNames(project: Project, moduleNames: Seq[String]): Unit = {
|
||||
val synchronousCache = Cache.synchronous
|
||||
val keys = synchronousCache.asMap().keys.filter(k => moduleNames.contains(k.moduleName))
|
||||
keys.foreach(synchronousCache.refresh)
|
||||
keys.foreach(synchronousCache.invalidate)
|
||||
}
|
||||
|
||||
def invalidate(project: Project): Unit = {
|
||||
@ -133,81 +88,59 @@ private[component] object BrowseModuleComponent {
|
||||
val project = key.project
|
||||
val moduleName = key.moduleName
|
||||
|
||||
key.psiFile match {
|
||||
case Some(psiFile) if !key.exported =>
|
||||
if (LoadComponent.isFileLoaded(psiFile)) {
|
||||
StackReplsManager.getProjectRepl(psiFile) match {
|
||||
case Some(repl) =>
|
||||
if (repl.isBusy) {
|
||||
Left(ReplIsBusy)
|
||||
} else if (!repl.available) {
|
||||
Left(ReplNotAvailable)
|
||||
} else {
|
||||
repl.getLocalModuleIdentifiers(moduleName, psiFile) match {
|
||||
case Some(output) if output.stderrLines.isEmpty && output.stdoutLines.nonEmpty => Right(output.stdoutLines.takeWhile(l => !l.startsWith("-- imported via")).flatMap(l => findModuleIdentifiers(project, l, moduleName)))
|
||||
case _ => Left(ReplNotAvailable)
|
||||
}
|
||||
}
|
||||
case _ => Left(ReplNotAvailable)
|
||||
}
|
||||
} else {
|
||||
Left(ModuleNotLoaded(key.moduleName))
|
||||
}
|
||||
case Some(psiFile) if key.exported =>
|
||||
val projectRepl = StackReplsManager.getProjectRepl(psiFile)
|
||||
projectRepl match {
|
||||
case Some(repl) =>
|
||||
if (repl.isBusy) {
|
||||
findInOtherRepl(project, psiFile, moduleName, repl) match {
|
||||
HaskellModuleNameIndex.findFilesByModuleName2(project, moduleName) match {
|
||||
case Right(files) => files.headOption match {
|
||||
case Some((moduleFile, isProjectFile)) =>
|
||||
if (isProjectFile) {
|
||||
getCurrentFile(project) match {
|
||||
case Some(cf) => findInRepl(project, StackReplsManager.getProjectRepl(cf), moduleName, None) match {
|
||||
case r@Right(_) => r
|
||||
case _ => Left(ReplIsBusy)
|
||||
}
|
||||
} else if (!repl.available) {
|
||||
findInOtherRepl(project, psiFile, moduleName, repl) match {
|
||||
case r@Right(_) => r
|
||||
case _ => Left(ReplNotAvailable)
|
||||
}
|
||||
} else {
|
||||
if (repl.isBrowseModuleLoaded(moduleName)) {
|
||||
repl.getModuleIdentifiers(moduleName, psiFile) match {
|
||||
case Some(output) if output.stderrLines.isEmpty && output.stdoutLines.nonEmpty => Right(output.stdoutLines.flatMap(l => findModuleIdentifiers(project, l, moduleName)))
|
||||
case _ => Left(ReplNotAvailable)
|
||||
}
|
||||
} else {
|
||||
findInOtherRepl(project, psiFile, moduleName, repl)
|
||||
case Left(_) =>
|
||||
val projectRepl = StackReplsManager.getProjectRepl(moduleFile)
|
||||
findInRepl(project, projectRepl, moduleName, Some(moduleFile))
|
||||
}
|
||||
case None =>
|
||||
val projectRepl = StackReplsManager.getProjectRepl(moduleFile)
|
||||
findInRepl(project, projectRepl, moduleName, Some(moduleFile))
|
||||
}
|
||||
case None => Left(ReplNotAvailable)
|
||||
}
|
||||
case None => findLibModuleIdentifiers(project, moduleName)
|
||||
} else {
|
||||
findLibModuleIdentifiers(project, moduleName)
|
||||
}
|
||||
case None => Left(ModuleNotAvailable(moduleName))
|
||||
}
|
||||
case Left(noInfo) => Left(noInfo)
|
||||
}
|
||||
}
|
||||
|
||||
private def findInOtherRepl(project: Project, psiFile: PsiFile, moduleName: String, repl: ProjectStackRepl) = {
|
||||
HaskellModuleNameIndex.findFileByModuleName(project, moduleName) match {
|
||||
case Right(files) => files.headOption match {
|
||||
case Some(f) => val otherRepl = StackReplsManager.getProjectRepl(f)
|
||||
if (otherRepl.contains(repl)) {
|
||||
Left(ReplNotAvailable)
|
||||
} else {
|
||||
otherRepl match {
|
||||
case Some(oRepl) =>
|
||||
if (oRepl.isBusy) {
|
||||
Left(ReplIsBusy)
|
||||
} else if (!oRepl.available) {
|
||||
Left(ReplNotAvailable)
|
||||
} else {
|
||||
oRepl.getModuleIdentifiers(moduleName, f) match {
|
||||
case Some(output) if output.stderrLines.isEmpty && output.stdoutLines.nonEmpty => Right(output.stdoutLines.flatMap(l => findModuleIdentifiers(project, l, moduleName)))
|
||||
case _ => Left(ReplNotAvailable)
|
||||
}
|
||||
}
|
||||
case None => Left(ReplNotAvailable)
|
||||
}
|
||||
}
|
||||
case None => Left(NoInfoAvailable(moduleName, "-"))
|
||||
private def getCurrentFile(project: Project): Option[PsiFile] = {
|
||||
if (StackProjectManager.isInitializing(project)) {
|
||||
None
|
||||
} else {
|
||||
FileEditorManager.getInstance(project).getSelectedFiles.headOption match {
|
||||
case Some(f) => HaskellFileUtil.convertToHaskellFileInReadAction(project, f).toOption
|
||||
case None => None
|
||||
}
|
||||
case Left(noInfo) => Left(noInfo)
|
||||
}
|
||||
}
|
||||
|
||||
private def findInRepl(project: Project, projectRepl: Option[ProjectStackRepl], moduleName: String, psiFile: Option[PsiFile]): Either[NoInfo, Seq[ModuleIdentifier]] = {
|
||||
projectRepl match {
|
||||
case Some(repl) =>
|
||||
if (repl.isBusy) {
|
||||
Left(ReplIsBusy)
|
||||
} else if (!repl.available) {
|
||||
Left(ReplNotAvailable)
|
||||
} else {
|
||||
if (psiFile.isEmpty || repl.isBrowseModuleLoaded(moduleName)) {
|
||||
repl.getModuleIdentifiers(moduleName, psiFile) match {
|
||||
case Some(output) if output.stderrLines.isEmpty && output.stdoutLines.nonEmpty => Right(output.stdoutLines.flatMap(l => findModuleIdentifiers(project, l, moduleName)))
|
||||
case _ => Left(ReplNotAvailable)
|
||||
}
|
||||
} else {
|
||||
Left(ModuleNotAvailable(moduleName))
|
||||
}
|
||||
}
|
||||
case None => Left(ReplNotAvailable)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,6 @@ import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.util.WaitFor
|
||||
import intellij.haskell.HaskellNotificationGroup
|
||||
import intellij.haskell.editor.FileModuleIdentifiers
|
||||
import intellij.haskell.external.repl.StackRepl.StackReplOutput
|
||||
import intellij.haskell.external.repl.{ProjectStackRepl, StackReplsManager}
|
||||
import intellij.haskell.navigation.HaskellReference
|
||||
@ -52,8 +51,7 @@ private[component] object DefinitionLocationComponent {
|
||||
case Left(noInfo) => Left(noInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
def findDefinitionLocation(psiFile: PsiFile, qualifiedNameElement: HaskellQualifiedNameElement, importQualifier: Option[String]): DefinitionLocationResult = {
|
||||
val key = Key(psiFile, qualifiedNameElement, importQualifier)
|
||||
@ -64,23 +62,10 @@ private[component] object DefinitionLocationComponent {
|
||||
Cache.synchronous().asMap().find { case (k, _) => k.psiFile == psiFile && k.qualifiedNameElement == qualifiedNameElement }.flatMap(_._2.toOption)
|
||||
}
|
||||
|
||||
def findReferencesInCache(targetFile: PsiFile): Seq[(PsiFile, HaskellQualifiedNameElement)] = {
|
||||
def invalidateOtherFiles(currentFile: PsiFile, name: String): Unit = {
|
||||
val synchronousCache = Cache.synchronous()
|
||||
synchronousCache.asMap().filter { case (k, v) => v.toOption.exists(l => {
|
||||
if (!l.namedElement.isValid) {
|
||||
synchronousCache.invalidate(k)
|
||||
false
|
||||
} else {
|
||||
l.namedElement.getContainingFile.getOriginalFile == targetFile
|
||||
}
|
||||
})
|
||||
}.keys.map(k => (k.psiFile, k.qualifiedNameElement)).toSeq
|
||||
}
|
||||
|
||||
def refresh(elements: Seq[HaskellQualifiedNameElement]): Unit = {
|
||||
val synchronousCache = Cache.synchronous()
|
||||
val keys = synchronousCache.asMap().keys.filter(k => elements.contains(k.qualifiedNameElement))
|
||||
keys.foreach(synchronousCache.refresh)
|
||||
val keys = synchronousCache.asMap().filter { case (k, v) => k.qualifiedNameElement.getIdentifierElement.getName == name && k.psiFile != currentFile }.keys
|
||||
keys.foreach(synchronousCache.invalidate)
|
||||
}
|
||||
|
||||
private def checkValidKey(key: Key): Boolean = {
|
||||
@ -128,105 +113,77 @@ private[component] object DefinitionLocationComponent {
|
||||
|
||||
val name = ApplicationUtil.runReadAction(identifierElement.getName)
|
||||
if (name.headOption.exists(_.isUpper)) {
|
||||
createDefinitionLocationResult(project, psiFile, key, name, withoutLastColumn = true)
|
||||
createDefinitionLocationResult(project, key, name, withoutLastColumn = true)
|
||||
} else {
|
||||
createDefinitionLocationResult(project, psiFile, key, name, withoutLastColumn = false)
|
||||
createDefinitionLocationResult(project, key, name, withoutLastColumn = false)
|
||||
}
|
||||
}
|
||||
|
||||
private def createDefinitionLocationResult(project: Project, psiFile: PsiFile, key: Key, name: String, withoutLastColumn: Boolean): DefinitionLocationResult = {
|
||||
private def findLocationByImportedIdentifiers(project: Project, key: Key, name: String): Option[Either[NoInfoAvailable, PackageModuleLocation]] = {
|
||||
val psiFile = key.psiFile
|
||||
val qName1 = key.qualifiedNameElement.getName
|
||||
val mids = FileModuleIdentifiers.findAvailableModuleIdentifiers(psiFile)
|
||||
ProgressManager.checkCanceled()
|
||||
val sourceFile = HaskellProjectUtil.isSourceFile(psiFile)
|
||||
ProgressManager.checkCanceled()
|
||||
val moduleName = HaskellPsiUtil.findModuleName(psiFile)
|
||||
ProgressManager.checkCanceled()
|
||||
if (sourceFile) {
|
||||
// Again workaround intero bug
|
||||
if (key.importQualifier.isDefined || key.qualifiedNameElement.getQualifierName.isDefined) {
|
||||
val qName1 = key.qualifiedNameElement.getName
|
||||
val result = FileModuleIdentifiers.getModuleIdentifiers(psiFile) match {
|
||||
case None => Some(Left(ReadActionTimeout("")))
|
||||
case Some(mids) =>
|
||||
ProgressManager.checkCanceled()
|
||||
val qName = key.importQualifier match {
|
||||
case None => qName1
|
||||
case Some(q) => q + "." + qName1
|
||||
}
|
||||
for {
|
||||
mid <- mids.find(_.name == qName)
|
||||
} yield {
|
||||
val findResult = HaskellReference.findIdentifiersByModuleAndName(project, mid.moduleName, name)
|
||||
findResult match {
|
||||
case Right(nes) if nes.nonEmpty => nes.headOption.map(ne => Right(PackageModuleLocation(mid.moduleName, ne, name, Some(qName)))).getOrElse(Left(NoInfoAvailable(name, psiFile.getName)))
|
||||
case _ => Left(NoInfoAvailable(name, psiFile.getName))
|
||||
}
|
||||
}
|
||||
}
|
||||
val qName2 = StringUtil.removeOuterParens(qName1)
|
||||
val qName = key.importQualifier match {
|
||||
case None => qName2
|
||||
case Some(q) => q + "." + qName2
|
||||
}
|
||||
for {
|
||||
mid <- mids.find(_.name == qName)
|
||||
} yield {
|
||||
HaskellReference.findIdentifiersByModuleAndName(project, Seq(mid.moduleName), name) match {
|
||||
case Right(nes) if nes.nonEmpty => nes.headOption.map(ne => Right(PackageModuleLocation(mid.moduleName, ne, name, Some(qName)))).getOrElse(Left(NoInfoAvailable(name, psiFile.getName)))
|
||||
case _ => Left(NoInfoAvailable(name, psiFile.getName))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ProgressManager.checkCanceled()
|
||||
private def createDefinitionLocationResult(project: Project, key: Key, name: String, withoutLastColumn: Boolean): DefinitionLocationResult = {
|
||||
val psiFile = key.psiFile
|
||||
ProgressManager.checkCanceled()
|
||||
val libraryFile = HaskellProjectUtil.isLibraryFile(psiFile)
|
||||
ProgressManager.checkCanceled()
|
||||
// Again workaround intero bug
|
||||
if (libraryFile || key.importQualifier.isDefined || key.qualifiedNameElement.getQualifierName.isDefined) {
|
||||
val locations = findLocationByImportedIdentifiers(project, key, name)
|
||||
|
||||
val result2 = result match {
|
||||
case Some(r@Right(_)) => r
|
||||
case None => Left(NoInfoAvailable(name, psiFile.getName)) // identifier not imported
|
||||
case Some(Left(_)) =>
|
||||
ProgressManager.checkCanceled()
|
||||
HaskellComponentsManager.findNameInfo(key.qualifiedNameElement, key.importQualifier) match {
|
||||
case Right(infos) =>
|
||||
ProgressManager.checkCanceled()
|
||||
|
||||
locations match {
|
||||
case Some(ls) => ls
|
||||
case None => if (libraryFile) {
|
||||
ProgressManager.checkCanceled()
|
||||
HaskellComponentsManager.findNameInfo(key.qualifiedNameElement) match {
|
||||
case Right(infos) => infos.headOption match {
|
||||
case Some(info) =>
|
||||
ProgressManager.checkCanceled()
|
||||
infos.headOption match {
|
||||
case Some(nameInfo) =>
|
||||
val findInfoResult = HaskellReference.findIdentifiersByNameInfo(nameInfo, key.qualifiedNameElement.getIdentifierElement, project)
|
||||
ProgressManager.checkCanceled()
|
||||
findInfoResult match {
|
||||
case Right(nes) =>
|
||||
nes.headOption match {
|
||||
case Some(r) => HaskellPsiUtil.findModuleName(r.getContainingFile) match {
|
||||
case Some(mn) => Right(PackageModuleLocation(mn, r, name, Some(qName1)))
|
||||
case None => Right(LocalModuleLocation(r.getContainingFile, r, name, Some(qName1)))
|
||||
}
|
||||
case None => Left(NoInfoAvailable(name, psiFile.getName))
|
||||
}
|
||||
case Left(noInfo) => Left(noInfo)
|
||||
HaskellReference.findIdentifiersByNameInfo(info, key.qualifiedNameElement.getIdentifierElement, project) match {
|
||||
case Right(ne) =>
|
||||
if (ne.isEmpty) {
|
||||
// TODO Use findIdentifierInFileByName in HaskellReference
|
||||
HaskellPsiUtil.findHaskellDeclarationElements(psiFile).toSeq.flatMap(_.getIdentifierElements).
|
||||
find(_.getName == name).
|
||||
map(e => Right(PackageModuleLocation("-", e, name))).getOrElse(Left(NoInfoAvailable(name, psiFile.getName)))
|
||||
Left(NoInfoAvailable("", ""))
|
||||
} else {
|
||||
ne.headOption.map(ne => Right(PackageModuleLocation("-", ne, name))).getOrElse(Left(NoInfoAvailable(name, psiFile.getName)))
|
||||
}
|
||||
case None => Left(NoInfoAvailable(name, psiFile.getName))
|
||||
case Left(noInfo) => Left(noInfo)
|
||||
}
|
||||
case Left(noInfo) => Left(noInfo)
|
||||
case None => Left(NoInfoAvailable(name, psiFile.getName))
|
||||
}
|
||||
case Left(noInfo) => Left(noInfo)
|
||||
}
|
||||
} else {
|
||||
findLocationByImportedIdentifiers(project, key, name).getOrElse(Left(NoInfoAvailable(psiFile.getName, name)))
|
||||
}
|
||||
|
||||
result2 match {
|
||||
case Right(location) => Right(location)
|
||||
case l =>
|
||||
HaskellNotificationGroup.logInfoEvent(project, "In createDefinitionLocationResult no result for " + name)
|
||||
l
|
||||
}
|
||||
} else {
|
||||
ProgressManager.checkCanceled()
|
||||
findLocationResultWithRepl(project, psiFile, moduleName, key, name, withoutLastColumn)
|
||||
}
|
||||
} else {
|
||||
ProgressManager.checkCanceled()
|
||||
HaskellComponentsManager.findNameInfo(key.qualifiedNameElement) match {
|
||||
case Right(infos) => infos.headOption match {
|
||||
case Some(info) =>
|
||||
ProgressManager.checkCanceled()
|
||||
val ne = HaskellReference.findIdentifiersByNameInfo(info, key.qualifiedNameElement.getIdentifierElement, project)
|
||||
ne match {
|
||||
case Right(nee) =>
|
||||
if (nee.isEmpty) {
|
||||
HaskellPsiUtil.findHaskellDeclarationElements(psiFile).toSeq.flatMap(_.getIdentifierElements).
|
||||
find(e => e.getName == name).
|
||||
map(e => Right(PackageModuleLocation("-", e, name))).getOrElse(Left(NoInfoAvailable(name, psiFile.getName)))
|
||||
Left(NoInfoAvailable("", ""))
|
||||
} else {
|
||||
nee.headOption.map(e => Right(PackageModuleLocation("-", e, name))).getOrElse(Left(NoInfoAvailable(name, psiFile.getName)))
|
||||
}
|
||||
case Left(noInfo) => Left(noInfo)
|
||||
}
|
||||
case None => Left(NoInfoAvailable(name, psiFile.getName))
|
||||
}
|
||||
case Left(noInfo) => Left(noInfo)
|
||||
}
|
||||
val moduleName = HaskellPsiUtil.findModuleName(psiFile)
|
||||
ProgressManager.checkCanceled()
|
||||
findLocationResultWithRepl(project, psiFile, moduleName, key, name, withoutLastColumn)
|
||||
}
|
||||
}
|
||||
|
||||
@ -266,7 +223,7 @@ private[component] object DefinitionLocationComponent {
|
||||
Left(ReplNotAvailable)
|
||||
} else {
|
||||
if (ApplicationManager.getApplication.isDispatchThread && !LoadComponent.isModuleLoaded(moduleName, psiFile)) {
|
||||
Left(ModuleNotLoaded(moduleName.getOrElse(psiFile.getName)))
|
||||
Left(ModuleNotAvailable(moduleName.getOrElse(psiFile.getName)))
|
||||
} else {
|
||||
f(repl) match {
|
||||
case Some(o) if o.stderrLines.isEmpty && o.stdoutLines.nonEmpty => Right(o)
|
||||
@ -295,7 +252,7 @@ private[component] object DefinitionLocationComponent {
|
||||
case (_, _) => Left(NoInfoAvailable(name, psiFile.getName))
|
||||
}
|
||||
case PackageModulePattern(_, mn) =>
|
||||
findFileByModuleName(project, mn) match {
|
||||
findFilesByModuleName(project, mn) match {
|
||||
case Right(files) =>
|
||||
files.headOption.flatMap(HaskellReference.findIdentifierInFileByName(_, name)) match {
|
||||
case Some(e) => Right(PackageModuleLocation(mn, e, name))
|
||||
@ -303,11 +260,10 @@ private[component] object DefinitionLocationComponent {
|
||||
}
|
||||
case Left(noInfo) => Left(noInfo)
|
||||
}
|
||||
case _ => Left(NoInfoAvailable(name, key.psiFile.getName))
|
||||
case _ => findLocationByImportedIdentifiers(project, key, name).getOrElse(Left(NoInfoAvailable(name, key.psiFile.getName)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private[component] def find(key: Key): DefinitionLocationResult = {
|
||||
val isDispatchThread = ApplicationManager.getApplication.isDispatchThread
|
||||
val isReadAccessAllowed = ApplicationManager.getApplication.isReadAccessAllowed
|
||||
@ -329,7 +285,7 @@ private[component] object DefinitionLocationComponent {
|
||||
} else {
|
||||
if (isDispatchThread) {
|
||||
HaskellNotificationGroup.logInfoEvent(psiFile.getProject, s"No info in DefinitionLocationComponent.find for ${qualifiedNameElement.getName} in ${psiFile.getName} because timeout in dispatch thread")
|
||||
Left(ModuleNotLoaded(psiFile.getName))
|
||||
Left(ModuleNotAvailable(psiFile.getName))
|
||||
}
|
||||
else {
|
||||
HaskellNotificationGroup.logInfoEvent(psiFile.getProject, s"Timeout in DefinitionLocationComponent.find for ${qualifiedNameElement.getName} in ${psiFile.getName}")
|
||||
@ -345,7 +301,7 @@ private[component] object DefinitionLocationComponent {
|
||||
val result = wait(v)
|
||||
result match {
|
||||
case Right(_) => result
|
||||
case Left(ReplIsBusy) | Left(ReadActionTimeout(_)) | Left(IndexNotReady) | Left(ModuleNotLoaded(_)) | Left(ReplNotAvailable) =>
|
||||
case Left(ReplIsBusy) | Left(ReadActionTimeout(_)) | Left(IndexNotReady) | Left(ModuleNotAvailable(_)) | Left(ReplNotAvailable) =>
|
||||
Cache.synchronous().invalidate(key)
|
||||
result
|
||||
case _ => result
|
||||
@ -357,7 +313,7 @@ private[component] object DefinitionLocationComponent {
|
||||
case Right(_) =>
|
||||
Cache.synchronous.put(key, result)
|
||||
result
|
||||
case Left(ReplIsBusy) | Left(ReadActionTimeout(_)) | Left(IndexNotReady) | Left(ReplNotAvailable) | Left(ModuleNotLoaded(_)) =>
|
||||
case Left(ReplIsBusy) | Left(ReadActionTimeout(_)) | Left(IndexNotReady) | Left(ReplNotAvailable) | Left(ModuleNotAvailable(_)) =>
|
||||
result
|
||||
case Left(_) =>
|
||||
Cache.synchronous.put(key, result)
|
||||
|
254
src/main/scala/intellij/haskell/external/component/FileModuleIdentifiers.scala
vendored
Normal file
254
src/main/scala/intellij/haskell/external/component/FileModuleIdentifiers.scala
vendored
Normal file
@ -0,0 +1,254 @@
|
||||
/*
|
||||
* Copyright 2014-2018 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 intellij.haskell.external.component
|
||||
|
||||
import java.util
|
||||
import java.util.concurrent.TimeoutException
|
||||
|
||||
import com.github.blemale.scaffeine.{LoadingCache, Scaffeine}
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiFile
|
||||
import intellij.haskell.HaskellNotificationGroup
|
||||
import intellij.haskell.external.component.HaskellComponentsManager.StackComponentInfo
|
||||
import intellij.haskell.psi.HaskellPsiUtil.findImportDeclarations
|
||||
import intellij.haskell.psi.{HaskellImportDeclaration, HaskellImportId, HaskellPsiUtil}
|
||||
import intellij.haskell.util.{ApplicationUtil, HaskellProjectUtil, ScalaFutureUtil}
|
||||
|
||||
import scala.collection.JavaConverters._
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent.{Await, Future}
|
||||
|
||||
object FileModuleIdentifiers {
|
||||
|
||||
private case class Key(psiFile: PsiFile)
|
||||
|
||||
private type Result = Option[ModuleIdentifiers]
|
||||
|
||||
private final val Cache: LoadingCache[Key, Result] = Scaffeine().build((k: Key) => findModuleIdentifiers(k))
|
||||
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
|
||||
def invalidate(psiFile: PsiFile): Unit = {
|
||||
Cache.invalidate(Key(psiFile))
|
||||
}
|
||||
|
||||
def refresh(psiFile: PsiFile): Unit = {
|
||||
Cache.refresh(Key(psiFile))
|
||||
}
|
||||
|
||||
// Invalidate files which have imported this module
|
||||
def invalidate(moduleName: String): Unit = {
|
||||
val keys = Cache.asMap().filter { case (_, v) => v.exists(_.exists(_.exists(_.exists(_.moduleName == moduleName)))) }.keys
|
||||
Cache.invalidateAll(keys)
|
||||
}
|
||||
|
||||
def invalidateAll(project: Project): Unit = {
|
||||
Cache.asMap().filter(_._1.psiFile.getProject == project).keys.foreach(Cache.invalidate)
|
||||
}
|
||||
|
||||
def findAvailableModuleIdentifiers(psiFile: PsiFile): Iterable[ModuleIdentifier] = {
|
||||
if (ApplicationManager.getApplication.isReadAccessAllowed) {
|
||||
val moduleIdentifiers = Future(getModuleIdentifiers(psiFile))
|
||||
|
||||
ScalaFutureUtil.waitWithCheckCancelled(psiFile.getProject, moduleIdentifiers, s"in findAvailableModuleIdentifiers to get all module identifiers for ${psiFile.getName}") match {
|
||||
case Some(mids) => mids.getOrElse(Iterable())
|
||||
case _ => Iterable()
|
||||
}
|
||||
} else {
|
||||
getModuleIdentifiers(psiFile).getOrElse(Iterable())
|
||||
}
|
||||
}
|
||||
|
||||
private def getModuleIdentifiers(psiFile: PsiFile): Option[Iterable[ModuleIdentifier]] = {
|
||||
val key = Key(psiFile)
|
||||
Cache.getIfPresent(key) match {
|
||||
case Some(Some(mids)) =>
|
||||
if (mids.toSeq.contains(None)) {
|
||||
Cache.invalidate(key)
|
||||
}
|
||||
Some(mids.flatten.flatten)
|
||||
case Some(None) =>
|
||||
Cache.invalidate(key)
|
||||
None
|
||||
case None =>
|
||||
if (ApplicationManager.getApplication.isReadAccessAllowed) {
|
||||
val f = Future(Cache.get(key))
|
||||
ScalaFutureUtil.waitWithCheckCancelled(psiFile.getProject, f, "getModuleIdentifiers", 10.seconds).flatten match {
|
||||
case Some(mids) =>
|
||||
if (mids.toSeq.contains(None)) {
|
||||
Cache.invalidate(key)
|
||||
}
|
||||
Some(mids.flatten.flatten)
|
||||
case None =>
|
||||
Cache.invalidate(key)
|
||||
None
|
||||
}
|
||||
} else {
|
||||
Cache.get(key) match {
|
||||
case Some(mids) =>
|
||||
if (mids.toSeq.contains(None)) {
|
||||
Cache.invalidate(key)
|
||||
}
|
||||
Some(mids.flatten.flatten)
|
||||
case None =>
|
||||
Cache.invalidate(key)
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private type ModuleIdentifiers = Iterable[Option[Iterable[ModuleIdentifier]]]
|
||||
|
||||
private def findModuleIdentifiers(k: Key): Option[ModuleIdentifiers] = {
|
||||
val project = k.psiFile.getProject
|
||||
val psiFile = k.psiFile
|
||||
|
||||
ApplicationUtil.runInReadActionWithWriteActionPriority(project, findImportDeclarations(psiFile), "In findModuleIdentifiers") match {
|
||||
case Right(importDeclarations) =>
|
||||
val noImplicitPrelude = if (HaskellProjectUtil.isSourceFile(psiFile)) {
|
||||
HaskellComponentsManager.findStackComponentInfo(psiFile).exists(info => FileModuleIdentifiers.isNoImplicitPreludeActive(info, psiFile))
|
||||
} else {
|
||||
false
|
||||
}
|
||||
val idsF1 = getModuleIdentifiersFromFullImportedModules(noImplicitPrelude, psiFile, importDeclarations)
|
||||
|
||||
val idsF2 = getModuleIdentifiersFromHidingIdsImportedModules(psiFile, importDeclarations)
|
||||
|
||||
val idsF3 = getModuleIdentifiersFromSpecIdsImportedModules(psiFile, importDeclarations)
|
||||
|
||||
val f = for {
|
||||
f1 <- idsF1
|
||||
f2 <- idsF2
|
||||
f3 <- idsF3
|
||||
} yield (f1, f2, f3)
|
||||
|
||||
try {
|
||||
val (x, y, z) = Await.result(f, 10.seconds)
|
||||
Some(x ++ y ++ z)
|
||||
} catch {
|
||||
case _: TimeoutException =>
|
||||
HaskellNotificationGroup.logInfoEvent(project, s"Timeout while find module identifiers for file ${k.psiFile.getName}")
|
||||
None
|
||||
}
|
||||
case Left(noInfo) =>
|
||||
HaskellNotificationGroup.logInfoEvent(project, s"Timeout while find import declarations in findModuleIdentifiers for file ${psiFile.getName}: ${noInfo.message}")
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
private def getModuleIdentifiersFromFullImportedModules(impliciPrelueActive: Boolean, psiFile: PsiFile, importDeclarations: Iterable[HaskellImportDeclaration]): Future[Iterable[Option[Iterable[ModuleIdentifier]]]] = {
|
||||
val importInfos = getFullImportedModules(impliciPrelueActive, psiFile, importDeclarations)
|
||||
|
||||
Future.sequence(importInfos.map(importInfo => {
|
||||
val allModuleIdentifiers = HaskellComponentsManager.findModuleIdentifiers(psiFile.getProject, importInfo.moduleName)
|
||||
allModuleIdentifiers.map(mi => mi.map(i => createQualifiedModuleIdentifiers(importInfo, i)))
|
||||
}))
|
||||
}
|
||||
|
||||
private def getModuleIdentifiersFromHidingIdsImportedModules(psiFile: PsiFile, importDeclarations: Iterable[HaskellImportDeclaration]): Future[Iterable[Option[Iterable[ModuleIdentifier]]]] = {
|
||||
val importInfos = getImportedModulesWithHidingIdsSpec(psiFile, importDeclarations)
|
||||
|
||||
Future.sequence(importInfos.map(importInfo => {
|
||||
val allModuleIdentifiers = HaskellComponentsManager.findModuleIdentifiers(psiFile.getProject, importInfo.moduleName)
|
||||
allModuleIdentifiers.map(ids => ids.map(is => createQualifiedModuleIdentifiers(importInfo, is.filterNot(mi => importInfo.ids.exists(_ == mi.name)))))
|
||||
}))
|
||||
}
|
||||
|
||||
private def getModuleIdentifiersFromSpecIdsImportedModules(psiFile: PsiFile, importDeclarations: Iterable[HaskellImportDeclaration]): Future[Iterable[Option[Iterable[ModuleIdentifier]]]] = {
|
||||
val importInfos = getImportedModulesWithSpecIds(psiFile, importDeclarations)
|
||||
|
||||
Future.sequence(importInfos.map(importInfo => {
|
||||
val allModuleIdentifiers = HaskellComponentsManager.findModuleIdentifiers(psiFile.getProject, importInfo.moduleName)
|
||||
allModuleIdentifiers.map(ids => ids.map(is => createQualifiedModuleIdentifiers(importInfo, is.filter(mi => importInfo.ids.exists(id => if (mi.isOperator) s"(${mi.name})" == id else id == mi.name)))))
|
||||
}))
|
||||
}
|
||||
|
||||
private sealed trait ImportInfo {
|
||||
def moduleName: String
|
||||
|
||||
def qualified: Boolean
|
||||
|
||||
def as: Option[String]
|
||||
}
|
||||
|
||||
private case class ImportFull(moduleName: String, qualified: Boolean, as: Option[String]) extends ImportInfo
|
||||
|
||||
private case class ImportWithHiding(moduleName: String, ids: Iterable[String], qualified: Boolean, as: Option[String]) extends ImportInfo
|
||||
|
||||
private case class ImportWithIds(moduleName: String, ids: Iterable[String], qualified: Boolean, as: Option[String]) extends ImportInfo
|
||||
|
||||
private def isNoImplicitPreludeActive(info: StackComponentInfo, psiFile: PsiFile): Boolean = {
|
||||
info.isImplicitPreludeActive || ApplicationUtil.runReadAction(HaskellPsiUtil.findLanguageExtensions(psiFile)).exists(p => ApplicationUtil.runReadAction(p.getText).contains("NoImplicitPrelude"))
|
||||
}
|
||||
|
||||
private def getFullImportedModules(implicitPreludeActive: Boolean, psiFile: PsiFile, importDeclarations: Iterable[HaskellImportDeclaration]): Iterable[ImportFull] = {
|
||||
val moduleNames = for {
|
||||
id <- importDeclarations
|
||||
if Option(id.getImportSpec).isEmpty
|
||||
mn <- ApplicationUtil.runReadAction(id.getModuleName)
|
||||
} yield ImportFull(mn, Option(id.getImportQualified).isDefined, Option(id.getImportQualifiedAs).map(qa => ApplicationUtil.runReadAction(qa.getQualifier.getName)))
|
||||
|
||||
if (moduleNames.map(_.moduleName).toSeq.contains(HaskellProjectUtil.Prelude) || implicitPreludeActive) {
|
||||
moduleNames
|
||||
} else {
|
||||
Iterable(ImportFull(HaskellProjectUtil.Prelude, qualified = false, None)) ++ moduleNames
|
||||
}
|
||||
}
|
||||
|
||||
private def getImportedModulesWithHidingIdsSpec(psiFile: PsiFile, importDeclarations: Iterable[HaskellImportDeclaration]): Iterable[ImportWithHiding] = {
|
||||
for {
|
||||
importDeclaration <- importDeclarations.filter(i => Option(i.getImportSpec).flatMap(is => Option(is.getImportHidingSpec)).isDefined)
|
||||
importIdList = importDeclaration.getImportSpec.getImportHidingSpec.getImportIdList
|
||||
mn <- ApplicationUtil.runReadAction(importDeclaration.getModuleName)
|
||||
} yield ImportWithHiding(
|
||||
mn,
|
||||
findImportIds(importIdList),
|
||||
Option(importDeclaration.getImportQualified).isDefined,
|
||||
Option(importDeclaration.getImportQualifiedAs).map(_.getQualifier).map(q => ApplicationUtil.runReadAction(q.getName))
|
||||
)
|
||||
}
|
||||
|
||||
private def getImportedModulesWithSpecIds(psiFile: PsiFile, importDeclarations: Iterable[HaskellImportDeclaration]): Iterable[ImportWithIds] = {
|
||||
for {
|
||||
importDeclaration <- importDeclarations.filter(i => Option(i.getImportSpec).flatMap(is => Option(is.getImportIdsSpec)).isDefined)
|
||||
importIdList = importDeclaration.getImportSpec.getImportIdsSpec.getImportIdList
|
||||
mn <- ApplicationUtil.runReadAction(importDeclaration.getModuleName)
|
||||
} yield ImportWithIds(
|
||||
mn,
|
||||
findImportIds(importIdList),
|
||||
Option(importDeclaration.getImportQualified).isDefined,
|
||||
Option(importDeclaration.getImportQualifiedAs).map(_.getQualifier).map(q => ApplicationUtil.runReadAction(q.getName))
|
||||
)
|
||||
}
|
||||
|
||||
private def createQualifiedModuleIdentifiers(importInfo: ImportInfo, moduleIdentifiers: Iterable[ModuleIdentifier]): Iterable[ModuleIdentifier] = {
|
||||
moduleIdentifiers.flatMap(mi => {
|
||||
(importInfo.as, importInfo.qualified) match {
|
||||
case (None, false) => Iterable(mi, mi.copy(name = mi.moduleName + "." + mi.name))
|
||||
case (None, true) => Iterable(mi.copy(name = mi.moduleName + "." + mi.name))
|
||||
case (Some(q), false) => Iterable(mi, mi.copy(name = q + "." + mi.name))
|
||||
case (Some(q), true) => Iterable(mi.copy(name = q + "." + mi.name))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private def findImportIds(importIdList: util.List[HaskellImportId]): Iterable[String] = {
|
||||
importIdList.asScala.flatMap(importId => Iterable(ApplicationUtil.runReadAction(importId.getCname.getName)) ++ importId.getCnameDotDotList.asScala.flatMap(cndd => Option(cndd.getCname).map(cn => ApplicationUtil.runReadAction(cn.getName))))
|
||||
}
|
||||
}
|
@ -27,7 +27,6 @@ import com.intellij.psi.{PsiElement, PsiFile}
|
||||
import com.intellij.util.WaitFor
|
||||
import intellij.haskell.HaskellNotificationGroup
|
||||
import intellij.haskell.cabal.CabalInfo
|
||||
import intellij.haskell.editor.FileModuleIdentifiers
|
||||
import intellij.haskell.external.component.DefinitionLocationComponent.DefinitionLocationResult
|
||||
import intellij.haskell.external.component.NameInfoComponentResult.NameInfoResult
|
||||
import intellij.haskell.external.component.TypeInfoComponentResult.TypeInfoResult
|
||||
@ -42,7 +41,7 @@ import scala.concurrent._
|
||||
|
||||
object HaskellComponentsManager {
|
||||
|
||||
case class StackComponentInfo(module: Module, packageName: String, target: String, stanzaType: StanzaType, sourceDirs: Seq[String], mainIs: Option[String], isImplicitPreludeActive: Boolean, buildDepends: Seq[String], exposedModuleNames: Seq[String] = Seq.empty)
|
||||
case class StackComponentInfo(module: Module, modulePath: String, packageName: String, target: String, stanzaType: StanzaType, sourceDirs: Seq[String], mainIs: Option[String], isImplicitPreludeActive: Boolean, buildDepends: Seq[String], exposedModuleNames: Seq[String] = Seq.empty)
|
||||
|
||||
def findModuleIdentifiersInCache(project: Project)(implicit ec: ExecutionContext): Iterable[ModuleIdentifier] = {
|
||||
val f = ApplicationManager.getApplication.executeOnPooledThread(ScalaUtil.callable {
|
||||
@ -73,29 +72,17 @@ object HaskellComponentsManager {
|
||||
}
|
||||
|
||||
def isReplBusy(psiFile: PsiFile): Boolean = {
|
||||
LoadComponent.isBusy(psiFile)
|
||||
LoadComponent.isReplBusy(psiFile)
|
||||
}
|
||||
|
||||
def findLibraryModuleIdentifiers(project: Project, moduleName: String)(implicit ec: ExecutionContext): Future[Option[Iterable[ModuleIdentifier]]] = {
|
||||
BrowseModuleComponent.findLibraryModuleIdentifiers(project, moduleName)
|
||||
}
|
||||
|
||||
def findExportedModuleIdentifiers(psiFile: PsiFile, moduleName: String)(implicit ec: ExecutionContext): Future[Option[Iterable[ModuleIdentifier]]] = {
|
||||
BrowseModuleComponent.findExportedIdentifiers(psiFile, moduleName)
|
||||
}
|
||||
|
||||
def findTopLevelModuleIdentifiers(psiFile: PsiFile, moduleName: String)(implicit ec: ExecutionContext): Future[Option[Iterable[ModuleIdentifier]]] = {
|
||||
BrowseModuleComponent.findTopLevelIdentifiers(psiFile, moduleName)
|
||||
def findModuleIdentifiers(project: Project, moduleName: String)(implicit ec: ExecutionContext): Future[Option[Iterable[ModuleIdentifier]]] = {
|
||||
BrowseModuleComponent.findModuleIdentifiers(project, moduleName)
|
||||
}
|
||||
|
||||
def findDefinitionLocation(psiFile: PsiFile, qualifiedNameElement: HaskellQualifiedNameElement, importQualifier: Option[String]): DefinitionLocationResult = {
|
||||
DefinitionLocationComponent.findDefinitionLocation(psiFile, qualifiedNameElement, importQualifier)
|
||||
}
|
||||
|
||||
def findNameInfo(qualifiedNameElement: HaskellQualifiedNameElement, importQualifier: Option[String] = None): NameInfoResult = {
|
||||
NameInfoComponent.findNameInfo(qualifiedNameElement, importQualifier)
|
||||
}
|
||||
|
||||
def findNameInfo(psiElement: PsiElement): NameInfoResult = {
|
||||
NameInfoComponent.findNameInfo(psiElement)
|
||||
}
|
||||
@ -117,11 +104,11 @@ object HaskellComponentsManager {
|
||||
}
|
||||
|
||||
def findStackComponentInfo(psiFile: PsiFile): Option[StackComponentInfo] = {
|
||||
HaskellProjectFileInfoComponent.findHaskellProjectFileInfo(psiFile).map(_.stackComponentInfo)
|
||||
HaskellModuleInfoComponent.findHaskellProjectFileInfo(psiFile)
|
||||
}
|
||||
|
||||
def findStackComponentInfo(project: Project, filePath: String): Option[StackComponentInfo] = {
|
||||
HaskellProjectFileInfoComponent.findHaskellProjectFileInfo(project, filePath).map(_.stackComponentInfo)
|
||||
HaskellModuleInfoComponent.findHaskellProjectFileInfo(project, filePath)
|
||||
}
|
||||
|
||||
def getGlobalProjectInfo(project: Project): Option[GlobalProjectInfo] = {
|
||||
@ -156,15 +143,11 @@ object HaskellComponentsManager {
|
||||
LoadComponent.load(psiFile, fileChanged, psiElement)
|
||||
}
|
||||
|
||||
def invalidateHaskellFileInfoCache(psiFile: PsiFile): Unit = {
|
||||
HaskellProjectFileInfoComponent.invalidate(psiFile)
|
||||
def invalidateFileInfos(psiFile: PsiFile): Unit = {
|
||||
HaskellModuleInfoComponent.invalidate(psiFile)
|
||||
}
|
||||
|
||||
def refreshDefinitionLocationCache(elements: Seq[HaskellQualifiedNameElement]): Unit = {
|
||||
DefinitionLocationComponent.refresh(elements)
|
||||
}
|
||||
|
||||
def invalidateDefinitionLocationCache(psiFile: PsiFile): Unit = {
|
||||
def invalidateLocations(psiFile: PsiFile): Unit = {
|
||||
DefinitionLocationComponent.invalidate(psiFile)
|
||||
}
|
||||
|
||||
@ -172,16 +155,16 @@ object HaskellComponentsManager {
|
||||
findStackComponentInfos(project).map(info => (info.module, info.packageName)).distinct
|
||||
}
|
||||
|
||||
def findReferencesInCache(targetFile: PsiFile): Seq[(PsiFile, HaskellQualifiedNameElement)] = {
|
||||
DefinitionLocationComponent.findReferencesInCache(targetFile)
|
||||
def invalidateOtherFilesLocations(currentFile: PsiFile, name: String): Unit = {
|
||||
DefinitionLocationComponent.invalidateOtherFiles(currentFile, name)
|
||||
}
|
||||
|
||||
def findLibraryPackageInfos(project: Project): Seq[PackageInfo] = {
|
||||
LibraryPackageInfoComponent.libraryPackageInfos(project).toSeq
|
||||
}
|
||||
|
||||
def refreshCacheForModules(project: Project, moduleNames: Seq[String]): Unit = {
|
||||
BrowseModuleComponent.refreshExportedModuleNames(project, moduleNames)
|
||||
def invalidateBrowseInfo(project: Project, moduleNames: Seq[String]): Unit = {
|
||||
BrowseModuleComponent.invalidateModuleNames(project, moduleNames)
|
||||
}
|
||||
|
||||
def findStackComponentInfos(project: Project): Seq[StackComponentInfo] = {
|
||||
@ -192,7 +175,7 @@ object HaskellComponentsManager {
|
||||
HaskellNotificationGroup.logInfoEvent(project, "Start to invalidate cache")
|
||||
GlobalProjectInfoComponent.invalidate(project)
|
||||
LibraryPackageInfoComponent.invalidate(project)
|
||||
HaskellProjectFileInfoComponent.invalidate(project)
|
||||
HaskellModuleInfoComponent.invalidate(project)
|
||||
BrowseModuleComponent.invalidate(project)
|
||||
NameInfoComponent.invalidateAll(project)
|
||||
DefinitionLocationComponent.invalidateAll(project)
|
||||
@ -260,7 +243,7 @@ object HaskellComponentsManager {
|
||||
|
||||
private def preloadLibraryIdentifiers(project: Project): Unit = {
|
||||
if (!project.isDisposed) {
|
||||
BrowseModuleComponent.findLibraryModuleIdentifiers(project, HaskellProjectUtil.Prelude)
|
||||
BrowseModuleComponent.findModuleIdentifiers(project, HaskellProjectUtil.Prelude)
|
||||
}
|
||||
|
||||
if (!project.isDisposed) {
|
||||
@ -282,33 +265,32 @@ object HaskellComponentsManager {
|
||||
})
|
||||
|
||||
if (!project.isDisposed) {
|
||||
importedLibraryModuleNames.toSeq.distinct.foreach(mn => {
|
||||
if (!project.isDisposed) {
|
||||
if (StackReplsManager.getGlobalRepl(project).exists(_.available)) {
|
||||
BrowseModuleComponent.findLibraryModuleIdentifiersSync(project, mn)
|
||||
if (StackReplsManager.getGlobalRepl(project).exists(_.available)) {
|
||||
importedLibraryModuleNames.toSeq.distinct.foreach(mn => {
|
||||
if (!project.isDisposed) {
|
||||
BrowseModuleComponent.findModuleIdentifiersSync(project, mn)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def preloadAllLibraryIdentifiers(project: Project): Unit = {
|
||||
|
||||
if (!project.isDisposed) {
|
||||
val componentInfos = findStackComponentInfos(project)
|
||||
val packageInfos = componentInfos.flatMap(info => findStackComponentGlobalInfo(info).map(_.packageInfos).getOrElse(Seq())).distinct
|
||||
|
||||
if (!project.isDisposed) {
|
||||
packageInfos.flatMap(_.exposedModuleNames).distinct.foreach(mn => {
|
||||
if (!project.isDisposed) {
|
||||
if (StackReplsManager.getGlobalRepl(project).exists(_.available)) {
|
||||
BrowseModuleComponent.findLibraryModuleIdentifiersSync(project, mn)
|
||||
if (StackReplsManager.getGlobalRepl(project).exists(_.available)) {
|
||||
packageInfos.flatMap(_.exposedModuleNames).distinct.foreach(mn => {
|
||||
if (!project.isDisposed) {
|
||||
BrowseModuleComponent.findModuleIdentifiersSync(project, mn)
|
||||
// We have to wait for other requests which have more priority because those are on dispatch thread
|
||||
Thread.sleep(100)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -73,10 +73,10 @@ class HaskellDocumentationProvider extends AbstractDocumentationProvider {
|
||||
if (definedInSameFile) {
|
||||
getQuickNavigateInfo(e, oe)
|
||||
} else {
|
||||
HaskellPsiUtil.findQualifiedNameParent(oe) match {
|
||||
HaskellPsiUtil.findQualifiedName(oe) match {
|
||||
case Some(qone) =>
|
||||
val presentationText = HaskellPsiUtil.findNamedElement(e).flatMap { ne =>
|
||||
if (HaskellPsiUtil.findExpressionParent(ne).isDefined || HaskellPsiUtil.findTypeSignatureDeclarationParent(ne).isDefined) {
|
||||
if (HaskellPsiUtil.findExpression(ne).isDefined || HaskellPsiUtil.findTypeSignatureDeclaration(ne).isDefined) {
|
||||
None
|
||||
} else {
|
||||
Some(DoubleNbsp + "<code>" +
|
||||
|
@ -29,37 +29,41 @@ import intellij.haskell.external.repl.StackReplsManager
|
||||
import intellij.haskell.runconfig.console.HaskellConsoleView
|
||||
import intellij.haskell.util.{HaskellFileUtil, HaskellProjectUtil, ScalaUtil}
|
||||
|
||||
private[component] object HaskellProjectFileInfoComponent {
|
||||
private[component] object HaskellModuleInfoComponent {
|
||||
|
||||
private case class Key(project: Project, filePath: String)
|
||||
|
||||
private final val Cache: LoadingCache[Key, Option[InternalHaskellProjectFileInfo]] = Scaffeine().build((k: Key) => createFileInfo(k))
|
||||
private final val Cache: LoadingCache[Key, Option[InternalHaskellModuleInfo]] = Scaffeine().build((k: Key) => createFileInfo(k))
|
||||
|
||||
def findHaskellProjectFileInfo(project: Project, filePath: String): Option[HaskellProjectFileInfo] = {
|
||||
def findHaskellProjectFileInfo(project: Project, filePath: String): Option[StackComponentInfo] = {
|
||||
val key = Key(project, filePath)
|
||||
Cache.get(key) match {
|
||||
case Some(internalInfo) =>
|
||||
internalInfo.message match {
|
||||
case Some(m) => HaskellNotificationGroup.warningEvent(project, m)
|
||||
case None => None
|
||||
Cache.getIfPresent(key) match {
|
||||
case Some(info) => info.flatMap(_.stackComponentInfo)
|
||||
case None =>
|
||||
Cache.get(key) match {
|
||||
case Some(internalInfo) =>
|
||||
internalInfo.message match {
|
||||
case Some(m) => HaskellNotificationGroup.warningEvent(project, m)
|
||||
case None => None
|
||||
}
|
||||
internalInfo.stackComponentInfo match {
|
||||
case Some(info) => Some(info)
|
||||
case _ => None
|
||||
}
|
||||
case _ =>
|
||||
Cache.invalidate(key)
|
||||
None
|
||||
}
|
||||
internalInfo.stackComponentInfo match {
|
||||
case Some(info) => Some(HaskellProjectFileInfo(info))
|
||||
case _ => None
|
||||
}
|
||||
case _ =>
|
||||
Cache.invalidate(key)
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
def findHaskellProjectFileInfo(psiFile: PsiFile): Option[HaskellProjectFileInfo] = {
|
||||
def findHaskellProjectFileInfo(psiFile: PsiFile): Option[StackComponentInfo] = {
|
||||
val project = psiFile.getProject
|
||||
|
||||
HaskellConsoleView.findConsoleInfo(psiFile) match {
|
||||
case Some(consoleInfo) =>
|
||||
val stackComponentInfos = StackReplsManager.getReplsManager(project).map(_.stackComponentInfos)
|
||||
stackComponentInfos.flatMap(_.find(_.target == consoleInfo.stackTarget)).map(HaskellProjectFileInfo)
|
||||
stackComponentInfos.flatMap(_.find(_.target == consoleInfo.stackTarget))
|
||||
case None =>
|
||||
val filePath = HaskellFileUtil.getAbsolutePath(psiFile)
|
||||
filePath.flatMap(k => findHaskellProjectFileInfo(project, k))
|
||||
@ -75,13 +79,13 @@ private[component] object HaskellProjectFileInfoComponent {
|
||||
HaskellFileUtil.getAbsolutePath(psiFile).foreach(fp => Cache.invalidate(Key(psiFile.getProject, fp)))
|
||||
}
|
||||
|
||||
private def createFileInfo(key: Key): Option[InternalHaskellProjectFileInfo] = {
|
||||
private def createFileInfo(key: Key): Option[InternalHaskellModuleInfo] = {
|
||||
val project = key.project
|
||||
val filePath = key.filePath
|
||||
|
||||
StackReplsManager.getReplsManager(project).map(_.stackComponentInfos).map(stackComponentInfos => {
|
||||
val info = getStackComponentInfo(project, filePath, stackComponentInfos)
|
||||
InternalHaskellProjectFileInfo(info._1, info._2)
|
||||
InternalHaskellModuleInfo(info._1, info._2)
|
||||
})
|
||||
}
|
||||
|
||||
@ -95,12 +99,15 @@ private[component] object HaskellProjectFileInfoComponent {
|
||||
val (stackComponentInfo, message) = if (sourceDirsByInfo.size > 1) {
|
||||
val sourceDirByInfo = sourceDirsByInfo.map({ case (info, sds) => (info, sds.maxBy(sd => Paths.get(sd).getNameCount)) })
|
||||
val mostSpecificSourceDirByInfo = ScalaUtil.maxsBy(sourceDirByInfo)({ case (_, sd) => Paths.get(sd).getNameCount })
|
||||
|
||||
val libStackInfo = mostSpecificSourceDirByInfo.find(_._1.stanzaType == LibType)
|
||||
|
||||
val message = if (mostSpecificSourceDirByInfo.size > 1) {
|
||||
Some(s"Ambiguous Stack target for file `$filePath`. It can belong to the source dir of more than one Stack target/Cabal stanza. The first one of `${mostSpecificSourceDirByInfo.map(_._1.target)}` is chosen.")
|
||||
Some(s"Ambiguous Stack target for file `$filePath`. It can belong to the source dir of more than one Stack target/Cabal stanza: `${mostSpecificSourceDirByInfo.map(_._1.target)}` | `${libStackInfo.map(_._1.target).getOrElse(mostSpecificSourceDirByInfo.map(_._1.target).headOption.getOrElse("-"))}` is chosen.")
|
||||
} else {
|
||||
None
|
||||
}
|
||||
(mostSpecificSourceDirByInfo.headOption.map(_._1), message)
|
||||
(libStackInfo.map(_._1).orElse(mostSpecificSourceDirByInfo.headOption.map(_._1)), message)
|
||||
} else {
|
||||
(sourceDirsByInfo.headOption.map(_._1), None)
|
||||
}
|
||||
@ -117,7 +124,5 @@ private[component] object HaskellProjectFileInfoComponent {
|
||||
}
|
||||
}
|
||||
|
||||
case class InternalHaskellProjectFileInfo(stackComponentInfo: Option[StackComponentInfo], message: Option[String])
|
||||
|
||||
case class HaskellProjectFileInfo(stackComponentInfo: StackComponentInfo)
|
||||
case class InternalHaskellModuleInfo(stackComponentInfo: Option[StackComponentInfo], message: Option[String])
|
||||
|
@ -18,12 +18,9 @@ package intellij.haskell.external.component
|
||||
|
||||
import com.intellij.codeInsight.documentation.DocumentationManager
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.{PsiElement, PsiFile}
|
||||
import intellij.haskell.editor.FileModuleIdentifiers
|
||||
import intellij.haskell.external.component.HaskellComponentsManager.StackComponentInfo
|
||||
import intellij.haskell.external.execution.{CompilationResult, HaskellCompilationResultHelper}
|
||||
import intellij.haskell.external.repl.ProjectStackRepl.{Failed, Loaded}
|
||||
import intellij.haskell.external.repl.ProjectStackRepl.Loaded
|
||||
import intellij.haskell.external.repl._
|
||||
import intellij.haskell.psi.HaskellPsiUtil
|
||||
import intellij.haskell.util.ScalaUtil
|
||||
@ -31,16 +28,11 @@ import intellij.haskell.util.index.HaskellModuleNameIndex
|
||||
|
||||
private[component] object LoadComponent {
|
||||
|
||||
def isFileLoaded(psiFile: PsiFile): Boolean = {
|
||||
private def isFileLoaded(psiFile: PsiFile): Boolean = {
|
||||
val projectRepl = StackReplsManager.getProjectRepl(psiFile)
|
||||
projectRepl.map(_.isFileLoaded(psiFile)).contains(Loaded)
|
||||
}
|
||||
|
||||
def isFileLoadedFailed(psiFile: PsiFile): Boolean = {
|
||||
val projectRepl = StackReplsManager.getProjectRepl(psiFile)
|
||||
projectRepl.map(_.isFileLoaded(psiFile)).contains(Failed)
|
||||
}
|
||||
|
||||
def isModuleLoaded(moduleName: Option[String], psiFile: PsiFile): Boolean = {
|
||||
isFileLoaded(psiFile) || {
|
||||
for {
|
||||
@ -50,12 +42,7 @@ private[component] object LoadComponent {
|
||||
}.contains(true)
|
||||
}
|
||||
|
||||
def isBusy(project: Project, stackComponentInfo: StackComponentInfo): Boolean = {
|
||||
val projectRepl = StackReplsManager.getRunningProjectRepl(project, stackComponentInfo)
|
||||
projectRepl.exists(_.isBusy)
|
||||
}
|
||||
|
||||
def isBusy(psiFile: PsiFile): Boolean = {
|
||||
def isReplBusy(psiFile: PsiFile): Boolean = {
|
||||
val projectRepl = StackReplsManager.getRunningProjectRepl(psiFile)
|
||||
projectRepl.exists(_.isBusy)
|
||||
}
|
||||
@ -76,28 +63,26 @@ private[component] object LoadComponent {
|
||||
|
||||
projectRepl.load(psiFile, Some(fileChanged)) match {
|
||||
case Some((loadOutput, loadFailed)) =>
|
||||
if (fileChanged) {
|
||||
ApplicationManager.getApplication.executeOnPooledThread(ScalaUtil.runnable {
|
||||
ApplicationManager.getApplication.executeOnPooledThread(ScalaUtil.runnable {
|
||||
|
||||
TypeInfoComponent.invalidate(psiFile)
|
||||
DefinitionLocationComponent.invalidate(psiFile)
|
||||
HaskellModuleNameIndex.invalidateNotFoundEntries(project)
|
||||
TypeInfoComponent.invalidate(psiFile)
|
||||
DefinitionLocationComponent.invalidate(psiFile)
|
||||
HaskellModuleNameIndex.invalidateNotFoundEntries(project)
|
||||
|
||||
// Have to refresh because import declarations can be changed
|
||||
FileModuleIdentifiers.refresh(psiFile)
|
||||
// Have to refresh because import declarations can be changed
|
||||
FileModuleIdentifiers.refresh(psiFile)
|
||||
|
||||
val moduleName = HaskellPsiUtil.findModuleName(psiFile)
|
||||
if (!loadFailed) {
|
||||
NameInfoComponent.invalidate(psiFile)
|
||||
moduleName.foreach(mn => {
|
||||
BrowseModuleComponent.invalidateExportedModuleName(project, mn)
|
||||
FileModuleIdentifiers.invalidate(mn)
|
||||
})
|
||||
val moduleName = HaskellPsiUtil.findModuleName(psiFile)
|
||||
if (!loadFailed) {
|
||||
NameInfoComponent.invalidate(psiFile)
|
||||
moduleName.foreach(mn => {
|
||||
BrowseModuleComponent.invalidateModuleName(project, mn)
|
||||
FileModuleIdentifiers.invalidate(mn)
|
||||
})
|
||||
|
||||
}
|
||||
DocumentationManager.getInstance(project).updateToolwindowContext()
|
||||
})
|
||||
}
|
||||
}
|
||||
DocumentationManager.getInstance(project).updateToolwindowContext()
|
||||
})
|
||||
Some(HaskellCompilationResultHelper.createCompilationResult(psiFile, loadOutput.stderrLines, loadFailed))
|
||||
case _ => None
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ private[component] object NameInfoComponent {
|
||||
private case class Key(psiFile: PsiFile, name: String)
|
||||
|
||||
def findNameInfo(psiElement: PsiElement): NameInfoResult = {
|
||||
HaskellPsiUtil.findQualifiedNameParent(psiElement) match {
|
||||
HaskellPsiUtil.findQualifiedName(psiElement) match {
|
||||
case Some(p) => findNameInfo(p, None)
|
||||
case None => Left(NoInfoAvailable(ApplicationUtil.runReadAction(psiElement.getText), ApplicationUtil.runReadAction(psiElement.getContainingFile.getName)))
|
||||
}
|
||||
@ -109,7 +109,7 @@ private[component] object NameInfoComponent {
|
||||
case Some(result) =>
|
||||
result match {
|
||||
case Right(_) => result
|
||||
case Left(ReplIsBusy) | Left(ReadActionTimeout(_)) | Left(IndexNotReady) | Left(ModuleNotLoaded(_)) | Left(ReplNotAvailable) =>
|
||||
case Left(ReplIsBusy) | Left(ReadActionTimeout(_)) | Left(IndexNotReady) | Left(ModuleNotAvailable(_)) | Left(ReplNotAvailable) =>
|
||||
Cache.invalidate(key)
|
||||
result
|
||||
case _ => result
|
||||
@ -117,20 +117,16 @@ private[component] object NameInfoComponent {
|
||||
case None =>
|
||||
if (isReadAccessAllowed) {
|
||||
ProgressManager.checkCanceled()
|
||||
if (isDispatchThread && !LoadComponent.isFileLoaded(psiFile)) {
|
||||
Left(ModuleNotLoaded(psiFile.getName))
|
||||
} else {
|
||||
val result = findNameInfos(key)
|
||||
result match {
|
||||
case Right(_) =>
|
||||
Cache.put(key, result)
|
||||
result
|
||||
case Left(ReplIsBusy) | Left(ReadActionTimeout(_)) | Left(IndexNotReady) | Left(ReplNotAvailable) | Left(ModuleNotLoaded(_)) =>
|
||||
result
|
||||
case Left(_) =>
|
||||
Cache.put(key, result)
|
||||
result
|
||||
}
|
||||
val result = findNameInfos(key)
|
||||
result match {
|
||||
case Right(_) =>
|
||||
Cache.put(key, result)
|
||||
result
|
||||
case Left(ReplIsBusy) | Left(ReadActionTimeout(_)) | Left(IndexNotReady) | Left(ReplNotAvailable) | Left(ModuleNotAvailable(_)) =>
|
||||
result
|
||||
case Left(_) =>
|
||||
Cache.put(key, result)
|
||||
result
|
||||
}
|
||||
} else {
|
||||
val result = Cache.get(key)
|
||||
@ -139,7 +135,7 @@ private[component] object NameInfoComponent {
|
||||
case Left(NoInfoAvailable(_, _)) =>
|
||||
Cache.put(key, result)
|
||||
result
|
||||
case Left(ReplNotAvailable) | Left(ReplIsBusy) | Left(IndexNotReady) | Left(ModuleNotLoaded(_)) | Left(ReadActionTimeout(_)) =>
|
||||
case Left(ReplNotAvailable) | Left(ReplIsBusy) | Left(IndexNotReady) | Left(ModuleNotAvailable(_)) | Left(ReadActionTimeout(_)) =>
|
||||
result
|
||||
}
|
||||
}
|
||||
|
@ -51,6 +51,16 @@ object ProjectLibraryFileWatcher {
|
||||
|
||||
private val buildStatus: concurrent.Map[Project, BuildStatus] = new ConcurrentHashMap[Project, BuildStatus]().asScala
|
||||
|
||||
def addBuild(project: Project, libComponentInfos: Set[StackComponentInfo]): Option[BuildStatus] = {
|
||||
synchronized {
|
||||
ProjectLibraryFileWatcher.buildStatus.get(project) match {
|
||||
case Some(Building(_)) => ProjectLibraryFileWatcher.buildStatus.put(project, Build(libComponentInfos))
|
||||
case Some(Build(componentInfos)) => ProjectLibraryFileWatcher.buildStatus.put(project, Build(componentInfos.++(libComponentInfos)))
|
||||
case None => ProjectLibraryFileWatcher.buildStatus.put(project, Build(libComponentInfos))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def checkLibraryBuild(project: Project, currentInfo: StackComponentInfo): Unit = synchronized {
|
||||
if (!StackProjectManager.isInitializing(project) && !StackProjectManager.isHaddockBuilding(project) && !project.isDisposed) {
|
||||
ProjectLibraryFileWatcher.buildStatus.get(project) match {
|
||||
@ -110,10 +120,11 @@ object ProjectLibraryFileWatcher {
|
||||
}
|
||||
})
|
||||
|
||||
HaskellComponentsManager.invalidateBrowseInfo(project, libComponentInfos.flatMap(_.exposedModuleNames).toSeq)
|
||||
|
||||
dependentFiles.foreach { vf =>
|
||||
HaskellFileUtil.convertToHaskellFileInReadAction(project, vf).toOption.flatten match {
|
||||
HaskellFileUtil.convertToHaskellFileInReadAction(project, vf).toOption match {
|
||||
case Some(psiFile) =>
|
||||
HaskellComponentsManager.refreshCacheForModules(project, libComponentInfos.flatMap(_.exposedModuleNames).toSeq)
|
||||
HaskellAnnotator.restartDaemonCodeAnalyzerForFile(psiFile)
|
||||
case None => HaskellNotificationGroup.logInfoEvent(project, s"Could not invalidate cache and restart daemon analyzer for file ${vf.getName}")
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ import com.intellij.openapi.actionSystem.ActionManager
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.components.ProjectComponent
|
||||
import com.intellij.openapi.progress.{PerformInBackgroundOption, ProgressIndicator, ProgressManager, Task}
|
||||
import com.intellij.openapi.project.{Project, ProjectUtil}
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.projectRoots.ProjectJdkTable
|
||||
import com.intellij.openapi.roots.{ModifiableRootModel, ModuleRootModificationUtil}
|
||||
import com.intellij.openapi.ui.Messages
|
||||
@ -184,7 +184,7 @@ object StackProjectManager {
|
||||
})
|
||||
|
||||
progressIndicator.setText("Busy with updating project and module settings")
|
||||
val projectPath = ProjectUtil.guessProjectDir(project).getPath
|
||||
val projectPath = project.getBasePath
|
||||
val projectModules = HaskellProjectUtil.findProjectHaskellModules(project)
|
||||
val packagePaths = StackProjectImportBuilder.getPackagePaths(project)
|
||||
val packagePathsToAdd = packagePaths.filterNot { relativePath =>
|
||||
@ -221,17 +221,11 @@ object StackProjectManager {
|
||||
val replsLoad = ApplicationManager.getApplication.executeOnPooledThread(ScalaUtil.runnable {
|
||||
StackReplsManager.getReplsManager(project).foreach(_.stackComponentInfos.filter(_.stanzaType == LibType).foreach { info =>
|
||||
progressIndicator.setText("Busy with starting project REPL " + info.packageName)
|
||||
val repl = StackReplsManager.getProjectRepl(project, info)
|
||||
if (repl.exists(_.available)) {
|
||||
repl match {
|
||||
case Some(r) =>
|
||||
val moduleNames = info.exposedModuleNames
|
||||
HaskellNotificationGroup.logInfoEvent(project, s"Loading project modules in REPL ${info.packageName} " + moduleNames.mkString(", "))
|
||||
r.load(moduleNames)
|
||||
case None => HaskellNotificationGroup.logWarningEvent(project, s"REPL ${info.packageName} is not started")
|
||||
}
|
||||
Thread.sleep(2000) // Have to wait between starting the REPLs otherwise timeouts while starting
|
||||
StackReplsManager.getProjectRepl(project, info) match {
|
||||
case Some(r) if r.available => HaskellNotificationGroup.logInfoEvent(project, s"REPL ${info.packageName} is started")
|
||||
case _ => HaskellNotificationGroup.logWarningEvent(project, s"REPL ${info.packageName} is not started")
|
||||
}
|
||||
Thread.sleep(1000) // Have to wait between starting the REPLs otherwise timeouts while starting
|
||||
})
|
||||
|
||||
val projectFiles = ApplicationUtil.scheduleInReadActionWithWriteActionPriority(project,
|
||||
@ -251,9 +245,9 @@ object StackProjectManager {
|
||||
projectFilesWithImportedModuleNames match {
|
||||
case Some(fm) =>
|
||||
fm.foreach { case (f, moduleNames) =>
|
||||
moduleNames.foreach(mn => HaskellModuleNameIndex.findFileByModuleName(project, mn))
|
||||
moduleNames.foreach(mn => HaskellModuleNameIndex.findFilesByModuleName(project, mn))
|
||||
HaskellNotificationGroup.logInfoEvent(project, "Loading module identifiers " + moduleNames.mkString(", "))
|
||||
moduleNames.foreach(m => BrowseModuleComponent.loadExportedIdentifiersSync(project, f, m))
|
||||
moduleNames.foreach(m => BrowseModuleComponent.findModuleIdentifiersSync(project, m))
|
||||
}
|
||||
case None => HaskellNotificationGroup.logInfoEvent(project, "Could not loaded module identifiers because of timeout ")
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ private[component] object TypeInfoComponent {
|
||||
val isDispatchThread = ApplicationManager.getApplication.isDispatchThread
|
||||
|
||||
(for {
|
||||
qne <- HaskellPsiUtil.findQualifiedNameParent(element)
|
||||
qne <- HaskellPsiUtil.findQualifiedName(element)
|
||||
pf <- getFile
|
||||
} yield {
|
||||
Key(pf, qne)
|
||||
@ -73,7 +73,7 @@ private[component] object TypeInfoComponent {
|
||||
}
|
||||
}.getOrElse(Left(NoInfoAvailable(selectionModel.getSelectedText, psiFile.getName)))
|
||||
} else {
|
||||
Left(ModuleNotLoaded(moduleName.getOrElse(psiFile.getName)))
|
||||
Left(ModuleNotAvailable(moduleName.getOrElse(psiFile.getName)))
|
||||
}
|
||||
}
|
||||
|
||||
@ -133,14 +133,14 @@ private[component] object TypeInfoComponent {
|
||||
case Right(_) => result
|
||||
case Left(NoInfoAvailable(_, _)) =>
|
||||
result
|
||||
case Left(ReplNotAvailable) | Left(ReplIsBusy) | Left(IndexNotReady) | Left(ModuleNotLoaded(_)) | Left(ReadActionTimeout(_)) =>
|
||||
case Left(ReplNotAvailable) | Left(ReplIsBusy) | Left(IndexNotReady) | Left(ModuleNotAvailable(_)) | Left(ReadActionTimeout(_)) =>
|
||||
synchronousCache.invalidate(key)
|
||||
result
|
||||
}
|
||||
case None =>
|
||||
val moduleName = HaskellPsiUtil.findModuleName(key.psiFile)
|
||||
if (isDispatchThread && !LoadComponent.isModuleLoaded(moduleName, key.psiFile)) {
|
||||
Left(ModuleNotLoaded(moduleName.getOrElse(key.psiFile.getName)))
|
||||
Left(ModuleNotAvailable(moduleName.getOrElse(key.psiFile.getName)))
|
||||
} else if (isDispatchThread) {
|
||||
val result = findTypeInfoResult(key)
|
||||
result match {
|
||||
@ -149,7 +149,7 @@ private[component] object TypeInfoComponent {
|
||||
result
|
||||
case Left(NoInfoAvailable(_, _)) =>
|
||||
result
|
||||
case Left(ReplNotAvailable) | Left(ReplIsBusy) | Left(IndexNotReady) | Left(ModuleNotLoaded(_)) | Left(ReadActionTimeout(_)) =>
|
||||
case Left(ReplNotAvailable) | Left(ReplIsBusy) | Left(IndexNotReady) | Left(ModuleNotAvailable(_)) | Left(ReadActionTimeout(_)) =>
|
||||
synchronousCache.invalidate(key)
|
||||
result
|
||||
}
|
||||
@ -160,7 +160,7 @@ private[component] object TypeInfoComponent {
|
||||
result
|
||||
case Left(NoInfoAvailable(_, _)) =>
|
||||
result
|
||||
case Left(ReplNotAvailable) | Left(ReplIsBusy) | Left(IndexNotReady) | Left(ModuleNotLoaded(_)) | Left(ReadActionTimeout(_)) =>
|
||||
case Left(ReplNotAvailable) | Left(ReplIsBusy) | Left(IndexNotReady) | Left(ModuleNotAvailable(_)) | Left(ReadActionTimeout(_)) =>
|
||||
synchronousCache.invalidate(key)
|
||||
result
|
||||
}
|
||||
|
@ -22,8 +22,8 @@ package object component {
|
||||
override def message: String = "No info because index is not ready"
|
||||
}
|
||||
|
||||
case class ModuleNotLoaded(fileName: String) extends NoInfo {
|
||||
override def message: String = s"No info because module of file $fileName is not loaded"
|
||||
case class ModuleNotAvailable(name: String) extends NoInfo {
|
||||
override def message: String = s"No info because $name is not loaded or found"
|
||||
}
|
||||
|
||||
case class ReadActionTimeout(readActionDescription: String) extends NoInfo {
|
||||
|
@ -16,6 +16,8 @@
|
||||
|
||||
package intellij.haskell.external.execution
|
||||
|
||||
import java.util.concurrent.LinkedBlockingDeque
|
||||
|
||||
import com.intellij.compiler.impl._
|
||||
import com.intellij.compiler.progress.CompilerTask
|
||||
import com.intellij.execution.ExecutionException
|
||||
@ -31,7 +33,7 @@ import intellij.haskell.settings.HaskellSettingsState
|
||||
import intellij.haskell.stackyaml.StackYamlComponent
|
||||
import intellij.haskell.util.{HaskellFileUtil, HaskellProjectUtil}
|
||||
|
||||
import scala.collection.mutable.ListBuffer
|
||||
import scala.collection.JavaConverters._
|
||||
import scala.concurrent.SyncVar
|
||||
|
||||
object StackCommandLine {
|
||||
@ -154,10 +156,11 @@ object StackCommandLine {
|
||||
|
||||
private class MessageViewProcessAdapter(val compileContext: CompileContext, val progressIndicator: ProgressIndicator) extends ProcessAdapter() {
|
||||
|
||||
private final val WhileBuildingText = "-- While building custom"
|
||||
private final val WhileBuildingText = "-- While building "
|
||||
private final var whileBuildingTextIsPassed = false
|
||||
|
||||
private val previousMessageLines = ListBuffer[String]()
|
||||
private val previousMessageLines = new LinkedBlockingDeque[String]
|
||||
@volatile
|
||||
private var globalError = false
|
||||
|
||||
override def onTextAvailable(event: ProcessEvent, outputType: Key[_]) {
|
||||
@ -175,7 +178,7 @@ object StackCommandLine {
|
||||
}
|
||||
|
||||
def addLastMessage(): Unit = {
|
||||
if (previousMessageLines.nonEmpty) {
|
||||
if (!previousMessageLines.isEmpty) {
|
||||
addMessage()
|
||||
}
|
||||
}
|
||||
@ -188,11 +191,11 @@ object StackCommandLine {
|
||||
}
|
||||
|
||||
// End of sentence which was over multiple lines
|
||||
if (previousMessageLines.nonEmpty && !text.startsWith(" ")) {
|
||||
if (!previousMessageLines.isEmpty && !text.startsWith(" ")) {
|
||||
addMessage()
|
||||
}
|
||||
|
||||
previousMessageLines.append(text)
|
||||
previousMessageLines.add(text)
|
||||
} else if (text.startsWith("Warning:")) {
|
||||
compileContext.addMessage(CompilerMessageCategory.WARNING, text, null, -1, -1)
|
||||
} else if (text.startsWith("Error:")) {
|
||||
@ -203,8 +206,9 @@ object StackCommandLine {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private def addMessage(): Unit = {
|
||||
val errorMessageLine = previousMessageLines.mkString(" ")
|
||||
val errorMessageLine = previousMessageLines.iterator().asScala.mkString(" ")
|
||||
val compilationProblem = HaskellCompilationResultHelper.parseErrorLine(errorMessageLine.replaceAll("\n", " "))
|
||||
compilationProblem match {
|
||||
case Some(p@CompilationProblem(filePath, lineNr, columnNr, message)) if p.isWarning =>
|
||||
|
@ -19,7 +19,7 @@ package intellij.haskell.external.repl
|
||||
import com.intellij.openapi.project.Project
|
||||
import intellij.haskell.external.repl.StackRepl.StackReplOutput
|
||||
|
||||
case class GlobalStackRepl(project: Project, replTimeout: Int) extends StackRepl(project, None, Seq("--no-package-hiding"), replTimeout) {
|
||||
case class GlobalStackRepl(project: Project, replTimeout: Int) extends StackRepl(project, None, Seq("--no-package-hiding", "--no-load"), replTimeout) {
|
||||
|
||||
private[this] var loadedModuleName: Option[String] = None
|
||||
|
||||
|
@ -29,7 +29,7 @@ import intellij.haskell.util.{HaskellFileUtil, ScalaFutureUtil}
|
||||
import scala.collection.JavaConverters._
|
||||
import scala.concurrent.Future
|
||||
|
||||
case class ProjectStackRepl(project: Project, stackComponentInfo: StackComponentInfo, replTimeout: Int) extends StackRepl(project, Some(stackComponentInfo), Seq(), replTimeout: Int) {
|
||||
case class ProjectStackRepl(project: Project, stackComponentInfo: StackComponentInfo, replTimeout: Int) extends StackRepl(project, Some(stackComponentInfo), Seq("--ghc-options", "-fobject-code"), replTimeout: Int) {
|
||||
|
||||
import intellij.haskell.external.repl.ProjectStackRepl._
|
||||
|
||||
@ -63,6 +63,9 @@ case class ProjectStackRepl(project: Project, stackComponentInfo: StackComponent
|
||||
@volatile
|
||||
private var busy = false
|
||||
|
||||
@volatile
|
||||
private var objectCodeEnabled = true
|
||||
|
||||
def isBusy: Boolean = {
|
||||
busy
|
||||
}
|
||||
@ -76,7 +79,7 @@ case class ProjectStackRepl(project: Project, stackComponentInfo: StackComponent
|
||||
val filePath = getFilePath(psiFile)
|
||||
|
||||
def execute = {
|
||||
findInfoForCommand(moduleName, psiFile, s":type-at $filePath $startLineNr $startColumnNr $endLineNr $endColumnNr $expression", setBusy = true)
|
||||
executeModuleLoadedCommand(moduleName, psiFile, s":type-at $filePath $startLineNr $startColumnNr $endLineNr $endColumnNr $expression")
|
||||
}
|
||||
|
||||
if (isReadAccessAllowed) {
|
||||
@ -90,7 +93,7 @@ case class ProjectStackRepl(project: Project, stackComponentInfo: StackComponent
|
||||
val filePath = getFilePath(psiFile)
|
||||
|
||||
def execute = {
|
||||
findInfoForCommand(moduleName, psiFile, s":loc-at $filePath $startLineNr $startColumnNr $endLineNr $endColumnNr $expression", setBusy = true)
|
||||
executeModuleLoadedCommand(moduleName, psiFile, s":loc-at $filePath $startLineNr $startColumnNr $endLineNr $endColumnNr $expression")
|
||||
}
|
||||
|
||||
if (isReadAccessAllowed) {
|
||||
@ -102,7 +105,7 @@ case class ProjectStackRepl(project: Project, stackComponentInfo: StackComponent
|
||||
|
||||
def findInfo(psiFile: PsiFile, name: String): Option[StackReplOutput] = {
|
||||
def execute = {
|
||||
executeWithLoad(psiFile, s":info $name")
|
||||
executeWithLoad(psiFile, s":info $name", mustBeByteCode = true)
|
||||
}
|
||||
|
||||
if (isReadAccessAllowed) {
|
||||
@ -129,37 +132,42 @@ case class ProjectStackRepl(project: Project, stackComponentInfo: StackComponent
|
||||
}
|
||||
}
|
||||
|
||||
private final val FailedModuleLoaded = "Failed, modules loaded: "
|
||||
private final val FailedModulesLoaded = "Failed, modules loaded: "
|
||||
|
||||
private final val OkModulesLoaded = "Ok, modules loaded: "
|
||||
|
||||
private def setLoadedModules(o: StackReplOutput): Unit = {
|
||||
private def setLoadedModules(output: StackReplOutput): Unit = {
|
||||
loadedDependentModules.clear()
|
||||
val loadedModuleNames = o.stdoutLines.find(l => l.startsWith(OkModulesLoaded) || l.startsWith(FailedModuleLoaded)).map(findLoadedModuleNames).getOrElse(Array())
|
||||
val loadedModuleNames = output.stdoutLines.find(l => l.startsWith(OkModulesLoaded) || l.startsWith(FailedModulesLoaded)).map(findLoadedModuleNames).getOrElse(Array())
|
||||
loadedModuleNames.foreach(mn => loadedDependentModules.put(mn, DependentModuleInfo()))
|
||||
loadedModuleNames.foreach(mn => everLoadedDependentModules.put(mn, DependentModuleInfo()))
|
||||
}
|
||||
|
||||
def load(moduleNames: Seq[String]): Unit = synchronized {
|
||||
val moduleNamesString = moduleNames.mkString(" ")
|
||||
executeWithSettingBusy(s":load $moduleNamesString").foreach(setLoadedModules)
|
||||
}
|
||||
|
||||
def load(psiFile: PsiFile, fileChanged: Option[Boolean]): Option[(StackReplOutput, Boolean)] = synchronized {
|
||||
val filePath = getFilePath(psiFile)
|
||||
val reload = if (fileChanged.contains(true)) {
|
||||
def load(psiFile: PsiFile, fileChanged: Option[Boolean], mustBeByteCode: Boolean = false): Option[(StackReplOutput, Boolean)] = synchronized {
|
||||
val forceBytecodeLoad = if (mustBeByteCode) objectCodeEnabled else false
|
||||
val reload = if (fileChanged.contains(true) && !forceBytecodeLoad) {
|
||||
val loaded = isFileLoaded(psiFile)
|
||||
loaded == Loaded || loaded == Failed
|
||||
} else {
|
||||
HaskellNotificationGroup.logInfoEvent(project, s"No :reload of file ${psiFile.getName} because this file is not changed or module not yet loaded")
|
||||
false
|
||||
}
|
||||
|
||||
val output = if (reload) {
|
||||
executeWithSettingBusy(s":reload")
|
||||
} else {
|
||||
executeWithSettingBusy(s":load $filePath")
|
||||
// In case module has to be compiled to byte-code: :set -fbyte-code AND load flag *
|
||||
val byteCodeFlag = if (forceBytecodeLoad) "*" else ""
|
||||
if (forceBytecodeLoad) {
|
||||
objectCodeEnabled = false
|
||||
executeWithSettingBusy(s":set -fbyte-code")
|
||||
} else if (!objectCodeEnabled) {
|
||||
objectCodeEnabled = true
|
||||
executeWithSettingBusy(s":set -fobject-code")
|
||||
}
|
||||
val filePath = getFilePath(psiFile)
|
||||
executeWithSettingBusy(s":load $byteCodeFlag$filePath")
|
||||
}
|
||||
|
||||
output match {
|
||||
case Some(o) =>
|
||||
val loadFailed = isLoadFailed(o)
|
||||
@ -177,26 +185,22 @@ case class ProjectStackRepl(project: Project, stackComponentInfo: StackComponent
|
||||
private def findLoadedModuleNames(line: String): Array[String] = {
|
||||
if (line.startsWith(OkModulesLoaded)) {
|
||||
line.replace(OkModulesLoaded, "").init.split(",").map(_.trim)
|
||||
} else if (line.startsWith(FailedModuleLoaded)) {
|
||||
line.replace(FailedModuleLoaded, "").init.split(",").map(_.trim)
|
||||
} else if (line.startsWith(FailedModulesLoaded)) {
|
||||
line.replace(FailedModulesLoaded, "").init.split(",").map(_.trim)
|
||||
} else {
|
||||
Array()
|
||||
}
|
||||
}
|
||||
|
||||
def getModuleIdentifiers(moduleName: String, psiFile: PsiFile): Option[StackReplOutput] = synchronized {
|
||||
if (isBrowseModuleLoaded(moduleName) || load(psiFile, None).exists(_._2 == false)) {
|
||||
def getModuleIdentifiers(moduleName: String, psiFile: Option[PsiFile]): Option[StackReplOutput] = synchronized {
|
||||
if (psiFile.isEmpty || isBrowseModuleLoaded(moduleName) || psiFile.exists(pf => load(pf, None).exists(_._2 == false))) {
|
||||
executeWithSettingBusy(s":browse! $moduleName")
|
||||
} else {
|
||||
HaskellNotificationGroup.logInfoEvent(project, s"Could not get module identifiers for module $moduleName because file ${psiFile.getName} is not loaded")
|
||||
HaskellNotificationGroup.logInfoEvent(project, s"Could not get module identifiers for module $moduleName because file ${psiFile.map(_.getName).getOrElse("-")} is not loaded")
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
def getLocalModuleIdentifiers(moduleName: String, psiFile: PsiFile): Option[StackReplOutput] = {
|
||||
executeWithLoad(psiFile, s":browse! *$moduleName", Some(moduleName))
|
||||
}
|
||||
|
||||
override def restart(forceExit: Boolean): Unit = synchronized {
|
||||
if (available && !starting) {
|
||||
exit(forceExit)
|
||||
@ -204,24 +208,20 @@ case class ProjectStackRepl(project: Project, stackComponentInfo: StackComponent
|
||||
}
|
||||
}
|
||||
|
||||
private def findInfoForCommand(moduleName: Option[String], psiFile: PsiFile, command: String, setBusy: Boolean): Option[StackReplOutput] = synchronized {
|
||||
private def executeModuleLoadedCommand(moduleName: Option[String], psiFile: PsiFile, command: String): Option[StackReplOutput] = synchronized {
|
||||
if (moduleName.exists(isModuleLoaded)) {
|
||||
if (setBusy) {
|
||||
executeWithSettingBusy(command)
|
||||
} else {
|
||||
executeWithoutSettingBusy(command)
|
||||
}
|
||||
executeWithSettingBusy(command)
|
||||
} else {
|
||||
executeWithLoad(psiFile, command)
|
||||
executeWithLoad(psiFile, command, mustBeByteCode = false)
|
||||
}
|
||||
}
|
||||
|
||||
private def executeWithLoad(psiFile: PsiFile, command: String, moduleName: Option[String] = None): Option[StackReplOutput] = synchronized {
|
||||
private def executeWithLoad(psiFile: PsiFile, command: String, moduleName: Option[String] = None, mustBeByteCode: Boolean): Option[StackReplOutput] = synchronized {
|
||||
loadedFile match {
|
||||
case Some(info) if info.psiFile == psiFile && !info.loadFailed => executeWithSettingBusy(command)
|
||||
case Some(info) if info.psiFile == psiFile && info.loadFailed => Some(StackReplOutput())
|
||||
case Some(info) if info.psiFile == psiFile & !info.loadFailed & (if (mustBeByteCode) !objectCodeEnabled else true) => executeWithSettingBusy(command)
|
||||
case Some(info) if info.psiFile == psiFile & info.loadFailed => Some(StackReplOutput())
|
||||
case _ =>
|
||||
load(psiFile, fileChanged = None)
|
||||
load(psiFile, fileChanged = None, mustBeByteCode)
|
||||
loadedFile match {
|
||||
case None => None
|
||||
case Some(info) if info.psiFile == psiFile && !info.loadFailed => executeWithSettingBusy(command)
|
||||
@ -231,18 +231,14 @@ case class ProjectStackRepl(project: Project, stackComponentInfo: StackComponent
|
||||
}
|
||||
|
||||
private def executeWithSettingBusy(command: String) = {
|
||||
busy = true
|
||||
try {
|
||||
busy = true
|
||||
executeWithoutSettingBusy(command)
|
||||
execute(command)
|
||||
} finally {
|
||||
busy = false
|
||||
}
|
||||
}
|
||||
|
||||
private def executeWithoutSettingBusy(command: String) = {
|
||||
execute(command)
|
||||
}
|
||||
|
||||
private def isLoadFailed(output: StackReplOutput): Boolean = {
|
||||
output.stdoutLines.lastOption.exists(_.contains("Failed, "))
|
||||
}
|
||||
|
@ -234,12 +234,12 @@ abstract class StackRepl(project: Project, componentInfo: Option[StackComponentI
|
||||
val replGhciOptionsFilePath = createGhciOptionsFile.getAbsolutePath
|
||||
val command = (Seq(stackPath, "repl") ++
|
||||
componentInfo.map(_.target).toSeq ++
|
||||
Seq("--with-ghc", "intero", "--no-load", "--no-build", "--ghci-options", s"-ghci-script=$replGhciOptionsFilePath", "--silent", "--ghc-options", "-v1") ++ extraOptions).mkString(" ")
|
||||
Seq("--with-ghc", "intero", "--no-build", "--ghci-options", s"-ghci-script=$replGhciOptionsFilePath", "--silent", "--ghc-options", "-v1") ++ extraOptions).mkString(" ")
|
||||
|
||||
logInfo(s"Stack REPL will be started with command: $command")
|
||||
|
||||
val processBuilder = Option(EnvironmentUtil.getEnvironmentMap) match {
|
||||
case None => Process(command, new File(project.getBasePath))
|
||||
case None => Process(command, new File(componentInfo.map(_.modulePath).getOrElse(project.getBasePath)))
|
||||
case Some(envMap) => Process(command, new File(project.getBasePath), envMap.asScala.toArray: _*)
|
||||
}
|
||||
|
||||
@ -271,7 +271,6 @@ abstract class StackRepl(project: Project, componentInfo: Option[StackComponentI
|
||||
if (stanzaType.isDefined) {
|
||||
execute(":set -fdefer-type-errors", forceExecute = true)
|
||||
execute(":set -fno-max-valid-substitutions", forceExecute = true) // TODO Check for min GHC version
|
||||
execute(":set -fobject-code", forceExecute = true)
|
||||
if (HaskellProjectUtil.setNoDiagnosticsShowCaretFlag(project)) {
|
||||
execute(s":set ${StackCommandLine.NoDiagnosticsShowCaretFlag}", forceExecute = true)
|
||||
}
|
||||
|
@ -84,10 +84,10 @@ private[external] object StackReplsManager {
|
||||
private def createStackComponentInfos(project: Project, moduleCabalInfos: Iterable[(Module, CabalInfo)]): Iterable[StackComponentInfo] = {
|
||||
moduleCabalInfos.flatMap {
|
||||
case (m: Module, cabalInfo: CabalInfo) => cabalInfo.cabalStanzas.map {
|
||||
case cs: LibraryCabalStanza => StackComponentInfo(m, cs.packageName, cs.targetName, LibType, cs.sourceDirs, None, cs.isNoImplicitPreludeActive, cs.buildDepends, cs.exposedModuleNames)
|
||||
case cs: ExecutableCabalStanza => StackComponentInfo(m, cs.packageName, cs.targetName, ExeType, cs.sourceDirs, cs.mainIs, cs.isNoImplicitPreludeActive, cs.buildDepends)
|
||||
case cs: TestSuiteCabalStanza => StackComponentInfo(m, cs.packageName, cs.targetName, TestSuiteType, cs.sourceDirs, cs.mainIs, cs.isNoImplicitPreludeActive, cs.buildDepends)
|
||||
case cs: BenchmarkCabalStanza => StackComponentInfo(m, cs.packageName, cs.targetName, BenchmarkType, cs.sourceDirs, cs.mainIs, cs.isNoImplicitPreludeActive, cs.buildDepends)
|
||||
case cs: LibraryCabalStanza => StackComponentInfo(m, cs.modulePath, cs.packageName, cs.targetName, LibType, cs.sourceDirs, None, cs.isNoImplicitPreludeActive, cs.buildDepends, cs.exposedModuleNames)
|
||||
case cs: ExecutableCabalStanza => StackComponentInfo(m, cs.modulePath, cs.packageName, cs.targetName, ExeType, cs.sourceDirs, cs.mainIs, cs.isNoImplicitPreludeActive, cs.buildDepends)
|
||||
case cs: TestSuiteCabalStanza => StackComponentInfo(m, cs.modulePath, cs.packageName, cs.targetName, TestSuiteType, cs.sourceDirs, cs.mainIs, cs.isNoImplicitPreludeActive, cs.buildDepends)
|
||||
case cs: BenchmarkCabalStanza => StackComponentInfo(m, cs.modulePath, cs.packageName, cs.targetName, BenchmarkType, cs.sourceDirs, cs.mainIs, cs.isNoImplicitPreludeActive, cs.buildDepends)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -47,6 +47,7 @@
|
||||
methods("q_con_qualifier1|q_con_qualifier2|q_con_qualifier3|q_con_qualifier4|qualifier")=[getName setName getNameIdentifier getReference getPresentation]
|
||||
implements("q_con_qualifier1|q_con_qualifier2|q_con_qualifier3|q_con_qualifier4|qualifier")="intellij.haskell.psi.HaskellQualifierElement"
|
||||
mixin("q_con_qualifier1|q_con_qualifier2|q_con_qualifier3|q_con_qualifier4|qualifier")="intellij.haskell.psi.impl.HaskellQualifierElementImpl"
|
||||
implements("q_con_qualifier1|q_con_qualifier2|q_con_qualifier3|q_con_qualifier4|qualifier")="intellij.haskell.psi.HaskellNamedElement"
|
||||
|
||||
implements("text_literal")=["intellij.haskell.psi.HaskellStringLiteralElement" "com.intellij.psi.PsiLanguageInjectionHost"]
|
||||
mixin("text_literal")="intellij.haskell.psi.impl.HaskellStringLiteralElementImpl"
|
||||
@ -170,7 +171,7 @@ q_names ::= q_name (COMMA q_name)*
|
||||
gtycon ::= QUOTE? q_name | LEFT_PAREN RIGHT_ARROW RIGHT_PAREN | LEFT_PAREN RIGHT_PAREN | QUOTE? LEFT_BRACKET RIGHT_BRACKET | LEFT_PAREN COMMA (COMMA)* RIGHT_PAREN
|
||||
|
||||
private constrs ::= constr (onls VERTICAL_BAR onls constr)*
|
||||
private constr ::= (type_signature | constr1 | constr2 | constr3)
|
||||
constr ::= (type_signature | constr1 | constr2 | constr3)
|
||||
constr1 ::= pragma? (onls q_name)? (onls pragma)? onls LEFT_BRACE onl fielddecl? ((onl COMMA)? onl fielddecl)* onl RIGHT_BRACE
|
||||
constr2 ::= pragma? onls (ttype | q_name | LEFT_PAREN q_name* RIGHT_PAREN | LEFT_BRACKET q_name* RIGHT_BRACKET) (onls pragma onls)? ((onls pragma)? onls ttype (onls pragma)?)* // Base.hs: data () = () and GHC.Types.hs: data [] a = [] | a : [a]
|
||||
constr3 ::= (onls pragma? onls ttype)+
|
||||
|
@ -61,7 +61,7 @@ class HLintQuickfix(startElement: PsiElement, endElement: PsiElement, startLineN
|
||||
}
|
||||
}
|
||||
|
||||
HaskellPsiUtil.findExpressionParent(commonParent).foreach(e => {
|
||||
HaskellPsiUtil.findExpression(commonParent).foreach(e => {
|
||||
val manager = PsiDocumentManager.getInstance(project)
|
||||
val document = manager.getDocument(psiFile)
|
||||
manager.doPostponedOperationsAndUnblockDocument(document)
|
||||
|
@ -20,9 +20,11 @@ import java.io.File
|
||||
|
||||
import com.intellij.ide.util.projectWizard._
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.application.{ApplicationManager, WriteAction}
|
||||
import com.intellij.openapi.application.{ApplicationManager, ModalityState, WriteAction}
|
||||
import com.intellij.openapi.module.{ModifiableModuleModel, Module, ModuleType}
|
||||
import com.intellij.openapi.project.{Project, ProjectManager, ProjectUtil}
|
||||
import com.intellij.openapi.progress.impl.CoreProgressManager
|
||||
import com.intellij.openapi.progress.{EmptyProgressIndicator, ProgressIndicator, Task}
|
||||
import com.intellij.openapi.project.{Project, ProjectManager}
|
||||
import com.intellij.openapi.projectRoots.SdkTypeId
|
||||
import com.intellij.openapi.roots._
|
||||
import com.intellij.openapi.roots.impl.libraries.ProjectLibraryTable
|
||||
@ -75,7 +77,7 @@ class HaskellModuleBuilder extends TemplateModuleBuilder(null, HaskellModuleType
|
||||
|
||||
if (isNewProjectWithoutExistingSources) {
|
||||
val packageRelativePath = StackYamlComponent.getPackagePaths(project).flatMap(_.headOption)
|
||||
packageRelativePath.flatMap(pp => HaskellModuleBuilder.createCabalInfo(rootModel.getProject, HaskellFileUtil.getAbsolutePath(ProjectUtil.guessProjectDir(project)), pp)) match {
|
||||
packageRelativePath.flatMap(pp => HaskellModuleBuilder.createCabalInfo(rootModel.getProject, project.getBasePath, pp)) match {
|
||||
case Some(ci) => cabalInfo = ci
|
||||
case None =>
|
||||
Messages.showErrorDialog(s"Could not create Haskell module because could not retrieve or parse Cabal file for package path `$packageRelativePath`", "No Cabal file info")
|
||||
@ -375,10 +377,18 @@ object HaskellModuleBuilder {
|
||||
getProjectLibraryTable(project).getLibraries.find(_.getName == library.getName).foreach(library => {
|
||||
val model = getProjectLibraryTable(project).getModifiableModel
|
||||
model.removeLibrary(library)
|
||||
ApplicationManager.getApplication.invokeAndWait(ScalaUtil.runnable(WriteAction.run(() => model.commit())))
|
||||
runSynchronously(createTask(project, ScalaUtil.runnable(WriteAction.run(() => model.commit()))))
|
||||
})
|
||||
}
|
||||
|
||||
private def createTask(project: Project, action: => Unit) = {
|
||||
new Task.Modal(project, "Creating/updating module", true) {
|
||||
override def run(indicator: ProgressIndicator): Unit = {
|
||||
action
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def createProjectLibrary(project: Project, libraryDependency: HaskellLibraryDependency, projectLibDirectory: File): Library = {
|
||||
val projectLibraryTableModel = getProjectLibraryTable(project).getModifiableModel
|
||||
val (libraryName, sourceRootPath) = (libraryDependency.nameVersion, getPackageDirectory(projectLibDirectory, libraryDependency))
|
||||
@ -388,8 +398,8 @@ object HaskellModuleBuilder {
|
||||
libraryModel.addRoot(sourceRootUrl, OrderRootType.CLASSES)
|
||||
libraryModel.addRoot(sourceRootUrl, OrderRootType.SOURCES)
|
||||
|
||||
ApplicationManager.getApplication.invokeAndWait(ScalaUtil.runnable(WriteAction.run(() => libraryModel.commit())))
|
||||
ApplicationManager.getApplication.invokeAndWait(ScalaUtil.runnable(WriteAction.run(() => projectLibraryTableModel.commit())))
|
||||
runSynchronously(createTask(project, WriteAction.run(() => libraryModel.commit())))
|
||||
runSynchronously(createTask(project, WriteAction.run(() => projectLibraryTableModel.commit())))
|
||||
library
|
||||
}
|
||||
|
||||
@ -399,15 +409,25 @@ object HaskellModuleBuilder {
|
||||
})
|
||||
}
|
||||
|
||||
private val progressManager = new CoreProgressManager
|
||||
|
||||
private def runSynchronously(task: Task): Unit = {
|
||||
if (ApplicationManager.getApplication.isDispatchThread) {
|
||||
progressManager.runProcessWithProgressSynchronously(task, null)
|
||||
}
|
||||
else ApplicationManager.getApplication.invokeAndWait(() => progressManager.runProcessWithProgressInCurrentThread(task, new EmptyProgressIndicator, ModalityState.defaultModalityState()))
|
||||
}
|
||||
|
||||
trait HaskellDependency {
|
||||
def name: String
|
||||
|
||||
def version: String
|
||||
|
||||
def nameVersion: String = s"$name-$version"
|
||||
def nameVersion = s"$name-$version"
|
||||
}
|
||||
|
||||
case class HaskellLibraryDependency(name: String, version: String) extends HaskellDependency
|
||||
|
||||
case class HaskellModuleDependency(name: String, version: String, module: Module) extends HaskellDependency
|
||||
|
||||
}
|
||||
|
@ -38,5 +38,5 @@ class StackProjectImportProvider(builder: StackProjectImportBuilder) extends Pro
|
||||
}
|
||||
}
|
||||
|
||||
override def getFileSample: String = "which contains stack.yaml"
|
||||
override def getFileSample: String = "<b>Haskell</b> project file (stack.yaml)"
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ class GotoByDeclarationContributor extends GotoClassContributor {
|
||||
|
||||
override def getItemsByName(name: String, pattern: String, project: Project, includeNonProjectItems: Boolean): Array[NavigationItem] = {
|
||||
val namedElements = GotoHelper.getNamedElements(name, pattern, project, includeNonProjectItems)
|
||||
val declarationElements = namedElements.map(ne => (ne, HaskellPsiUtil.findHighestDeclarationElementParent(ne)))
|
||||
val declarationElements = namedElements.map(ne => (ne, HaskellPsiUtil.findHighestDeclarationElement(ne)))
|
||||
declarationElements.sortWith(sortByClassDeclarationFirst).flatMap(_._2).toArray
|
||||
}
|
||||
|
||||
|
@ -30,6 +30,59 @@ import intellij.haskell.util.index.HaskellModuleNameIndex
|
||||
|
||||
class HaskellReference(element: HaskellNamedElement, textRange: TextRange) extends PsiPolyVariantReferenceBase[HaskellNamedElement](element, textRange) {
|
||||
|
||||
private def findModule(project: Project, modId: HaskellModid) = {
|
||||
ProgressManager.checkCanceled()
|
||||
|
||||
HaskellModuleNameIndex.findFilesByModuleName(project, modId.getName) match {
|
||||
case Right(files) => files.headOption.map(HaskellFileResolveResult)
|
||||
case Left(noInfo) => Some(NoResolveResult(noInfo))
|
||||
}
|
||||
}
|
||||
|
||||
private def findQualifierDeclaration(project: Project, psiFile: PsiFile, qualifier: HaskellQualifier) = {
|
||||
ProgressManager.checkCanceled()
|
||||
|
||||
val importDeclarations = HaskellPsiUtil.findImportDeclarations(psiFile)
|
||||
findQualifier(importDeclarations, qualifier) match {
|
||||
case Some(ne) => Some(HaskellNamedElementResolveResult(ne))
|
||||
case None => None
|
||||
ProgressManager.checkCanceled()
|
||||
|
||||
findHaskellFile(importDeclarations, qualifier, project) match {
|
||||
case Right(r) => r.map(HaskellFileResolveResult)
|
||||
case Left(noInfo) => Some(NoResolveResult(noInfo))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def findImportedIdentifierDeclaration(project: Project, psiFile: PsiFile, namedElement: HaskellNamedElement) = {
|
||||
ProgressManager.checkCanceled()
|
||||
|
||||
HaskellPsiUtil.findImportDeclaration(namedElement) match {
|
||||
case Some(d) =>
|
||||
val importQualifier = Option(d.getImportQualifiedAs).map(_.getQualifier.getName).orElse(d.getModuleName)
|
||||
ProgressManager.checkCanceled()
|
||||
|
||||
resolveReference(namedElement, psiFile, project, importQualifier) match {
|
||||
case Right(r) => Some(HaskellNamedElementResolveResult(r))
|
||||
case Left(_) => None
|
||||
}
|
||||
case None => None
|
||||
}
|
||||
}
|
||||
|
||||
private def isPartOfModId(namedElement: HaskellNamedElement): Boolean = {
|
||||
Option(namedElement.getParent).map(_.getNode.getElementType).contains(HaskellTypes.HS_MODID)
|
||||
}
|
||||
|
||||
private def isPartOfQualifier(namedElement: HaskellNamedElement): Boolean = {
|
||||
Option(namedElement.getParent).map(_.getNode.getElementType).contains(HaskellTypes.HS_QUALIFIER)
|
||||
}
|
||||
|
||||
private def isPartOfQualifiedAs(namedElement: HaskellNamedElement): Boolean = {
|
||||
Option(namedElement.getParent).map(_.getNode.getElementType).contains(HaskellTypes.HS_IMPORT_QUALIFIED_AS)
|
||||
}
|
||||
|
||||
override def multiResolve(incompleteCode: Boolean): Array[ResolveResult] = {
|
||||
val project = element.getProject
|
||||
if (StackProjectManager.isInitializing(project)) {
|
||||
@ -41,69 +94,48 @@ class HaskellReference(element: HaskellNamedElement, textRange: TextRange) exten
|
||||
} else {
|
||||
ProgressManager.checkCanceled()
|
||||
val psiFile = element.getContainingFile.getOriginalFile
|
||||
|
||||
val result = element match {
|
||||
case mi: HaskellModid => HaskellModuleNameIndex.findFileByModuleName(project, mi.getName) match {
|
||||
case Right(files) => files.headOption.map(HaskellFileResolveResult)
|
||||
case Left(noInfo) => Some(NoResolveResult(noInfo))
|
||||
}
|
||||
|
||||
case qe: HaskellQualifierElement =>
|
||||
val importDeclarations = HaskellPsiUtil.findImportDeclarations(psiFile)
|
||||
findQualifier(importDeclarations, qe) match {
|
||||
case Some(q) => HaskellPsiUtil.findNamedElement(q).map(HaskellNamedElementResolveResult)
|
||||
case None => findHaskellFile(importDeclarations, qe, project) match {
|
||||
case Right(r) => r.map(HaskellFileResolveResult)
|
||||
case Left(noInfo) => Some(NoResolveResult(noInfo))
|
||||
}
|
||||
}
|
||||
case q: HaskellQualifier if isPartOfQualifiedAs(q) => Some(HaskellNamedElementResolveResult(q))
|
||||
case q: HaskellQualifier => findQualifierDeclaration(project, psiFile, q)
|
||||
case mid: HaskellModid => findModule(project, mid)
|
||||
case ne: HaskellNamedElement if isPartOfQualifier(ne) | isPartOfQualifiedAs(ne) | isPartOfModId(ne) => None
|
||||
case ne: HaskellNamedElement =>
|
||||
HaskellPsiUtil.findImportDeclarationParent(ne) match {
|
||||
case Some(id) =>
|
||||
val importQualifier = Option(id.getImportQualifiedAs).map(_.getQualifier.getName).orElse(id.getModuleName)
|
||||
resolveReference(ne, psiFile, project, importQualifier) match {
|
||||
ProgressManager.checkCanceled()
|
||||
|
||||
findImportedIdentifierDeclaration(project, psiFile, ne).orElse {
|
||||
|
||||
HaskellPsiUtil.findTypeSignatureDeclaration(ne) match {
|
||||
case None => resolveReference(ne, psiFile, project = project, None) match {
|
||||
case Right(r) => Some(HaskellNamedElementResolveResult(r))
|
||||
case Left(noInfo) => Some(NoResolveResult(noInfo))
|
||||
}
|
||||
case None =>
|
||||
if (HaskellPsiUtil.findQualifierParent(ne).isDefined || HaskellPsiUtil.findModIdElement(element).isDefined) {
|
||||
// Because they are already handled by HaskellQualifierElement and HaskellModId case
|
||||
None
|
||||
} else {
|
||||
ProgressManager.checkCanceled()
|
||||
HaskellPsiUtil.findTypeSignatureDeclarationParent(ne) match {
|
||||
case None => resolveReference(ne, psiFile, project = project, None) match {
|
||||
case Right(r) => Some(HaskellNamedElementResolveResult(r))
|
||||
case Left(noInfo) => Some(NoResolveResult(noInfo))
|
||||
case Some(ts) =>
|
||||
|
||||
def find(e: PsiElement): Option[HaskellNamedElement] = {
|
||||
Option(PsiTreeUtil.findSiblingForward(e, HaskellTypes.HS_TOP_DECLARATION_LINE, null)) match {
|
||||
case Some(d) if Option(d.getFirstChild).flatMap(c => Option(c.getFirstChild)).exists(_.isInstanceOf[HaskellExpression]) => HaskellPsiUtil.findNamedElements(d).headOption.find(_.getName == ne.getName)
|
||||
case _ => None
|
||||
}
|
||||
case Some(ts) =>
|
||||
}
|
||||
|
||||
def find(e: PsiElement): Option[HaskellNamedElement] = {
|
||||
Option(PsiTreeUtil.findSiblingForward(e, HaskellTypes.HS_TOP_DECLARATION_LINE, null)) match {
|
||||
case Some(d) if Option(d.getFirstChild).flatMap(c => Option(c.getFirstChild)).exists(_.isInstanceOf[HaskellExpression]) => HaskellPsiUtil.findNamedElements(d).headOption.find(_.getName == ne.getName)
|
||||
case _ => None
|
||||
}
|
||||
}
|
||||
ProgressManager.checkCanceled()
|
||||
|
||||
ProgressManager.checkCanceled()
|
||||
|
||||
// Work around Intero bug.
|
||||
Option(ts.getParent).flatMap(p => Option(p.getParent)) match {
|
||||
case Some(p) =>
|
||||
find(p) match {
|
||||
case Some(ee) => Some(HaskellNamedElementResolveResult(ee))
|
||||
case None => resolveReference(ne, psiFile, project, None) match {
|
||||
case Right(r) => Some(HaskellNamedElementResolveResult(r))
|
||||
case Left(noInfo) => Some(NoResolveResult(noInfo))
|
||||
}
|
||||
}
|
||||
// Work around Intero bug.
|
||||
Option(ts.getParent).flatMap(p => Option(p.getParent)) match {
|
||||
case Some(p) =>
|
||||
find(p) match {
|
||||
case Some(ee) => Some(HaskellNamedElementResolveResult(ee))
|
||||
case None => resolveReference(ne, psiFile, project, None) match {
|
||||
case Right(r) => Some(HaskellNamedElementResolveResult(r))
|
||||
case Left(noInfo) => Some(NoResolveResult(noInfo))
|
||||
}
|
||||
}
|
||||
case None => resolveReference(ne, psiFile, project, None) match {
|
||||
case Right(r) => Some(HaskellNamedElementResolveResult(r))
|
||||
case Left(noInfo) => Some(NoResolveResult(noInfo))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case _ => None
|
||||
}
|
||||
@ -123,7 +155,7 @@ class HaskellReference(element: HaskellNamedElement, textRange: TextRange) exten
|
||||
NoInfoAvailable(ApplicationUtil.runReadAction(namedElement.getName), psiFile.getName)
|
||||
}
|
||||
|
||||
HaskellPsiUtil.findQualifiedNameParent(namedElement) match {
|
||||
HaskellPsiUtil.findQualifiedName(namedElement) match {
|
||||
case Some(qualifiedNameElement) =>
|
||||
ProgressManager.checkCanceled()
|
||||
resolveReferenceByDefinitionLocation(qualifiedNameElement, psiFile, importQualifier)
|
||||
@ -142,15 +174,14 @@ class HaskellReference(element: HaskellNamedElement, textRange: TextRange) exten
|
||||
}
|
||||
|
||||
private def findQualifier(importDeclarations: Iterable[HaskellImportDeclaration], qualifierElement: HaskellQualifierElement): Option[HaskellNamedElement] = {
|
||||
importDeclarations.flatMap(id => Option(id.getImportQualifiedAs)).flatMap(iqa => Option(iqa.getQualifier)).find(_.getName == qualifierElement.getName).
|
||||
orElse(importDeclarations.filter(id => Option(id.getImportQualified).isDefined && Option(id.getImportQualifiedAs).isEmpty).find(mi => Option(mi.getModid).map(_.getName).contains(qualifierElement.getName)).map(_.getModid))
|
||||
importDeclarations.flatMap(id => Option(id.getImportQualifiedAs)).flatMap(iqa => Option(iqa.getQualifier)).find(_.getName == qualifierElement.getName)
|
||||
}
|
||||
|
||||
private def findHaskellFile(importDeclarations: Iterable[HaskellImportDeclaration], qualifierElement: HaskellQualifierElement, project: Project): Either[NoInfo, Option[PsiFile]] = {
|
||||
private def findHaskellFile(importDeclarations: Iterable[HaskellImportDeclaration], qualifierElement: HaskellNamedElement, project: Project): Either[NoInfo, Option[PsiFile]] = {
|
||||
val result = for {
|
||||
id <- importDeclarations.find(id => id.getModuleName.contains(qualifierElement.getName))
|
||||
mn <- id.getModuleName
|
||||
} yield HaskellModuleNameIndex.findFileByModuleName(project, mn)
|
||||
} yield HaskellModuleNameIndex.findFilesByModuleName(project, mn)
|
||||
|
||||
result match {
|
||||
case Some(Right(files)) => Right(files.headOption)
|
||||
@ -163,34 +194,55 @@ class HaskellReference(element: HaskellNamedElement, textRange: TextRange) exten
|
||||
object HaskellReference {
|
||||
|
||||
def resolveInstanceReferences(project: Project, namedElement: HaskellNamedElement, nameInfos: Iterable[NameInfoComponentResult.NameInfo]): Seq[HaskellNamedElement] = {
|
||||
val result = nameInfos.map(ni => findIdentifiersByNameInfo(ni, namedElement, project)).toSeq.distinct
|
||||
if (result.contains(Left(ReadActionTimeout))) {
|
||||
val identifiers = nameInfos.map(ni => findIdentifiersByNameInfo(ni, namedElement, project)).toSeq.distinct
|
||||
if (identifiers.contains(Left(ReadActionTimeout))) {
|
||||
HaskellEditorUtil.showStatusBarBalloonMessage(project, "Navigating to instance declarations is not available at this moment")
|
||||
Seq()
|
||||
} else {
|
||||
result.flatMap(_.toOption).flatten
|
||||
identifiers.flatMap(_.toOption).flatten
|
||||
}
|
||||
}
|
||||
|
||||
def findIdentifiersByLibraryNameInfo(project: Project, libraryNameInfo: LibraryNameInfo, name: String): Either[NoInfo, Seq[HaskellNamedElement]] = {
|
||||
findIdentifiersByModuleAndName(project, libraryNameInfo.moduleName, name)
|
||||
findIdentifiersByModuleAndName(project, Seq(libraryNameInfo.moduleName), name)
|
||||
}
|
||||
|
||||
def findIdentifiersByModuleAndName(project: Project, moduleName: String, name: String): Either[NoInfo, Seq[HaskellNamedElement]] = {
|
||||
def findIdentifiersByModuleAndName(project: Project, moduleNames: Seq[String], name: String): Either[NoInfo, Seq[HaskellNamedElement]] = {
|
||||
ProgressManager.checkCanceled()
|
||||
|
||||
// For know we just take the first module which contains element with same name
|
||||
for {
|
||||
moduleNameFiles <- HaskellModuleNameIndex.findFileByModuleName(project, moduleName)
|
||||
() = ProgressManager.checkCanceled()
|
||||
ne <- Right(moduleNameFiles.flatMap(findIdentifierInFileByName(_, name)))
|
||||
} yield ne
|
||||
moduleNames.map(mn => HaskellModuleNameIndex.findFilesByModuleName(project, mn)).find(_.isLeft) match {
|
||||
case Some(Left(noInfo)) => Left(noInfo)
|
||||
case _ =>
|
||||
ProgressManager.checkCanceled()
|
||||
|
||||
val identifiers = moduleNames.map(mn => HaskellModuleNameIndex.findFilesByModuleName(project, mn)).flatMap(_.toSeq.flatMap(_.flatMap(f => findIdentifierInFileByName(f, name))))
|
||||
if (identifiers.isEmpty) {
|
||||
val importedModuleNames = moduleNames.map(mn => HaskellModuleNameIndex.findFilesByModuleName(project, mn)).flatMap(_.toSeq.flatMap(_.flatMap(pf => HaskellPsiUtil.findImportDeclarations(pf).flatMap(_.getModuleName))))
|
||||
if (importedModuleNames.isEmpty) {
|
||||
Right(Seq())
|
||||
} else {
|
||||
findIdentifiersByModuleAndName(project, importedModuleNames, name)
|
||||
}
|
||||
} else {
|
||||
Right(identifiers)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def findIdentifierInFileByName(psifile: PsiFile, name: String): Option[HaskellNamedElement] = {
|
||||
import scala.collection.JavaConverters._
|
||||
|
||||
def findInDeclarations = {
|
||||
ProgressManager.checkCanceled()
|
||||
|
||||
val topLevelExpressions = HaskellPsiUtil.findTopLevelExpressions(psifile)
|
||||
|
||||
ProgressManager.checkCanceled()
|
||||
|
||||
val expressionIdentifiers = topLevelExpressions.flatMap(_.getQNameList.asScala.headOption.map(_.getIdentifierElement)).find(_.getName == name)
|
||||
|
||||
ProgressManager.checkCanceled()
|
||||
|
||||
if (expressionIdentifiers.isEmpty) {
|
||||
val declarationElements = HaskellPsiUtil.findHaskellDeclarationElements(psifile)
|
||||
|
||||
ProgressManager.checkCanceled()
|
||||
@ -200,30 +252,8 @@ object HaskellReference {
|
||||
ProgressManager.checkCanceled()
|
||||
|
||||
declarationIdentifiers.toSeq.sortWith(sortByClassDeclarationFirst).headOption
|
||||
}
|
||||
|
||||
ProgressManager.checkCanceled()
|
||||
|
||||
if (HaskellProjectUtil.isLibraryFile(psifile)) {
|
||||
findInDeclarations
|
||||
} else if (HaskellProjectUtil.isSourceFile(psifile)) {
|
||||
ProgressManager.checkCanceled()
|
||||
|
||||
val topLevelExpressions = HaskellPsiUtil.findTopLevelExpressions(psifile)
|
||||
|
||||
ProgressManager.checkCanceled()
|
||||
|
||||
val expressionIdentifiers = topLevelExpressions.flatMap(_.getQNameList.asScala.headOption.map(_.getIdentifierElement)).find(_.getName == name)
|
||||
|
||||
ProgressManager.checkCanceled()
|
||||
|
||||
if (expressionIdentifiers.isEmpty) {
|
||||
findInDeclarations
|
||||
} else {
|
||||
expressionIdentifiers
|
||||
}
|
||||
} else {
|
||||
None
|
||||
expressionIdentifiers
|
||||
}
|
||||
}
|
||||
|
||||
@ -240,12 +270,12 @@ object HaskellReference {
|
||||
orElse {
|
||||
ProgressManager.checkCanceled()
|
||||
None
|
||||
}.orElse(HaskellPsiUtil.findHighestDeclarationElementParent(element).flatMap(_.getIdentifierElements.find(_.getName == name)).
|
||||
}.orElse(HaskellPsiUtil.findHighestDeclarationElement(element).flatMap(_.getIdentifierElements.find(_.getName == name)).
|
||||
orElse {
|
||||
ProgressManager.checkCanceled()
|
||||
None
|
||||
}.orElse(HaskellPsiUtil.findQualifiedNameParent(element).map(_.getIdentifierElement)).find(_.getName == name)).orElse {
|
||||
HaskellPsiUtil.findTypeParent(element).flatMap(_.getQNameList.asScala.map(_.getIdentifierElement).find(_.getName == name))
|
||||
}.orElse(HaskellPsiUtil.findQualifiedName(element).map(_.getIdentifierElement)).find(_.getName == name)).orElse {
|
||||
HaskellPsiUtil.findTtype(element).flatMap(_.getQNameList.asScala.map(_.getIdentifierElement).find(_.getName == name))
|
||||
}
|
||||
} yield namedElement
|
||||
|
||||
@ -255,7 +285,7 @@ object HaskellReference {
|
||||
}
|
||||
|
||||
private def sortByClassDeclarationFirst(namedElement1: HaskellNamedElement, namedElement2: HaskellNamedElement): Boolean = {
|
||||
(HaskellPsiUtil.findDeclarationElementParent(namedElement1), HaskellPsiUtil.findDeclarationElementParent(namedElement2)) match {
|
||||
(HaskellPsiUtil.findDeclarationElement(namedElement1), HaskellPsiUtil.findDeclarationElement(namedElement2)) match {
|
||||
case (Some(_: HaskellClassDeclaration), _) => true
|
||||
case (_, _) => false
|
||||
}
|
||||
@ -270,7 +300,7 @@ object HaskellReference {
|
||||
val (virtualFile, psiFile) = HaskellProjectUtil.findFile(pni.filePath, project)
|
||||
ProgressManager.checkCanceled()
|
||||
(virtualFile, psiFile) match {
|
||||
case (Some(vf), Right(Some(pf))) => findIdentifierByLocation(project, vf, pf, pni.lineNr, pni.columnNr, name).map(r => Right(Seq(r))).getOrElse(Left(NoInfoAvailable(name, "-")))
|
||||
case (Some(vf), Right(pf)) => findIdentifierByLocation(project, vf, pf, pni.lineNr, pni.columnNr, name).map(r => Right(Seq(r))).getOrElse(Left(NoInfoAvailable(name, "-")))
|
||||
case (_, Right(_)) => Left(NoInfoAvailable(name, "-"))
|
||||
case (_, Left(noInfo)) => Left(noInfo)
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ class HoogleByNameContributor extends ChooseByNameContributor {
|
||||
val navigationItems = HoogleComponent.runHoogle(project, hooglePattern, count = 25).getOrElse(Seq()).flatMap {
|
||||
case ModulePattern(moduleName) =>
|
||||
ProgressManager.checkCanceled()
|
||||
HaskellModuleNameIndex.findFileByModuleName(project, moduleName) match {
|
||||
HaskellModuleNameIndex.findFilesByModuleName(project, moduleName) match {
|
||||
case Right(files) => files
|
||||
case _ => Seq()
|
||||
}
|
||||
@ -69,10 +69,10 @@ class HoogleByNameContributor extends ChooseByNameContributor {
|
||||
ProgressManager.checkCanceled()
|
||||
val navigationItemByNameInfo = result.toOption.flatMap(_.headOption) match {
|
||||
case Some(lni: LibraryNameInfo) => HaskellReference.findIdentifiersByLibraryNameInfo(project, lni, name).toOption.getOrElse(Seq()).
|
||||
flatMap(HaskellPsiUtil.findDeclarationElementParent).map(d => createLibraryNavigationItem(d, moduleName))
|
||||
flatMap(HaskellPsiUtil.findDeclarationElement).map(d => createLibraryNavigationItem(d, moduleName))
|
||||
case Some(pni: ProjectNameInfo) =>
|
||||
HaskellProjectUtil.findFile(pni.filePath, project) match {
|
||||
case (Some(virtualFile), Right(Some(psiFile))) => HaskellReference.findIdentifierByLocation(project, virtualFile, psiFile, pni.lineNr, pni.columnNr, name).flatMap(HaskellPsiUtil.findDeclarationElementParent).toSeq
|
||||
case (Some(virtualFile), Right(psiFile)) => HaskellReference.findIdentifierByLocation(project, virtualFile, psiFile, pni.lineNr, pni.columnNr, name).flatMap(HaskellPsiUtil.findDeclarationElement).toSeq
|
||||
case (_, _) => Seq()
|
||||
}
|
||||
case _ => Seq()
|
||||
@ -81,11 +81,11 @@ class HoogleByNameContributor extends ChooseByNameContributor {
|
||||
if (navigationItemByNameInfo.nonEmpty) {
|
||||
navigationItemByNameInfo
|
||||
} else {
|
||||
val identifier = HaskellReference.findIdentifiersByModuleAndName(project, moduleName, name).toOption
|
||||
if (identifier.isEmpty) {
|
||||
val identifiers = HaskellReference.findIdentifiersByModuleAndName(project, Seq(moduleName), name).toOption
|
||||
if (identifiers.isEmpty) {
|
||||
NotFoundResult(moduleName, declaration)
|
||||
} else {
|
||||
identifier.getOrElse(Seq()).flatMap(HaskellPsiUtil.findDeclarationElementParent)
|
||||
identifiers.getOrElse(Seq()).map(e => HaskellPsiUtil.findDeclarationElement(e).getOrElse(e))
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -94,7 +94,7 @@ class HoogleByNameContributor extends ChooseByNameContributor {
|
||||
Seq(NotFoundNavigationItem(d))
|
||||
}
|
||||
|
||||
navigationItems.zipWithIndex.map({ case (item, i) => new NavigationItem {
|
||||
navigationItems.groupBy(_.getPresentation.getLocationString).flatMap(_._2.headOption).zipWithIndex.map({ case (item, i) => new NavigationItem {
|
||||
|
||||
// Hack to display items in same order as given by Hoogle
|
||||
override def getName: String = {
|
||||
|
@ -21,42 +21,6 @@ import com.intellij.psi.PsiElement
|
||||
|
||||
object HaskellElementCondition {
|
||||
|
||||
final val ImportDeclarationsCondition = new Condition[PsiElement]() {
|
||||
override def value(psiElement: PsiElement): Boolean = {
|
||||
psiElement match {
|
||||
case _: HaskellImportDeclarations => true
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final val ImportDeclarationCondition = new Condition[PsiElement]() {
|
||||
override def value(psiElement: PsiElement): Boolean = {
|
||||
psiElement match {
|
||||
case _: HaskellImportDeclaration => true
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final val ImportHidingSpecCondition = new Condition[PsiElement]() {
|
||||
override def value(psiElement: PsiElement): Boolean = {
|
||||
psiElement match {
|
||||
case _: HaskellImportHidingSpec => true
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final val ImportSpecCondition = new Condition[PsiElement]() {
|
||||
override def value(psiElement: PsiElement): Boolean = {
|
||||
psiElement match {
|
||||
case _: HaskellImportSpec => true
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final val QualifiedNameElementCondition = new Condition[PsiElement]() {
|
||||
override def value(psiElement: PsiElement): Boolean = {
|
||||
psiElement match {
|
||||
@ -66,15 +30,6 @@ object HaskellElementCondition {
|
||||
}
|
||||
}
|
||||
|
||||
final val TtypeElementCondition = new Condition[PsiElement]() {
|
||||
override def value(psiElement: PsiElement): Boolean = {
|
||||
psiElement match {
|
||||
case _: HaskellTtype => true
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final val DeclarationElementCondition = new Condition[PsiElement]() {
|
||||
override def value(psiElement: PsiElement): Boolean = {
|
||||
psiElement match {
|
||||
@ -84,24 +39,6 @@ object HaskellElementCondition {
|
||||
}
|
||||
}
|
||||
|
||||
final val DataDeclarationElementCondition = new Condition[PsiElement]() {
|
||||
override def value(psiElement: PsiElement): Boolean = {
|
||||
psiElement match {
|
||||
case _: HaskellDataDeclaration => true
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final val NewTypeDeclarationElementCondition = new Condition[PsiElement]() {
|
||||
override def value(psiElement: PsiElement): Boolean = {
|
||||
psiElement match {
|
||||
case _: HaskellNewtypeDeclaration => true
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final val HighestDeclarationElementCondition = new Condition[PsiElement]() {
|
||||
override def value(psiElement: PsiElement): Boolean = {
|
||||
psiElement match {
|
||||
@ -112,33 +49,6 @@ object HaskellElementCondition {
|
||||
}
|
||||
}
|
||||
|
||||
final val ModuleDeclarationCondition = new Condition[PsiElement]() {
|
||||
override def value(psiElement: PsiElement): Boolean = {
|
||||
psiElement match {
|
||||
case _: HaskellModuleDeclaration => true
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final val TypeSignatureCondition = new Condition[PsiElement]() {
|
||||
override def value(psiElement: PsiElement): Boolean = {
|
||||
psiElement match {
|
||||
case _: HaskellTypeSignature => true
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final val ExpressionCondition = new Condition[PsiElement]() {
|
||||
override def value(psiElement: PsiElement): Boolean = {
|
||||
psiElement match {
|
||||
case _: HaskellExpression => true
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final val NamedElementCondition = new Condition[PsiElement]() {
|
||||
override def value(psiElement: PsiElement): Boolean = {
|
||||
psiElement match {
|
||||
@ -147,22 +57,4 @@ object HaskellElementCondition {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final val ModIdElementCondition = new Condition[PsiElement]() {
|
||||
override def value(psiElement: PsiElement): Boolean = {
|
||||
psiElement match {
|
||||
case _: HaskellModid => true
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final val FileHeaderCondition = new Condition[PsiElement]() {
|
||||
override def value(psiElement: PsiElement): Boolean = {
|
||||
psiElement match {
|
||||
case _: HaskellFileHeader => true
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -54,7 +54,21 @@ object HaskellPsiUtil {
|
||||
def findModIdElement(psiElement: PsiElement): Option[HaskellModid] = {
|
||||
psiElement match {
|
||||
case e: HaskellModid => Some(e)
|
||||
case e => Option(PsiTreeUtil.findFirstParent(e, ModIdElementCondition)).map(_.asInstanceOf[HaskellModid])
|
||||
case e => Option(TreeUtil.findParent(e.getNode, HaskellTypes.HS_MODID)).map(_.getPsi.asInstanceOf[HaskellModid])
|
||||
}
|
||||
}
|
||||
|
||||
def findDataConstr(psiElement: PsiElement): Option[HaskellConstr] = {
|
||||
psiElement match {
|
||||
case e: HaskellConstr => Some(e)
|
||||
case e => Option(TreeUtil.findParent(e.getNode, HaskellTypes.HS_CONSTR)).map(_.getPsi.asInstanceOf[HaskellConstr])
|
||||
}
|
||||
}
|
||||
|
||||
def findDataFieldDecl(psiElement: PsiElement): Option[HaskellFielddecl] = {
|
||||
psiElement match {
|
||||
case e: HaskellFielddecl => Some(e)
|
||||
case e => Option(TreeUtil.findParent(e.getNode, HaskellTypes.HS_FIELDDECL)).map(_.getPsi.asInstanceOf[HaskellFielddecl])
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,7 +86,7 @@ object HaskellPsiUtil {
|
||||
}
|
||||
|
||||
def findTopLevelDeclarations(psiFile: PsiFile): Iterable[HaskellDeclarationElement] = {
|
||||
findHaskellDeclarationElements(psiFile).filterNot(e => e.getNode.getElementType == HS_IMPORT_DECLARATION)
|
||||
findHaskellDeclarationElements(psiFile).filterNot(e => Seq(HS_IMPORT_DECLARATION, HS_MODULE_DECLARATION).contains(e.getNode.getElementType))
|
||||
}
|
||||
|
||||
def findModuleDeclaration(psiFile: PsiFile): Option[HaskellModuleDeclaration] = {
|
||||
@ -111,102 +125,63 @@ object HaskellPsiUtil {
|
||||
}
|
||||
}
|
||||
|
||||
def findQualifierParent(psiElement: PsiElement): Option[HaskellQualifier] = {
|
||||
psiElement match {
|
||||
case e: HaskellQualifier => Some(e)
|
||||
case e => Option(TreeUtil.findParent(e.getNode, HaskellTypes.HS_QUALIFIER)).map(_.getPsi.asInstanceOf[HaskellQualifier])
|
||||
}
|
||||
}
|
||||
|
||||
def findQualifiedNameParent(psiElement: PsiElement): Option[HaskellQualifiedNameElement] = {
|
||||
def findQualifiedName(psiElement: PsiElement): Option[HaskellQualifiedNameElement] = {
|
||||
psiElement match {
|
||||
case e: HaskellQualifiedNameElement => Some(e)
|
||||
case e => Option(PsiTreeUtil.findFirstParent(e, QualifiedNameElementCondition)).map(_.asInstanceOf[HaskellQualifiedNameElement])
|
||||
}
|
||||
}
|
||||
|
||||
def findTypeParent(psiElement: PsiElement): Option[HaskellTtype] = {
|
||||
def findTtype(psiElement: PsiElement): Option[HaskellTtype] = {
|
||||
psiElement match {
|
||||
case e: HaskellTtype => Some(e)
|
||||
case e => Option(PsiTreeUtil.findFirstParent(e, TtypeElementCondition)).map(_.asInstanceOf[HaskellTtype])
|
||||
case e => Option(TreeUtil.findParent(e.getNode, HaskellTypes.HS_TTYPE)).map(_.getPsi.asInstanceOf[HaskellTtype])
|
||||
}
|
||||
}
|
||||
|
||||
def findImportDeclarationsParent(psiElement: PsiElement): Option[HaskellImportDeclarations] = {
|
||||
def findImportDeclarations(psiElement: PsiElement): Option[HaskellImportDeclarations] = {
|
||||
psiElement match {
|
||||
case e: HaskellImportDeclarations => Some(e)
|
||||
case e => Option(PsiTreeUtil.findFirstParent(e, ImportDeclarationsCondition)).map(_.asInstanceOf[HaskellImportDeclarations])
|
||||
case e => Option(TreeUtil.findParent(e.getNode, HaskellTypes.HS_IMPORT_DECLARATIONS)).map(_.getPsi.asInstanceOf[HaskellImportDeclarations])
|
||||
}
|
||||
}
|
||||
|
||||
def findImportDeclarationParent(psiElement: PsiElement): Option[HaskellImportDeclaration] = {
|
||||
def findImportDeclaration(psiElement: PsiElement): Option[HaskellImportDeclaration] = {
|
||||
psiElement match {
|
||||
case e: HaskellImportDeclaration => Some(e)
|
||||
case e => Option(PsiTreeUtil.findFirstParent(e, ImportDeclarationCondition)).map(_.asInstanceOf[HaskellImportDeclaration])
|
||||
case e => Option(TreeUtil.findParent(e.getNode, HaskellTypes.HS_IMPORT_DECLARATION)).map(_.getPsi.asInstanceOf[HaskellImportDeclaration])
|
||||
}
|
||||
}
|
||||
|
||||
def findImportHidingDeclarationParent(psiElement: PsiElement): Option[HaskellImportHidingSpec] = {
|
||||
psiElement match {
|
||||
case e: HaskellImportHidingSpec => Some(e)
|
||||
case e => Option(PsiTreeUtil.findFirstParent(e, ImportHidingSpecCondition)).map(_.asInstanceOf[HaskellImportHidingSpec])
|
||||
}
|
||||
}
|
||||
|
||||
def findHighestDeclarationElementParent(psiElement: PsiElement): Option[HaskellDeclarationElement] = {
|
||||
def findHighestDeclarationElement(psiElement: PsiElement): Option[HaskellDeclarationElement] = {
|
||||
psiElement match {
|
||||
case e: HaskellDeclarationElement => Some(e)
|
||||
case e => Option(PsiTreeUtil.findFirstParent(e, HighestDeclarationElementCondition)).map(_.asInstanceOf[HaskellDeclarationElement])
|
||||
}
|
||||
}
|
||||
|
||||
def findDeclarationElementParent(psiElement: PsiElement): Option[HaskellDeclarationElement] = {
|
||||
def findDeclarationElement(psiElement: PsiElement): Option[HaskellDeclarationElement] = {
|
||||
psiElement match {
|
||||
case e: HaskellDeclarationElement => Some(e)
|
||||
case e => Option(PsiTreeUtil.findFirstParent(e, DeclarationElementCondition)).map(_.asInstanceOf[HaskellDeclarationElement])
|
||||
}
|
||||
}
|
||||
|
||||
def findDataDeclarationElementParent(psiElement: PsiElement): Option[HaskellDataDeclaration] = {
|
||||
psiElement match {
|
||||
case e: HaskellDataDeclaration => Some(e)
|
||||
case e => Option(PsiTreeUtil.findFirstParent(e, DataDeclarationElementCondition)).map(_.asInstanceOf[HaskellDataDeclaration])
|
||||
}
|
||||
}
|
||||
|
||||
def findNewTypeDeclarationElementParent(psiElement: PsiElement): Option[HaskellNewtypeDeclaration] = {
|
||||
psiElement match {
|
||||
case e: HaskellNewtypeDeclaration => Some(e)
|
||||
case e => Option(PsiTreeUtil.findFirstParent(e, NewTypeDeclarationElementCondition)).map(_.asInstanceOf[HaskellNewtypeDeclaration])
|
||||
}
|
||||
}
|
||||
|
||||
def findTopLevelTypeSignatures(psiFile: PsiFile): Iterable[HaskellTypeSignature] = {
|
||||
PsiTreeUtil.findChildrenOfType(psiFile, classOf[HaskellTypeSignature]).asScala.filter(_.getParent.getNode.getElementType == HS_TOP_DECLARATION)
|
||||
}
|
||||
|
||||
def findTopLevelExpressions(psiFile: PsiFile): Iterable[HaskellExpression] = {
|
||||
PsiTreeUtil.findChildrenOfType(psiFile, classOf[HaskellExpression]).asScala
|
||||
}
|
||||
|
||||
def findModuleDeclarationParent(psiElement: PsiElement): Option[HaskellModuleDeclaration] = {
|
||||
psiElement match {
|
||||
case e: HaskellModuleDeclaration => Some(e)
|
||||
case e => Option(PsiTreeUtil.findFirstParent(e, ModuleDeclarationCondition)).map(_.asInstanceOf[HaskellModuleDeclaration])
|
||||
}
|
||||
}
|
||||
|
||||
def findTypeSignatureDeclarationParent(psiElement: PsiElement): Option[HaskellTypeSignature] = {
|
||||
def findTypeSignatureDeclaration(psiElement: PsiElement): Option[HaskellTypeSignature] = {
|
||||
psiElement match {
|
||||
case e: HaskellTypeSignature => Some(e)
|
||||
case e => Option(PsiTreeUtil.findFirstParent(e, TypeSignatureCondition)).map(_.asInstanceOf[HaskellTypeSignature])
|
||||
case e => Option(TreeUtil.findParent(e.getNode, HaskellTypes.HS_TYPE_SIGNATURE)).map(_.getPsi.asInstanceOf[HaskellTypeSignature])
|
||||
}
|
||||
}
|
||||
|
||||
def findExpressionParent(psiElement: PsiElement): Option[HaskellExpression] = {
|
||||
def findExpression(psiElement: PsiElement): Option[HaskellExpression] = {
|
||||
psiElement match {
|
||||
case e: HaskellExpression => Some(e)
|
||||
case e => Option(PsiTreeUtil.findFirstParent(e, ExpressionCondition)).map(_.asInstanceOf[HaskellExpression])
|
||||
case e => Option(TreeUtil.findParent(e.getNode, HaskellTypes.HS_EXPRESSION)).map(_.getPsi.asInstanceOf[HaskellExpression])
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -275,13 +275,13 @@ object HaskellPsiImplUtil {
|
||||
def getItemPresentableText(element: PsiElement, shortened: Boolean = true): String = {
|
||||
HaskellPsiUtil.findNamedElement(element) match {
|
||||
case Some(namedElement) =>
|
||||
HaskellPsiUtil.findHighestDeclarationElementParent(element) match {
|
||||
case Some(de) if de.getIdentifierElements.exists(_ == namedElement) => HaskellPsiUtil.findDeclarationElementParent(namedElement).map(de => getDeclarationInfo(de, shortened)).
|
||||
orElse(HaskellPsiUtil.findExpressionParent(namedElement).map(e => StringUtil.removeCommentsAndWhiteSpaces(e.getText))).
|
||||
HaskellPsiUtil.findHighestDeclarationElement(element) match {
|
||||
case Some(de) if de.getIdentifierElements.exists(_ == namedElement) => HaskellPsiUtil.findDeclarationElement(namedElement).map(de => getDeclarationInfo(de, shortened)).
|
||||
orElse(HaskellPsiUtil.findExpression(namedElement).map(e => StringUtil.removeCommentsAndWhiteSpaces(e.getText))).
|
||||
getOrElse(s"${namedElement.getName} `in` ${getDeclarationInfo(de, shortened)}")
|
||||
case Some(de) => s"${namedElement.getName} `in` ${getDeclarationInfo(de, shortened)}"
|
||||
case _ if shortened && HaskellPsiUtil.findExpressionParent(namedElement).isDefined => getContainingLineText(namedElement).getOrElse(namedElement.getName).trim
|
||||
case _ => HaskellPsiUtil.findExpressionParent(namedElement).map(_.getText).getOrElse(namedElement.getName)
|
||||
case _ if shortened && HaskellPsiUtil.findExpression(namedElement).isDefined => getContainingLineText(namedElement).getOrElse(namedElement.getName).trim
|
||||
case _ => HaskellPsiUtil.findExpression(namedElement).map(_.getText).getOrElse(namedElement.getName)
|
||||
}
|
||||
case _ => element.getText
|
||||
}
|
||||
@ -308,7 +308,7 @@ object HaskellPsiImplUtil {
|
||||
val psiFile = namedElement.getContainingFile.getOriginalFile
|
||||
for {
|
||||
doc <- HaskellFileUtil.findDocument(psiFile)
|
||||
element <- HaskellPsiUtil.findQualifiedNameParent(namedElement)
|
||||
element <- HaskellPsiUtil.findQualifiedName(namedElement)
|
||||
start = findNewline(element, e => e.getPrevSibling).getTextOffset
|
||||
end = findNewline(element, e => e.getNextSibling).getTextOffset
|
||||
} yield StringUtil.removeCommentsAndWhiteSpaces(doc.getCharsSequence.subSequence(start, end).toString.trim)
|
||||
@ -332,12 +332,13 @@ object HaskellPsiImplUtil {
|
||||
}
|
||||
|
||||
def getIdentifierElements(dataDeclaration: HaskellDataDeclaration): Seq[HaskellNamedElement] = {
|
||||
val constrs = dataDeclaration.getConstrList.asScala
|
||||
dataDeclaration.getSimpletype.getIdentifierElements ++
|
||||
dataDeclaration.getTypeSignatureList.asScala.flatMap(_.getIdentifierElements) ++
|
||||
dataDeclaration.getConstr1List.asScala.flatMap(c => Option(c.getQName).map(_.getIdentifierElement).toSeq ++
|
||||
constrs.flatMap(constr => Option(constr.getConstr1)).flatMap(c => Option(c.getQName).map(_.getIdentifierElement) ++
|
||||
c.getFielddeclList.asScala.flatMap(_.getQNames.getQNameList.asScala.headOption.map(_.getIdentifierElement))) ++
|
||||
dataDeclaration.getConstr3List.asScala.flatMap(_.getTtypeList.asScala.headOption.flatMap(_.getQNameList.asScala.headOption.map(_.getIdentifierElement))) ++
|
||||
dataDeclaration.getConstr2List.asScala.flatMap(c => Option(c.getQName).map(_.getIdentifierElement).orElse(c.getTtypeList.asScala.headOption.flatMap(_.getQNameList.asScala.headOption.map(_.getIdentifierElement))))
|
||||
constrs.flatMap(constr => Option(constr.getConstr3)).flatMap(_.getTtypeList.asScala.headOption.flatMap(_.getQNameList.asScala.headOption.map(_.getIdentifierElement))) ++
|
||||
constrs.flatMap(constr => Option(constr.getConstr2)).flatMap(c => Option(c.getQName).map(_.getIdentifierElement).orElse(c.getTtypeList.asScala.headOption.flatMap(_.getQNameList.asScala.headOption.map(_.getIdentifierElement))))
|
||||
}
|
||||
|
||||
def getIdentifierElements(typeDeclaration: HaskellTypeDeclaration): Seq[HaskellNamedElement] = {
|
||||
|
@ -42,7 +42,7 @@ class HaskellMoveFileHandler extends MoveFileHandler {
|
||||
override def updateMovedFile(psiFile: PsiFile): Unit = {
|
||||
HaskellPsiUtil.invalidateModuleName(psiFile)
|
||||
HaskellComponentsManager.clearLoadedModule(psiFile)
|
||||
HaskellComponentsManager.invalidateHaskellFileInfoCache(psiFile)
|
||||
HaskellComponentsManager.invalidateFileInfos(psiFile)
|
||||
HaskellAnnotator.restartDaemonCodeAnalyzerForFile(psiFile)
|
||||
}
|
||||
|
||||
|
@ -18,12 +18,16 @@ package intellij.haskell.refactor
|
||||
|
||||
import java.util
|
||||
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.{PsiElement, PsiFile}
|
||||
import com.intellij.refactoring.listeners.RefactoringElementListener
|
||||
import com.intellij.refactoring.rename.RenamePsiElementProcessor
|
||||
import intellij.haskell.HaskellNotificationGroup
|
||||
import intellij.haskell.external.component.HaskellComponentsManager
|
||||
import intellij.haskell.external.component.{HaskellComponentsManager, ProjectLibraryFileWatcher}
|
||||
import intellij.haskell.external.repl.StackRepl.LibType
|
||||
import intellij.haskell.psi.HaskellPsiUtil
|
||||
import intellij.haskell.util.HaskellProjectUtil
|
||||
import intellij.haskell.util.{HaskellFileUtil, HaskellProjectUtil, ScalaUtil}
|
||||
|
||||
class HaskellRenameVariableProcessor extends RenamePsiElementProcessor {
|
||||
|
||||
@ -33,11 +37,14 @@ class HaskellRenameVariableProcessor extends RenamePsiElementProcessor {
|
||||
val oldName = HaskellPsiUtil.findNamedElement(targetElement).map(_.getName)
|
||||
oldName match {
|
||||
case Some(n) =>
|
||||
val targetFile = targetElement.getContainingFile.getOriginalFile
|
||||
val usageElements = HaskellComponentsManager.findReferencesInCache(targetFile).filterNot(_._1 == targetFile).filter(_._2.getIdentifierElement.getName == n).map(_._2)
|
||||
if (usageElements.nonEmpty) {
|
||||
HaskellComponentsManager.refreshDefinitionLocationCache(usageElements)
|
||||
}
|
||||
val project = targetElement.getProject
|
||||
for {
|
||||
f <- getCurrentFile(project)
|
||||
_ = HaskellComponentsManager.invalidateOtherFilesLocations(f, n)
|
||||
tf <- Option(targetElement.getContainingFile).map(_.getOriginalFile)
|
||||
tInfo <- if (tf != f) HaskellComponentsManager.findStackComponentInfo(tf) else None
|
||||
_ = if (tInfo.stanzaType == LibType) ProjectLibraryFileWatcher.addBuild(project, Set(tInfo)) else ()
|
||||
} yield ()
|
||||
case None => ()
|
||||
}
|
||||
}
|
||||
@ -61,4 +68,21 @@ class HaskellRenameVariableProcessor extends RenamePsiElementProcessor {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override def getPostRenameCallback(element: PsiElement, newName: String, elementListener: RefactoringElementListener): Runnable = {
|
||||
ScalaUtil.runnable {
|
||||
val project = element.getProject
|
||||
|
||||
HaskellFileUtil.saveAllFiles(project)
|
||||
|
||||
getCurrentFile(project).foreach { f =>
|
||||
val componentInfo = HaskellComponentsManager.findStackComponentInfo(f)
|
||||
componentInfo.foreach(ci => ProjectLibraryFileWatcher.checkLibraryBuild(project, ci))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def getCurrentFile(project: Project) = {
|
||||
FileEditorManager.getInstance(project).getSelectedFiles.headOption.flatMap(f => HaskellFileUtil.convertToHaskellFileDispatchThread(project, f))
|
||||
}
|
||||
}
|
||||
|
@ -93,7 +93,7 @@ class HaskellConsoleView(val project: Project, val configuration: HaskellConsole
|
||||
def executeCommand(commandText: String, addToHistory: Boolean = true): Unit = {
|
||||
commandText.trim() match {
|
||||
case LoadPattern(moduleName) =>
|
||||
val psiFile = HaskellModuleNameIndex.findFileByModuleName(project, moduleName).toOption.flatMap(_.headOption)
|
||||
val psiFile = HaskellModuleNameIndex.findFilesByModuleName(project, moduleName).toOption.flatMap(_.headOption)
|
||||
psiFile.foreach(hf => HaskellConsoleViewMap.projectFileByConfigName.put(configuration.getName, hf))
|
||||
case _ => ()
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ public class HaskellSettingsPersistentStateComponent implements PersistentStateC
|
||||
}
|
||||
|
||||
static class HaskellSettingsState {
|
||||
public Integer replTimeout = 5;
|
||||
public Integer replTimeout = 30;
|
||||
public String hlintOptions = "";
|
||||
public Boolean reformatCodeBeforeCommit = false;
|
||||
public Boolean optimizeImportsBeforeCommit = false;
|
||||
|
@ -34,7 +34,7 @@ import com.intellij.psi.impl.PsiManagerEx
|
||||
import com.intellij.psi.{PsiDocumentManager, PsiFile, PsiManager}
|
||||
import intellij.haskell.HaskellFileType
|
||||
import intellij.haskell.action.SelectionContext
|
||||
import intellij.haskell.external.component.NoInfo
|
||||
import intellij.haskell.external.component.{NoInfo, NoInfoAvailable}
|
||||
|
||||
object HaskellFileUtil {
|
||||
|
||||
@ -150,15 +150,16 @@ object HaskellFileUtil {
|
||||
}
|
||||
}
|
||||
|
||||
def convertToHaskellFileInReadAction(project: Project, virtualFile: VirtualFile): Either[NoInfo, Option[PsiFile]] = {
|
||||
def convertToHaskellFileInReadAction(project: Project, virtualFile: VirtualFile): Either[NoInfo, PsiFile] = {
|
||||
val psiManager = PsiManager.getInstance(project)
|
||||
|
||||
val actionMessage = s"Converting ${virtualFile.getName} to psi file"
|
||||
ApplicationUtil.runReadAction(findCachedPsiFile(psiManager, virtualFile)) match {
|
||||
case pf@Some(_) => Right(pf)
|
||||
case Some(pf) => Right(pf)
|
||||
case None => ApplicationUtil.runInReadActionWithWriteActionPriority(project, findPsiFile(psiManager, virtualFile), readActionDescription = actionMessage) match {
|
||||
case r@Right(_) => r
|
||||
case l@Left(_) => l
|
||||
case Right(Some(pf)) => Right(pf)
|
||||
case Right(None) => Left(NoInfoAvailable(virtualFile.getName, "-"))
|
||||
case Left(noInfo) => Left(noInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ import com.intellij.openapi.vfs.{LocalFileSystem, VirtualFile}
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.psi.{PsiElement, PsiFile}
|
||||
import intellij.haskell.HaskellNotificationGroup
|
||||
import intellij.haskell.external.component.{HaskellComponentsManager, NoInfo}
|
||||
import intellij.haskell.external.component.{HaskellComponentsManager, NoInfo, NoInfoAvailable}
|
||||
import intellij.haskell.module.HaskellModuleType
|
||||
import intellij.haskell.sdk.HaskellSdkType
|
||||
|
||||
@ -48,11 +48,11 @@ object HaskellProjectUtil {
|
||||
findProjectHaskellModules(project).nonEmpty
|
||||
}
|
||||
|
||||
def findFile(filePath: String, project: Project): (Option[VirtualFile], Either[NoInfo, Option[PsiFile]]) = {
|
||||
def findFile(filePath: String, project: Project): (Option[VirtualFile], Either[NoInfo, PsiFile]) = {
|
||||
val virtualFile = Option(LocalFileSystem.getInstance().findFileByPath(HaskellFileUtil.makeFilePathAbsolute(filePath, project)))
|
||||
val psiFile = virtualFile.map(f => HaskellFileUtil.convertToHaskellFileInReadAction(project, f)) match {
|
||||
case Some(r) => r
|
||||
case None => Right(None)
|
||||
case None => Left(NoInfoAvailable(filePath, "-"))
|
||||
}
|
||||
(virtualFile, psiFile)
|
||||
}
|
||||
|
@ -50,21 +50,22 @@ object HaskellModuleNameIndex {
|
||||
|
||||
private case class Key(project: Project, moduleName: String)
|
||||
|
||||
type Result = Either[NoInfo, Seq[PsiFile]]
|
||||
type Result = Either[NoInfo, Seq[(PsiFile, Boolean)]]
|
||||
|
||||
private final val Cache: LoadingCache[Key, Result] = Scaffeine().build((k: Key) => find(k, 2.second, reschedule = false))
|
||||
|
||||
private def find(key: Key, timeout: FiniteDuration, reschedule: Boolean): Either[NoInfo, Seq[PsiFile]] = {
|
||||
findFiles(key.project, key.moduleName, timeout, reschedule) match {
|
||||
case Right(files) =>
|
||||
private def find(key: Key, timeout: FiniteDuration, reschedule: Boolean): Either[NoInfo, Seq[(PsiFile, Boolean)]] = {
|
||||
val project = key.project
|
||||
findFiles(project, key.moduleName, timeout, reschedule) match {
|
||||
case Right(virtualFiles) =>
|
||||
if (ApplicationManager.getApplication.isReadAccessAllowed) {
|
||||
Right(files.flatMap(vf => HaskellFileUtil.convertToHaskellFileDispatchThread(key.project, vf)))
|
||||
Right(virtualFiles.flatMap { case (vf, isPf) => HaskellFileUtil.convertToHaskellFileDispatchThread(project, vf).map((_, isPf)).toSeq })
|
||||
} else {
|
||||
val result = files.map(vf => HaskellFileUtil.convertToHaskellFileInReadAction(key.project, vf))
|
||||
if (result.exists(_.isLeft)) {
|
||||
Left(ReadActionTimeout("Read action timeout while converting virtual file to psi file"))
|
||||
val psiFiles = virtualFiles.map { case (vf, isPf) => (HaskellFileUtil.convertToHaskellFileInReadAction(project, vf), isPf) }
|
||||
if (psiFiles.exists(_._1.isLeft)) {
|
||||
Left(ReadActionTimeout("Read action timeout while converting virtual files to psi files"))
|
||||
} else {
|
||||
Right(result.flatMap(_.toOption).flatten)
|
||||
Right(psiFiles.flatMap { case (pf, isPf) => pf.toSeq.map((_, isPf)) })
|
||||
}
|
||||
}
|
||||
case Left(noInfo) => Left(noInfo)
|
||||
@ -81,33 +82,26 @@ object HaskellModuleNameIndex {
|
||||
})
|
||||
}
|
||||
|
||||
def findFilesByModuleName(project: Project, moduleName: String): Either[NoInfo, Seq[PsiFile]] = {
|
||||
findFilesByModuleName2(project, moduleName).map(_.map(_._1))
|
||||
}
|
||||
|
||||
// IntelliJ tends to send a lot of the same requests from HaskellReference to find a module name.
|
||||
// This makes the UI unresponsive if the module name can not be found because user is not finished with typing the module name.
|
||||
// So it seems to be no good solution to do the searching in UI thread because cache can not set before new request comes in.
|
||||
// This makes the UI unresponsive if the module can not be found while user is still typing the module name.
|
||||
// So it seems to be not the right solution to do the searching in UI thread because cache can not set before new request comes in.
|
||||
// So using Cache is solution because Cache.get blocks next request for same key while busy.
|
||||
def findFileByModuleName(project: Project, moduleName: String): Either[NoInfo, Seq[PsiFile]] = {
|
||||
def findFilesByModuleName2(project: Project, moduleName: String): Either[NoInfo, Seq[(PsiFile, Boolean)]] = {
|
||||
val key = Key(project, moduleName)
|
||||
Cache.getIfPresent(key) match {
|
||||
case Some(r@Right(_)) => r
|
||||
case _ =>
|
||||
// if (ApplicationManager.getApplication.isReadAccessAllowed) {
|
||||
// find(key, 1.second, reschedule = false) match {
|
||||
// case r@Right(_) =>
|
||||
// Cache.put(key, r)
|
||||
// r
|
||||
// case Left(noInfo) =>
|
||||
// Cache.invalidate(key)
|
||||
// Left(noInfo)
|
||||
// }
|
||||
// } else {
|
||||
Cache.get(key) match {
|
||||
case r@Right(_) => r
|
||||
case Left(noInfo) =>
|
||||
// No invalidate to prevent UI becomes unresponsive after many calls for same module name which does not exists
|
||||
// No invalidate here to prevent UI becomes unresponsive after many calls for same module name which module does not exists
|
||||
// In LoadComponent the "not found" entries will be invalidated eventually
|
||||
Left(noInfo)
|
||||
}
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@ -125,7 +119,7 @@ object HaskellModuleNameIndex {
|
||||
Cache.invalidate(Key(project, moduleName))
|
||||
}
|
||||
|
||||
private def findFiles(project: Project, moduleName: String, timeout: FiniteDuration, reschedule: Boolean): Either[NoInfo, Seq[VirtualFile]] = {
|
||||
private def findFiles(project: Project, moduleName: String, timeout: FiniteDuration, reschedule: Boolean): Either[NoInfo, Seq[(VirtualFile, Boolean)]] = {
|
||||
if (moduleName == HaskellProjectUtil.Prelude) {
|
||||
Right(Seq())
|
||||
} else {
|
||||
@ -143,9 +137,8 @@ object HaskellModuleNameIndex {
|
||||
|
||||
files match {
|
||||
case Left(noInfo) => Left(noInfo)
|
||||
case Right(Some(f)) =>
|
||||
val firstFile = f.find(f => HaskellProjectUtil.isSourceFile(project, f)).orElse(f.headOption)
|
||||
Right(firstFile.toSeq ++ firstFile.map(pf => f.filterNot(_ == pf)).getOrElse(Seq()))
|
||||
case Right(Some(vfs)) =>
|
||||
Right(vfs.map(f => (f, HaskellProjectUtil.isSourceFile(project, f))).sortBy(!_._2))
|
||||
case Right(None) => Left(IndexNotReady)
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user