Skip to content

Commit 0b0f0e5

Browse files
committed
refactor: use offsets instead of text search
1 parent 3ddc344 commit 0b0f0e5

File tree

2 files changed

+84
-105
lines changed

2 files changed

+84
-105
lines changed

src/main/kotlin/com/github/tempest/framework/views/completion/TemplateBracketBackspaceHandler.kt

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,18 @@ import com.intellij.psi.PsiFile
77

88
class TemplateBracketBackspaceHandler : BackspaceHandlerDelegate() {
99

10-
override fun beforeCharDeleted(c: Char, file: PsiFile, editor: Editor) {}
10+
override fun beforeCharDeleted(c: Char, file: PsiFile, editor: Editor) = Unit
1111

1212
override fun charDeleted(c: Char, file: PsiFile, editor: Editor): Boolean {
13-
if (!file.name.endsWith(TempestFrameworkUtil.TEMPLATE_SUFFIX)) return false
14-
15-
if (c != '!' && c != '-') return false
16-
17-
TemplateBracketTypedHandler.INSTANCE.synchronizeBracketsAfterDeletion(file.project, editor)
13+
if (!file.name.endsWith(TempestFrameworkUtil.TEMPLATE_SUFFIX) || c !in SPECIAL_CHARS) {
14+
return false
15+
}
1816

17+
TemplateBracketTypedHandler.INSTANCE.synchronizeBracketsAfterDeletion(file.project, editor, c)
1918
return false
2019
}
20+
21+
private companion object {
22+
val SPECIAL_CHARS = setOf('!', '-')
23+
}
2124
}

src/main/kotlin/com/github/tempest/framework/views/completion/TemplateBracketTypedHandler.kt

Lines changed: 75 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)