@@ -10,19 +10,16 @@ import com.intellij.psi.PsiFile
1010import com.intellij.openapi.util.TextRange
1111import com.intellij.application.options.CodeStyle
1212import com.intellij.openapi.util.Ref
13- import com.intellij.openapi.util.Key
1413import com.intellij.openapi.editor.actionSystem.EditorActionHandler
1514import com.intellij.openapi.diagnostic.Logger
1615
1716/* *
1817 * ZY 回车换行缩进处理器
19- * 在 .zy 文件中按下回车后,继承上一行的缩进(空格/制表符原样保留),保持作用域内缩进一致
18+ * 重构版本:简化逻辑,直接响应,准确处理缩进
2019 */
2120class ZyEnterHandler : EnterHandlerDelegateAdapter () {
2221
2322 companion object {
24- // 标记本次回车已由 preprocessEnter 处理,避免 postProcessEnter 再次插入缩进导致光标错位
25- private val ENTER_HANDLED_KEY : Key <Boolean > = Key .create(" zy.enter.handled" )
2623 private val LOG : Logger = Logger .getInstance(ZyEnterHandler ::class .java)
2724 }
2825
@@ -34,149 +31,111 @@ class ZyEnterHandler : EnterHandlerDelegateAdapter() {
3431 dataContext : DataContext ,
3532 originalHandler : EditorActionHandler ?
3633 ): EnterHandlerDelegate .Result {
34+ // 只处理.zy文件
3735 if (! file.name.endsWith(" .zy" )) {
3836 return EnterHandlerDelegate .Result .Continue
3937 }
4038
4139 val document = editor.document
4240 val offset = caretOffsetRef.get()
43- if (offset <= 0 ) return EnterHandlerDelegate .Result .Continue
44-
45- val text = document.charsSequence
46- // 向后寻找下一个非空白字符
47- fun nextNonWs (from : Int ): Char {
48- var i = from
49- while (i < text.length) {
50- val ch = text[i]
51- if (ch != ' ' && ch != ' \t ' && ch != ' \r ' && ch != ' \n ' ) return ch
52- i++
41+
42+ // 获取当前行信息
43+ val currentLine = document.getLineNumber(offset)
44+ val currentLineStart = document.getLineStartOffset(currentLine)
45+ val currentLineEnd = document.getLineEndOffset(currentLine)
46+ val currentLineText = document.getText(TextRange (currentLineStart, currentLineEnd))
47+
48+ // 获取光标前后的字符
49+ val beforeCursor = if (offset > currentLineStart) document.getText(TextRange (offset - 1 , offset)) else " "
50+ val afterCursor = if (offset < currentLineEnd) document.getText(TextRange (offset, offset + 1 )) else " "
51+
52+ // 计算当前行的基础缩进
53+ val baseIndent = getLineIndent(currentLineText)
54+
55+ // 获取缩进设置
56+ val indentOptions = CodeStyle .getSettings(file).getIndentOptions(file.fileType)
57+ val indentUnit = if (indentOptions.USE_TAB_CHARACTER ) " \t " else " " .repeat(indentOptions.INDENT_SIZE )
58+
59+ var newIndent = baseIndent
60+ var extraNewlines = " "
61+
62+ // 处理特殊情况
63+ when {
64+ // 情况1:在成对括号之间(如 {|}、[|]、(|))
65+ isPairBrackets(beforeCursor, afterCursor) -> {
66+ newIndent = baseIndent + indentUnit
67+ extraNewlines = " \n " + baseIndent
5368 }
54- return ' \u0000 '
55- }
56- // 向前寻找上一个非空白字符
57- fun prevNonWs (before : Int ): Char {
58- var i = before - 1
59- while (i >= 0 ) {
60- val ch = text[i]
61- if (ch != ' ' && ch != ' \t ' && ch != ' \r ' && ch != ' \n ' ) return ch
62- i--
69+ // 情况2:在开括号后面
70+ isOpenBracket(beforeCursor) -> {
71+ newIndent = baseIndent + indentUnit
6372 }
64- return ' \u0000 '
65- }
66- val prevChar = prevNonWs(offset)
67- val nextChar = nextNonWs(offset)
68-
69- // 情况1:位于成对括号之间的回车,插入两行并正确缩进({}、[]、())
70- if ((prevChar == ' {' && nextChar == ' }' ) || (prevChar == ' [' && nextChar == ' ]' ) || (prevChar == ' (' && nextChar == ' )' )) {
71- val line = document.getLineNumber(offset)
72- val lineStart = document.getLineStartOffset(line)
73- val baseIndentText = document.getText(TextRange (lineStart, offset - 1 ))
74- val leadingIndent = buildString {
75- for (ch in baseIndentText) {
76- if (ch == ' ' || ch == ' \t ' ) append(ch) else break
73+ // 情况3:在闭括号前面
74+ isCloseBracket(afterCursor) -> {
75+ newIndent = if (baseIndent.length >= indentUnit.length) {
76+ baseIndent.substring(0 , baseIndent.length - indentUnit.length)
77+ } else {
78+ " "
7779 }
7880 }
79- val indentOptions = CodeStyle .getSettings(file).getIndentOptions(file.fileType)
80- val unitIndent = if (indentOptions.USE_TAB_CHARACTER ) " \t " else " " .repeat(indentOptions.INDENT_SIZE )
81- val innerIndent = leadingIndent + unitIndent
82-
83- val insertion = " \n " + innerIndent + " \n " + leadingIndent
84- document.insertString(offset, insertion)
85- // 直接设置光标位置到中间行缩进末尾,避免依赖 caretAdvanceRef 在 Stop 模式下可能无效
86- editor.caretModel.moveToOffset(offset + 1 + innerIndent.length)
87- editor.putUserData(ENTER_HANDLED_KEY , true )
88- return EnterHandlerDelegate .Result .Stop
89- }
90-
91- // 情况2:紧随开括号后的回车,当前行直接 +1 级缩进(适用于 { [ ())
92- if (prevChar == ' {' || prevChar == ' [' || prevChar == ' (' ) {
93- val line = document.getLineNumber(offset)
94- val lineStart = document.getLineStartOffset(line)
95- val baseIndentText = document.getText(TextRange (lineStart, lineStart + (document.getLineEndOffset(line) - lineStart).coerceAtLeast(0 ))).let { lineText ->
96- // 仅提取行首空白作为基底缩进
97- buildString {
98- for (ch in lineText) {
99- if (ch == ' ' || ch == ' \t ' ) append(ch) else break
100- }
81+ // 情况4:普通换行,继承当前行缩进
82+ else -> {
83+ // 检查当前行最后的非空白字符
84+ val trimmedLine = currentLineText.trimEnd()
85+ if (trimmedLine.endsWith(" {" ) || trimmedLine.endsWith(" [" ) || trimmedLine.endsWith(" (" )) {
86+ newIndent = baseIndent + indentUnit
10187 }
10288 }
103- val leadingIndent = baseIndentText
104- val indentOptions = CodeStyle .getSettings(file).getIndentOptions(file.fileType)
105- val unitIndent = if (indentOptions.USE_TAB_CHARACTER ) " \t " else " " .repeat(indentOptions.INDENT_SIZE )
106- val innerIndent = leadingIndent + unitIndent
107- val insertion = " \n " + innerIndent
108- document.insertString(offset, insertion)
109- caretAdvanceRef.set(1 + innerIndent.length)
110- editor.putUserData(ENTER_HANDLED_KEY , true )
111- return EnterHandlerDelegate .Result .Stop
11289 }
113-
114- return EnterHandlerDelegate .Result .Continue
90+
91+ // 插入换行和缩进
92+ val insertion = " \n " + newIndent + extraNewlines
93+ document.insertString(offset, insertion)
94+
95+ // 设置光标位置
96+ val newCaretPosition = offset + 1 + newIndent.length
97+ editor.caretModel.moveToOffset(newCaretPosition)
98+
99+ LOG .debug(" Enter processed: baseIndent='$baseIndent ', newIndent='$newIndent ', caretPos=$newCaretPosition " )
100+
101+ return EnterHandlerDelegate .Result .Stop
115102 }
116-
117- override fun postProcessEnter (file : PsiFile , editor : Editor , dataContext : DataContext ): EnterHandlerDelegate .Result {
118- if (! file.name.endsWith(" .zy" )) return EnterHandlerDelegate .Result .Continue
119-
120- // 如果 preprocessEnter 已处理本次回车,则跳过后续缩进调整,避免重复缩进导致光标位置错误
121- if (editor.getUserData(ENTER_HANDLED_KEY ) == true ) {
122- editor.putUserData(ENTER_HANDLED_KEY , false )
123- return EnterHandlerDelegate .Result .Continue
124- }
125-
126- val document: Document = editor.document
127- val caretOffset = editor.caretModel.offset
128- val currentLine = document.getLineNumber(caretOffset)
129- if (currentLine <= 0 ) return EnterHandlerDelegate .Result .Continue
130-
131- val prevLine = currentLine - 1
132- val prevStart = document.getLineStartOffset(prevLine)
133- val prevEnd = document.getLineEndOffset(prevLine)
134- val prevText = document.getText(TextRange (prevStart, prevEnd))
135-
136- // 计算上一行前导空白(保留空格/Tab 原样)
137- val leadingIndent = buildString {
138- for (ch in prevText) {
139- if (ch == ' ' || ch == ' \t ' ) append(ch) else break
103+
104+ /* *
105+ * 获取行的前导缩进
106+ */
107+ private fun getLineIndent (lineText : String ): String {
108+ val indent = StringBuilder ()
109+ for (char in lineText) {
110+ if (char == ' ' || char == ' \t ' ) {
111+ indent.append(char)
112+ } else {
113+ break
140114 }
141115 }
142-
143- // 根据上一行结尾和当前行起始,调整缩进层级
144- val trimmedPrev = prevText.trimEnd()
145- // 仅当上一行最后一个非空白字符是开括号时才增加一级
146- fun lastNonWsChar (s : String ): Char? {
147- for (i in s.length - 1 downTo 0 ) {
148- val ch = s[i]
149- if (ch != ' ' && ch != ' \t ' ) return ch
150- }
151- return null
152- }
153- val lastCh = lastNonWsChar(trimmedPrev)
154- val prevEndsWithOpen = lastCh == ' {' || lastCh == ' [' || lastCh == ' ('
155-
156- // 若当前行以闭合符开头,则减少一级缩进
157- val currLineStart = document.getLineStartOffset(currentLine)
158- val currLineEnd = document.getLineEndOffset(currentLine)
159- val currHeadText = if (currLineStart < currLineEnd) document.getText(TextRange (currLineStart, (currLineStart + 3 ).coerceAtMost(currLineEnd))) else " "
160- val currStartsWithClose = currHeadText.startsWith(" }" ) || currHeadText.startsWith(" )" ) || currHeadText.startsWith(" ]" )
161-
162- // 依据项目代码风格设置确定缩进单位(Tab 或 N 个空格)
163- val indentOptions = CodeStyle .getSettings(file).getIndentOptions(file.fileType)
164- val unitIndent = if (indentOptions.USE_TAB_CHARACTER ) " \t " else " " .repeat(indentOptions.INDENT_SIZE )
165- val baseIndent = leadingIndent
166- val finalIndent = buildString {
167- append(baseIndent)
168- if (prevEndsWithOpen) append(unitIndent)
169- if (currStartsWithClose && length >= unitIndent.length) delete(length - unitIndent.length, length)
170- }
171-
172- if (finalIndent.isNotEmpty()) {
173- document.insertString(caretOffset, finalIndent)
174- editor.caretModel.moveToOffset(caretOffset + finalIndent.length)
175- return EnterHandlerDelegate .Result .DefaultSkipIndent
176- }
177-
178- return EnterHandlerDelegate .Result .Continue
116+ return indent.toString()
179117 }
180- }
181-
182-
118+
119+ /* *
120+ * 检查是否在成对括号之间
121+ */
122+ private fun isPairBrackets (before : String , after : String ): Boolean {
123+ return (before == " {" && after == " }" ) ||
124+ (before == " [" && after == " ]" ) ||
125+ (before == " (" && after == " )" )
126+ }
127+
128+ /* *
129+ * 检查是否是开括号
130+ */
131+ private fun isOpenBracket (char : String ): Boolean {
132+ return char == " {" || char == " [" || char == " ("
133+ }
134+
135+ /* *
136+ * 检查是否是闭括号
137+ */
138+ private fun isCloseBracket (char : String ): Boolean {
139+ return char == " }" || char == " ]" || char == " )"
140+ }
141+ }
0 commit comments