@@ -17,119 +17,81 @@ class TemplateBracketTypedHandler : TypedHandlerDelegate() {
1717 val offset = editor.caretModel.offset
1818 val text = editor.document.charsSequence
1919
20- if (c == ' {' || c == ' !' || c == ' -' ) {
21- val result = handleAutoComplete(project, editor, text, offset)
22- if (result == Result .STOP ) return result
23- }
24-
25- if (c == ' !' || c == ' -' ) {
26- synchronizeBrackets(project, editor, text, offset)
27- }
28-
29- if (c == ' ' ) {
30- handleSpaceInBrackets(project, editor, text, offset)
31- }
32-
33- return Result .CONTINUE
34- }
35-
36- private fun handleSpaceInBrackets (project : Project , editor : Editor , text : CharSequence , offset : Int ) {
37- val textBefore = text.subSequence(0 , offset).toString()
38- val textAfter = text.subSequence(offset, text.length).toString()
39-
40- for (pair in BRACKET_PAIRS ) {
41- if (textBefore.endsWith(pair.opening + " " ) && textAfter.startsWith(pair.closing)) {
42- WriteCommandAction .runWriteCommandAction(project) {
43- editor.document.insertString(offset, " " )
20+ return when (c) {
21+ ' {' -> {
22+ insertClosingBracket(project, editor, offset, " }" )
23+ Result .STOP
24+ }
25+ ' !' , ' -' -> {
26+ if (text.matchesAt(offset - 3 , DOUBLE_BRACE_OPEN )) {
27+ handleDoubledSpecialChar(project, editor, offset, c)
28+ Result .STOP
29+ } else {
30+ Result .CONTINUE
4431 }
45- return
4632 }
33+ ' ' -> {
34+ handleSpaceInBrackets(project, editor, text, offset)
35+ Result .CONTINUE
36+ }
37+ else -> Result .CONTINUE
4738 }
4839 }
4940
50- private fun handleAutoComplete (project : Project , editor : Editor , text : CharSequence , offset : Int ): Result {
51- if (offset < 2 ) return Result .CONTINUE
52-
53- val textBefore = text.subSequence(0 , offset).toString()
41+ private fun handleDoubledSpecialChar (project : Project , editor : Editor , offset : Int , char : Char ) {
42+ WriteCommandAction .runWriteCommandAction(project) {
43+ editor.document.insertString(offset, char.toString())
44+ editor.caretModel.moveToOffset(offset + 1 )
45+ transformClosingBracket(editor, offset + 1 , char)
46+ }
47+ }
5448
55- for (pair in AUTO_COMPLETE_PAIRS ) {
56- if (textBefore.endsWith(pair.opening)) {
57- if (! hasAnyClosingBracketAhead(text, offset)) {
58- insertClosingBracket(project, editor, offset, pair.closing)
59- return Result .STOP
60- }
49+ private fun handleSpaceInBrackets (project : Project , editor : Editor , text : CharSequence , offset : Int ) {
50+ BRACKET_PAIRS .firstOrNull { pair ->
51+ text.matchesAt(offset - pair.opening.length - 1 , pair.opening) &&
52+ text[offset - 1 ] == ' ' &&
53+ text.matchesAt(offset, pair.closing)
54+ }?.let {
55+ WriteCommandAction .runWriteCommandAction(project) {
56+ editor.document.insertString(offset, " " )
6157 }
6258 }
63-
64- return Result .CONTINUE
6559 }
6660
67- private fun synchronizeBrackets (project : Project , editor : Editor , text : CharSequence , offset : Int ) {
68- val textBefore = text.subSequence(0 , offset).toString()
69-
70- val currentPair = BRACKET_PAIRS .find { textBefore.endsWith(it.opening) } ? : return
71-
72- if (currentPair.opening == " {{" ) return
73-
74- val textAfter = text.subSequence(offset, text.length).toString()
75-
76- for (otherPair in BRACKET_PAIRS ) {
77- if (otherPair.closing == currentPair.closing) continue
61+ private fun transformClosingBracket (editor : Editor , offset : Int , char : Char ) {
62+ val text = editor.document.charsSequence
63+ val newClosing = " $char$char$DOUBLE_BRACE_CLOSE "
7864
79- val closingIndex = findFirstUnnestedClosing(textAfter, otherPair.closing, currentPair.opening)
80- if (closingIndex != - 1 ) {
81- replaceText(project, editor, offset + closingIndex, otherPair.closing.length, currentPair.closing)
82- return
83- }
65+ findNextUnnestedClosing(text, offset, DOUBLE_BRACE_CLOSE )?.let { closingIndex ->
66+ editor.document.replaceString(closingIndex, closingIndex + 2 , newClosing)
8467 }
8568 }
8669
87- fun synchronizeBracketsAfterDeletion (project : Project , editor : Editor ) {
70+ fun synchronizeBracketsAfterDeletion (project : Project , editor : Editor , deletedChar : Char ) {
8871 val offset = editor.caretModel.offset
8972 val text = editor.document.charsSequence
90- val textBefore = text.subSequence(0 , offset).toString()
91-
92- if (! textBefore.endsWith(" {{" )) return
93-
94- if (textBefore.endsWith(" {{!!" ) || textBefore.endsWith(" {{--" )) return
9573
96- val textAfter = text.subSequence(offset, text.length).toString()
97-
98- for (pair in BRACKET_PAIRS ) {
99- if (pair.closing == " }}" ) continue
100-
101- val closingIndex = findFirstUnnestedClosing(textAfter, pair.closing, " {{" )
102- if (closingIndex != - 1 ) {
103- replaceText(project, editor, offset + closingIndex, pair.closing.length, " }}" )
104- return
74+ WriteCommandAction .runWriteCommandAction(project) {
75+ when {
76+ text.matchesAt(offset - 3 , DOUBLE_BRACE_OPEN ) && text.getOrNull(offset - 1 ) == deletedChar -> {
77+ editor.document.deleteString(offset - 1 , offset)
78+ transformClosingAfterDeletion(editor, offset - 1 , deletedChar)
79+ }
80+ text.matchesAt(offset - 2 , DOUBLE_BRACE_OPEN ) && text.getOrNull(offset) == deletedChar -> {
81+ editor.document.deleteString(offset, offset + 1 )
82+ transformClosingAfterDeletion(editor, offset, deletedChar)
83+ }
10584 }
10685 }
10786 }
10887
109- private fun findFirstUnnestedClosing (textAfter : String , closing : String , opening : String ): Int {
110- val closingIndex = textAfter.indexOf(closing)
111- if (closingIndex == - 1 ) return - 1
112-
113- val textBeforeClosing = textAfter.take(closingIndex)
114- val nextOpenIndex = textBeforeClosing.indexOf(opening)
115-
116- return if (nextOpenIndex == - 1 ) closingIndex else - 1
117- }
118-
119- private fun hasAnyClosingBracketAhead (text : CharSequence , offset : Int ): Boolean {
120- val textAfter = text.subSequence(offset, text.length).toString()
88+ private fun transformClosingAfterDeletion (editor : Editor , offset : Int , char : Char ) {
89+ val text = editor.document.charsSequence
90+ val doubledClosing = " $char$char$DOUBLE_BRACE_CLOSE "
12191
122- for (pair in BRACKET_PAIRS ) {
123- val nextClose = textAfter.indexOf(pair.closing)
124- if (nextClose != - 1 ) {
125- val nextOpen = textAfter.indexOf(pair.opening)
126- if (nextOpen == - 1 || nextClose < nextOpen) {
127- return true
128- }
129- }
92+ findNextUnnestedClosing(text, offset, doubledClosing)?.let { closingIndex ->
93+ editor.document.replaceString(closingIndex, closingIndex + 4 , DOUBLE_BRACE_CLOSE )
13094 }
131-
132- return false
13395 }
13496
13597 private fun insertClosingBracket (project : Project , editor : Editor , offset : Int , closing : String ) {
@@ -138,24 +100,38 @@ class TemplateBracketTypedHandler : TypedHandlerDelegate() {
138100 }
139101 }
140102
141- private fun replaceText (project : Project , editor : Editor , start : Int , length : Int , newText : String ) {
142- WriteCommandAction .runWriteCommandAction(project) {
143- editor.document.replaceString(start, start + length, newText)
103+ private fun findNextUnnestedClosing (text : CharSequence , startOffset : Int , pattern : String ): Int? {
104+ val patternLength = pattern.length
105+ val searchRange = startOffset.. (text.length - patternLength)
106+
107+ for (i in searchRange) {
108+ if (text.matchesAt(i, pattern) && ! hasOpeningBetween(text, startOffset, i)) {
109+ return i
110+ }
111+ }
112+ return null
113+ }
114+
115+ private fun hasOpeningBetween (text : CharSequence , start : Int , end : Int ): Boolean {
116+ return (start until end - 1 ).any { i ->
117+ text.matchesAt(i, DOUBLE_BRACE_OPEN )
144118 }
145119 }
146120
147121 companion object {
148122 val INSTANCE = TemplateBracketTypedHandler ()
123+
124+ private const val DOUBLE_BRACE_OPEN = " {{"
125+ private const val DOUBLE_BRACE_CLOSE = " }}"
149126 }
150127}
151128
152- val BRACKET_PAIRS = listOf (
153- TemplateBracketTypedHandler .BracketPair (" {{--" , " --}}" ),
154- TemplateBracketTypedHandler .BracketPair (" {{!!" , " !!}}" ),
155- TemplateBracketTypedHandler .BracketPair (" {{" , " }}" ),
156- )
129+ private fun CharSequence.matchesAt (index : Int , pattern : String ): Boolean {
130+ if (index < 0 || index + pattern.length > length) return false
131+ return (pattern.indices).all { i -> this [index + i] == pattern[i] }
132+ }
157133
158- private val AUTO_COMPLETE_PAIRS = listOf (
134+ val BRACKET_PAIRS = listOf (
159135 TemplateBracketTypedHandler .BracketPair (" {{--" , " --}}" ),
160136 TemplateBracketTypedHandler .BracketPair (" {{!!" , " !!}}" ),
161137 TemplateBracketTypedHandler .BracketPair (" {{" , " }}" ),
0 commit comments