mirror of
https://github.com/ilyakooo0/intellij-haskell.git
synced 2024-08-17 10:40:44 +03:00
Various performance improvements.
This commit is contained in:
parent
caf009f18d
commit
efe93aaf39
@ -17,15 +17,14 @@
|
||||
package intellij.haskell.editor
|
||||
|
||||
import com.intellij.codeInsight.completion._
|
||||
import com.intellij.codeInsight.lookup.LookupElementBuilder
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.codeInsight.lookup.{LookupElement, LookupElementBuilder, LookupElementPresentation}
|
||||
import com.intellij.openapi.progress.ProgressManager
|
||||
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.{PsiElement, PsiFile, TokenType}
|
||||
import com.intellij.util.{ProcessingContext, WaitFor}
|
||||
import com.intellij.util.ProcessingContext
|
||||
import icons.HaskellIcons
|
||||
import intellij.haskell.annotator.HaskellAnnotator
|
||||
import intellij.haskell.external.component.HaskellComponentsManager.StackComponentInfo
|
||||
@ -34,11 +33,9 @@ import intellij.haskell.psi.HaskellTypes._
|
||||
import intellij.haskell.psi._
|
||||
import intellij.haskell.runconfig.console.{HaskellConsoleView, HaskellConsoleViewMap}
|
||||
import intellij.haskell.util._
|
||||
import intellij.haskell.{HaskellFile, HaskellNotificationGroup, HaskellParserDefinition}
|
||||
import intellij.haskell.{HaskellFile, HaskellParserDefinition}
|
||||
import org.apache.commons.lang.StringEscapeUtils
|
||||
|
||||
import scala.concurrent._
|
||||
import scala.concurrent.duration._
|
||||
import scala.jdk.CollectionConverters._
|
||||
|
||||
class HaskellCompletionContributor extends CompletionContributor {
|
||||
@ -218,6 +215,7 @@ class HaskellCompletionContributor extends CompletionContributor {
|
||||
ProgressManager.checkCanceled()
|
||||
|
||||
val localLookupElements = currentElement.map(ce => findLocalElements(ce).filterNot(_ == ce).map(createLocalLookupElement)).getOrElse(LazyList())
|
||||
ProgressManager.checkCanceled()
|
||||
resultSet.addAllElements(localLookupElements.asJavaCollection)
|
||||
}
|
||||
}
|
||||
@ -249,7 +247,7 @@ class HaskellCompletionContributor extends CompletionContributor {
|
||||
if (typedHoleSuggestionsWithHeader.nonEmpty) {
|
||||
val typedHoleSuggestionLines = typedHoleSuggestionsWithHeader.tail
|
||||
val typedHoleSuggestions = typedHoleSuggestionLines.filterNot(_.trim.startsWith("("))
|
||||
val lookupElements = typedHoleSuggestions.flatMap(createLookupElement _).to(LazyList)
|
||||
val lookupElements = typedHoleSuggestions.flatMap(createLookupElement).to(LazyList)
|
||||
originalResultSet.addAllElements(lookupElements.asJavaCollection)
|
||||
}
|
||||
case None => ()
|
||||
@ -339,26 +337,10 @@ class HaskellCompletionContributor extends CompletionContributor {
|
||||
}
|
||||
|
||||
private def getGlobalInfo(psiFile: PsiFile): Option[(StackComponentInfo, StackComponentGlobalInfo)] = {
|
||||
val infos = ApplicationManager.getApplication.executeOnPooledThread(ScalaUtil.callable {
|
||||
for {
|
||||
info <- HaskellComponentsManager.findStackComponentInfo(psiFile)
|
||||
globalInfo <- HaskellComponentsManager.findStackComponentGlobalInfo(info)
|
||||
} yield (info, globalInfo)
|
||||
})
|
||||
|
||||
new WaitFor(ApplicationUtil.timeout, 1) {
|
||||
override def condition(): Boolean = {
|
||||
ProgressManager.checkCanceled()
|
||||
infos.isDone
|
||||
}
|
||||
}
|
||||
|
||||
if (infos.isDone) {
|
||||
infos.get()
|
||||
} else {
|
||||
HaskellNotificationGroup.logInfoEvent(psiFile.getProject, "Timeout in getGlobalInfo for " + psiFile.getName)
|
||||
None
|
||||
}
|
||||
for {
|
||||
info <- HaskellComponentsManager.findStackComponentInfo(psiFile)
|
||||
globalInfo <- HaskellComponentsManager.findStackComponentGlobalInfo(info)
|
||||
} yield (info, globalInfo)
|
||||
}
|
||||
|
||||
private def findAvailableIdsForImportModuleSpec(stackComponentGlobalInfo: StackComponentGlobalInfo, psiFile: PsiFile, element: PsiElement) = {
|
||||
@ -366,24 +348,15 @@ class HaskellCompletionContributor extends CompletionContributor {
|
||||
|
||||
HaskellPsiUtil.findImportDeclaration(element).flatMap(_.getModuleName) match {
|
||||
case Some(moduleName) =>
|
||||
val ids = blocking {
|
||||
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 = {
|
||||
ScalaFutureUtil.waitForValue(psiFile.getProject,
|
||||
HaskellComponentsManager.findModuleIdentifiers(psiFile.getProject, moduleName)
|
||||
, "Getting module identifiers").flatten match {
|
||||
case Some(ids) => ids.map(x => {
|
||||
ProgressManager.checkCanceled()
|
||||
ids.isCompleted
|
||||
}
|
||||
createLookupElement(x, addParens = true)
|
||||
})
|
||||
case None => Iterable()
|
||||
}
|
||||
|
||||
if (ids.isCompleted) {
|
||||
Await.result(ids, 1.milli)
|
||||
} else {
|
||||
HaskellNotificationGroup.logInfoEvent(psiFile.getProject, "Timeout in findAvailableIdsForImportModuleSpec for " + psiFile.getName)
|
||||
Iterable()
|
||||
}
|
||||
|
||||
case None => Iterable()
|
||||
}
|
||||
}
|
||||
@ -392,7 +365,7 @@ class HaskellCompletionContributor extends CompletionContributor {
|
||||
LookupElementBuilder.create(moduleName).withTailText(" module", true)
|
||||
}
|
||||
|
||||
private def getInsideImportClausesLookupElements = {
|
||||
private lazy val getInsideImportClausesLookupElements = {
|
||||
InsideImportKeywords.map(c => LookupElementBuilder.create(c).withTailText(" clause", true))
|
||||
}
|
||||
|
||||
@ -400,23 +373,23 @@ class HaskellCompletionContributor extends CompletionContributor {
|
||||
HaskellComponentsManager.getSupportedLanguageExtension(project).map(n => LookupElementBuilder.create(n).withIcon(HaskellIcons.HaskellSmallBlueLogo).withTailText(" language extension", true))
|
||||
}
|
||||
|
||||
private def getPragmaStartEndIdsLookupElements = {
|
||||
private lazy val getPragmaStartEndIdsLookupElements = {
|
||||
PragmaIds.map(p => LookupElementBuilder.create(p).withIcon(HaskellIcons.HaskellSmallBlueLogo).withTailText(" pragma", true))
|
||||
}
|
||||
|
||||
private def getFileHeaderPragmaIdsLookupElements = {
|
||||
private lazy val getFileHeaderPragmaIdsLookupElements = {
|
||||
FileHeaderPragmaIds.map(p => LookupElementBuilder.create(p).withIcon(HaskellIcons.HaskellSmallBlueLogo).withTailText(" pragma", true))
|
||||
}
|
||||
|
||||
private def getModulePragmaIdsLookupElements = {
|
||||
private lazy val getModulePragmaIdsLookupElements = {
|
||||
ModulePragmaIds.map(p => LookupElementBuilder.create(p).withIcon(HaskellIcons.HaskellSmallBlueLogo).withTailText(" pragma", true))
|
||||
}
|
||||
|
||||
private def getCommentIdsLookupElements = {
|
||||
private lazy val getCommentIdsLookupElements = {
|
||||
CommentIds.map(p => LookupElementBuilder.create(p).withIcon(HaskellIcons.HaskellSmallBlueLogo).withTailText(" comment", true))
|
||||
}
|
||||
|
||||
private def getHaddockIdsLookupElements = {
|
||||
private lazy val getHaddockIdsLookupElements = {
|
||||
HaddockIds.map(p => LookupElementBuilder.create(p).withIcon(HaskellIcons.HaskellSmallBlueLogo).withTailText(" haddock", true))
|
||||
}
|
||||
|
||||
@ -426,7 +399,7 @@ class HaskellCompletionContributor extends CompletionContributor {
|
||||
FileModuleIdentifiers.findAvailableModuleIdentifiers(psiFile).map(createLookupElement(_)) ++ findTopLeveLookupElements(psiFile, moduleName, currentElement)
|
||||
}
|
||||
|
||||
private def getKeywordLookupElements = {
|
||||
private lazy val getKeywordLookupElements = {
|
||||
Keywords.map(createKeywordLookupElement)
|
||||
}
|
||||
|
||||
@ -434,7 +407,7 @@ class HaskellCompletionContributor extends CompletionContributor {
|
||||
LookupElementBuilder.create(keyword).withIcon(HaskellIcons.HaskellSmallBlueLogo).withTailText(" keyword", true)
|
||||
}
|
||||
|
||||
private def getSpecialReservedIdLookupElements = {
|
||||
private lazy val getSpecialReservedIdLookupElements = {
|
||||
SpecialReservedIds.map(sr => LookupElementBuilder.create(sr).withIcon(HaskellIcons.HaskellSmallBlueLogo).withTailText(" special keyword", true))
|
||||
}
|
||||
|
||||
@ -475,8 +448,15 @@ class HaskellCompletionContributor extends CompletionContributor {
|
||||
}
|
||||
|
||||
def createLocalLookupElement(namedElement: HaskellNamedElement): LookupElementBuilder = {
|
||||
val typeSignature = HaskellComponentsManager.findTypeInfoForElement(namedElement).map(_.typeSignature)
|
||||
LookupElementBuilder.create(namedElement.getName).withTypeText(typeSignature.map(StringUtil.unescapeXmlEntities).getOrElse("")).withIcon(HaskellIcons.HaskellSmallBlueLogo)
|
||||
ProgressManager.checkCanceled()
|
||||
|
||||
def typeSignature = HaskellComponentsManager.findTypeInfoForElement(namedElement).map(_.typeSignature).map(StringUtil.unescapeXmlEntities).getOrElse("")
|
||||
|
||||
LookupElementBuilder.create(namedElement.getName).withIcon(HaskellIcons.HaskellSmallBlueLogo).withRenderer((element: LookupElement, presentation: LookupElementPresentation) => {
|
||||
presentation.setTypeText(typeSignature)
|
||||
presentation.setItemText(namedElement.getName)
|
||||
presentation.setIcon(HaskellIcons.HaskellSmallBlueLogo)
|
||||
})
|
||||
}
|
||||
|
||||
private def createLocalTopLevelLookupElement(name: String, declaration: String, module: String): LookupElementBuilder = {
|
||||
|
@ -18,17 +18,14 @@ package intellij.haskell.external.component
|
||||
|
||||
import com.github.blemale.scaffeine.{AsyncLoadingCache, Scaffeine}
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.progress.ProgressManager
|
||||
import com.intellij.openapi.project.{IndexNotReadyException, Project}
|
||||
import com.intellij.psi.search.{FileTypeIndex, GlobalSearchScope}
|
||||
import com.intellij.util.WaitFor
|
||||
import intellij.haskell.external.component.HaskellComponentsManager.StackComponentInfo
|
||||
import intellij.haskell.external.repl.StackRepl.{BenchmarkType, TestSuiteType}
|
||||
import intellij.haskell.psi.HaskellPsiUtil
|
||||
import intellij.haskell.util.{ApplicationUtil, HaskellFileUtil}
|
||||
import intellij.haskell.util.{ApplicationUtil, HaskellFileUtil, ScalaFutureUtil}
|
||||
import intellij.haskell.{HaskellFileType, HaskellNotificationGroup}
|
||||
|
||||
import scala.concurrent.Await
|
||||
import scala.concurrent.duration._
|
||||
import scala.jdk.CollectionConverters._
|
||||
|
||||
@ -38,7 +35,7 @@ private[component] object AvailableModuleNamesComponent {
|
||||
|
||||
private case class Key(stackComponentInfo: StackComponentInfo)
|
||||
|
||||
private final val Cache: AsyncLoadingCache[Key, Iterable[String]] = Scaffeine().expireAfterWrite(10.seconds).buildAsync((k: Key) => findAvailableProjectModuleNamesWithIndex(k.stackComponentInfo))
|
||||
private final val Cache: AsyncLoadingCache[Key, Iterable[String]] = Scaffeine().refreshAfterWrite(10.seconds).buildAsync((k: Key) => findAvailableProjectModuleNamesWithIndex(k.stackComponentInfo))
|
||||
|
||||
def findAvailableModuleNamesWithIndex(stackComponentInfo: StackComponentInfo): Iterable[String] = {
|
||||
// A module can be a project module AND library module
|
||||
@ -51,21 +48,11 @@ private[component] object AvailableModuleNamesComponent {
|
||||
|
||||
def findAvailableProjectModuleNames(stackComponentInfo: StackComponentInfo): Iterable[String] = {
|
||||
val key = Key(stackComponentInfo)
|
||||
|
||||
val f = Cache.get(key)
|
||||
new WaitFor(ApplicationUtil.timeout, 1) {
|
||||
override def condition(): Boolean = {
|
||||
ProgressManager.checkCanceled()
|
||||
f.isCompleted
|
||||
}
|
||||
}
|
||||
|
||||
if (f.isCompleted) {
|
||||
Await.result(f, 1.milli)
|
||||
} else {
|
||||
HaskellNotificationGroup.logInfoEvent(stackComponentInfo.module.getProject, "Timeout in findAvailableProjectModuleNames " + stackComponentInfo)
|
||||
Cache.synchronous().invalidate(key)
|
||||
Iterable()
|
||||
ScalaFutureUtil.waitForValue(stackComponentInfo.module.getProject, Cache.get(key), s"getting project module names for ${key.stackComponentInfo.packageName}", 1.second) match {
|
||||
case Some(files) => files
|
||||
case _ =>
|
||||
Cache.synchronous().invalidate(key)
|
||||
Iterable()
|
||||
}
|
||||
}
|
||||
|
||||
@ -98,7 +85,7 @@ private[component] object AvailableModuleNamesComponent {
|
||||
HaskellNotificationGroup.logInfoEvent(project, s"Index not ready while findHaskellFiles for module ${currentModule.getName} ")
|
||||
Iterable()
|
||||
}
|
||||
}, s"find Haskell files for module ${currentModule.getName}", 5.seconds).toOption.toIterable.flatten
|
||||
}, s"find Haskell files for module ${currentModule.getName}").toOption.toIterable.flatten
|
||||
}
|
||||
|
||||
|
||||
|
@ -25,7 +25,7 @@ import intellij.haskell.psi.HaskellPsiUtil
|
||||
import intellij.haskell.util.index.HaskellModuleNameIndex
|
||||
import intellij.haskell.util.{ApplicationUtil, HaskellFileUtil, HaskellProjectUtil, StringUtil}
|
||||
|
||||
import scala.concurrent.{ExecutionContext, Future, blocking}
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
|
||||
private[component] object BrowseModuleComponent {
|
||||
|
||||
@ -44,14 +44,14 @@ private[component] object BrowseModuleComponent {
|
||||
def findModuleIdentifiers(project: Project, moduleName: String)(implicit ec: ExecutionContext): Future[Option[Iterable[ModuleIdentifier]]] = {
|
||||
val key = Key(project, moduleName)
|
||||
val result = Cache.get(key)
|
||||
blocking(result.map {
|
||||
result.map {
|
||||
case Right(ids) => Some(ids)
|
||||
case Left(NoInfoAvailable(_, _)) | Left(NoMatchingExport) =>
|
||||
None
|
||||
case Left(ReplNotAvailable) | Left(IndexNotReady) | Left(ModuleNotAvailable(_)) | Left(ReadActionTimeout(_)) =>
|
||||
Cache.synchronous().invalidate(key)
|
||||
None
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
def findModuleIdentifiersSync(project: Project, moduleName: String): BrowseModuleInternalResult = {
|
||||
@ -126,8 +126,9 @@ private[component] object BrowseModuleComponent {
|
||||
projectRepl match {
|
||||
case Some(repl) =>
|
||||
if (repl.available) {
|
||||
repl.getModuleIdentifiers(moduleName, psiFile) match {
|
||||
repl.getModuleIdentifiers(project, moduleName, psiFile) match {
|
||||
case Some(output) if output.stderrLines.isEmpty && output.stdoutLines.nonEmpty => Right(output.stdoutLines.flatMap(l => createProjectModuleIdentifier(project, l, moduleName)))
|
||||
case None => Left(ReplNotAvailable)
|
||||
case _ => Left(ModuleNotAvailable(moduleName))
|
||||
}
|
||||
} else {
|
||||
@ -144,6 +145,7 @@ private[component] object BrowseModuleComponent {
|
||||
(repl.getModuleIdentifiers(moduleName), moduleFile) match {
|
||||
case (Some(o), _) if o.stderrLines.isEmpty && o.stdoutLines.nonEmpty => Right(o.stdoutLines.flatMap(l => createLibraryModuleIdentifier(project, l, moduleName)))
|
||||
case (_, Some(f)) => Right(ApplicationUtil.runReadAction(HaskellPsiUtil.findTopLevelDeclarations(f)).map(d => ApplicationUtil.runReadAction(d.getName)).flatMap(d => createLibraryModuleIdentifier(project, d, moduleName)).toSeq)
|
||||
case (None, _) => Left(ReplNotAvailable)
|
||||
case _ => Left(ModuleNotAvailable(moduleName))
|
||||
}
|
||||
} else {
|
||||
|
@ -26,6 +26,8 @@ import intellij.haskell.psi._
|
||||
import intellij.haskell.util._
|
||||
import intellij.haskell.util.index.HaskellModuleNameIndex._
|
||||
|
||||
import scala.concurrent.TimeoutException
|
||||
|
||||
private[component] object DefinitionLocationComponent {
|
||||
private final val LocAtPattern = """(.+):\(([\d]+),([\d]+)\)-\(([\d]+),([\d]+)\)""".r
|
||||
private final val PackageModulePattern = """([\w\-\d.]+)(?:-.*)?:([\w.\-]+)""".r
|
||||
@ -40,29 +42,17 @@ private[component] object DefinitionLocationComponent {
|
||||
def findDefinitionLocation(psiFile: PsiFile, qualifiedNameElement: HaskellQualifiedNameElement, importQualifier: Option[String]): DefinitionLocationResult = {
|
||||
val key = Key(psiFile, qualifiedNameElement, importQualifier)
|
||||
|
||||
Cache.getIfPresent(key) match {
|
||||
case Some(v) =>
|
||||
val result = v
|
||||
result match {
|
||||
case Right(_) => result
|
||||
case Left(ReadActionTimeout(_)) | Left(IndexNotReady) | Left(ModuleNotAvailable(_)) | Left(ReplNotAvailable) =>
|
||||
Cache.invalidate(key)
|
||||
result
|
||||
case _ => result
|
||||
}
|
||||
case None =>
|
||||
// Not using Cache.get otherwise the threads which have read access are waiting and on the other hand, each background thread of the Cache needs a read action.
|
||||
val result = findDefinitionLocationResult(key)
|
||||
result match {
|
||||
case Right(_) =>
|
||||
Cache.put(key, result)
|
||||
result
|
||||
case Left(ReadActionTimeout(_)) | Left(IndexNotReady) | Left(ReplNotAvailable) | Left(ModuleNotAvailable(_)) =>
|
||||
result
|
||||
case Left(_) =>
|
||||
Cache.put(key, result)
|
||||
result
|
||||
}
|
||||
try {
|
||||
val result = ApplicationUtil.runReadAction(Cache.get(key))
|
||||
result match {
|
||||
case Right(_) => result
|
||||
case Left(ReadActionTimeout(_)) | Left(IndexNotReady) | Left(ModuleNotAvailable(_)) | Left(ReplNotAvailable) =>
|
||||
Cache.invalidate(key)
|
||||
result
|
||||
case _ => result
|
||||
}
|
||||
} catch {
|
||||
case e: TimeoutException => Left(ReadActionTimeout(e.getMessage))
|
||||
}
|
||||
}
|
||||
|
||||
@ -168,6 +158,8 @@ private[component] object DefinitionLocationComponent {
|
||||
Option(namedElement.getContainingFile).flatMap(HaskellPsiUtil.findModuleName).getOrElse("-")
|
||||
}
|
||||
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
|
||||
private def findLocationByImportedIdentifiers(project: Project, key: Key, name: String): Either[NoInfo, PackageModuleLocation] = {
|
||||
ProgressManager.checkCanceled()
|
||||
|
||||
@ -179,7 +171,15 @@ private[component] object DefinitionLocationComponent {
|
||||
case Some(q) => q + "." + qNameName
|
||||
}
|
||||
|
||||
val moduleIdentifiers = FileModuleIdentifiers.findAvailableModuleIdentifiers(psiFile).filter(_.name == qName)
|
||||
val moduleIdentifiers = {
|
||||
if (HaskellProjectUtil.isSourceFile(psiFile)) {
|
||||
FileModuleIdentifiers.findAvailableModuleIdentifiers(psiFile)
|
||||
} else {
|
||||
HaskellPsiUtil.findModuleName(psiFile).flatMap(mn => ScalaFutureUtil.waitForValue(project,
|
||||
HaskellComponentsManager.findModuleIdentifiers(project, mn)
|
||||
, "find module identifiers in DefinitionLocationComponent")).flatten.getOrElse(Iterable())
|
||||
}.filter(_.name == qName)
|
||||
}
|
||||
|
||||
ProgressManager.checkCanceled()
|
||||
|
||||
@ -200,6 +200,7 @@ private[component] object DefinitionLocationComponent {
|
||||
sp <- LineColumnPosition.fromOffset(vf, qualifiedNameElement.getTextRange.getStartOffset)
|
||||
ep <- LineColumnPosition.fromOffset(vf, qualifiedNameElement.getTextRange.getEndOffset)
|
||||
endColumnNr = if (withoutLastColumn) ep.columnNr - 1 else ep.columnNr
|
||||
if key.qualifiedNameElement.isValid
|
||||
} yield {
|
||||
repl: ProjectStackRepl => repl.findLocationInfo(moduleName, psiFile, sp.lineNr, sp.columnNr, ep.lineNr, endColumnNr, name)
|
||||
}
|
||||
|
@ -20,7 +20,6 @@ import java.util
|
||||
import java.util.concurrent.TimeoutException
|
||||
|
||||
import com.github.blemale.scaffeine.{AsyncLoadingCache, Scaffeine}
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiFile
|
||||
import intellij.haskell.HaskellNotificationGroup
|
||||
@ -30,7 +29,7 @@ import intellij.haskell.psi.{HaskellImportDeclaration, HaskellImportId, HaskellP
|
||||
import intellij.haskell.util.{ApplicationUtil, HaskellProjectUtil, ScalaFutureUtil}
|
||||
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent.{Await, Future, blocking}
|
||||
import scala.concurrent.{Await, Future}
|
||||
import scala.jdk.CollectionConverters._
|
||||
|
||||
object FileModuleIdentifiers {
|
||||
@ -64,28 +63,22 @@ object FileModuleIdentifiers {
|
||||
}
|
||||
|
||||
def findAvailableModuleIdentifiers(psiFile: PsiFile): Iterable[ModuleIdentifier] = {
|
||||
val message = s"find available module identifiers for ${psiFile.getName}"
|
||||
if (ApplicationManager.getApplication.isReadAccessAllowed) {
|
||||
val moduleIdentifiers = getModuleIdentifiers(psiFile)
|
||||
ScalaFutureUtil.waitWithCheckCancelled(psiFile.getProject, moduleIdentifiers, message).getOrElse(Iterable())
|
||||
} else {
|
||||
ScalaFutureUtil.waitForValue(psiFile.getProject, getModuleIdentifiers(psiFile), message).getOrElse(Iterable())
|
||||
}
|
||||
val message = s"find available module identifiers for ${psiFile.getVirtualFile.getPath}"
|
||||
val moduleIdentifiers = getModuleIdentifiers(psiFile)
|
||||
ScalaFutureUtil.waitForValue(psiFile.getProject, moduleIdentifiers, message).getOrElse(Iterable())
|
||||
}
|
||||
|
||||
private def getModuleIdentifiers(psiFile: PsiFile): Future[Iterable[ModuleIdentifier]] = {
|
||||
val key = Key(psiFile)
|
||||
blocking {
|
||||
Cache.get(key).map {
|
||||
case Some(mids) =>
|
||||
if (mids.toSeq.contains(None)) {
|
||||
Cache.synchronous.invalidate(key)
|
||||
}
|
||||
mids.flatten.flatten
|
||||
case None =>
|
||||
Cache.get(key).map {
|
||||
case Some(mids) =>
|
||||
if (mids.toSeq.contains(None)) {
|
||||
Cache.synchronous.invalidate(key)
|
||||
Iterable()
|
||||
}
|
||||
}
|
||||
mids.flatten.flatten
|
||||
case None =>
|
||||
Cache.synchronous.invalidate(key)
|
||||
Iterable()
|
||||
}
|
||||
}
|
||||
|
||||
@ -95,36 +88,31 @@ object FileModuleIdentifiers {
|
||||
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 => isNoImplicitPreludeActive(info, psiFile))
|
||||
} else {
|
||||
// TODO: This can give unexpected behavior when finding references in library files
|
||||
false
|
||||
}
|
||||
val idsF1 = getModuleIdentifiersFromFullImportedModules(noImplicitPrelude, psiFile, importDeclarations)
|
||||
val importDeclarations = ApplicationUtil.runReadAction(findImportDeclarations(psiFile))
|
||||
val noImplicitPrelude = if (HaskellProjectUtil.isSourceFile(psiFile)) {
|
||||
HaskellComponentsManager.findStackComponentInfo(psiFile).exists(info => isNoImplicitPreludeActive(info, psiFile))
|
||||
} else {
|
||||
// TODO: This can give unexpected behavior when finding references in library files
|
||||
false
|
||||
}
|
||||
val idsF1 = getModuleIdentifiersFromFullImportedModules(noImplicitPrelude, psiFile, importDeclarations)
|
||||
|
||||
val idsF2 = getModuleIdentifiersFromHidingIdsImportedModules(psiFile, importDeclarations)
|
||||
val idsF2 = getModuleIdentifiersFromHidingIdsImportedModules(psiFile, importDeclarations)
|
||||
|
||||
val idsF3 = getModuleIdentifiersFromSpecIdsImportedModules(psiFile, importDeclarations)
|
||||
val idsF3 = getModuleIdentifiersFromSpecIdsImportedModules(psiFile, importDeclarations)
|
||||
|
||||
val f = for {
|
||||
f1 <- idsF1
|
||||
f2 <- idsF2
|
||||
f3 <- idsF3
|
||||
} yield (f1, f2, f3)
|
||||
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}")
|
||||
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.getVirtualFile.getPath}")
|
||||
None
|
||||
}
|
||||
}
|
||||
@ -132,34 +120,28 @@ object FileModuleIdentifiers {
|
||||
private def getModuleIdentifiersFromFullImportedModules(noImplicitPrelude: Boolean, psiFile: PsiFile, importDeclarations: Iterable[HaskellImportDeclaration]): Future[Iterable[Option[Iterable[ModuleIdentifier]]]] = {
|
||||
val importInfos = getFullImportedModules(noImplicitPrelude, psiFile, importDeclarations)
|
||||
|
||||
blocking {
|
||||
Future.sequence(importInfos.map(importInfo => {
|
||||
val allModuleIdentifiers = BrowseModuleComponent.findModuleIdentifiers(psiFile.getProject, importInfo.moduleName)
|
||||
allModuleIdentifiers.map(mi => mi.map(i => createQualifiedModuleIdentifiers(importInfo, i)))
|
||||
}))
|
||||
}
|
||||
Future.sequence(importInfos.map(importInfo => {
|
||||
val allModuleIdentifiers = BrowseModuleComponent.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)
|
||||
|
||||
blocking {
|
||||
Future.sequence(importInfos.map(importInfo => {
|
||||
val allModuleIdentifiers = BrowseModuleComponent.findModuleIdentifiers(psiFile.getProject, importInfo.moduleName)
|
||||
allModuleIdentifiers.map(ids => ids.map(is => createQualifiedModuleIdentifiers(importInfo, is.filterNot(mi => importInfo.ids.exists(_ == mi.name)))))
|
||||
}))
|
||||
}
|
||||
Future.sequence(importInfos.map(importInfo => {
|
||||
val allModuleIdentifiers = BrowseModuleComponent.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)
|
||||
|
||||
blocking {
|
||||
Future.sequence(importInfos.map(importInfo => {
|
||||
val allModuleIdentifiers = BrowseModuleComponent.findModuleIdentifiers(psiFile.getProject, importInfo.moduleName)
|
||||
allModuleIdentifiers.map(ids => ids.map(is => createQualifiedModuleIdentifiers(importInfo, is.filter(mi => importInfo.ids.exists(_ == mi.name)))))
|
||||
}))
|
||||
}
|
||||
Future.sequence(importInfos.map(importInfo => {
|
||||
val allModuleIdentifiers = BrowseModuleComponent.findModuleIdentifiers(psiFile.getProject, importInfo.moduleName)
|
||||
allModuleIdentifiers.map(ids => ids.map(is => createQualifiedModuleIdentifiers(importInfo, is.filter(mi => importInfo.ids.exists(_ == mi.name)))))
|
||||
}))
|
||||
}
|
||||
|
||||
private sealed trait ImportInfo {
|
||||
|
@ -47,7 +47,7 @@ object HaskellComponentsManager {
|
||||
BrowseModuleComponent.findModuleIdentifiersInCache(project)
|
||||
}
|
||||
}
|
||||
ScalaFutureUtil.waitWithCheckCancelled(project, f, "find module identifiers in cache") match {
|
||||
ScalaFutureUtil.waitForValue(project, f, "find module identifiers in cache") match {
|
||||
case Some(ids) => ids
|
||||
case None => Iterable()
|
||||
}
|
||||
@ -70,10 +70,6 @@ object HaskellComponentsManager {
|
||||
NameInfoComponent.findNameInfo(psiElement)
|
||||
}
|
||||
|
||||
def findNameInfoByModuleName(project: Project, moduleName: String, name: String): NameInfoResult = {
|
||||
NameInfoComponent.NameInfoByModuleComponent.findNameInfoByModuleName(project, moduleName, name)
|
||||
}
|
||||
|
||||
def findAvailableModuleNamesWithIndex(stackComponentInfo: StackComponentInfo): Iterable[String] = {
|
||||
AvailableModuleNamesComponent.findAvailableModuleNamesWithIndex(stackComponentInfo)
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ package intellij.haskell.external.component
|
||||
|
||||
import java.nio.file.Paths
|
||||
|
||||
import com.github.blemale.scaffeine.{LoadingCache, Scaffeine}
|
||||
import com.github.blemale.scaffeine.{AsyncLoadingCache, Scaffeine}
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.psi.PsiFile
|
||||
@ -27,33 +27,29 @@ import intellij.haskell.external.component.HaskellComponentsManager.StackCompone
|
||||
import intellij.haskell.external.repl.StackRepl.LibType
|
||||
import intellij.haskell.external.repl.StackReplsManager
|
||||
import intellij.haskell.runconfig.console.HaskellConsoleView
|
||||
import intellij.haskell.util.{HaskellFileUtil, HaskellProjectUtil, ScalaUtil}
|
||||
import intellij.haskell.util.{HaskellFileUtil, HaskellProjectUtil, ScalaFutureUtil, ScalaUtil}
|
||||
|
||||
private[component] object HaskellModuleInfoComponent {
|
||||
|
||||
private case class Key(project: Project, filePath: String)
|
||||
|
||||
private final val Cache: LoadingCache[Key, Option[InternalHaskellModuleInfo]] = Scaffeine().build((k: Key) => createFileInfo(k))
|
||||
private final val Cache: AsyncLoadingCache[Key, Option[InternalHaskellModuleInfo]] = Scaffeine().buildAsync((k: Key) => createFileInfo(k))
|
||||
|
||||
def findHaskellProjectFileInfo(project: Project, filePath: String): Option[StackComponentInfo] = {
|
||||
val key = Key(project, filePath)
|
||||
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
|
||||
ScalaFutureUtil.waitForValue(project, Cache.get(key), "Getting project file info").flatten 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.synchronous.invalidate(key)
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@ -71,12 +67,12 @@ private[component] object HaskellModuleInfoComponent {
|
||||
}
|
||||
|
||||
def invalidate(project: Project): Unit = {
|
||||
val keys = Cache.asMap().keys.filter(_.project == project)
|
||||
keys.foreach(Cache.invalidate)
|
||||
val keys = Cache.synchronous.asMap().keys.filter(_.project == project)
|
||||
keys.foreach(Cache.synchronous.invalidate)
|
||||
}
|
||||
|
||||
def invalidate(psiFile: PsiFile): Unit = {
|
||||
HaskellFileUtil.getAbsolutePath(psiFile).foreach(fp => Cache.invalidate(Key(psiFile.getProject, fp)))
|
||||
HaskellFileUtil.getAbsolutePath(psiFile).foreach(fp => Cache.synchronous.invalidate(Key(psiFile.getProject, fp)))
|
||||
}
|
||||
|
||||
private def createFileInfo(key: Key): Option[InternalHaskellModuleInfo] = {
|
||||
|
@ -35,6 +35,7 @@ import scala.jdk.CollectionConverters._
|
||||
object HoogleComponent {
|
||||
|
||||
private def hooglePath = HaskellSettingsState.hooglePath.getOrElse(GlobalInfo.defaultHooglePath.toString)
|
||||
|
||||
private final val HoogleDbName = "hoogle"
|
||||
|
||||
def runHoogle(project: Project, pattern: String, count: Int = 100): Option[Seq[String]] = {
|
||||
@ -157,7 +158,7 @@ object HoogleComponent {
|
||||
private def runHoogle(project: Project, arguments: Seq[String]): Option[ProcessOutput] = {
|
||||
ProgressManager.checkCanceled()
|
||||
|
||||
ScalaFutureUtil.waitWithCheckCancelled(project,
|
||||
ScalaFutureUtil.waitForValue(project,
|
||||
Future {
|
||||
blocking {
|
||||
CommandLine.run(project, hooglePath, Seq(s"--database=${hoogleDbPath(project)}") ++ arguments, logOutput = true)
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
package intellij.haskell.external.component
|
||||
|
||||
import com.github.blemale.scaffeine.{LoadingCache, Scaffeine}
|
||||
import com.github.blemale.scaffeine.{AsyncLoadingCache, Scaffeine}
|
||||
import com.intellij.openapi.progress.ProgressManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.{PsiElement, PsiFile}
|
||||
@ -24,7 +24,7 @@ import intellij.haskell.external.repl.StackRepl.StackReplOutput
|
||||
import intellij.haskell.external.repl.StackReplsManager
|
||||
import intellij.haskell.psi._
|
||||
import intellij.haskell.util.StringUtil.escapeString
|
||||
import intellij.haskell.util.{HaskellProjectUtil, StringUtil}
|
||||
import intellij.haskell.util.{HaskellProjectUtil, ScalaFutureUtil, StringUtil}
|
||||
|
||||
private[component] object NameInfoComponent {
|
||||
|
||||
@ -35,7 +35,7 @@ private[component] object NameInfoComponent {
|
||||
private final val ModuleInfoPattern = """(.+)-- Defined in [`‘]([\w\.\-]+)['’]""".r
|
||||
private final val InfixInfoPattern = """(infix.+)""".r
|
||||
|
||||
private final val Cache: LoadingCache[Key, NameInfoResult] = Scaffeine().build((k: Key) => findNameInfoResult(k))
|
||||
private final val Cache: AsyncLoadingCache[Key, NameInfoResult] = Scaffeine().buildAsync((k: Key) => findNameInfoResult(k))
|
||||
|
||||
private case class Key(psiFile: PsiFile, name: String)
|
||||
|
||||
@ -47,12 +47,11 @@ private[component] object NameInfoComponent {
|
||||
}
|
||||
|
||||
def invalidate(psiFile: PsiFile): Unit = {
|
||||
Cache.asMap().filter(_._1.psiFile == psiFile).keys.foreach(Cache.invalidate)
|
||||
Cache.synchronous.asMap().filter(_._1.psiFile == psiFile).keys.foreach(Cache.synchronous.invalidate)
|
||||
}
|
||||
|
||||
def invalidateAll(project: Project): Unit = {
|
||||
Cache.asMap().map(_._1.psiFile).filter(_.getProject == project).foreach(invalidate)
|
||||
NameInfoByModuleComponent.invalidateAll(project)
|
||||
Cache.synchronous.asMap().map(_._1.psiFile).filter(_.getProject == project).foreach(invalidate)
|
||||
}
|
||||
|
||||
private def findNameInfo(qualifiedNameElement: HaskellQualifiedNameElement, importQualifier: Option[String]): NameInfoResult = {
|
||||
@ -68,46 +67,32 @@ private[component] object NameInfoComponent {
|
||||
|
||||
ProgressManager.checkCanceled()
|
||||
|
||||
Cache.getIfPresent(key) match {
|
||||
ScalaFutureUtil.waitForValue(qualifiedNameElement.getProject, Cache.get(key), s"Getting name info for $name") match {
|
||||
case Some(result) =>
|
||||
result match {
|
||||
case Right(_) => result
|
||||
case Left(ReadActionTimeout(_)) | Left(IndexNotReady) | Left(ModuleNotAvailable(_)) | Left(ReplNotAvailable) =>
|
||||
Cache.invalidate(key)
|
||||
Cache.synchronous.invalidate(key)
|
||||
result
|
||||
case _ => result
|
||||
}
|
||||
case None =>
|
||||
ProgressManager.checkCanceled()
|
||||
val result = findNameInfoResult(key)
|
||||
result match {
|
||||
case Right(_) =>
|
||||
Cache.put(key, result)
|
||||
result
|
||||
case Left(ReadActionTimeout(_)) | Left(IndexNotReady) | Left(ReplNotAvailable) | Left(ModuleNotAvailable(_)) =>
|
||||
result
|
||||
case Left(_) =>
|
||||
Cache.put(key, result)
|
||||
result
|
||||
}
|
||||
Cache.synchronous.invalidate(key)
|
||||
Left(ReadActionTimeout("Getting name info"))
|
||||
}
|
||||
}
|
||||
|
||||
private def findNameInfoResult(key: Key): NameInfoResult = {
|
||||
ProgressManager.checkCanceled()
|
||||
|
||||
val psiFile = key.psiFile
|
||||
val project = psiFile.getProject
|
||||
val name = key.name
|
||||
val isSourceFile = HaskellProjectUtil.isSourceFile(psiFile)
|
||||
ProgressManager.checkCanceled()
|
||||
if (isSourceFile) {
|
||||
StackReplsManager.getProjectRepl(psiFile) match {
|
||||
case Some(repl) =>
|
||||
if (!repl.available) {
|
||||
Left(ReplNotAvailable)
|
||||
} else {
|
||||
ProgressManager.checkCanceled()
|
||||
repl.findInfo(psiFile, name) match {
|
||||
case Some(output) if output.stdoutLines.nonEmpty & output.stderrLines.isEmpty => Right(createNameInfos(project, output))
|
||||
case None => Left(ReplNotAvailable)
|
||||
@ -120,7 +105,6 @@ private[component] object NameInfoComponent {
|
||||
HaskellPsiUtil.findModuleName(psiFile) match {
|
||||
case None => Left(NoInfoAvailable(key.name, psiFile.getName))
|
||||
case Some(mn) =>
|
||||
ProgressManager.checkCanceled()
|
||||
StackReplsManager.getGlobalRepl(project) match {
|
||||
case Some(repl) =>
|
||||
repl.findInfo(mn, name) match {
|
||||
@ -153,41 +137,6 @@ private[component] object NameInfoComponent {
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
object NameInfoByModuleComponent {
|
||||
|
||||
private case class Key(project: Project, moduleName: String, name: String)
|
||||
|
||||
private final val Cache: LoadingCache[Key, NameInfoResult] = Scaffeine().build((k: Key) => loadByModuleAndName(k))
|
||||
|
||||
private def loadByModuleAndName(key: Key): NameInfoResult = {
|
||||
findNameInfos(key)
|
||||
}
|
||||
|
||||
private def findNameInfos(key: Key): Either[NoInfo, Iterable[NameInfo]] = {
|
||||
val output = StackReplsManager.getGlobalRepl(key.project).flatMap(_.findInfo(key.moduleName, key.name))
|
||||
output match {
|
||||
case Some(o) => Right(createNameInfos(key.project, o))
|
||||
case None => Left(ReplNotAvailable)
|
||||
}
|
||||
}
|
||||
|
||||
def findNameInfoByModuleName(project: Project, moduleName: String, name: String): NameInfoResult = {
|
||||
val key = Key(project, moduleName, name)
|
||||
val result = Cache.get(key)
|
||||
result match {
|
||||
case Right(_) => result
|
||||
case Left(_) =>
|
||||
Cache.invalidate(key)
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
private[component] def invalidateAll(project: Project): Unit = {
|
||||
Cache.asMap().filter(_._1.project == project).keys.foreach(Cache.invalidate)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
object NameInfoComponentResult {
|
||||
|
@ -16,10 +16,10 @@
|
||||
|
||||
package intellij.haskell.external.component
|
||||
|
||||
import com.github.blemale.scaffeine.{LoadingCache, Scaffeine}
|
||||
import com.github.blemale.scaffeine.{AsyncLoadingCache, Scaffeine}
|
||||
import com.intellij.openapi.project.Project
|
||||
import intellij.haskell.external.component.HaskellComponentsManager.StackComponentInfo
|
||||
import intellij.haskell.util.HaskellProjectUtil
|
||||
import intellij.haskell.util.{HaskellProjectUtil, ScalaFutureUtil}
|
||||
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent.{Await, Future, blocking}
|
||||
@ -32,14 +32,14 @@ private[component] object StackComponentGlobalInfoComponent {
|
||||
|
||||
private type Result = Option[StackComponentGlobalInfo]
|
||||
|
||||
private final val Cache: LoadingCache[Key, Result] = Scaffeine().build((k: Key) => createStackInfo(k))
|
||||
private final val Cache: AsyncLoadingCache[Key, Result] = Scaffeine().buildAsync((k: Key) => createStackInfo(k))
|
||||
|
||||
def findStackComponentGlobalInfo(stackComponentInfo: StackComponentInfo): Option[StackComponentGlobalInfo] = {
|
||||
val key = Key(stackComponentInfo)
|
||||
Cache.get(key) match {
|
||||
ScalaFutureUtil.waitForValue(stackComponentInfo.module.getProject, Cache.get(key), "Getting global info").flatten match {
|
||||
case result@Some(_) => result
|
||||
case _ =>
|
||||
Cache.invalidate(key)
|
||||
Cache.synchronous.invalidate(key)
|
||||
None
|
||||
}
|
||||
}
|
||||
@ -74,8 +74,8 @@ private[component] object StackComponentGlobalInfoComponent {
|
||||
}
|
||||
|
||||
def invalidate(project: Project): Unit = {
|
||||
val keys = Cache.asMap().keys.filter(_.stackComponentInfo.module.getProject == project)
|
||||
keys.foreach(Cache.invalidate)
|
||||
val keys = Cache.synchronous.asMap().keys.filter(_.stackComponentInfo.module.getProject == project)
|
||||
keys.foreach(Cache.synchronous.invalidate)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,7 @@ import com.intellij.openapi.projectRoots.ProjectJdkTable
|
||||
import com.intellij.openapi.roots.{ModifiableRootModel, ModuleRootModificationUtil}
|
||||
import com.intellij.openapi.ui.Messages
|
||||
import com.intellij.openapi.vfs.VirtualFileManager
|
||||
import com.intellij.psi.{PsiManager, PsiTreeChangeAdapter, PsiTreeChangeEvent}
|
||||
import intellij.haskell.action.HaskellReformatAction
|
||||
import intellij.haskell.annotator.HaskellAnnotator
|
||||
import intellij.haskell.external.execution.StackCommandLine
|
||||
@ -243,7 +244,9 @@ object StackProjectManager {
|
||||
|
||||
projectFilesWithImportedModuleNames match {
|
||||
case Some(fm) =>
|
||||
fm.foreach { case (_, moduleNames) =>
|
||||
fm.foreach { case (pf, moduleNames) =>
|
||||
HaskellPsiUtil.findModuleName(pf).foreach(BrowseModuleComponent.findModuleIdentifiersSync(project, _))
|
||||
|
||||
moduleNames.foreach(mn => HaskellModuleNameIndex.findFilesByModuleName(project, mn))
|
||||
HaskellNotificationGroup.logInfoEvent(project, "Loading module identifiers " + moduleNames.mkString(", "))
|
||||
moduleNames.foreach(m => BrowseModuleComponent.findModuleIdentifiersSync(project, m))
|
||||
@ -305,6 +308,25 @@ object StackProjectManager {
|
||||
}
|
||||
}
|
||||
|
||||
if (!project.isDisposed) {
|
||||
PsiManager.getInstance(project).addPsiTreeChangeListener(new PsiTreeChangeAdapter {
|
||||
|
||||
private def invalidateTypeInfo(event: PsiTreeChangeEvent): Unit = {
|
||||
val element = Option(event.getOldChild).orElse(Option(event.getNewChild)).flatMap(e => HaskellPsiUtil.findExpression(e)).orElse(Option(event.getParent))
|
||||
val elements = element.map(e => HaskellPsiUtil.findQualifiedNamedElements(e)).getOrElse(Seq()).toSeq
|
||||
TypeInfoComponent.invalidateElements(event.getFile, elements)
|
||||
}
|
||||
|
||||
override def childReplaced(event: PsiTreeChangeEvent): Unit = {
|
||||
invalidateTypeInfo(event)
|
||||
}
|
||||
|
||||
override def childrenChanged(event: PsiTreeChangeEvent): Unit = {
|
||||
invalidateTypeInfo(event)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
progressIndicator.setText("Busy preloading caches")
|
||||
if (!preloadLibraryFilesCacheFuture.isDone || !preloadStackComponentInfoCache.isDone || !preloadLibraryIdentifiersCacheFuture.isDone || !replsLoad.isDone) {
|
||||
FutureUtil.waitForValue(project, preloadStackComponentInfoCache, "preloading project cache", 600)
|
||||
|
@ -23,7 +23,9 @@ import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.{PsiElement, PsiFile}
|
||||
import intellij.haskell.external.repl.{ProjectStackRepl, StackReplsManager}
|
||||
import intellij.haskell.psi._
|
||||
import intellij.haskell.util.{HaskellFileUtil, LineColumnPosition}
|
||||
import intellij.haskell.util.{ApplicationUtil, HaskellFileUtil, LineColumnPosition}
|
||||
|
||||
import scala.concurrent.TimeoutException
|
||||
|
||||
private[component] object TypeInfoComponent {
|
||||
|
||||
@ -64,14 +66,18 @@ private[component] object TypeInfoComponent {
|
||||
case None => Left(ReplNotAvailable)
|
||||
}
|
||||
}
|
||||
}.getOrElse(Left(NoInfoAvailable(selectionModel.getSelectedText, psiFile.getName)))
|
||||
}.getOrElse(Left(NoInfoAvailable(selectionModel.getSelectedText, psiFile.getName)))
|
||||
} else {
|
||||
Left(ModuleNotAvailable(moduleName.getOrElse(psiFile.getName)))
|
||||
}
|
||||
}
|
||||
|
||||
def invalidateElements(psiFile: PsiFile, namedElements: Seq[HaskellQualifiedNameElement]): Unit = {
|
||||
namedElements.foreach(qne => Cache.invalidate(Key(psiFile, qne)))
|
||||
}
|
||||
|
||||
def invalidate(psiFile: PsiFile): Unit = {
|
||||
val keys = Cache.asMap().filter(_._1.psiFile == psiFile).keys
|
||||
val keys = Cache.asMap().filter(_._1.psiFile == psiFile).keys.filterNot(_.qualifiedNameElement.isValid)
|
||||
Cache.invalidateAll(keys)
|
||||
}
|
||||
|
||||
@ -93,6 +99,7 @@ private[component] object TypeInfoComponent {
|
||||
t = qne.getText
|
||||
_ = ProgressManager.checkCanceled()
|
||||
mn = HaskellPsiUtil.findModuleName(psiFile)
|
||||
if key.qualifiedNameElement.isValid
|
||||
} yield {
|
||||
ProgressManager.checkCanceled()
|
||||
repl: ProjectStackRepl => repl.findTypeInfo(mn, key.psiFile, sp.lineNr, sp.columnNr, ep.lineNr, ep.columnNr, t)
|
||||
@ -116,26 +123,18 @@ private[component] object TypeInfoComponent {
|
||||
}
|
||||
|
||||
private def findTypeInfo(key: Key): TypeInfoResult = {
|
||||
Cache.getIfPresent(key) match {
|
||||
case Some(result) => result match {
|
||||
try {
|
||||
val result = ApplicationUtil.runReadAction(Cache.get(key))
|
||||
result match {
|
||||
case Right(_) => result
|
||||
case Left(NoInfoAvailable(_, _)) | Left(NoMatchingExport) =>
|
||||
result
|
||||
case Left(ReplNotAvailable) | Left(IndexNotReady) | Left(ModuleNotAvailable(_)) | Left(ReadActionTimeout(_)) =>
|
||||
case Left(ReadActionTimeout(_)) | Left(IndexNotReady) | Left(ModuleNotAvailable(_)) | Left(ReplNotAvailable) =>
|
||||
Cache.invalidate(key)
|
||||
result
|
||||
case _ => result
|
||||
|
||||
}
|
||||
case None =>
|
||||
val result = Cache.get(key)
|
||||
result match {
|
||||
case Right(_) =>
|
||||
result
|
||||
case Left(NoInfoAvailable(_, _)) | Left(NoMatchingExport) =>
|
||||
result
|
||||
case Left(ReplNotAvailable) | Left(IndexNotReady) | Left(ModuleNotAvailable(_)) | Left(ReadActionTimeout(_)) =>
|
||||
Cache.invalidate(key)
|
||||
result
|
||||
}
|
||||
} catch {
|
||||
case e: TimeoutException => Left(ReadActionTimeout(e.getMessage))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,28 +18,45 @@ package intellij.haskell.external.repl
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import intellij.haskell.external.repl.StackRepl.StackReplOutput
|
||||
import intellij.haskell.util.ScalaFutureUtil
|
||||
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent._
|
||||
|
||||
case class GlobalStackRepl(project: Project, replTimeout: Int) extends StackRepl(project, None, Seq("--no-package-hiding", "--no-load"), replTimeout) {
|
||||
|
||||
@volatile
|
||||
private[this] var loadedModuleName: Option[String] = None
|
||||
|
||||
def clearLoadedModules(): Unit = {
|
||||
loadedModuleName = None
|
||||
}
|
||||
|
||||
def getModuleIdentifiers(moduleName: String): Option[StackReplOutput] = synchronized {
|
||||
execute(s":browse! $moduleName")
|
||||
def getModuleIdentifiers(moduleName: String): Option[StackReplOutput] = {
|
||||
ScalaFutureUtil.waitForValue(project, Future {
|
||||
blocking {
|
||||
synchronized {
|
||||
execute(s":browse! $moduleName")
|
||||
}
|
||||
}
|
||||
}, ":browse in GlobalStackRepl").flatten
|
||||
}
|
||||
|
||||
def findInfo(moduleName: String, name: String): Option[StackReplOutput] = synchronized {
|
||||
loadModule(moduleName)
|
||||
def findInfo(moduleName: String, name: String): Option[StackReplOutput] = {
|
||||
ScalaFutureUtil.waitForValue(project, Future {
|
||||
blocking {
|
||||
synchronized {
|
||||
loadModule(moduleName)
|
||||
|
||||
if (loadedModuleName.contains(moduleName)) {
|
||||
execute(s":info $name")
|
||||
} else {
|
||||
// No info means never info because it's library
|
||||
Some(StackReplOutput())
|
||||
}
|
||||
if (loadedModuleName.contains(moduleName)) {
|
||||
execute(s":info $name")
|
||||
} else {
|
||||
// No info means NEVER info because it's library
|
||||
Some(StackReplOutput())
|
||||
}
|
||||
}
|
||||
}
|
||||
}, ":info in GlobalStackRepl").flatten
|
||||
}
|
||||
|
||||
override def restart(forceExit: Boolean): Unit = synchronized {
|
||||
|
@ -18,7 +18,6 @@ package intellij.haskell.external.repl
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiFile
|
||||
import intellij.haskell.HaskellNotificationGroup
|
||||
@ -26,7 +25,6 @@ import intellij.haskell.external.component.HaskellComponentsManager.StackCompone
|
||||
import intellij.haskell.external.repl.StackRepl.StackReplOutput
|
||||
import intellij.haskell.util.{HaskellFileUtil, ScalaFutureUtil}
|
||||
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent.{Future, blocking}
|
||||
import scala.jdk.CollectionConverters._
|
||||
|
||||
@ -66,8 +64,6 @@ case class ProjectStackRepl(project: Project, stackComponentInfo: StackComponent
|
||||
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
|
||||
private def isReadAccessAllowed = ApplicationManager.getApplication.isReadAccessAllowed
|
||||
|
||||
def findTypeInfo(moduleName: Option[String], psiFile: PsiFile, startLineNr: Int, startColumnNr: Int, endLineNr: Int, endColumnNr: Int, expression: String): Option[StackReplOutput] = {
|
||||
val filePath = getFilePath(psiFile)
|
||||
|
||||
@ -77,11 +73,7 @@ case class ProjectStackRepl(project: Project, stackComponentInfo: StackComponent
|
||||
}
|
||||
}
|
||||
|
||||
if (isReadAccessAllowed) {
|
||||
ScalaFutureUtil.waitWithCheckCancelled(project, Future(execute), "Wait on :type-at in ProjectStackRepl", 30.seconds).flatten
|
||||
} else {
|
||||
execute
|
||||
}
|
||||
ScalaFutureUtil.waitForValue(project, Future(execute), ":type-at in ProjectStackRepl").flatten
|
||||
}
|
||||
|
||||
def findLocationInfo(moduleName: Option[String], psiFile: PsiFile, startLineNr: Int, startColumnNr: Int, endLineNr: Int, endColumnNr: Int, expression: String): Option[StackReplOutput] = {
|
||||
@ -93,11 +85,7 @@ case class ProjectStackRepl(project: Project, stackComponentInfo: StackComponent
|
||||
}
|
||||
}
|
||||
|
||||
if (isReadAccessAllowed) {
|
||||
ScalaFutureUtil.waitWithCheckCancelled(project, Future(execute), "Wait on :loc-at in ProjectStackRepl", timeout = 30.seconds).flatten
|
||||
} else {
|
||||
execute
|
||||
}
|
||||
ScalaFutureUtil.waitForValue(project, Future(execute), ":loc-at in ProjectStackRepl").flatten
|
||||
}
|
||||
|
||||
def findInfo(psiFile: PsiFile, name: String): Option[StackReplOutput] = {
|
||||
@ -107,11 +95,7 @@ case class ProjectStackRepl(project: Project, stackComponentInfo: StackComponent
|
||||
}
|
||||
}
|
||||
|
||||
if (isReadAccessAllowed) {
|
||||
ScalaFutureUtil.waitWithCheckCancelled(psiFile.getProject, Future(execute), "Wait on :info in ProjectStackRepl", 30.seconds).flatten
|
||||
} else {
|
||||
execute
|
||||
}
|
||||
ScalaFutureUtil.waitForValue(psiFile.getProject, Future(execute), ":info in ProjectStackRepl").flatten
|
||||
}
|
||||
|
||||
def isModuleLoaded(moduleName: String): Boolean = {
|
||||
@ -195,13 +179,20 @@ case class ProjectStackRepl(project: Project, stackComponentInfo: StackComponent
|
||||
}
|
||||
}
|
||||
|
||||
def getModuleIdentifiers(moduleName: String, psiFile: Option[PsiFile]): Option[StackReplOutput] = synchronized {
|
||||
if (psiFile.isEmpty || isBrowseModuleLoaded(moduleName) || psiFile.exists(pf => load(pf, fileModified = false, mustBeByteCode = false).exists(_._2 == false))) {
|
||||
execute(s":browse! $moduleName")
|
||||
} else {
|
||||
HaskellNotificationGroup.logInfoEvent(project, s"Couldn't get module identifiers for module $moduleName because file ${psiFile.map(_.getName).getOrElse("-")} isn't loaded")
|
||||
None
|
||||
}
|
||||
def getModuleIdentifiers(project: Project, moduleName: String, psiFile: Option[PsiFile]): Option[StackReplOutput] = {
|
||||
ScalaFutureUtil.waitForValue(project,
|
||||
Future {
|
||||
blocking {
|
||||
synchronized {
|
||||
if (psiFile.isEmpty || isBrowseModuleLoaded(moduleName) || psiFile.exists(pf => load(pf, fileModified = false, mustBeByteCode = false).exists(_._2 == false))) {
|
||||
execute(s":browse! $moduleName")
|
||||
} else {
|
||||
HaskellNotificationGroup.logInfoEvent(project, s"Couldn't get module identifiers for module $moduleName because file ${psiFile.map(_.getName).getOrElse("-")} isn't loaded")
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}, "getModuleIdentifiers in ProjectStackRepl").flatten
|
||||
}
|
||||
|
||||
override def restart(forceExit: Boolean): Unit = synchronized {
|
||||
|
@ -158,7 +158,7 @@ abstract class StackRepl(project: Project, componentInfo: Option[StackComponentI
|
||||
val timeout = if (ghciCommand == GhciCommand.Load || ghciCommand == GhciCommand.Browse) LoadTimeout else DefaultTimeout
|
||||
|
||||
val deadline = timeout.fromNow
|
||||
while (deadline.hasTimeLeft && !hasReachedEndOfOutput) {
|
||||
while (deadline.hasTimeLeft && !hasReachedEndOfOutput && !project.isDisposed) {
|
||||
drainQueues()
|
||||
|
||||
// We have to wait...
|
||||
@ -255,7 +255,7 @@ abstract class StackRepl(project: Project, componentInfo: Option[StackComponentI
|
||||
}
|
||||
|
||||
val deadline = DefaultTimeout.fromNow
|
||||
while (process.isAlive() && deadline.hasTimeLeft && !isStarted && !hasDependencyError) {
|
||||
while (process.isAlive() && deadline.hasTimeLeft && !isStarted && !hasDependencyError && !project.isDisposed) {
|
||||
// We have to wait till REPL is started
|
||||
Thread.sleep(DelayBetweenReadsInMillis)
|
||||
}
|
||||
|
@ -17,11 +17,13 @@
|
||||
package intellij.haskell.inspection
|
||||
|
||||
import com.intellij.codeInspection._
|
||||
import com.intellij.openapi.application.ReadAction
|
||||
import com.intellij.openapi.progress.ProgressManager
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.tree.IElementType
|
||||
import com.intellij.psi.{PsiElement, PsiFile, TokenType}
|
||||
import com.intellij.util.WaitFor
|
||||
import com.intellij.util.concurrency.AppExecutorUtil
|
||||
import intellij.haskell.HaskellNotificationGroup
|
||||
import intellij.haskell.external.component.{HLintComponent, HLintInfo}
|
||||
import intellij.haskell.psi.HaskellTypes._
|
||||
@ -45,21 +47,24 @@ class HLintInspectionTool extends LocalInspectionTool {
|
||||
|
||||
ProgressManager.checkCanceled()
|
||||
|
||||
new WaitFor(500, 1) {
|
||||
override def condition(): Boolean = {
|
||||
ProgressManager.checkCanceled()
|
||||
!HaskellFileUtil.isDocumentUnsaved(document)
|
||||
ReadAction.nonBlocking {
|
||||
ScalaUtil.callable {
|
||||
new WaitFor(500, 1) {
|
||||
override def condition(): Boolean = {
|
||||
!HaskellFileUtil.isDocumentUnsaved(document)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}.expireWith(psiFile.getProject).cancelWith(ProgressManager.getInstance().getProgressIndicator).submit(AppExecutorUtil.getAppExecutorService).get
|
||||
|
||||
ProgressManager.checkCanceled()
|
||||
|
||||
val result = ScalaFutureUtil.waitWithCheckCancelled(psiFile.getProject,
|
||||
val result = ScalaFutureUtil.waitForValue(psiFile.getProject,
|
||||
Future {
|
||||
blocking {
|
||||
HLintComponent.check(psiFile)
|
||||
}
|
||||
}, "Running HLint", timeout = 2.seconds) match {
|
||||
}, "Running HLint", 5.second) match {
|
||||
case Some(r) => r
|
||||
case None => Seq()
|
||||
}
|
||||
|
@ -216,7 +216,10 @@ object HaskellReference {
|
||||
}.getOrElse(Left(ModuleNotAvailable(moduleNames.mkString(" | "))))
|
||||
}
|
||||
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
|
||||
private def findIdentifiersByModuleAndName2(project: Project, moduleNames: Seq[String], name: String, prioIdInExpression: Boolean): Seq[(String, Seq[HaskellNamedElement])] = {
|
||||
|
||||
ProgressManager.checkCanceled()
|
||||
|
||||
moduleNames.distinct.flatMap(mn => HaskellModuleNameIndex.findFilesByModuleName(project, mn) match {
|
||||
@ -226,11 +229,24 @@ object HaskellReference {
|
||||
|
||||
val identifiers = files.flatMap(f => findIdentifierInFileByName(f, name, prioIdInExpression))
|
||||
if (identifiers.isEmpty) {
|
||||
val importedModuleNames = files.flatMap(f => FileModuleIdentifiers.findAvailableModuleIdentifiers(f).filter(mid => mid.name == name || mid.name == "_" + name || mid.name == mid.moduleName + "." + name).map(_.moduleName))
|
||||
val importedModuleNames = files.flatMap { f => {
|
||||
if (HaskellProjectUtil.isSourceFile(f)) {
|
||||
FileModuleIdentifiers.findAvailableModuleIdentifiers(f)
|
||||
} else {
|
||||
HaskellPsiUtil.findModuleName(f).flatMap(mn => ScalaFutureUtil.waitForValue(project,
|
||||
HaskellComponentsManager.findModuleIdentifiers(project, mn), "find module identifiers in HaskellReference")).flatten.getOrElse(Iterable())
|
||||
}
|
||||
}
|
||||
}.filter(mid => mid.name == name || mid.name == "_" + name || mid.name == mid.moduleName + "." + name).map(_.moduleName)
|
||||
if (importedModuleNames.isEmpty) {
|
||||
Seq()
|
||||
} else {
|
||||
findIdentifiersByModuleAndName2(project, importedModuleNames, name, prioIdInExpression)
|
||||
if (moduleNames == Seq(mn)) {
|
||||
val moduleNames = files.flatMap(HaskellPsiUtil.findImportDeclarations).flatMap(_.getModuleName)
|
||||
findIdentifiersByModuleAndName2(project, moduleNames, name, prioIdInExpression)
|
||||
} else {
|
||||
findIdentifiersByModuleAndName2(project, importedModuleNames, name, prioIdInExpression)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Seq((mn, identifiers))
|
||||
|
@ -16,13 +16,15 @@
|
||||
|
||||
package intellij.haskell.util
|
||||
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.application.{ApplicationManager, ReadAction}
|
||||
import com.intellij.openapi.progress.util.ReadTask.Continuation
|
||||
import com.intellij.openapi.progress.util.{ProgressIndicatorUtils, ReadTask}
|
||||
import com.intellij.openapi.progress.{ProgressIndicator, ProgressManager}
|
||||
import com.intellij.openapi.project.{DumbService, Project}
|
||||
import com.intellij.util.concurrency.AppExecutorUtil
|
||||
import intellij.haskell.HaskellNotificationGroup
|
||||
import intellij.haskell.external.component.{NoInfo, ReadActionTimeout}
|
||||
|
||||
@ -30,14 +32,6 @@ import scala.concurrent.duration._
|
||||
|
||||
object ApplicationUtil {
|
||||
|
||||
def timeout: Int = {
|
||||
if (ApplicationManager.getApplication.isDispatchThread) {
|
||||
50
|
||||
} else {
|
||||
60000
|
||||
}
|
||||
}
|
||||
|
||||
private def isReadAccessAllowed = {
|
||||
ApplicationManager.getApplication.isReadAccessAllowed
|
||||
}
|
||||
@ -46,13 +40,14 @@ object ApplicationUtil {
|
||||
if (isReadAccessAllowed) {
|
||||
f
|
||||
} else {
|
||||
ApplicationManager.getApplication.runReadAction(ScalaUtil.computable(f))
|
||||
val progressIndicator = Option(ProgressManager.getInstance().getProgressIndicator)
|
||||
val readAction = ReadAction.nonBlocking(ScalaUtil.callable(f))
|
||||
progressIndicator.foreach(readAction.cancelWith)
|
||||
readAction.submit(AppExecutorUtil.getAppExecutorService).get(2, TimeUnit.SECONDS)
|
||||
}
|
||||
}
|
||||
|
||||
private final val RunInReadActionTimeout = 50.millis
|
||||
|
||||
def runInReadActionWithWriteActionPriority[A](project: Project, f: => A, readActionDescription: => String, timeout: FiniteDuration = RunInReadActionTimeout): Either[NoInfo, A] = {
|
||||
def runInReadActionWithWriteActionPriority[A](project: Project, f: => A, readActionDescription: => String): Either[NoInfo, A] = {
|
||||
val r = new AtomicReference[A]
|
||||
|
||||
if (isReadAccessAllowed) {
|
||||
@ -67,15 +62,15 @@ object ApplicationUtil {
|
||||
}
|
||||
}
|
||||
|
||||
val deadline = timeout.fromNow
|
||||
val deadline = 100.millis.fromNow
|
||||
|
||||
while (!run() && deadline.hasTimeLeft && !project.isDisposed) {
|
||||
Thread.sleep(1)
|
||||
Thread.sleep(2)
|
||||
}
|
||||
|
||||
val result = r.get()
|
||||
if (result == null) {
|
||||
HaskellNotificationGroup.logInfoEvent(project, s"Timeout ($timeout) in runInReadActionWithWriteActionPriority while $readActionDescription")
|
||||
HaskellNotificationGroup.logInfoEvent(project, s"Timeout in runInReadActionWithWriteActionPriority while $readActionDescription")
|
||||
Left(ReadActionTimeout(readActionDescription))
|
||||
} else {
|
||||
Right(result)
|
||||
@ -83,9 +78,7 @@ object ApplicationUtil {
|
||||
}
|
||||
}
|
||||
|
||||
private final val ScheduleInReadActionTimeout = 60.seconds
|
||||
|
||||
def scheduleInReadActionWithWriteActionPriority[A](project: Project, f: => A, scheduleInReadActionDescription: => String, timeout: FiniteDuration = ScheduleInReadActionTimeout, reschedule: Boolean = true): Either[NoInfo, A] = {
|
||||
def scheduleInReadActionWithWriteActionPriority[A](project: Project, f: => A, scheduleInReadActionDescription: => String, timeout: FiniteDuration = 60.seconds, reschedule: Boolean = true): Either[NoInfo, A] = {
|
||||
val r = new AtomicReference[A]
|
||||
var cancelled = false
|
||||
|
||||
@ -127,7 +120,7 @@ object ApplicationUtil {
|
||||
|
||||
val result = r.get()
|
||||
if (result == null) {
|
||||
HaskellNotificationGroup.logInfoEvent(project, s"Timeout ($timeout) in scheduleInReadActionWithWriteActionPriority while $scheduleInReadActionDescription and reschedule $reschedule")
|
||||
HaskellNotificationGroup.logInfoEvent(project, s"Timeout in scheduleInReadActionWithWriteActionPriority while $scheduleInReadActionDescription and reschedule $reschedule")
|
||||
Left(ReadActionTimeout(scheduleInReadActionDescription))
|
||||
} else {
|
||||
Right(result)
|
||||
|
@ -1,5 +1,6 @@
|
||||
package intellij.haskell.util
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.progress.ProgressManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.util.WaitFor
|
||||
@ -10,35 +11,34 @@ import scala.concurrent.{Await, Future, TimeoutException}
|
||||
|
||||
object ScalaFutureUtil {
|
||||
|
||||
def waitForValue[T](project: Project, future: Future[T], actionDescription: String, timeout: FiniteDuration = 5.seconds): Option[T] = {
|
||||
try {
|
||||
Option(Await.result(future, timeout))
|
||||
} catch {
|
||||
case _: TimeoutException =>
|
||||
HaskellNotificationGroup.logInfoEvent(project, s"Timeout while $actionDescription")
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
def waitWithCheckCancelled[T](project: Project, future: Future[T], actionDescription: String, timeout: FiniteDuration = 5.seconds): Option[T] = {
|
||||
try {
|
||||
new WaitFor(timeout.toMillis.toInt, 1) {
|
||||
override def condition(): Boolean = {
|
||||
ProgressManager.checkCanceled()
|
||||
future.isCompleted || project.isDisposed
|
||||
def waitForValue[T](project: Project, future: Future[T], actionDescription: String, timeout: FiniteDuration = 200.millis): Option[T] = {
|
||||
if (ApplicationManager.getApplication.isReadAccessAllowed) {
|
||||
try {
|
||||
new WaitFor(timeout.toMillis.toInt, 10) {
|
||||
override def condition(): Boolean = {
|
||||
ProgressManager.checkCanceled()
|
||||
future.isCompleted || project.isDisposed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (project.isDisposed) {
|
||||
None
|
||||
} else {
|
||||
Option(Await.result(future, 1.milli))
|
||||
if (project.isDisposed) {
|
||||
None
|
||||
} else {
|
||||
Option(Await.result(future, 1.milli))
|
||||
}
|
||||
} catch {
|
||||
case _: TimeoutException =>
|
||||
HaskellNotificationGroup.logInfoEvent(project, s"Timeout in read waitForValue while $actionDescription")
|
||||
None
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
Option(Await.result(future, 10.second))
|
||||
} catch {
|
||||
case _: TimeoutException =>
|
||||
HaskellNotificationGroup.logInfoEvent(project, s"Timeout in waitForValue while $actionDescription")
|
||||
None
|
||||
}
|
||||
} catch {
|
||||
case _: TimeoutException =>
|
||||
HaskellNotificationGroup.logInfoEvent(project, s"Timeout in waitWithCheckCancelled while $actionDescription")
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ object HaskellModuleNameIndex {
|
||||
|
||||
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 final val Cache: LoadingCache[Key, Result] = Scaffeine().build((k: Key) => find(k, 5.second, reschedule = false))
|
||||
|
||||
private def find(key: Key, timeout: FiniteDuration, reschedule: Boolean): Either[NoInfo, Seq[(PsiFile, Boolean)]] = {
|
||||
val project = key.project
|
||||
@ -68,7 +68,8 @@ object HaskellModuleNameIndex {
|
||||
Right(psiFiles.flatMap { case (pf, isPf) => pf.toSeq.map((_, isPf)) })
|
||||
}
|
||||
}
|
||||
case Left(noInfo) => Left(noInfo)
|
||||
case Left(noInfo) =>
|
||||
Left(noInfo)
|
||||
}
|
||||
}
|
||||
|
||||
@ -94,16 +95,14 @@ object HaskellModuleNameIndex {
|
||||
// So using Cache is solution because Cache.get blocks next request for same key while busy.
|
||||
def findFilesByModuleName2(project: Project, moduleName: String): Either[NoInfo, Seq[(PsiFile, isProjectFile)]] = {
|
||||
val key = Key(project, moduleName)
|
||||
Cache.getIfPresent(key) match {
|
||||
case Some(r@Right(_)) => r
|
||||
case _ =>
|
||||
Cache.get(key) match {
|
||||
case r@Right(_) => r
|
||||
case Left(noInfo) =>
|
||||
// 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)
|
||||
}
|
||||
val result = Cache.get(key)
|
||||
result match {
|
||||
case Right(_) => result
|
||||
case Left(_) =>
|
||||
// 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
|
||||
result
|
||||
case _ => result
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user