Improved Hoogle documentation. Fix issue #411.

This commit is contained in:
Rik van der Kleij 2019-06-15 22:17:48 +02:00
parent 6e0f73d7c0
commit 91989d16f6
6 changed files with 35 additions and 52 deletions

View File

@ -49,8 +49,7 @@ class ShowNameInfoAction extends AnAction {
private def createInfoText(nameInfo: NameInfo): String = {
nameInfo match {
case pi: ProjectNameInfo => s"${pi.declaration} -- ${pi.filePath}"
case li: LibraryNameInfo => s"${li.shortenedDeclaration} -- ${li.moduleName}"
case bi: BuiltInNameInfo => s"${bi.shortenedDeclaration} -- ${bi.moduleName} BUILT-IN"
case li: LibraryNameInfo => s"${li.shortenedDeclaration} -- ${li.moduleName} ${li.packageName.getOrElse("")}"
case ii: InfixInfo => ii.declaration
}
}

View File

@ -131,10 +131,10 @@ private[component] object DefinitionLocationComponent {
ProgressManager.checkCanceled()
HaskellReference.findIdentifiersByNameInfo(info, key.qualifiedNameElement.getIdentifierElement, project) match {
case Right((mn, ne)) => Right(PackageModuleLocation(findModuleName(ne), ne, name))
case Right((mn, ne, pn)) => Right(PackageModuleLocation(findModuleName(ne), ne, name, pn))
case Left(noInfo) =>
HaskellReference.findIdentifierInFileByName(psiFile, name).
map(ne => Right(PackageModuleLocation(findModuleName(ne), ne, name))).getOrElse(Left(noInfo))
map(ne => Right(PackageModuleLocation(findModuleName(ne), ne, name, None))).getOrElse(Left(noInfo))
}
case None => Left(NoInfoAvailable(name, psiFile.getName))
}
@ -179,7 +179,7 @@ private[component] object DefinitionLocationComponent {
Left(ModuleNotAvailable("Prelude"))
} else {
HaskellReference.findIdentifiersByModulesAndName(project, moduleNames, name) match {
case Right((mn, ne)) => Right(PackageModuleLocation(mn, ne, name))
case Right((mn, ne)) => Right(PackageModuleLocation(mn, ne, name, None))
case Left(noInfo) => Left(noInfo)
}
}
@ -209,7 +209,8 @@ private[component] object DefinitionLocationComponent {
Left(ReplNotAvailable)
} else {
f(repl) match {
case Some(o) if o.stderrLines.isEmpty && o.stdoutLines.nonEmpty => Right(o)
case Some(o) if o.stderrLines.isEmpty && o.stdoutLines.nonEmpty => Right(o.stdoutLines)
case Some(o) if o.stdoutLines.isEmpty && o.stderrLines.nonEmpty => Right(o.stderrLines) // For some unknown reason REPL write sometimes correct output to stderr
case None => Left(ReplNotAvailable)
case _ => Left(NoInfoAvailable(name, psiFile.getName))
}
@ -219,7 +220,7 @@ private[component] object DefinitionLocationComponent {
}
locationInfo match {
case Right(o) => o.stdoutLines.headOption.map(l => createLocationByReplResult(project, psiFile, l, key, name)) match {
case Right(o) => o.headOption.map(l => createLocationByReplResult(project, psiFile, l, key, name)) match {
case Some(r) => r
case None => Left(NoInfoAvailable(name, key.psiFile.getName))
}
@ -243,13 +244,13 @@ private[component] object DefinitionLocationComponent {
}
case (_, _) => Left(NoInfoAvailable(name, psiFile.getName))
}
case PackageModulePattern(_, mn) =>
case PackageModulePattern(pn, mn) =>
findFilesByModuleName(project, mn) match {
case Right(files) =>
ProgressManager.checkCanceled()
files.headOption.flatMap(HaskellReference.findIdentifierInFileByName(_, name)) match {
case Some(e) => Right(PackageModuleLocation(mn, e, name))
case Some(e) => Right(PackageModuleLocation(mn, e, name, Some(pn)))
case None => Left(NoInfoAvailable(name, key.psiFile.getName))
}
case Left(noInfo) => Left(noInfo)
@ -266,7 +267,7 @@ sealed trait DefinitionLocation {
def originalName: String
}
case class PackageModuleLocation(moduleName: String, namedElement: HaskellNamedElement, originalName: String) extends DefinitionLocation
case class PackageModuleLocation(moduleName: String, namedElement: HaskellNamedElement, originalName: String, packageName: Option[String]) extends DefinitionLocation
case class LocalModuleLocation(psiFile: PsiFile, namedElement: HaskellNamedElement, originalName: String) extends DefinitionLocation

View File

@ -24,7 +24,7 @@ import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.project.Project
import intellij.haskell.external.execution.{CommandLine, StackCommandLine}
import intellij.haskell.psi.{HaskellPsiUtil, HaskellQualifiedNameElement}
import intellij.haskell.util.{HaskellProjectUtil, HtmlElement, ScalaFutureUtil}
import intellij.haskell.util.{HtmlElement, ScalaFutureUtil}
import intellij.haskell.{GlobalInfo, HaskellNotificationGroup}
import scala.collection.JavaConverters._
@ -61,37 +61,24 @@ object HoogleComponent {
val name = qualifiedNameElement.getIdentifierElement.getName
val psiFile = qualifiedNameElement.getContainingFile.getOriginalFile
if (HaskellProjectUtil.isSourceFile(psiFile)) {
DefinitionLocationComponent.findDefinitionLocation(psiFile, qualifiedNameElement, None) match {
case Left(noInfo) =>
HaskellNotificationGroup.logWarningEvent(project, s"No documentation because no location info could be found for identifier `$name` because ${noInfo.message}")
None
case Right(info) =>
val moduleName = info match {
case PackageModuleLocation(mn, _, _) => Some(mn)
case LocalModuleLocation(pf, _, _) => HaskellPsiUtil.findModuleName(pf)
}
moduleName match {
case None =>
HaskellNotificationGroup.logWarningEvent(project, s"No documentation because could not find module for identifier `$name`")
None
case Some(mn) =>
ProgressManager.checkCanceled()
HoogleComponent.createDocumentation(project, name, mn)
}
}
} else if (HaskellProjectUtil.isLibraryFile(psiFile)) {
val moduleName = HaskellPsiUtil.findModuleName(psiFile)
moduleName.flatMap(mn => createDocumentation(project, name, mn))
} else {
None
DefinitionLocationComponent.findDefinitionLocation(psiFile, qualifiedNameElement, None) match {
case Left(noInfo) =>
HaskellNotificationGroup.logWarningEvent(project, s"No documentation because no location info could be found for identifier `$name` because ${noInfo.message}")
None
case Right(info) =>
val locationName = info match {
case PackageModuleLocation(_, _, _, pn) => pn
case LocalModuleLocation(pf, _, _) => HaskellPsiUtil.findModuleName(pf)
}
ProgressManager.checkCanceled()
HoogleComponent.createDocumentation(project, name, locationName)
}
} else {
Some("No documentation because Hoogle (database) is not available")
}
}
private def createDocumentation(project: Project, name: String, moduleName: String): Option[String] = {
private def createDocumentation(project: Project, name: String, locationName: Option[String]): Option[String] = {
def mkString(lines: Seq[String]) = {
lines.mkString("\n").
replace("<", HtmlElement.Lt).
@ -102,7 +89,7 @@ object HoogleComponent {
ProgressManager.checkCanceled()
runHoogle(project, Seq("-i", name, s"+$moduleName", "+Prelude")).
runHoogle(project, Seq("-i", name) ++ locationName.map("+" + _).toSeq).
flatMap(processOutput =>
if (processOutput.getStdoutLines.isEmpty || processOutput.getStdout.contains("No results found")) {
None

View File

@ -93,7 +93,7 @@ private[component] object LibraryPackageInfoComponent {
private final val PackageNameVersionPattern = """([\w\-]+)-([\d\.]+)(?:\-.*)?""".r
private def toPackageNameversion(depends: String): Option[PackageNameVersion] = {
def toPackageNameversion(depends: String): Option[PackageNameVersion] = {
depends match {
case PackageNameVersionPattern(name, version) => Some(PackageNameVersion(name, version))
case _ => None

View File

@ -142,14 +142,12 @@ private[component] object NameInfoComponent {
private def createNameInfo(outputLine: String, project: Project): Option[NameInfo] = {
val result = outputLine match {
case ProjectInfoPattern(declaration, filePath, lineNr, colNr) => Some(ProjectNameInfo(declaration, filePath, lineNr.toInt, colNr.toInt))
case LibraryModuleInfoPattern(declaration, libraryName, moduleName) =>
if (libraryName == "ghc-prim" || libraryName == "integer-gmp") {
Some(BuiltInNameInfo(declaration, libraryName, "GHC.Base"))
case LibraryModuleInfoPattern(declaration, packageId, moduleName) =>
LibraryPackageInfoComponent.toPackageNameversion(packageId) match {
case Some(packageNameVersion) => Some(LibraryNameInfo(declaration, Some(packageNameVersion.name), moduleName))
case None => Some(LibraryNameInfo(declaration, None, moduleName))
}
else {
Some(LibraryNameInfo(declaration, moduleName))
}
case ModuleInfoPattern(declaration, moduleName) => Some(LibraryNameInfo(declaration, moduleName))
case ModuleInfoPattern(declaration, moduleName) => Some(LibraryNameInfo(declaration, None, moduleName))
case InfixInfoPattern(declaration) => Some(InfixInfo(declaration))
case _ => None
}
@ -206,9 +204,7 @@ object NameInfoComponentResult {
case class ProjectNameInfo(declaration: String, filePath: String, lineNr: Int, columnNr: Int) extends NameInfo
case class LibraryNameInfo(declaration: String, moduleName: String) extends NameInfo
case class BuiltInNameInfo(declaration: String, libraryName: String, moduleName: String) extends NameInfo
case class LibraryNameInfo(declaration: String, packageName: Option[String], moduleName: String) extends NameInfo
case class InfixInfo(declaration: String) extends NameInfo

View File

@ -167,7 +167,7 @@ class HaskellReference(element: HaskellNamedElement, textRange: TextRange) exten
ProgressManager.checkCanceled()
HaskellComponentsManager.findDefinitionLocation(psiFile, qualifiedNameElement, importQualifier) match {
case Right(PackageModuleLocation(_, ne, _)) => Right(ne)
case Right(PackageModuleLocation(_, ne, _, _)) => Right(ne)
case Right(LocalModuleLocation(_, ne, _)) => Right(ne)
case Left(noInfo) => Left(noInfo)
}
@ -303,7 +303,7 @@ object HaskellReference {
}
}
def findIdentifiersByNameInfo(nameInfo: NameInfo, namedElement: HaskellNamedElement, project: Project): Either[NoInfo, (Option[String], HaskellNamedElement)] = {
def findIdentifiersByNameInfo(nameInfo: NameInfo, namedElement: HaskellNamedElement, project: Project): Either[NoInfo, (Option[String], HaskellNamedElement, Option[String])] = {
ProgressManager.checkCanceled()
val name = namedElement.getName
@ -312,11 +312,11 @@ object HaskellReference {
val (virtualFile, psiFile) = HaskellFileUtil.findFileInRead(project, pni.filePath)
ProgressManager.checkCanceled()
(virtualFile, psiFile) match {
case (Some(vf), Right(pf)) => findIdentifierByLocation(project, vf, pf, pni.lineNr, pni.columnNr, name).map(r => Right(HaskellPsiUtil.findModuleName(pf), r)).getOrElse(Left(NoInfoAvailable(name, "-")))
case (Some(vf), Right(pf)) => findIdentifierByLocation(project, vf, pf, pni.lineNr, pni.columnNr, name).map(r => Right(HaskellPsiUtil.findModuleName(pf), r, None)).getOrElse(Left(NoInfoAvailable(name, "-")))
case (_, Right(_)) => Left(NoInfoAvailable(name, "-"))
case (_, Left(noInfo)) => Left(noInfo)
}
case lni: LibraryNameInfo => findIdentifiersByLibraryNameInfo(project, lni, name).map({ case (mn, nes) => (Some(mn), nes) })
case lni: LibraryNameInfo => findIdentifiersByLibraryNameInfo(project, lni, name).map({ case (mn, nes) => (Some(mn), nes, lni.packageName) })
case _ => Left(NoInfoAvailable(name, "-"))
}
}