@@ -6,6 +6,7 @@ import com.github.lppedd.cc.lookupElement.TemplateSegment
66import com.intellij.codeInsight.template.Template
77import com.intellij.codeInsight.template.TemplateEditingAdapter
88import com.intellij.codeInsight.template.impl.TemplateState
9+ import com.intellij.codeInsight.template.impl.TemplateStateBase
910import com.intellij.openapi.application.runWriteAction
1011import com.intellij.openapi.command.WriteCommandAction
1112import com.intellij.openapi.diagnostic.logger
@@ -17,6 +18,14 @@ import kotlin.math.min
1718 * @author Edoardo Luppi
1819 */
1920internal class CCTemplateEditingListener : TemplateEditingAdapter () {
21+ private class MyHashMap : HashMap <String , String > {
22+ constructor () : super ()
23+ constructor (map: Map <String , String >) : super (map)
24+
25+ override fun containsKey (key : String ): Boolean =
26+ false
27+ }
28+
2029 private companion object {
2130 private val logger = logger<CCTemplateEditingListener >()
2231 }
@@ -41,6 +50,30 @@ internal class CCTemplateEditingListener : TemplateEditingAdapter() {
4150 return
4251 }
4352
53+ // This is a workaround to restore the old (cannot remember how old tho) template behavior,
54+ // where stepping to a previous template segment would not erase the currently inputted value.
55+ // To achieve this, we store the currently inputted value for the segment we are stepping
56+ // away from into the segments' predefined values map.
57+ //
58+ // MyHashMap is used to trick 'TemplateState.checkIfTabStop' in believing the segment
59+ // we are stepping into does not have a predefined value. Otherwise, the template engine
60+ // simply skips that segment.
61+ var predefinedValues = templateState.getPredefinedVariableValues()
62+
63+ if (predefinedValues !is MyHashMap ) {
64+ predefinedValues = predefinedValues?.let (::MyHashMap ) ? : MyHashMap ()
65+ templateState.setPredefinedVariableValues(predefinedValues)
66+ }
67+
68+ if (newIndex < oldIndex) {
69+ val segmentName = template.getSegmentName(oldIndex)
70+ val segmentRange = templateState.getSegmentRange(oldIndex)
71+ predefinedValues[segmentName] = templateState.editor.document.getText(segmentRange)
72+ } else {
73+ val segmentName = template.getSegmentName(newIndex)
74+ predefinedValues.remove(segmentName)
75+ }
76+
4477 if (oldIndex == TemplateSegment .BodyOrFooterType && newIndex > oldIndex) {
4578 if (templateState.getSegmentRange(TemplateSegment .BodyOrFooterType ).isEmpty) {
4679 deleteFooterValue(templateState)
@@ -50,9 +83,8 @@ internal class CCTemplateEditingListener : TemplateEditingAdapter() {
5083 }
5184
5285 val newOffset = templateState.getSegmentRange(newIndex).startOffset
53- val editor = templateState.editor
54- editor.moveCaretToOffset(newOffset)
55- editor.scheduleAutoPopup()
86+ templateState.editor.moveCaretToOffset(newOffset)
87+ templateState.editor.scheduleAutoPopup()
5688 }
5789
5890 override fun beforeTemplateFinished (templateState : TemplateState , template : Template ) {
@@ -111,6 +143,24 @@ internal class CCTemplateEditingListener : TemplateEditingAdapter() {
111143 }
112144 }
113145
146+ @Suppress(" UNCHECKED_CAST" )
147+ private fun TemplateStateBase.getPredefinedVariableValues (): MutableMap <String , String >? =
148+ logger.runAndLogError(null ) {
149+ TemplateStateBase ::class .java.getDeclaredMethod(" getPredefinedVariableValues" ).let {
150+ it.isAccessible = true
151+ it.invoke(this ) as MutableMap <String , String >?
152+ }
153+ }
154+
155+ private fun TemplateStateBase.setPredefinedVariableValues (map : MutableMap <String , String >? ) {
156+ logger.runAndLogError(Unit ) {
157+ TemplateStateBase ::class .java.getDeclaredMethod(" setPredefinedVariableValues" , Map ::class .java).let {
158+ it.isAccessible = true
159+ it.invoke(this , map)
160+ }
161+ }
162+ }
163+
114164 @Compatibility(
115165 keepForHistoricReasons = true ,
116166 minVersion = " 202.4357.23" ,
0 commit comments