mirror of
https://github.com/ilyakooo0/intellij-haskell.git
synced 2024-08-17 02:30:42 +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 = {
|
||||
addWiths(LookupElementBuilder.create(
|
||||
if (moduleIdentifier.isOperator && addParens)
|
||||
if (moduleIdentifier.operator && addParens)
|
||||
s"""(${moduleIdentifier.name})"""
|
||||
else
|
||||
moduleIdentifier.name
|
||||
|
@ -21,8 +21,9 @@ import com.intellij.openapi.fileEditor.FileEditorManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiFile
|
||||
import intellij.haskell.external.repl._
|
||||
import intellij.haskell.psi.HaskellPsiUtil
|
||||
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}
|
||||
|
||||
@ -30,7 +31,6 @@ private[component] object BrowseModuleComponent {
|
||||
|
||||
private case class Key(project: Project, moduleName: String)
|
||||
|
||||
type BrowseModuleResult = Iterable[ModuleIdentifier]
|
||||
private type BrowseModuleInternalResult = Either[NoInfo, Iterable[ModuleIdentifier]]
|
||||
|
||||
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 {
|
||||
case Right(ids) => Some(ids)
|
||||
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 = {
|
||||
val key = Key(project, moduleName)
|
||||
Cache.synchronous().get(key)
|
||||
@ -104,11 +101,11 @@ private[component] object BrowseModuleComponent {
|
||||
findInRepl(project, projectRepl, moduleName, Some(moduleFile))
|
||||
}
|
||||
} else {
|
||||
findLibraryModuleIdentifiers(project, moduleName)
|
||||
findLibraryModuleIdentifiers(project, Some(moduleFile), moduleName)
|
||||
}
|
||||
case None =>
|
||||
// E.g. module name is Prelude which does not refer to file
|
||||
findLibraryModuleIdentifiers(project, moduleName)
|
||||
findLibraryModuleIdentifiers(project, None, moduleName)
|
||||
}
|
||||
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]] = {
|
||||
projectRepl match {
|
||||
case Some(repl) =>
|
||||
if (!repl.available) {
|
||||
Left(ReplNotAvailable)
|
||||
} else {
|
||||
if (repl.available) {
|
||||
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))
|
||||
}
|
||||
} else {
|
||||
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 {
|
||||
case Some(repl) =>
|
||||
if (!repl.available) {
|
||||
Left(ReplNotAvailable)
|
||||
} else {
|
||||
repl.getModuleIdentifiers(moduleName) match {
|
||||
case Some(o) if o.stderrLines.isEmpty && o.stdoutLines.nonEmpty => Right(o.stdoutLines.flatMap(l => findModuleIdentifiers(project, l, moduleName).toSeq))
|
||||
if (repl.available) {
|
||||
(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 _ => Left(ModuleNotAvailable(moduleName))
|
||||
}
|
||||
} else {
|
||||
Left(ReplNotAvailable)
|
||||
}
|
||||
case None => Left(ReplNotAvailable)
|
||||
}
|
||||
}
|
||||
|
||||
// This kind of declarations are returned in case DuplicateRecordFields are enabled
|
||||
private final val Module$SelPattern =
|
||||
"""([\w\.\-]+)\.\$sel:([^:]+)(?::[^:]+)?::(.*)""".r
|
||||
private final val Module$SelPattern = """([\w.\-]+)\.\$sel:([^:]+)(?::[^:]+)?::(.*)""".r
|
||||
|
||||
private def findModuleIdentifiers(project: Project, declarationLine: String, moduleName: String): Option[ModuleIdentifier] = {
|
||||
declarationLine match {
|
||||
case Module$SelPattern(mn, name, fieldType) => Some(createModuleIdentifier(name, mn, s"$name :: $fieldType"))
|
||||
case _ => DeclarationLineUtil.findName(declarationLine) map (nd => createModuleIdentifier(nd.name, moduleName, nd.declaration))
|
||||
}
|
||||
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 createModuleIdentifier(name: String, moduleName: String, declaration: String) = {
|
||||
ModuleIdentifier(StringUtil.removeOuterParens(name), moduleName, declaration, isOperator = StringUtil.isWithinParens(name))
|
||||
private def createProjectModuleIdentifier(project: Project, declarationLine: String, moduleName: String): Option[ModuleIdentifier] = {
|
||||
declarationLine match {
|
||||
case Module$SelPattern(mn, id, fieldType) => Some(ModuleIdentifier(id, mn, s"$id :: $fieldType", StringUtil.isWithinParens(id)))
|
||||
case _ => DeclarationUtil.getDeclarationInfo(declarationLine, containsQualifiedIds = false).
|
||||
map(declarationInfo => ModuleIdentifier(declarationInfo.id, moduleName, declarationInfo.declarationLine, declarationInfo.operator))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// value of name is without (operator) parens
|
||||
case class ModuleIdentifier(name: String, moduleName: String, declaration: String, isOperator: Boolean)
|
||||
/**
|
||||
* @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)
|
||||
|
@ -18,20 +18,23 @@ package intellij.haskell.external.component
|
||||
|
||||
import intellij.haskell.util.StringUtil
|
||||
|
||||
object DeclarationLineUtil {
|
||||
object DeclarationUtil {
|
||||
|
||||
def findName(declarationLine: String): Option[NameAndShortDeclaration] = {
|
||||
val declaration = StringUtil.shortenHaskellDeclaration(declarationLine)
|
||||
def getDeclarationInfo(declarationLine: String, containsQualifiedIds: Boolean): Option[DeclarationInfo] = {
|
||||
val declaration = StringUtil.removeCommentsAndWhiteSpaces(declarationLine)
|
||||
val allTokens = declaration.split("""\s+""")
|
||||
val name = if (allTokens.isEmpty || allTokens(0) == "--") {
|
||||
(if (allTokens.isEmpty || allTokens(0) == "--") {
|
||||
None
|
||||
} else if (Seq("class", "instance").contains(allTokens(0))) {
|
||||
declaration.split("""where|=\s""").headOption.flatMap { d =>
|
||||
val tokens = d.trim.split("""=>""")
|
||||
if (tokens.size == 1) {
|
||||
Option(allTokens(1))
|
||||
} else {
|
||||
val tokens = d.trim.split("=>")
|
||||
val size = tokens.size
|
||||
if (size == 1) {
|
||||
Option(tokens(0))
|
||||
} else if (size > 1) {
|
||||
Option(tokens.last.trim.split("""\s+""")(0))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
} else if (allTokens(0) == "type" && allTokens(1) == "role") {
|
||||
@ -39,17 +42,29 @@ object DeclarationLineUtil {
|
||||
} else if (Seq("data", "type", "newtype").contains(allTokens(0).trim)) {
|
||||
Option(allTokens(1))
|
||||
} else {
|
||||
val tokens = declaration.split("""::""")
|
||||
val tokens = declaration.split("::")
|
||||
if (tokens.size > 1) {
|
||||
val name = tokens(0).trim
|
||||
Option(name)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
name.map(n => NameAndShortDeclaration(StringUtil.removeOuterParens(n), declaration))
|
||||
}).map(name => {
|
||||
val operator = StringUtil.isWithinParens(name)
|
||||
val id = if (operator) {
|
||||
StringUtil.removeOuterParens(name)
|
||||
} else {
|
||||
name
|
||||
}
|
||||
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._
|
||||
|
||||
private[component] object DefinitionLocationComponent {
|
||||
private final val LocAtPattern = """(.+)\:\(([\d]+),([\d]+)\)-\(([\d]+),([\d]+)\)""".r
|
||||
private final val PackageModulePattern = """([\w\-\d\.]+)(?:\-.*)?\:([\w\.\-]+)""".r
|
||||
private final val LocAtPattern = """(.+):\(([\d]+),([\d]+)\)-\(([\d]+),([\d]+)\)""".r
|
||||
private final val PackageModulePattern = """([\w\-\d.]+)(?:-.*)?:([\w.\-]+)""".r
|
||||
|
||||
// importQualifier is only set for identifiers in import declarations
|
||||
private case class Key(psiFile: PsiFile, qualifiedNameElement: HaskellQualifiedNameElement, importQualifier: Option[String])
|
||||
@ -76,16 +76,16 @@ private[component] object DefinitionLocationComponent {
|
||||
}
|
||||
|
||||
def invalidate(psiFile: PsiFile): Unit = {
|
||||
val keys = Cache.asMap().flatMap { case (k, v) =>
|
||||
val keys = Cache.asMap().filter { case (k, v) =>
|
||||
if (checkValidKey(k)) {
|
||||
v.toOption match {
|
||||
case Some(definitionLocation) if checkValidLocation(definitionLocation) && checkValidName(k, definitionLocation) => None
|
||||
case _ => Some(k)
|
||||
case Some(definitionLocation) if checkValidLocation(definitionLocation) && checkValidName(k, definitionLocation) => false
|
||||
case _ => true
|
||||
}
|
||||
} else {
|
||||
Some(k)
|
||||
true
|
||||
}
|
||||
}
|
||||
}.keys
|
||||
Cache.invalidateAll(keys)
|
||||
}
|
||||
|
||||
@ -139,7 +139,7 @@ private[component] object DefinitionLocationComponent {
|
||||
ProgressManager.checkCanceled()
|
||||
|
||||
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) =>
|
||||
HaskellReference.findIdentifierInFileByName(psiFile, name, prioIdInExpression = true).
|
||||
map(ne => Right(PackageModuleLocation(findModuleName(ne), ne, name, None))).getOrElse(Left(noInfo))
|
||||
@ -179,17 +179,15 @@ private[component] object DefinitionLocationComponent {
|
||||
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()
|
||||
|
||||
if (moduleNames.contains(HaskellProjectUtil.Prelude)) {
|
||||
Left(ModuleNotAvailable("Prelude"))
|
||||
} else {
|
||||
HaskellReference.findIdentifiersByModulesAndName(project, moduleNames, name) match {
|
||||
case Right((mn, ne)) => Right(PackageModuleLocation(mn, ne, name, None))
|
||||
case Left(noInfo) => Left(noInfo)
|
||||
}
|
||||
val moduleNames = moduleIdentifiers.map(mi => if (mi.moduleName == HaskellProjectUtil.Prelude) mi.preludeBaseModuleName.getOrElse(HaskellProjectUtil.Prelude) else mi.moduleName).toSeq
|
||||
|
||||
HaskellReference.findIdentifiersByModulesAndName(project, moduleNames.filterNot(_ == HaskellProjectUtil.Prelude), name) match {
|
||||
case Right((mn, ne)) => Right(PackageModuleLocation(mn, ne, name, None))
|
||||
case Left(noInfo) => Left(noInfo)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -157,7 +157,7 @@ object FileModuleIdentifiers {
|
||||
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(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 shortenedDeclaration: String = StringUtil.shortenHaskellDeclaration(declaration)
|
||||
def shortenedDeclaration: String = StringUtil.sanitizeDeclaration(declaration)
|
||||
|
||||
def escapedDeclaration: String = escapeString(declaration).replaceAll("""\s+""", " ")
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ import com.intellij.psi.stubs.StubIndex
|
||||
import com.intellij.util.{ArrayUtil, Processor}
|
||||
import intellij.haskell.HaskellLanguage
|
||||
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 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] = {
|
||||
val namedElements = GotoHelper.getNamedElements(name, pattern, project, includeNonProjectItems)
|
||||
val declarationElements = namedElements.map(ne => (ne, HaskellPsiUtil.findHighestDeclarationElement(ne)))
|
||||
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
|
||||
}
|
||||
namedElements.flatMap(ne => HaskellPsiUtil.findHighestDeclarationElement(ne)).toArray
|
||||
}
|
||||
|
||||
override def getQualifiedName(item: NavigationItem): String = {
|
||||
|
@ -226,7 +226,7 @@ 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).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) {
|
||||
Seq()
|
||||
} else {
|
||||
|
@ -26,8 +26,8 @@ import javax.swing.Icon
|
||||
|
||||
class HoogleByNameContributor extends ChooseByNameContributor {
|
||||
|
||||
private final val DeclarationPattern = """([\w\.\-]+) (.*)""".r
|
||||
private final val ModulePattern = """module ([\w\.\-]+)""".r
|
||||
private final val DeclarationPattern = """([\w.\-]+) (.*)""".r
|
||||
private final val ModulePattern = """module ([\w.\-]+)""".r
|
||||
private final val PackagePattern = """package (.*)""".r
|
||||
|
||||
override def getNames(project: Project, includeNonProjectItems: Boolean): Array[String] = {
|
||||
@ -51,22 +51,22 @@ class HoogleByNameContributor extends ChooseByNameContributor {
|
||||
case Right(files) => files
|
||||
case _ => Seq()
|
||||
}
|
||||
case PackagePattern(packageName) =>
|
||||
case PackagePattern(_) =>
|
||||
ProgressManager.checkCanceled()
|
||||
Seq()
|
||||
case DeclarationPattern(moduleName, declaration) =>
|
||||
ProgressManager.checkCanceled()
|
||||
DeclarationLineUtil.findName(declaration) match {
|
||||
case Some(nameAndShortDeclaration) =>
|
||||
val identifiers = HaskellReference.findIdentifiersByModulesAndName(project, Seq(moduleName), nameAndShortDeclaration.name, prioIdInExpression = false).toOption.map(_._2).toSeq
|
||||
DeclarationUtil.getDeclarationInfo(declaration, containsQualifiedIds = false) match {
|
||||
case Some(declarationInfo) =>
|
||||
val identifiers = HaskellReference.findIdentifiersByModulesAndName(project, Seq(moduleName), declarationInfo.id, prioIdInExpression = false).toOption.map(_._2).toSeq
|
||||
if (identifiers.isEmpty) {
|
||||
Seq()
|
||||
Seq(NotFoundNavigationItem(declaration, moduleName))
|
||||
} else {
|
||||
identifiers.map(e => HaskellPsiUtil.findDeclarationElement(e).getOrElse(e)).map(d => createLibraryNavigationItem(d, moduleName))
|
||||
}
|
||||
case None => Seq()
|
||||
}
|
||||
case d =>
|
||||
case _ =>
|
||||
ProgressManager.checkCanceled()
|
||||
Seq()
|
||||
}
|
||||
@ -109,4 +109,23 @@ class HoogleByNameContributor extends ChooseByNameContributor {
|
||||
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 icons.HaskellIcons
|
||||
import intellij.haskell.HaskellFileType
|
||||
import intellij.haskell.psi.HaskellTypes._
|
||||
import intellij.haskell.psi._
|
||||
import intellij.haskell.refactor.HaskellRenameFileProcessor
|
||||
import intellij.haskell.util.{HaskellFileUtil, StringUtil}
|
||||
import intellij.haskell.util.StringUtil
|
||||
import javax.swing._
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import scala.jdk.CollectionConverters._
|
||||
|
||||
object HaskellPsiImplUtil {
|
||||
@ -224,12 +222,12 @@ object HaskellPsiImplUtil {
|
||||
|
||||
new HaskellItemPresentation(declarationElement) {
|
||||
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 {
|
||||
case Some(namedElement) =>
|
||||
HaskellPsiUtil.findHighestDeclarationElement(element) match {
|
||||
@ -244,39 +242,10 @@ object HaskellPsiImplUtil {
|
||||
}
|
||||
}
|
||||
|
||||
private def getDeclarationInfo(declarationElement: HaskellDeclarationElement, shortened: Boolean): String = {
|
||||
val info = declarationElement match {
|
||||
private def getDeclarationText(declarationElement: HaskellDeclarationElement): String = {
|
||||
declarationElement match {
|
||||
case md: HaskellModuleDeclaration => s"module ${md.getModid.getName}"
|
||||
case de if shortened => StringUtil.shortenHaskellDeclaration(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)
|
||||
case de => StringUtil.sanitizeDeclaration(de.getText)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,23 +24,27 @@ import scala.collection.mutable.ListBuffer
|
||||
|
||||
object StringUtil {
|
||||
|
||||
private final val PackageQualifierPattern = """([a-zA-Z\-]+\-[\.0-9]+\:)?([A-Z][\w\\\-]*\.)+"""
|
||||
private final val PackageQualifierPattern2 = """^([a-zA-Z\-]+\-[\.0-9]+\:)?"""
|
||||
private final val PackageModuleQualifierPattern = """([a-zA-Z\-]+\-[\.0-9]+\:)?([A-Z][\w\\\-]*\.)+"""
|
||||
private final val PackageQualifierPattern = """^([a-zA-Z\-]+\-[\.0-9]+\:)?"""
|
||||
|
||||
def escapeString(s: String): String = {
|
||||
XmlStringUtil.escapeString(s, false, false)
|
||||
}
|
||||
|
||||
def removePackageQualifier(s: String): String = {
|
||||
s.replaceAll(PackageQualifierPattern2, "")
|
||||
def removePackageModuleQualifier(s: String): String = {
|
||||
s.replaceAll(PackageModuleQualifierPattern, "")
|
||||
}
|
||||
|
||||
def shortenHaskellDeclaration(declaration: String): String = {
|
||||
removeCommentsAndWhiteSpaces(declaration.replaceAll(PackageQualifierPattern, ""))
|
||||
def removePackageQualifier(s: String): String = {
|
||||
s.replaceAll(PackageQualifierPattern, "")
|
||||
}
|
||||
|
||||
def sanitizeDeclaration(declaration: String): String = {
|
||||
removeCommentsAndWhiteSpaces(declaration.replaceAll(PackageModuleQualifierPattern, ""))
|
||||
}
|
||||
|
||||
def removeCommentsAndWhiteSpaces(code: String): String = {
|
||||
code.replaceAll("""\{\-[^\}]+\-\}""", " ").replaceAll("""\-\-.*""", " ").replaceAll("""\s+""", " ")
|
||||
code.replaceAll("""\{-[^\}]+-\}""", " ").replaceAll("""--.*""", " ").replaceAll("""\s+""", " ")
|
||||
}
|
||||
|
||||
def removeOuterParens(name: String): String = {
|
||||
|
Loading…
Reference in New Issue
Block a user