Skip to content

Commit

Permalink
Previously, rename was not supported in any way. User would have to m…
Browse files Browse the repository at this point in the history
…anually rename everything by hand and that could prove really tedious.

Now, we offer renaming of any symbol with a small number of exclusions:
- unapply
- equals
- hashcode

This also introduces finding parents of overriden symbols.
  • Loading branch information
tgodzik committed Nov 6, 2019
1 parent 60592c9 commit f1a8e69
Show file tree
Hide file tree
Showing 15 changed files with 995 additions and 59 deletions.
Expand Up @@ -24,9 +24,7 @@ private[implementation] case class ClassLocation(
asSeenFrom match {
case None => Some(parentASF)
case Some(childASF) =>
Some(parentASF.map {
case (key, value) => key -> childASF.getOrElse(value, value)
})
Some(AsSeenFrom.translateAsSeenFrom(childASF, parentASF))
}
}
this.copy(asSeenFrom = newASF)
Expand All @@ -39,22 +37,7 @@ private[implementation] case class ClassLocation(
): ClassLocation = {
classInfo.signature match {
case clsSig: ClassSignature =>
val newASF = for {
typeScope <- clsSig.typeParameters.toList
asf <- asSeenFrom.toList
(key, value) <- asf
} yield {
val translated = if (translateKey) key else value
Try(translated.toInt) match {
case Success(ind) if typeScope.symlinks.size > ind =>
if (translateKey)
typeScope.symlinks(ind).desc.name.toString() -> value
else
key -> typeScope.symlinks(ind).desc.name.toString()
case _ =>
key -> value
}
}
val newASF = AsSeenFrom.toRealNames(clsSig, translateKey, asSeenFrom)
ClassLocation(symbol, file, newASF.toMap)
case other => this
}
Expand All @@ -81,9 +64,20 @@ private[implementation] object ClassLocation {
typeRef: TypeRef,
typeParameters: Option[Scope]
): ClassLocation = {
val asSeenFrom = calculateAsSeenFrom(typeRef, typeParameters)
val asSeenFrom = AsSeenFrom.calculateAsSeenFrom(typeRef, typeParameters)
ClassLocation(symbol, file, asSeenFrom)
}
}
object AsSeenFrom {

def translateAsSeenFrom(
childASF: Map[String, String],
parentASF: Map[String, String]
): Map[String, String] = {
parentASF.map {
case (key, value) => key -> childASF.getOrElse(value, value)
}
}

def calculateAsSeenFrom(
parentType: TypeRef,
Expand All @@ -105,4 +99,30 @@ private[implementation] object ClassLocation {
case other => None
}.toMap
}

// Translate postion based names to real names in the class
def toRealNames(
classSig: ClassSignature,
translateKey: Boolean,
asSeenFrom: Option[Map[String, String]]
): Map[String, String] = {
val newASF = for {
typeScope <- classSig.typeParameters.toList
asf <- asSeenFrom.toList
(key, value) <- asf
} yield {
val translated = if (translateKey) key else value
Try(translated.toInt) match {
case Success(ind) if typeScope.symlinks.size > ind =>
if (translateKey)
typeScope.symlinks(ind).desc.name.toString() -> value
else
key -> typeScope.symlinks(ind).desc.name.toString()
case _ =>
key -> value
}
}
newASF.toMap
}

}
Expand Up @@ -88,26 +88,19 @@ final class ImplementationProvider(
val source = params.getTextDocument.getUri.toAbsolutePath
lazy val global = globalTable.globalSymbolTableFor(source)
val locations = for {
currentDocument <- findSemanticdb(source).toIterable
positionOccurrence = definitionProvider.positionOccurrence(
source,
params,
currentDocument
)
symbolOccurrence <- {
lazy val mtagsOccurrence = Mtags
.allToplevels(source.toInput)
.occurrences
.find(_.encloses(params.getPosition))
positionOccurrence.occurrence.orElse(mtagsOccurrence).toIterable
}
(symbolOccurrence, currentDocument) <- definitionProvider
.symbolOccurence(
source,
params
)
.toIterable
} yield {
// 1. Search locally for symbol
// 2. Search inside workspace
// 3. Search classpath via GlobalSymbolTable
def symbolSearch(symbol: String): Option[SymbolInformation] = {
findSymbol(currentDocument, symbol)
.orElse(findClassDef(symbol))
.orElse(findSymbolDef(symbol))
.orElse(global.flatMap(_.safeInfo(symbol)))
}
val sym = symbolOccurrence.symbol
Expand Down Expand Up @@ -147,6 +140,112 @@ final class ImplementationProvider(
locations.flatten.toList
}

def symbolParent(
symbol: String,
textDocument: TextDocument
): Seq[Location] = {

def findClassInfo(owner: String) = {
if (owner.nonEmpty) {
findSymbol(textDocument, owner)
} else {
textDocument.symbols.find {
case sym =>
sym.signature match {
case sig: ClassSignature =>
sig.declarations.exists(_.symlinks.contains(symbol))
case _ => false
}
}
}
}
val results = for {
currentInfo <- findSymbol(textDocument, symbol)
if (!isClassLike(currentInfo))
classInfo <- findClassInfo(symbol.owner)
} yield {
classInfo.signature match {
case sig: ClassSignature =>
methodInClassSignature(sig, currentInfo)
case _ => Nil
}
}
results.getOrElse(Seq.empty)
}

private def methodInClassSignature(
sig: ClassSignature,
childInfo: SymbolInformation,
childASF: Map[String, String] = Map.empty
): Seq[Location] = {
sig.parents.flatMap {
case parentSym: TypeRef =>
val parentTextDocument = findSemanticDbForSymbol(parentSym.symbol)
def search(symbol: String) =
parentTextDocument.flatMap(findSymbol(_, symbol))
val parentASF =
AsSeenFrom.calculateAsSeenFrom(parentSym, sig.typeParameters)
val asSeenFrom = AsSeenFrom.translateAsSeenFrom(childASF, parentASF)
search(parentSym.symbol).map(_.signature) match {
case Some(parenClassSig: ClassSignature) =>
val fromParent = methodInClassSignature(
parenClassSig,
childInfo,
asSeenFrom
)
if (fromParent.isEmpty) {
locationFromCurrent(
childInfo,
sig,
parenClassSig,
asSeenFrom,
search,
parentTextDocument
)
} else {
fromParent
}
case _ => Nil
}

case _ => Nil
}
}

private def locationFromCurrent(
childInfo: SymbolInformation,
sig: ClassSignature,
parenClassSig: ClassSignature,
asSeenFrom: Map[String, String],
search: String => Option[SymbolInformation],
parentTextDocument: Option[TextDocument]
) = {
val foundSymbol = MethodImplementation.findParent(
childInfo,
sig,
parenClassSig,
asSeenFrom,
search
)
for {
symbol <- foundSymbol
parentDoc <- parentTextDocument
source = workspace.resolve(parentDoc.uri)
implOccurrence <- findDefOccurrence(
parentDoc,
symbol,
source
)
range <- implOccurrence.range
distance = TokenEditDistance.fromBuffer(
source,
parentDoc.text,
buffer
)
revised <- distance.toRevised(range.toLSP)
} yield new Location(source.toNIO.toUri().toString(), revised)
}

private def symbolLocationsFromContext(
symbol: String,
source: AbsolutePath,
Expand All @@ -166,10 +265,10 @@ final class ImplementationProvider(
lazy val global = globalTable.globalSymbolTableFor(source)
def localSearch(symbol: String): Option[SymbolInformation] = {
findSymbol(implDocument, symbol)
.orElse(findClassDef(symbol))
.orElse(findSymbolDef(symbol))
.orElse(global.flatMap(_.safeInfo(symbol)))
}
MethodImplementation.find(
MethodImplementation.findInherited(
parentSymbolInfo,
symbolClass,
classContext,
Expand Down Expand Up @@ -233,7 +332,7 @@ final class ImplementationProvider(
}
}

private def findClassDef(symbol: String): Option[SymbolInformation] = {
private def findSymbolDef(symbol: String): Option[SymbolInformation] = {
findSemanticDbForSymbol(symbol).flatMap(findSymbol(_, symbol))
}

Expand Down Expand Up @@ -415,10 +514,14 @@ object ImplementationProvider {
findSymbol: String => Option[SymbolInformation]
): MethodSignature = {
val allParams = signature.parameterLists.map { scope =>
val hardlinks = scope.symlinks.flatMap { sym =>
findSymbol(sym)
if (scope.symlinks.size > scope.hardlinks.size) {
val hardlinks = scope.symlinks.flatMap { sym =>
findSymbol(sym)
}
scope.copy(hardlinks = hardlinks)
} else {
scope
}
scope.copy(hardlinks = hardlinks)
}
signature.copy(parameterLists = allParams)
}
Expand Down
Expand Up @@ -28,7 +28,35 @@ object MethodImplementation {

import ImplementationProvider._

def find(
def findParent(
childSymbol: SymbolInformation,
childClassSig: ClassSignature,
parentClassSig: ClassSignature,
asSeenFromMap: Map[String, String],
findSymbol: String => Option[SymbolInformation]
): Option[String] = {
val validMethods = for {
declarations <- parentClassSig.declarations.toIterable
methodSymbol <- declarations.symlinks
methodSymbolInfo <- findSymbol(methodSymbol)
asSeenFrom = AsSeenFrom.toRealNames(
parentClassSig,
translateKey = true,
Some(asSeenFromMap)
)
context = Context(
findSymbol,
findSymbol,
asSeenFrom
)
if isOverridenMethod(methodSymbolInfo, childSymbol, findParent = true)(
context
)
} yield methodSymbol
validMethods.headOption
}

def findInherited(
parentSymbol: SymbolInformation,
parentClassSymbol: SymbolInformation,
inheritanceContext: InheritanceContext,
Expand All @@ -43,16 +71,6 @@ object MethodImplementation {
.asSeenFromMap
}

def isOverridenMethod(
methodSymbolInfo: SymbolInformation
)(implicit context: Context): Boolean = {
(methodSymbolInfo.kind.isField || methodSymbolInfo.kind.isMethod) &&
methodSymbolInfo.displayName == parentSymbol.displayName &&
signaturesEqual(parentSymbol.signature, methodSymbolInfo.signature)(
context
)
}

val validMethods = for {
symbolInfo <- classSymbolInfo.toIterable
if symbolInfo.signature.isInstanceOf[ClassSignature]
Expand All @@ -66,11 +84,35 @@ object MethodImplementation {
inheritanceContext.findSymbol,
asSeenFrom
)
if isOverridenMethod(methodSymbolInfo)(context)
if isOverridenMethod(methodSymbolInfo, parentSymbol)(context)
} yield methodSymbol
validMethods.headOption
}

private def isOverridenMethod(
methodSymbolInfo: SymbolInformation,
otherSymbol: SymbolInformation,
findParent: Boolean = false
)(implicit context: Context): Boolean = {
val isVisiblySame = (methodSymbolInfo.kind.isField || methodSymbolInfo.kind.isMethod) &&
methodSymbolInfo.displayName == otherSymbol.displayName
if (findParent) {
isVisiblySame && signaturesEqual(
methodSymbolInfo.signature,
otherSymbol.signature
)(
context
)
} else {
isVisiblySame && signaturesEqual(
otherSymbol.signature,
methodSymbolInfo.signature
)(
context
)
}
}

private def symbolsAreEqual(
symParent: String,
symChild: String
Expand Down Expand Up @@ -169,11 +211,13 @@ object MethodImplementation {
)
val returnTypesEqual =
typesAreEqual(sig1.returnType, sig2.returnType)(newContext)
lazy val enrichedSig =
lazy val enrichedSig1 =
addParameterSignatures(sig1, context.findSymbol)
lazy val enrichedSig2 =
addParameterSignatures(sig2, context.findSymbol)
returnTypesEqual && paramsAreEqual(
sig1.parameterLists,
enrichedSig.parameterLists
enrichedSig1.parameterLists,
enrichedSig2.parameterLists
)(newContext)
case (v1: ValueSignature, v2: ValueSignature) =>
typesAreEqual(v1.tpe, v2.tpe)
Expand Down
Expand Up @@ -11,4 +11,5 @@ case class Buffers(map: TrieMap[AbsolutePath, String] = TrieMap.empty) {
def put(key: AbsolutePath, value: String): Unit = map.put(key, value)
def get(key: AbsolutePath): Option[String] = map.get(key)
def remove(key: AbsolutePath): Unit = map.remove(key)
def contains(key: AbsolutePath): Boolean = map.contains(key)
}

0 comments on commit f1a8e69

Please sign in to comment.