mirror of
https://github.com/ilyakooo0/intellij-haskell.git
synced 2024-10-26 23:25:11 +03:00
Improve Hoogle navigation
This commit is contained in:
parent
bcbfa2b869
commit
eed48c55ed
@ -467,7 +467,7 @@ class HaskellCompletionContributor extends CompletionContributor {
|
|||||||
|
|
||||||
private def createLookupElement(moduleIdentifier: ModuleIdentifier, addParens: Boolean = false): LookupElementBuilder = {
|
private def createLookupElement(moduleIdentifier: ModuleIdentifier, addParens: Boolean = false): LookupElementBuilder = {
|
||||||
addWiths(LookupElementBuilder.create(
|
addWiths(LookupElementBuilder.create(
|
||||||
if (moduleIdentifier.isOperator && addParens)
|
if (moduleIdentifier.operator && addParens)
|
||||||
s"""(${moduleIdentifier.name})"""
|
s"""(${moduleIdentifier.name})"""
|
||||||
else
|
else
|
||||||
moduleIdentifier.name
|
moduleIdentifier.name
|
||||||
|
@ -21,8 +21,9 @@ import com.intellij.openapi.fileEditor.FileEditorManager
|
|||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.psi.PsiFile
|
import com.intellij.psi.PsiFile
|
||||||
import intellij.haskell.external.repl._
|
import intellij.haskell.external.repl._
|
||||||
|
import intellij.haskell.psi.HaskellPsiUtil
|
||||||
import intellij.haskell.util.index.HaskellModuleNameIndex
|
import intellij.haskell.util.index.HaskellModuleNameIndex
|
||||||
import intellij.haskell.util.{HaskellFileUtil, StringUtil}
|
import intellij.haskell.util.{ApplicationUtil, HaskellFileUtil, HaskellProjectUtil, StringUtil}
|
||||||
|
|
||||||
import scala.concurrent.{ExecutionContext, Future, blocking}
|
import scala.concurrent.{ExecutionContext, Future, blocking}
|
||||||
|
|
||||||
@ -30,7 +31,6 @@ private[component] object BrowseModuleComponent {
|
|||||||
|
|
||||||
private case class Key(project: Project, moduleName: String)
|
private case class Key(project: Project, moduleName: String)
|
||||||
|
|
||||||
type BrowseModuleResult = Iterable[ModuleIdentifier]
|
|
||||||
private type BrowseModuleInternalResult = Either[NoInfo, Iterable[ModuleIdentifier]]
|
private type BrowseModuleInternalResult = Either[NoInfo, Iterable[ModuleIdentifier]]
|
||||||
|
|
||||||
private final val Cache: AsyncLoadingCache[Key, BrowseModuleInternalResult] = Scaffeine().buildAsync((k: Key) => {
|
private final val Cache: AsyncLoadingCache[Key, BrowseModuleInternalResult] = Scaffeine().buildAsync((k: Key) => {
|
||||||
@ -41,7 +41,9 @@ private[component] object BrowseModuleComponent {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
private def matchResult(key: Key, result: Future[BrowseModuleInternalResult])(implicit ec: ExecutionContext): Future[Option[Iterable[ModuleIdentifier]]] = {
|
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 {
|
blocking(result.map {
|
||||||
case Right(ids) => Some(ids)
|
case Right(ids) => Some(ids)
|
||||||
case Left(NoInfoAvailable(_, _)) | Left(NoMatchingExport) =>
|
case Left(NoInfoAvailable(_, _)) | Left(NoMatchingExport) =>
|
||||||
@ -52,11 +54,6 @@ private[component] object BrowseModuleComponent {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
def findModuleIdentifiers(project: Project, moduleName: String)(implicit ec: ExecutionContext): Future[Option[Iterable[ModuleIdentifier]]] = {
|
|
||||||
val key = Key(project, moduleName)
|
|
||||||
matchResult(key, Cache.get(key))
|
|
||||||
}
|
|
||||||
|
|
||||||
def findModuleIdentifiersSync(project: Project, moduleName: String): BrowseModuleInternalResult = {
|
def findModuleIdentifiersSync(project: Project, moduleName: String): BrowseModuleInternalResult = {
|
||||||
val key = Key(project, moduleName)
|
val key = Key(project, moduleName)
|
||||||
Cache.synchronous().get(key)
|
Cache.synchronous().get(key)
|
||||||
@ -104,11 +101,11 @@ private[component] object BrowseModuleComponent {
|
|||||||
findInRepl(project, projectRepl, moduleName, Some(moduleFile))
|
findInRepl(project, projectRepl, moduleName, Some(moduleFile))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
findLibraryModuleIdentifiers(project, moduleName)
|
findLibraryModuleIdentifiers(project, Some(moduleFile), moduleName)
|
||||||
}
|
}
|
||||||
case None =>
|
case None =>
|
||||||
// E.g. module name is Prelude which does not refer to file
|
// E.g. module name is Prelude which does not refer to file
|
||||||
findLibraryModuleIdentifiers(project, moduleName)
|
findLibraryModuleIdentifiers(project, None, moduleName)
|
||||||
}
|
}
|
||||||
case Left(noInfo) => Left(noInfo)
|
case Left(noInfo) => Left(noInfo)
|
||||||
}
|
}
|
||||||
@ -128,48 +125,60 @@ private[component] object BrowseModuleComponent {
|
|||||||
private def findInRepl(project: Project, projectRepl: Option[ProjectStackRepl], moduleName: String, psiFile: Option[PsiFile]): Either[NoInfo, Seq[ModuleIdentifier]] = {
|
private def findInRepl(project: Project, projectRepl: Option[ProjectStackRepl], moduleName: String, psiFile: Option[PsiFile]): Either[NoInfo, Seq[ModuleIdentifier]] = {
|
||||||
projectRepl match {
|
projectRepl match {
|
||||||
case Some(repl) =>
|
case Some(repl) =>
|
||||||
if (!repl.available) {
|
if (repl.available) {
|
||||||
Left(ReplNotAvailable)
|
|
||||||
} else {
|
|
||||||
repl.getModuleIdentifiers(moduleName, psiFile) match {
|
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 Some(output) if output.stderrLines.isEmpty && output.stdoutLines.nonEmpty => Right(output.stdoutLines.flatMap(l => createProjectModuleIdentifier(project, l, moduleName)))
|
||||||
case _ => Left(ModuleNotAvailable(moduleName))
|
case _ => Left(ModuleNotAvailable(moduleName))
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Left(ReplNotAvailable)
|
||||||
}
|
}
|
||||||
case None => Left(ReplNotAvailable)
|
case None => Left(ReplNotAvailable)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private def findLibraryModuleIdentifiers(project: Project, moduleName: String): Either[NoInfo, Seq[ModuleIdentifier]] = {
|
private def findLibraryModuleIdentifiers(project: Project, moduleFile: Option[PsiFile], moduleName: String): Either[NoInfo, Seq[ModuleIdentifier]] = {
|
||||||
StackReplsManager.getGlobalRepl(project) match {
|
StackReplsManager.getGlobalRepl(project) match {
|
||||||
case Some(repl) =>
|
case Some(repl) =>
|
||||||
if (!repl.available) {
|
if (repl.available) {
|
||||||
Left(ReplNotAvailable)
|
(repl.getModuleIdentifiers(moduleName), moduleFile) match {
|
||||||
} else {
|
case (Some(o), _) if o.stderrLines.isEmpty && o.stdoutLines.nonEmpty => Right(o.stdoutLines.flatMap(l => createLibraryModuleIdentifier(project, l, moduleName)))
|
||||||
repl.getModuleIdentifiers(moduleName) match {
|
case (_, Some(f)) => Right(ApplicationUtil.runReadAction(HaskellPsiUtil.findTopLevelDeclarations(f)).map(d => ApplicationUtil.runReadAction(d.getName)).flatMap(d => createLibraryModuleIdentifier(project, d, moduleName)).toSeq)
|
||||||
case Some(o) if o.stderrLines.isEmpty && o.stdoutLines.nonEmpty => Right(o.stdoutLines.flatMap(l => findModuleIdentifiers(project, l, moduleName).toSeq))
|
|
||||||
case _ => Left(ModuleNotAvailable(moduleName))
|
case _ => Left(ModuleNotAvailable(moduleName))
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Left(ReplNotAvailable)
|
||||||
}
|
}
|
||||||
case None => Left(ReplNotAvailable)
|
case None => Left(ReplNotAvailable)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This kind of declarations are returned in case DuplicateRecordFields are enabled
|
// This kind of declarations are returned in case DuplicateRecordFields are enabled
|
||||||
private final val Module$SelPattern =
|
private final val Module$SelPattern = """([\w.\-]+)\.\$sel:([^:]+)(?::[^:]+)?::(.*)""".r
|
||||||
"""([\w\.\-]+)\.\$sel:([^:]+)(?::[^:]+)?::(.*)""".r
|
|
||||||
|
|
||||||
private def findModuleIdentifiers(project: Project, declarationLine: String, moduleName: String): Option[ModuleIdentifier] = {
|
private def createLibraryModuleIdentifier(project: Project, declarationLine: String, moduleName: String): Option[ModuleIdentifier] = {
|
||||||
|
DeclarationUtil.getDeclarationInfo(declarationLine, containsQualifiedIds = true).map(declarationInfo => {
|
||||||
|
val id = declarationInfo.id
|
||||||
|
if (moduleName == HaskellProjectUtil.Prelude) {
|
||||||
|
val baseModuleName = declarationInfo.qualifiedId.map(_.split('.').init.mkString("."))
|
||||||
|
ModuleIdentifier(id, moduleName, declarationInfo.declarationLine, declarationInfo.operator, baseModuleName)
|
||||||
|
} else {
|
||||||
|
ModuleIdentifier(id, moduleName, declarationInfo.declarationLine, declarationInfo.operator)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private def createProjectModuleIdentifier(project: Project, declarationLine: String, moduleName: String): Option[ModuleIdentifier] = {
|
||||||
declarationLine match {
|
declarationLine match {
|
||||||
case Module$SelPattern(mn, name, fieldType) => Some(createModuleIdentifier(name, mn, s"$name :: $fieldType"))
|
case Module$SelPattern(mn, id, fieldType) => Some(ModuleIdentifier(id, mn, s"$id :: $fieldType", StringUtil.isWithinParens(id)))
|
||||||
case _ => DeclarationLineUtil.findName(declarationLine) map (nd => createModuleIdentifier(nd.name, moduleName, nd.declaration))
|
case _ => DeclarationUtil.getDeclarationInfo(declarationLine, containsQualifiedIds = false).
|
||||||
|
map(declarationInfo => ModuleIdentifier(declarationInfo.id, moduleName, declarationInfo.declarationLine, declarationInfo.operator))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private def createModuleIdentifier(name: String, moduleName: String, declaration: String) = {
|
/**
|
||||||
ModuleIdentifier(StringUtil.removeOuterParens(name), moduleName, declaration, isOperator = StringUtil.isWithinParens(name))
|
* @param name is without (operator) parentheses.
|
||||||
}
|
* @param preludeBaseModuleName is module name of the Prelude identifier where it's defined in base package.
|
||||||
}
|
*/
|
||||||
|
case class ModuleIdentifier(name: String, moduleName: String, declaration: String, operator: Boolean, preludeBaseModuleName: Option[String] = None)
|
||||||
// value of name is without (operator) parens
|
|
||||||
case class ModuleIdentifier(name: String, moduleName: String, declaration: String, isOperator: Boolean)
|
|
||||||
|
@ -18,20 +18,23 @@ package intellij.haskell.external.component
|
|||||||
|
|
||||||
import intellij.haskell.util.StringUtil
|
import intellij.haskell.util.StringUtil
|
||||||
|
|
||||||
object DeclarationLineUtil {
|
object DeclarationUtil {
|
||||||
|
|
||||||
def findName(declarationLine: String): Option[NameAndShortDeclaration] = {
|
def getDeclarationInfo(declarationLine: String, containsQualifiedIds: Boolean): Option[DeclarationInfo] = {
|
||||||
val declaration = StringUtil.shortenHaskellDeclaration(declarationLine)
|
val declaration = StringUtil.removeCommentsAndWhiteSpaces(declarationLine)
|
||||||
val allTokens = declaration.split("""\s+""")
|
val allTokens = declaration.split("""\s+""")
|
||||||
val name = if (allTokens.isEmpty || allTokens(0) == "--") {
|
(if (allTokens.isEmpty || allTokens(0) == "--") {
|
||||||
None
|
None
|
||||||
} else if (Seq("class", "instance").contains(allTokens(0))) {
|
} else if (Seq("class", "instance").contains(allTokens(0))) {
|
||||||
declaration.split("""where|=\s""").headOption.flatMap { d =>
|
declaration.split("""where|=\s""").headOption.flatMap { d =>
|
||||||
val tokens = d.trim.split("""=>""")
|
val tokens = d.trim.split("=>")
|
||||||
if (tokens.size == 1) {
|
val size = tokens.size
|
||||||
Option(allTokens(1))
|
if (size == 1) {
|
||||||
} else {
|
Option(tokens(0))
|
||||||
|
} else if (size > 1) {
|
||||||
Option(tokens.last.trim.split("""\s+""")(0))
|
Option(tokens.last.trim.split("""\s+""")(0))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (allTokens(0) == "type" && allTokens(1) == "role") {
|
} else if (allTokens(0) == "type" && allTokens(1) == "role") {
|
||||||
@ -39,17 +42,29 @@ object DeclarationLineUtil {
|
|||||||
} else if (Seq("data", "type", "newtype").contains(allTokens(0).trim)) {
|
} else if (Seq("data", "type", "newtype").contains(allTokens(0).trim)) {
|
||||||
Option(allTokens(1))
|
Option(allTokens(1))
|
||||||
} else {
|
} else {
|
||||||
val tokens = declaration.split("""::""")
|
val tokens = declaration.split("::")
|
||||||
if (tokens.size > 1) {
|
if (tokens.size > 1) {
|
||||||
val name = tokens(0).trim
|
val name = tokens(0).trim
|
||||||
Option(name)
|
Option(name)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
}).map(name => {
|
||||||
|
val operator = StringUtil.isWithinParens(name)
|
||||||
|
val id = if (operator) {
|
||||||
|
StringUtil.removeOuterParens(name)
|
||||||
|
} else {
|
||||||
|
name
|
||||||
}
|
}
|
||||||
name.map(n => NameAndShortDeclaration(StringUtil.removeOuterParens(n), declaration))
|
if (containsQualifiedIds) {
|
||||||
|
DeclarationInfo(StringUtil.removePackageModuleQualifier(id), Some(id), StringUtil.removePackageModuleQualifier(declaration), operator)
|
||||||
|
} else {
|
||||||
|
DeclarationInfo(id, None, declaration, operator)
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
case class DeclarationInfo(id: String, qualifiedId: Option[String], declarationLine: String, operator: Boolean)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case class NameAndShortDeclaration(name: String, declaration: String)
|
|
@ -27,8 +27,8 @@ import intellij.haskell.util._
|
|||||||
import intellij.haskell.util.index.HaskellModuleNameIndex._
|
import intellij.haskell.util.index.HaskellModuleNameIndex._
|
||||||
|
|
||||||
private[component] object DefinitionLocationComponent {
|
private[component] object DefinitionLocationComponent {
|
||||||
private final val LocAtPattern = """(.+)\:\(([\d]+),([\d]+)\)-\(([\d]+),([\d]+)\)""".r
|
private final val LocAtPattern = """(.+):\(([\d]+),([\d]+)\)-\(([\d]+),([\d]+)\)""".r
|
||||||
private final val PackageModulePattern = """([\w\-\d\.]+)(?:\-.*)?\:([\w\.\-]+)""".r
|
private final val PackageModulePattern = """([\w\-\d.]+)(?:-.*)?:([\w.\-]+)""".r
|
||||||
|
|
||||||
// importQualifier is only set for identifiers in import declarations
|
// importQualifier is only set for identifiers in import declarations
|
||||||
private case class Key(psiFile: PsiFile, qualifiedNameElement: HaskellQualifiedNameElement, importQualifier: Option[String])
|
private case class Key(psiFile: PsiFile, qualifiedNameElement: HaskellQualifiedNameElement, importQualifier: Option[String])
|
||||||
@ -76,16 +76,16 @@ private[component] object DefinitionLocationComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def invalidate(psiFile: PsiFile): Unit = {
|
def invalidate(psiFile: PsiFile): Unit = {
|
||||||
val keys = Cache.asMap().flatMap { case (k, v) =>
|
val keys = Cache.asMap().filter { case (k, v) =>
|
||||||
if (checkValidKey(k)) {
|
if (checkValidKey(k)) {
|
||||||
v.toOption match {
|
v.toOption match {
|
||||||
case Some(definitionLocation) if checkValidLocation(definitionLocation) && checkValidName(k, definitionLocation) => None
|
case Some(definitionLocation) if checkValidLocation(definitionLocation) && checkValidName(k, definitionLocation) => false
|
||||||
case _ => Some(k)
|
case _ => true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Some(k)
|
true
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}.keys
|
||||||
Cache.invalidateAll(keys)
|
Cache.invalidateAll(keys)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,7 +139,7 @@ private[component] object DefinitionLocationComponent {
|
|||||||
ProgressManager.checkCanceled()
|
ProgressManager.checkCanceled()
|
||||||
|
|
||||||
HaskellReference.findIdentifiersByNameInfo(info, key.qualifiedNameElement.getIdentifierElement, project) match {
|
HaskellReference.findIdentifiersByNameInfo(info, key.qualifiedNameElement.getIdentifierElement, project) match {
|
||||||
case Right((mn, ne, pn)) => Right(PackageModuleLocation(findModuleName(ne), ne, name, pn))
|
case Right((mn, ne, pn)) => Right(PackageModuleLocation(mn.getOrElse("-"), ne, name, pn))
|
||||||
case Left(noInfo) =>
|
case Left(noInfo) =>
|
||||||
HaskellReference.findIdentifierInFileByName(psiFile, name, prioIdInExpression = true).
|
HaskellReference.findIdentifierInFileByName(psiFile, name, prioIdInExpression = true).
|
||||||
map(ne => Right(PackageModuleLocation(findModuleName(ne), ne, name, None))).getOrElse(Left(noInfo))
|
map(ne => Right(PackageModuleLocation(findModuleName(ne), ne, name, None))).getOrElse(Left(noInfo))
|
||||||
@ -179,19 +179,17 @@ private[component] object DefinitionLocationComponent {
|
|||||||
case Some(q) => q + "." + qNameName
|
case Some(q) => q + "." + qNameName
|
||||||
}
|
}
|
||||||
|
|
||||||
val moduleNames = FileModuleIdentifiers.findAvailableModuleIdentifiers(psiFile).filter(_.name == qName).map(_.moduleName).toSeq
|
val moduleIdentifiers = FileModuleIdentifiers.findAvailableModuleIdentifiers(psiFile).filter(_.name == qName)
|
||||||
|
|
||||||
ProgressManager.checkCanceled()
|
ProgressManager.checkCanceled()
|
||||||
|
|
||||||
if (moduleNames.contains(HaskellProjectUtil.Prelude)) {
|
val moduleNames = moduleIdentifiers.map(mi => if (mi.moduleName == HaskellProjectUtil.Prelude) mi.preludeBaseModuleName.getOrElse(HaskellProjectUtil.Prelude) else mi.moduleName).toSeq
|
||||||
Left(ModuleNotAvailable("Prelude"))
|
|
||||||
} else {
|
HaskellReference.findIdentifiersByModulesAndName(project, moduleNames.filterNot(_ == HaskellProjectUtil.Prelude), name) match {
|
||||||
HaskellReference.findIdentifiersByModulesAndName(project, moduleNames, name) match {
|
|
||||||
case Right((mn, ne)) => Right(PackageModuleLocation(mn, ne, name, None))
|
case Right((mn, ne)) => Right(PackageModuleLocation(mn, ne, name, None))
|
||||||
case Left(noInfo) => Left(noInfo)
|
case Left(noInfo) => Left(noInfo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private def findLocationByRepl(project: Project, psiFile: PsiFile, moduleName: Option[String], key: Key, name: String, withoutLastColumn: Boolean): DefinitionLocationResult = {
|
private def findLocationByRepl(project: Project, psiFile: PsiFile, moduleName: Option[String], key: Key, name: String, withoutLastColumn: Boolean): DefinitionLocationResult = {
|
||||||
ProgressManager.checkCanceled()
|
ProgressManager.checkCanceled()
|
||||||
|
@ -157,7 +157,7 @@ object FileModuleIdentifiers {
|
|||||||
blocking {
|
blocking {
|
||||||
Future.sequence(importInfos.map(importInfo => {
|
Future.sequence(importInfos.map(importInfo => {
|
||||||
val allModuleIdentifiers = BrowseModuleComponent.findModuleIdentifiers(psiFile.getProject, importInfo.moduleName)
|
val allModuleIdentifiers = BrowseModuleComponent.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)))))
|
allModuleIdentifiers.map(ids => ids.map(is => createQualifiedModuleIdentifiers(importInfo, is.filter(mi => importInfo.ids.exists(_ == mi.name)))))
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -197,7 +197,7 @@ object NameInfoComponentResult {
|
|||||||
|
|
||||||
def declaration: String
|
def declaration: String
|
||||||
|
|
||||||
def shortenedDeclaration: String = StringUtil.shortenHaskellDeclaration(declaration)
|
def shortenedDeclaration: String = StringUtil.sanitizeDeclaration(declaration)
|
||||||
|
|
||||||
def escapedDeclaration: String = escapeString(declaration).replaceAll("""\s+""", " ")
|
def escapedDeclaration: String = escapeString(declaration).replaceAll("""\s+""", " ")
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ import com.intellij.psi.stubs.StubIndex
|
|||||||
import com.intellij.util.{ArrayUtil, Processor}
|
import com.intellij.util.{ArrayUtil, Processor}
|
||||||
import intellij.haskell.HaskellLanguage
|
import intellij.haskell.HaskellLanguage
|
||||||
import intellij.haskell.psi.stubs.index.HaskellAllNameIndex
|
import intellij.haskell.psi.stubs.index.HaskellAllNameIndex
|
||||||
import intellij.haskell.psi.{HaskellClassDeclaration, HaskellDeclarationElement, HaskellNamedElement, HaskellPsiUtil}
|
import intellij.haskell.psi.{HaskellNamedElement, HaskellPsiUtil}
|
||||||
import intellij.haskell.util.HaskellProjectUtil
|
import intellij.haskell.util.HaskellProjectUtil
|
||||||
|
|
||||||
import scala.collection.mutable.ListBuffer
|
import scala.collection.mutable.ListBuffer
|
||||||
@ -39,15 +39,7 @@ class GotoByDeclarationContributor extends GotoClassContributor {
|
|||||||
|
|
||||||
override def getItemsByName(name: String, pattern: String, project: Project, includeNonProjectItems: Boolean): Array[NavigationItem] = {
|
override def getItemsByName(name: String, pattern: String, project: Project, includeNonProjectItems: Boolean): Array[NavigationItem] = {
|
||||||
val namedElements = GotoHelper.getNamedElements(name, pattern, project, includeNonProjectItems)
|
val namedElements = GotoHelper.getNamedElements(name, pattern, project, includeNonProjectItems)
|
||||||
val declarationElements = namedElements.map(ne => (ne, HaskellPsiUtil.findHighestDeclarationElement(ne)))
|
namedElements.flatMap(ne => HaskellPsiUtil.findHighestDeclarationElement(ne)).toArray
|
||||||
declarationElements.sortWith(sortByClassDeclarationFirst).flatMap(_._2).toArray
|
|
||||||
}
|
|
||||||
|
|
||||||
private def sortByClassDeclarationFirst(namedAndDeclarationElement1: (HaskellNamedElement, Option[HaskellDeclarationElement]), namedAndDeclarationElement2: (HaskellNamedElement, Option[HaskellDeclarationElement])): Boolean = {
|
|
||||||
(namedAndDeclarationElement1._2, namedAndDeclarationElement2._2) match {
|
|
||||||
case (Some(_: HaskellClassDeclaration), _) => true
|
|
||||||
case (_, _) => false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override def getQualifiedName(item: NavigationItem): String = {
|
override def getQualifiedName(item: NavigationItem): String = {
|
||||||
|
@ -226,7 +226,7 @@ object HaskellReference {
|
|||||||
|
|
||||||
val identifiers = files.flatMap(f => findIdentifierInFileByName(f, name, prioIdInExpression))
|
val identifiers = files.flatMap(f => findIdentifierInFileByName(f, name, prioIdInExpression))
|
||||||
if (identifiers.isEmpty) {
|
if (identifiers.isEmpty) {
|
||||||
val importedModuleNames = files.flatMap(f => FileModuleIdentifiers.findAvailableModuleIdentifiers(f).filter(mid => mid.name == name || mid.name == "_" + name).map(_.moduleName))
|
val importedModuleNames = files.flatMap(f => FileModuleIdentifiers.findAvailableModuleIdentifiers(f).filter(mid => mid.name == name || mid.name == "_" + name || mid.name == mid.moduleName + "." + name).map(_.moduleName))
|
||||||
if (importedModuleNames.isEmpty) {
|
if (importedModuleNames.isEmpty) {
|
||||||
Seq()
|
Seq()
|
||||||
} else {
|
} else {
|
||||||
|
@ -26,8 +26,8 @@ import javax.swing.Icon
|
|||||||
|
|
||||||
class HoogleByNameContributor extends ChooseByNameContributor {
|
class HoogleByNameContributor extends ChooseByNameContributor {
|
||||||
|
|
||||||
private final val DeclarationPattern = """([\w\.\-]+) (.*)""".r
|
private final val DeclarationPattern = """([\w.\-]+) (.*)""".r
|
||||||
private final val ModulePattern = """module ([\w\.\-]+)""".r
|
private final val ModulePattern = """module ([\w.\-]+)""".r
|
||||||
private final val PackagePattern = """package (.*)""".r
|
private final val PackagePattern = """package (.*)""".r
|
||||||
|
|
||||||
override def getNames(project: Project, includeNonProjectItems: Boolean): Array[String] = {
|
override def getNames(project: Project, includeNonProjectItems: Boolean): Array[String] = {
|
||||||
@ -51,22 +51,22 @@ class HoogleByNameContributor extends ChooseByNameContributor {
|
|||||||
case Right(files) => files
|
case Right(files) => files
|
||||||
case _ => Seq()
|
case _ => Seq()
|
||||||
}
|
}
|
||||||
case PackagePattern(packageName) =>
|
case PackagePattern(_) =>
|
||||||
ProgressManager.checkCanceled()
|
ProgressManager.checkCanceled()
|
||||||
Seq()
|
Seq()
|
||||||
case DeclarationPattern(moduleName, declaration) =>
|
case DeclarationPattern(moduleName, declaration) =>
|
||||||
ProgressManager.checkCanceled()
|
ProgressManager.checkCanceled()
|
||||||
DeclarationLineUtil.findName(declaration) match {
|
DeclarationUtil.getDeclarationInfo(declaration, containsQualifiedIds = false) match {
|
||||||
case Some(nameAndShortDeclaration) =>
|
case Some(declarationInfo) =>
|
||||||
val identifiers = HaskellReference.findIdentifiersByModulesAndName(project, Seq(moduleName), nameAndShortDeclaration.name, prioIdInExpression = false).toOption.map(_._2).toSeq
|
val identifiers = HaskellReference.findIdentifiersByModulesAndName(project, Seq(moduleName), declarationInfo.id, prioIdInExpression = false).toOption.map(_._2).toSeq
|
||||||
if (identifiers.isEmpty) {
|
if (identifiers.isEmpty) {
|
||||||
Seq()
|
Seq(NotFoundNavigationItem(declaration, moduleName))
|
||||||
} else {
|
} else {
|
||||||
identifiers.map(e => HaskellPsiUtil.findDeclarationElement(e).getOrElse(e)).map(d => createLibraryNavigationItem(d, moduleName))
|
identifiers.map(e => HaskellPsiUtil.findDeclarationElement(e).getOrElse(e)).map(d => createLibraryNavigationItem(d, moduleName))
|
||||||
}
|
}
|
||||||
case None => Seq()
|
case None => Seq()
|
||||||
}
|
}
|
||||||
case d =>
|
case _ =>
|
||||||
ProgressManager.checkCanceled()
|
ProgressManager.checkCanceled()
|
||||||
Seq()
|
Seq()
|
||||||
}
|
}
|
||||||
@ -109,4 +109,23 @@ class HoogleByNameContributor extends ChooseByNameContributor {
|
|||||||
override def canNavigateToSource: Boolean = namedElement.canNavigateToSource
|
override def canNavigateToSource: Boolean = namedElement.canNavigateToSource
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case class NotFoundNavigationItem(declaration: String, moduleName: String) extends NavigationItem {
|
||||||
|
override def getName: String = declaration
|
||||||
|
|
||||||
|
override def getPresentation: ItemPresentation = new ItemPresentation {
|
||||||
|
override def getIcon(unused: Boolean): Icon = null
|
||||||
|
|
||||||
|
override def getLocationString: String = moduleName
|
||||||
|
|
||||||
|
override def getPresentableText: String = getName
|
||||||
|
}
|
||||||
|
|
||||||
|
override def canNavigateToSource: Boolean = false
|
||||||
|
|
||||||
|
override def canNavigate: Boolean = false
|
||||||
|
|
||||||
|
override def navigate(requestFocus: Boolean): Unit = ()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -22,13 +22,11 @@ import com.intellij.psi.{PsiElement, PsiReference}
|
|||||||
import com.intellij.util.ArrayUtil
|
import com.intellij.util.ArrayUtil
|
||||||
import icons.HaskellIcons
|
import icons.HaskellIcons
|
||||||
import intellij.haskell.HaskellFileType
|
import intellij.haskell.HaskellFileType
|
||||||
import intellij.haskell.psi.HaskellTypes._
|
|
||||||
import intellij.haskell.psi._
|
import intellij.haskell.psi._
|
||||||
import intellij.haskell.refactor.HaskellRenameFileProcessor
|
import intellij.haskell.refactor.HaskellRenameFileProcessor
|
||||||
import intellij.haskell.util.{HaskellFileUtil, StringUtil}
|
import intellij.haskell.util.StringUtil
|
||||||
import javax.swing._
|
import javax.swing._
|
||||||
|
|
||||||
import scala.annotation.tailrec
|
|
||||||
import scala.jdk.CollectionConverters._
|
import scala.jdk.CollectionConverters._
|
||||||
|
|
||||||
object HaskellPsiImplUtil {
|
object HaskellPsiImplUtil {
|
||||||
@ -224,12 +222,12 @@ object HaskellPsiImplUtil {
|
|||||||
|
|
||||||
new HaskellItemPresentation(declarationElement) {
|
new HaskellItemPresentation(declarationElement) {
|
||||||
def getPresentableText: String = {
|
def getPresentableText: String = {
|
||||||
getDeclarationInfo(declarationElement, shortened = true)
|
getDeclarationText(declarationElement)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def getItemPresentableText(element: PsiElement, shortened: Boolean = true): String = {
|
def getItemPresentableText(element: PsiElement): String = {
|
||||||
HaskellPsiUtil.findNamedElement(element) match {
|
HaskellPsiUtil.findNamedElement(element) match {
|
||||||
case Some(namedElement) =>
|
case Some(namedElement) =>
|
||||||
HaskellPsiUtil.findHighestDeclarationElement(element) match {
|
HaskellPsiUtil.findHighestDeclarationElement(element) match {
|
||||||
@ -244,39 +242,10 @@ object HaskellPsiImplUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private def getDeclarationInfo(declarationElement: HaskellDeclarationElement, shortened: Boolean): String = {
|
private def getDeclarationText(declarationElement: HaskellDeclarationElement): String = {
|
||||||
val info = declarationElement match {
|
declarationElement match {
|
||||||
case md: HaskellModuleDeclaration => s"module ${md.getModid.getName}"
|
case md: HaskellModuleDeclaration => s"module ${md.getModid.getName}"
|
||||||
case de if shortened => StringUtil.shortenHaskellDeclaration(de.getText)
|
case de => StringUtil.sanitizeDeclaration(de.getText)
|
||||||
case de => de.getText
|
|
||||||
}
|
|
||||||
if (shortened && info.length > 100) {
|
|
||||||
getFirstLineDeclarationText(declarationElement) + "..."
|
|
||||||
} else {
|
|
||||||
info
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private def getFirstLineDeclarationText(declarationElement: HaskellDeclarationElement) = {
|
|
||||||
StringUtil.removeCommentsAndWhiteSpaces(declarationElement.getNode.getChildren(null).takeWhile(n => n.getElementType != HS_WHERE && n.getElementType != HS_EQUAL).map(_.getText).mkString(" "))
|
|
||||||
}
|
|
||||||
|
|
||||||
private def getContainingLineText(namedElement: PsiElement) = {
|
|
||||||
val psiFile = namedElement.getContainingFile.getOriginalFile
|
|
||||||
for {
|
|
||||||
doc <- HaskellFileUtil.findDocument(psiFile)
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
@tailrec
|
|
||||||
def findNewline(psiElement: PsiElement, getSibling: PsiElement => PsiElement): PsiElement = {
|
|
||||||
Option(getSibling(psiElement)) match {
|
|
||||||
case None => psiElement
|
|
||||||
case Some(e) if e.getNode.getElementType == HS_NEWLINE => e
|
|
||||||
case Some(e) => findNewline(e, getSibling)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,23 +24,27 @@ import scala.collection.mutable.ListBuffer
|
|||||||
|
|
||||||
object StringUtil {
|
object StringUtil {
|
||||||
|
|
||||||
private final val PackageQualifierPattern = """([a-zA-Z\-]+\-[\.0-9]+\:)?([A-Z][\w\\\-]*\.)+"""
|
private final val PackageModuleQualifierPattern = """([a-zA-Z\-]+\-[\.0-9]+\:)?([A-Z][\w\\\-]*\.)+"""
|
||||||
private final val PackageQualifierPattern2 = """^([a-zA-Z\-]+\-[\.0-9]+\:)?"""
|
private final val PackageQualifierPattern = """^([a-zA-Z\-]+\-[\.0-9]+\:)?"""
|
||||||
|
|
||||||
def escapeString(s: String): String = {
|
def escapeString(s: String): String = {
|
||||||
XmlStringUtil.escapeString(s, false, false)
|
XmlStringUtil.escapeString(s, false, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
def removePackageQualifier(s: String): String = {
|
def removePackageModuleQualifier(s: String): String = {
|
||||||
s.replaceAll(PackageQualifierPattern2, "")
|
s.replaceAll(PackageModuleQualifierPattern, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
def shortenHaskellDeclaration(declaration: String): String = {
|
def removePackageQualifier(s: String): String = {
|
||||||
removeCommentsAndWhiteSpaces(declaration.replaceAll(PackageQualifierPattern, ""))
|
s.replaceAll(PackageQualifierPattern, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
def sanitizeDeclaration(declaration: String): String = {
|
||||||
|
removeCommentsAndWhiteSpaces(declaration.replaceAll(PackageModuleQualifierPattern, ""))
|
||||||
}
|
}
|
||||||
|
|
||||||
def removeCommentsAndWhiteSpaces(code: String): String = {
|
def removeCommentsAndWhiteSpaces(code: String): String = {
|
||||||
code.replaceAll("""\{\-[^\}]+\-\}""", " ").replaceAll("""\-\-.*""", " ").replaceAll("""\s+""", " ")
|
code.replaceAll("""\{-[^\}]+-\}""", " ").replaceAll("""--.*""", " ").replaceAll("""\s+""", " ")
|
||||||
}
|
}
|
||||||
|
|
||||||
def removeOuterParens(name: String): String = {
|
def removeOuterParens(name: String): String = {
|
||||||
|
Loading…
Reference in New Issue
Block a user