Skip to content

Commit 67bcb1a

Browse files
committed
filter duplicate CC menu entries
When there are nested binary expressions show only side transformations for one of the expressions.
1 parent d0d120e commit 67bcb1a

File tree

4 files changed

+64
-10
lines changed

4 files changed

+64
-10
lines changed

editor-runtime/src/commonMain/kotlin/org/modelix/editor/CaretSelection.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ class CaretSelection(val layoutable: LayoutableCell, val start: Int, val end: In
148148
val actions = providers.flatMap { it.flattenApplicableActions(params) }
149149
val matchingActions = actions
150150
.filter { it.getMatchingText().startsWith(typedText) }
151+
.applyShadowing()
151152
if (matchingActions.isNotEmpty()) {
152153
if (matchingActions.size == 1 && matchingActions.first().getMatchingText() == typedText) {
153154
matchingActions.first().execute()

editor-runtime/src/commonMain/kotlin/org/modelix/editor/CellTemplate.kt

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,7 @@ import org.modelix.model.api.INode
1414
import org.modelix.model.api.IProperty
1515
import org.modelix.model.api.addNewChild
1616
import org.modelix.model.api.getChildren
17-
import org.modelix.model.api.getContainmentLink
1817
import org.modelix.model.api.getReferenceTarget
19-
import org.modelix.model.api.index
2018
import org.modelix.model.api.moveChild
2119
import org.modelix.model.api.setPropertyValue
2220
import org.modelix.model.api.setReferenceTarget
@@ -135,16 +133,28 @@ class ConstantCellTemplate<NodeT : ITypedNode, ConceptT : ITypedConcept>(concept
135133
}
136134

137135
override fun createWrapperAction(nodeToWrap: INode, wrappingLink: IChildLink): List<ICodeCompletionAction> {
138-
return listOf(WrapperAction(nodeToWrap, wrappingLink))
136+
return listOf(SideTransformWrapper(nodeToWrap.toNonExisting(), wrappingLink))
139137
}
140138

141-
inner class WrapperAction(val nodeToWrap: INode, val wrappingLink: IChildLink) : ICodeCompletionAction {
139+
inner class SideTransformWrapper(val nodeToWrap: INonExistingNode, val wrappingLink: IChildLink) : ICodeCompletionAction {
142140
override fun getMatchingText(): String = text
143141
override fun getDescription(): String = concept.getShortName()
144142
override fun execute() {
145-
val wrapper = nodeToWrap.parent!!.addNewChild(nodeToWrap.getContainmentLink()!!, nodeToWrap.index(), concept)
146-
wrapper.moveChild(wrappingLink, 0, nodeToWrap)
143+
val wrapper = nodeToWrap.getParent()!!.getOrCreateNode(null).addNewChild(nodeToWrap.getContainmentLink()!!, nodeToWrap.index(), concept)
144+
wrapper.moveChild(wrappingLink, 0, nodeToWrap.getOrCreateNode(null))
147145
}
146+
147+
override fun shadows(shadowed: ICodeCompletionAction): Boolean {
148+
if (shadowed !is ConstantCellTemplate<*, *>.SideTransformWrapper) return false
149+
if (shadowed.getTemplate().concept != concept) return false
150+
val commonAncestor = nodeToWrap.commonAncestor(shadowed.nodeToWrap)
151+
val ownDepth = nodeToWrap.ancestors(true).takeWhile { it != commonAncestor }.count()
152+
val otherDepth = shadowed.nodeToWrap.ancestors(true).takeWhile { it != commonAncestor }.count()
153+
if (ownDepth > otherDepth) return true
154+
return false
155+
}
156+
157+
fun getTemplate() = this@ConstantCellTemplate
148158
}
149159

150160
inner class InstantiateNodeAction(val location: INonExistingNode) : ICodeCompletionAction {

editor-runtime/src/commonMain/kotlin/org/modelix/editor/CodeCompletionMenu.kt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ class CodeCompletionMenu(
2828
val matchingText = it.getMatchingText()
2929
matchingText.isNotEmpty() && matchingText.startsWith(parameters.pattern)
3030
}
31+
.applyShadowing()
3132
.sortedBy { it.getMatchingText().lowercase() }
3233
}
3334

@@ -200,6 +201,8 @@ interface ICodeCompletionAction : IActionOrProvider {
200201
fun getMatchingText(): String
201202
fun getDescription(): String
202203
fun execute()
204+
fun shadows(shadowed: ICodeCompletionAction) = false
205+
fun shadowedBy(shadowing: ICodeCompletionAction) = false
203206
}
204207

205208
class CodeCompletionActionWithPostprocessor(val action: ICodeCompletionAction, val after: () -> Unit) : ICodeCompletionAction by action {
@@ -243,4 +246,15 @@ enum class CompletionPosition {
243246
CENTER,
244247
LEFT,
245248
RIGHT
249+
}
250+
251+
fun List<ICodeCompletionAction>.applyShadowing(): List<ICodeCompletionAction> {
252+
return groupBy { it.getMatchingText() }.flatMap { applyShadowingToGroup(it.value) }
253+
}
254+
255+
private fun applyShadowingToGroup(actions: List<ICodeCompletionAction>): List<ICodeCompletionAction> {
256+
return actions.filter { a1 ->
257+
val isShadowed = actions.any { a2 -> a2 !== a1 && (a2.shadows(a1) || a1.shadowedBy(a2)) }
258+
!isShadowed
259+
}
246260
}

editor-runtime/src/commonMain/kotlin/org/modelix/editor/INonExistingNode.kt

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,37 @@ import org.modelix.model.api.remove
3030

3131
interface INonExistingNode {
3232
fun getParent(): INonExistingNode?
33+
fun getContainmentLink(): IChildLink?
34+
fun index(): Int
3335
fun replaceNode(subConcept: IConcept?): INode
3436
fun getOrCreateNode(subConcept: IConcept?): INode
3537
fun expectedConcept(): IConcept?
3638
fun getVisibleReferenceTargets(link: IReferenceLink): List<INode>
3739
}
3840

39-
data class SpecializedNonExistingNode(val node: INonExistingNode, val subConcept: IConcept): INonExistingNode {
40-
override fun getParent(): INonExistingNode? {
41-
TODO("Not yet implemented")
41+
fun INonExistingNode.ancestors(includeSelf: Boolean = false): Sequence<INonExistingNode> {
42+
return generateSequence(if (includeSelf) this else getParent()) { it.getParent() }
43+
}
44+
45+
fun INonExistingNode.commonAncestor(otherNode: INonExistingNode): INonExistingNode? {
46+
val ancestors1 = HashSet<INonExistingNode>()
47+
val ancestors2 = HashSet<INonExistingNode>()
48+
49+
for (ancestorPair in ancestors(true).zip(otherNode.ancestors(true))) {
50+
ancestors1.add(ancestorPair.first)
51+
ancestors2.add(ancestorPair.second)
52+
if (ancestors1.contains(ancestorPair.second)) return ancestorPair.second
53+
if (ancestors2.contains(ancestorPair.first)) return ancestorPair.first
4254
}
55+
return null
56+
}
57+
58+
data class SpecializedNonExistingNode(val node: INonExistingNode, val subConcept: IConcept): INonExistingNode {
59+
override fun getParent(): INonExistingNode? = node.getParent()
60+
61+
override fun getContainmentLink(): IChildLink? = node.getContainmentLink()
62+
63+
override fun index(): Int = node.index()
4364

4465
override fun replaceNode(subConcept: IConcept?): INode {
4566
return node.replaceNode(coerceOutputConcept(subConcept))
@@ -81,6 +102,10 @@ fun INonExistingNode.coerceOutputConcept(subConcept: IConcept?): IConcept? {
81102
data class ExistingNode(val node: INode) : INonExistingNode {
82103
override fun getParent(): INonExistingNode? = node.parent?.let { ExistingNode(it) }
83104

105+
override fun getContainmentLink() = node.getContainmentLink()
106+
107+
override fun index() = node.index()
108+
84109
override fun replaceNode(subConcept: IConcept?): INode {
85110
val parent = node.parent ?: throw RuntimeException("cannot replace the root node")
86111
val newNode = parent.addNewChild(node.roleInParent, node.index(), coerceOutputConcept(subConcept))
@@ -109,9 +134,13 @@ data class ExistingNode(val node: INode) : INonExistingNode {
109134

110135
fun INode.toNonExisting() = ExistingNode(this)
111136

112-
data class NonExistingChild(private val parent: INonExistingNode, val link: IChildLink, val index: Int = 0) : INonExistingNode {
137+
data class NonExistingChild(private val parent: INonExistingNode, val link: IChildLink, private val index: Int = 0) : INonExistingNode {
113138
override fun getParent() = parent
114139

140+
override fun getContainmentLink() = link
141+
142+
override fun index(): Int = index
143+
115144
override fun replaceNode(subConcept: IConcept?): INode {
116145
val parentNode = parent.getOrCreateNode(null)
117146
val existing = parentNode.getChildren(link).toList().getOrNull(index)

0 commit comments

Comments
 (0)