Let ChangesetBuilder.invalidated search even container elements (#6548)

This commit is contained in:
Jaroslav Tulach 2023-05-05 11:46:00 +02:00 committed by GitHub
parent 0578196623
commit a842130ff3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 138 additions and 19 deletions

View File

@ -194,8 +194,12 @@ final class ChangesetBuilder[A: TextEditor: IndexedSource](
else {
val edit = edits.dequeue()
val locationEdit = ChangesetBuilder.toLocationEdit(edit, source)
val invalidatedSet =
ChangesetBuilder.invalidated(tree, locationEdit.location)
var invalidatedSet =
ChangesetBuilder.invalidated(tree, locationEdit.location, true)
if (invalidatedSet.isEmpty) {
invalidatedSet =
ChangesetBuilder.invalidated(tree, locationEdit.location, false)
}
val newTree = ChangesetBuilder.updateLocations(tree, locationEdit)
val newSource = TextEditor[A].edit(source, edit)
go(newTree, newSource, edits, ids ++= invalidatedSet.map(_.id))
@ -278,7 +282,7 @@ object ChangesetBuilder {
* @param id the node id
* @param location the node location
*/
private case class Node(id: NodeId, location: Location) {
private case class Node(id: NodeId, location: Location, leaf: Boolean) {
/** Shift the node location.
*
@ -302,7 +306,7 @@ object ChangesetBuilder {
* @return the node if `ir` contains a location
*/
def fromIr(ir: IR): Option[Node] =
ir.location.map(loc => Node(NodeId(ir), loc.location))
ir.location.map(loc => Node(NodeId(ir), loc.location, true))
/** Create an artificial node with fixed [[NodeId]]. It is used to select
* nodes by location in the tree.
@ -311,7 +315,11 @@ object ChangesetBuilder {
* @return a select node
*/
def select(location: Location): Node =
new Node(NodeId(UUID.nameUUIDFromBytes(Array()), None, None), location)
new Node(
NodeId(UUID.nameUUIDFromBytes(Array()), None, None),
location,
false
)
implicit val ordering: Ordering[Node] = (x: Node, y: Node) => {
val compareStart =
@ -336,17 +344,55 @@ object ChangesetBuilder {
* @return the tree representation of the IR
*/
private def buildTree(ir: IR): Tree = {
@scala.annotation.tailrec
def go(input: mutable.Queue[IR], acc: Tree): Tree =
if (input.isEmpty) acc
else {
val ir = input.dequeue()
if (ir.children.isEmpty) {
Node.fromIr(ir).foreach(acc.add)
def depthFirstSearch(currentIr: IR, acc: Tree): Unit = {
if (currentIr.children.isEmpty) {
Node.fromIr(currentIr).foreach(acc.add)
} else {
val hasImportantId = currentIr.getExternalId.nonEmpty
if (hasImportantId) {
val collectChildrenIntervals = new Tree()
currentIr.children.map(depthFirstSearch(_, collectChildrenIntervals))
def fillGapsInChildrenNodesWithNonLeafNodes(
previousPosition: Int,
nextNode: Node
): Int = {
if (previousPosition < nextNode.location.start) {
val nodeBetweenPreviousPositionAndNextNode =
Node(
NodeId(currentIr),
Location(previousPosition, nextNode.location.start),
false
)
acc += nodeBetweenPreviousPositionAndNextNode
}
acc += nextNode
nextNode.location.end
}
val beginOfNonLeafIr = currentIr.location.get.location.start
val endOfNonLeafIr = currentIr.location.get.location.end
val lastCoveredPosition =
collectChildrenIntervals.foldLeft(beginOfNonLeafIr)(
fillGapsInChildrenNodesWithNonLeafNodes
)
val hasRemainingTextAfterLastChild =
lastCoveredPosition < endOfNonLeafIr
if (hasRemainingTextAfterLastChild) {
val nodeAfterLastChild = Node(
NodeId(currentIr),
Location(lastCoveredPosition, endOfNonLeafIr),
false
)
acc += nodeAfterLastChild
}
} else {
currentIr.children.map(depthFirstSearch(_, acc))
}
go(input ++= ir.children, acc)
}
go(mutable.Queue(ir), mutable.TreeSet())
}
val collectNodes = new Tree()
depthFirstSearch(ir, collectNodes)
collectNodes
}
/** Update the tree locations after applying the edit.
@ -371,12 +417,18 @@ object ChangesetBuilder {
* @param edit the location of the edit
* @return the invalidated nodes of the tree
*/
private def invalidated(tree: Tree, edit: Location): Tree = {
private def invalidated(
tree: Tree,
edit: Location,
onlyLeafs: Boolean
): Tree = {
val invalidated = mutable.TreeSet[ChangesetBuilder.Node]()
tree.iterator.foreach { node =>
if (intersect(edit, node)) {
invalidated += node
tree -= node
if (!onlyLeafs || node.leaf) {
if (intersect(edit, node)) {
invalidated += node
tree -= node
}
}
}
invalidated

View File

@ -375,10 +375,77 @@ class ChangesetBuilderTest extends CompilerTest {
)
}
"toggle defaulted boolean parameter" in {
implicit val moduleContext: ModuleContext = freshModuleContext
val code =
"""
|from Standard.Base import all
|from Standard.Base.Data.Boolean import Boolean
|import Standard.Visualization
|
|main =
| text2 = " B B B "
| text1 = "B"
| text3 = "@"
| operator2 = text2.replace text1 text3 (Case_Sensitivity.Insensitive)
|
|
|
|
|#### METADATA ####
|[[{"index":{"value":5},"size":{"value":8}},"6d97d061-2c1d-4540-8f7d-3d8aa765323d"],[{"index":{"value":13},"size":{"value":1}},"a6a1b572-4867-4317-8a6e-b496b286ea2d"],[{"index":{"value":14},"size":{"value":4}},"b82e52c6-e2f0-4e16-b835-59ab8838cb2c"],[{"index":{"value":5},"size":{"value":13}},"4dd74fbb-c6df-4109-8821-f4643d483355"],[{"index":{"value":0},"size":{"value":29}},"82014253-a4f5-431b-971b-2348f5a2ca1f"],[{"index":{"value":35},"size":{"value":8}},"b22aad40-a237-4d47-a140-930d731ca80e"],[{"index":{"value":43},"size":{"value":1}},"9d7f45f1-f537-480d-b65f-37a90c77455a"],[{"index":{"value":44},"size":{"value":4}},"c6ad377f-1fce-431d-bbd7-cfe88624c50d"],[{"index":{"value":35},"size":{"value":13}},"202d7fa4-c782-4bb8-900f-7bf5f9ebad99"],[{"index":{"value":48},"size":{"value":1}},"6847cb14-aa49-455d-be9d-e82a39460005"],[{"index":{"value":49},"size":{"value":4}},"36dcdde8-1667-495a-ab87-7154c8d3be67"],[{"index":{"value":35},"size":{"value":18}},"d67c4ce2-711a-4dd9-a1f8-b48f2730b141"],[{"index":{"value":53},"size":{"value":1}},"295fa6b1-260d-4f23-8bd6-64a1d60f01e5"],[{"index":{"value":54},"size":{"value":7}},"923a2769-ddc0-4339-b919-941155a4d1b7"],[{"index":{"value":35},"size":{"value":26}},"64ef6c8a-810e-42f4-8334-8b12c4ab14bc"],[{"index":{"value":69},"size":{"value":7}},"e8cf68da-2b41-4c70-b163-567a223459fd"],[{"index":{"value":30},"size":{"value":46}},"0b934140-6464-4a05-ae6c-416670e2c7e2"],[{"index":{"value":84},"size":{"value":8}},"eaa41303-7d43-420f-8d64-da8d376fab65"],[{"index":{"value":92},"size":{"value":1}},"d582d163-0d4b-4406-96a3-e4591766406d"],[{"index":{"value":93},"size":{"value":13}},"2649f663-b1b8-44a1-9158-f2cf23038471"],[{"index":{"value":84},"size":{"value":22}},"5b4e8628-e0b7-408e-8070-e5636514c7ac"],[{"index":{"value":77},"size":{"value":29}},"78558eb9-cfb0-41d1-8f9a-0f0c17db3b7c"],[{"index":{"value":108},"size":{"value":4}},"58c9224e-644d-40c1-be25-068b6b6dc947"],[{"index":{"value":113},"size":{"value":1}},"d01b8ec7-e780-40a3-b401-97e89e000484"],[{"index":{"value":120},"size":{"value":5}},"aa43682d-01ee-453d-9bcb-34c905d8d314"],[{"index":{"value":126},"size":{"value":1}},"2b5d6c3f-e030-4ee4-9102-b573f066efa0"],[{"index":{"value":128},"size":{"value":16}},"54493640-4981-4437-ad6e-fc80ab1b581f"],[{"index":{"value":120},"size":{"value":24}},"c04888d6-bc60-4534-aa7e-df3dd5d52410"],[{"index":{"value":149},"size":{"value":5}},"85ae1943-cfcf-4e53-97c7-f73fdae42529"],[{"index":{"value":155},"size":{"value":1}},"113ae47b-3a1d-497e-8a2a-b7775be6c66e"],[{"index":{"value":157},"size":{"value":3}},"c143e355-9d64-4780-b387-dd364eefa2f9"],[{"index":{"value":149},"size":{"value":11}},"73113bd8-d3cb-4b06-a4d7-425a88bca849"],[{"index":{"value":165},"size":{"value":5}},"a286047b-54a5-45bb-995e-60f207e2af65"],[{"index":{"value":171},"size":{"value":1}},"abace020-1a84-4fa4-a5ee-2f98f73dfcb1"],[{"index":{"value":173},"size":{"value":3}},"ed3e40fb-d19e-4fb6-bebd-f74ee179ae08"],[{"index":{"value":165},"size":{"value":11}},"8ac6594b-c472-41d5-9b00-f13e6f30d5f2"],[{"index":{"value":181},"size":{"value":9}},"25eaa42e-026d-4afd-a433-dde5804b7a5c"],[{"index":{"value":191},"size":{"value":1}},"799a83db-5893-40f7-9f1e-e07c7a3071cd"],[{"index":{"value":193},"size":{"value":5}},"49332623-fe9a-4fe5-9657-7c0ea2b23942"],[{"index":{"value":198},"size":{"value":1}},"7690e4c2-31e7-4a12-8f5c-78c4484b9553"],[{"index":{"value":199},"size":{"value":7}},"3606a45e-50be-49c5-9dda-415263145a15"],[{"index":{"value":193},"size":{"value":13}},"91640cfc-f985-49da-95c8-87181d725299"],[{"index":{"value":207},"size":{"value":5}},"afb24ea2-2f35-4c7e-8394-3ba34626484e"],[{"index":{"value":193},"size":{"value":19}},"86eb77c6-14cc-487a-8993-bf9998244321"],[{"index":{"value":213},"size":{"value":5}},"52a3e9b8-f8d0-43c9-9f6d-9785359c7ba7"],[{"index":{"value":193},"size":{"value":25}},"1cddcb6a-95c8-445b-9a6f-267093b2609c"],[{"index":{"value":220},"size":{"value":16}},"df114ed3-b64d-4e74-ad60-b43a7147ec19"],[{"index":{"value":236},"size":{"value":1}},"b6a14995-bb54-4956-9abd-9b8cff898ad3"],[{"index":{"value":237},"size":{"value":11}},"fdf942d8-8064-4067-ae38-131b80fa6e0c"],[{"index":{"value":220},"size":{"value":28}},"b8df7e14-9004-4f61-8076-09354f8dbd36"],[{"index":{"value":193},"size":{"value":56}},"0d4b3071-3f7d-472a-bfa7-af118222894e"],[{"index":{"value":181},"size":{"value":68}},"d9ee4c00-74b3-4859-a252-6c9105085076"],[{"index":{"value":115},"size":{"value":135}},"3eba123a-ea77-4db5-89d8-800d35e102e7"],[{"index":{"value":108},"size":{"value":142}},"11d6a77c-3644-4a27-8e15-16808af51c19"]]
|""".stripMargin.linesIterator.mkString("\n")
val edit =
TextEdit(Range(Position(9, 72), Position(9, 72)), " Boolean.True")
val ir = code.preprocessModule
val atId = "0d4b3071-3f7d-472a-bfa7-af118222894e"
val at = findIR(ir, atId)
val atCode = findCode(code, at)
atCode shouldBe "text2.replace text1 text3 (Case_Sensitivity.Insensitive)"
val all = new ChangesetBuilder(Rope(code), ir).invalidated(Seq(edit))
all
.map(n => n.externalId.getOrElse(n.internalId))
.map(findCode(code, ir, _)) should contain theSameElementsAs Seq(
atCode
)
}
}
def findIR(ir: IR, uuid: String): IR = {
val list = ir.preorder.filter(
_.location
.map(_.id.map(_.toString() == uuid).getOrElse(false))
.getOrElse(false)
)
if (list.isEmpty) {
null
} else {
list.head
}
}
def findCode(code: String, at: IR): String = {
val loc = at.location.get.location
code.substring(loc.start, loc.end)
}
def findCode(code: String, ir: IR, uuid: UUID): String = {
val at = findIR(ir, uuid.toString())
if (at == null) {
uuid.toString
} else {
findCode(code, at)
}
}
def invalidated(ir: IR, code: String, edits: TextEdit*): Set[IR.Identifier] =
new ChangesetBuilder(Rope(code), ir).invalidated(edits).map(_.internalId)
new ChangesetBuilder(Rope(code), ir)
.invalidated(edits)
.map(n => n.externalId.getOrElse(n.internalId))
def invalidatedAll(
ir: IR,