Skip to content

Commit 6e00870

Browse files
committed
optimize indent and select word
1 parent 10a0b40 commit 6e00870

File tree

5 files changed

+364
-9
lines changed

5 files changed

+364
-9
lines changed

build.gradle.kts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import de.undercouch.gradle.tasks.download.Download
33
plugins {
44
id("java")
55
id("org.jetbrains.kotlin.jvm") version "2.1.20-Beta2"
6-
id("org.jetbrains.intellij.platform") version "2.2.1"
6+
id("org.jetbrains.intellij.platform") version "2.6.0"
77
id("de.undercouch.download") version "5.6.0"
88
}
99

@@ -179,7 +179,7 @@ dependencies {
179179
intellijPlatform {
180180
intellijIdeaCommunity(Versions.ideaSDK)
181181
bundledPlugins("com.intellij.java", "org.jetbrains.kotlin")
182-
plugins("com.redhat.devtools.lsp4ij:0.9.0")
182+
plugins("com.redhat.devtools.lsp4ij:0.14.0")
183183
}
184184
}
185185

@@ -229,7 +229,7 @@ tasks {
229229

230230
// 插件XML配置
231231
patchPluginXml {
232-
dependsOn(installDependencies)
232+
// dependsOn(installDependencies)
233233
sinceBuild.set(buildVersionData.sinceBuild)
234234
untilBuild.set(buildVersionData.untilBuild)
235235
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package com.tang.intellij.lua.codeInsight.editorActions
2+
3+
import com.intellij.codeInsight.editorActions.ExtendWordSelectionHandlerBase
4+
import com.intellij.openapi.editor.Editor
5+
import com.intellij.openapi.util.TextRange
6+
import com.intellij.psi.PsiElement
7+
import com.tang.intellij.lua.lang.type.LuaString
8+
import com.tang.intellij.lua.psi.LuaTypes
9+
10+
/**
11+
* 处理Lua字符串中的单词选择
12+
* 当双击字符串内的单词时,只选择单词而不是整个字符串
13+
*/
14+
class LuaWordSelectionHandler : ExtendWordSelectionHandlerBase() {
15+
16+
override fun canSelect(e: PsiElement): Boolean {
17+
return e.node.elementType == LuaTypes.STRING
18+
}
19+
20+
override fun select(e: PsiElement, editorText: CharSequence, cursorOffset: Int, editor: Editor): List<TextRange>? {
21+
val text = e.text
22+
val content = LuaString.getContent(text)
23+
24+
// 计算相对于字符串内容的光标位置
25+
val relativeOffset = cursorOffset - e.textRange.startOffset - content.start
26+
27+
// 如果光标不在字符串内容区域内,返回null让默认处理器处理
28+
if (relativeOffset < 0 || relativeOffset >= content.value.length || content.value.isEmpty()) {
29+
return null
30+
}
31+
32+
val stringContent = content.value
33+
34+
// 查找当前光标位置的单词
35+
val wordRange = findWordAt(stringContent, relativeOffset) ?: return null
36+
37+
val absoluteStart = e.textRange.startOffset + content.start + wordRange.first
38+
val absoluteEnd = e.textRange.startOffset + content.start + wordRange.second
39+
40+
// 确保范围有效且在字符串内容范围内
41+
if (absoluteStart >= e.textRange.startOffset + content.start &&
42+
absoluteEnd <= e.textRange.startOffset + content.end &&
43+
absoluteStart < absoluteEnd) {
44+
return listOf(TextRange(absoluteStart, absoluteEnd))
45+
}
46+
47+
return null
48+
}
49+
50+
/**
51+
* 在指定位置查找单词
52+
*/
53+
private fun findWordAt(text: String, offset: Int): Pair<Int, Int>? {
54+
if (offset < 0 || offset >= text.length) return null
55+
56+
var start = offset
57+
var end = offset
58+
59+
// 如果当前字符不是单词字符,寻找最近的单词
60+
if (!isWordChar(text[offset])) {
61+
// 向左查找
62+
var leftPos = offset - 1
63+
while (leftPos >= 0 && !isWordChar(text[leftPos])) leftPos--
64+
65+
// 向右查找
66+
var rightPos = offset
67+
while (rightPos < text.length && !isWordChar(text[rightPos])) rightPos++
68+
69+
// 选择最近的单词
70+
when {
71+
leftPos >= 0 && (rightPos >= text.length || (offset - leftPos <= rightPos - offset)) -> {
72+
start = leftPos
73+
end = leftPos + 1
74+
}
75+
rightPos < text.length -> {
76+
start = rightPos
77+
end = rightPos + 1
78+
}
79+
else -> return null
80+
}
81+
}
82+
83+
// 向左扩展到单词开始
84+
while (start > 0 && isWordChar(text[start - 1])) start--
85+
86+
// 向右扩展到单词结束
87+
while (end < text.length && isWordChar(text[end])) end++
88+
89+
return if (start < end) Pair(start, end) else null
90+
}
91+
92+
/**
93+
* 判断字符是否为单词字符(字母、数字、下划线)
94+
*/
95+
private fun isWordChar(char: Char): Boolean {
96+
return char.isLetterOrDigit() || char == '_'
97+
}
98+
}
Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
/*
2+
* Copyright (c) 2017. tangzx(love.tangzx@qq.com)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.tang.intellij.lua.editor;
18+
19+
import com.intellij.lang.Language;
20+
import com.intellij.openapi.editor.Document;
21+
import com.intellij.openapi.editor.Editor;
22+
import com.intellij.openapi.project.Project;
23+
import com.intellij.openapi.util.TextRange;
24+
import com.intellij.psi.PsiDocumentManager;
25+
import com.intellij.psi.PsiElement;
26+
import com.intellij.psi.PsiFile;
27+
import com.intellij.psi.codeStyle.lineIndent.LineIndentProvider;
28+
import com.intellij.psi.tree.IElementType;
29+
import com.tang.intellij.lua.lang.LuaLanguage;
30+
import com.tang.intellij.lua.psi.*;
31+
import org.jetbrains.annotations.NotNull;
32+
import org.jetbrains.annotations.Nullable;
33+
34+
/**
35+
* Provides smart line indentation for Lua files
36+
*/
37+
public class LuaLineIndentProvider implements LineIndentProvider {
38+
39+
@Override
40+
public @Nullable String getLineIndent(@NotNull Project project, @NotNull Editor editor, @Nullable Language language, int offset) {
41+
if (!isSuitableFor(language)) {
42+
return null;
43+
}
44+
45+
PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
46+
if (file == null) {
47+
return null;
48+
}
49+
50+
return calculateIndent(project, editor, file, offset);
51+
}
52+
53+
@Override
54+
public boolean isSuitableFor(@Nullable Language language) {
55+
return language instanceof LuaLanguage;
56+
}
57+
58+
private @Nullable String calculateIndent(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file, int offset) {
59+
Document document = editor.getDocument();
60+
61+
if (offset == 0) {
62+
return "";
63+
}
64+
65+
// 获取当前行号
66+
int lineNumber = document.getLineNumber(offset);
67+
if (lineNumber == 0) {
68+
return "";
69+
}
70+
71+
// 获取前一行的缩进
72+
String previousLineIndent = getPreviousLineIndent(document, lineNumber);
73+
74+
// 获取前一行的最后一个有意义的元素
75+
PsiElement elementAtPrevLine = getLastMeaningfulElementInPreviousLine(file, document, lineNumber);
76+
77+
if (elementAtPrevLine == null) {
78+
return previousLineIndent;
79+
}
80+
81+
// 根据前一行的内容决定缩进
82+
return calculateIndentBasedOnPreviousLine(editor, project, elementAtPrevLine, previousLineIndent);
83+
}
84+
85+
private String getPreviousLineIndent(Document document, int currentLineNumber) {
86+
if (currentLineNumber <= 0) {
87+
return "";
88+
}
89+
90+
int prevLineStartOffset = document.getLineStartOffset(currentLineNumber - 1);
91+
int prevLineEndOffset = document.getLineEndOffset(currentLineNumber - 1);
92+
String prevLineText = document.getText(new TextRange(prevLineStartOffset, prevLineEndOffset));
93+
94+
// 提取前一行的缩进
95+
StringBuilder indent = new StringBuilder();
96+
for (char c : prevLineText.toCharArray()) {
97+
if (c == ' ' || c == '\t') {
98+
indent.append(c);
99+
} else {
100+
break;
101+
}
102+
}
103+
return indent.toString();
104+
}
105+
106+
private @Nullable PsiElement getLastMeaningfulElementInPreviousLine(PsiFile file, Document document, int currentLineNumber) {
107+
if (currentLineNumber <= 0) {
108+
return null;
109+
}
110+
111+
int prevLineStartOffset = document.getLineStartOffset(currentLineNumber - 1);
112+
int prevLineEndOffset = document.getLineEndOffset(currentLineNumber - 1);
113+
114+
// 从行尾开始向前查找有意义的元素
115+
for (int offset = prevLineEndOffset - 1; offset >= prevLineStartOffset; offset--) {
116+
PsiElement element = file.findElementAt(offset);
117+
if (element != null && !isWhitespaceOrComment(element)) {
118+
return element;
119+
}
120+
}
121+
122+
return null;
123+
}
124+
125+
private boolean isWhitespaceOrComment(PsiElement element) {
126+
IElementType elementType = element.getNode().getElementType();
127+
return
128+
elementType == LuaTypes.SHORT_COMMENT ||
129+
elementType == LuaTypes.DOC_COMMENT ||
130+
elementType == LuaTypes.BLOCK_COMMENT;
131+
}
132+
133+
private String calculateIndentBasedOnPreviousLine(Editor editor, Project project, PsiElement element, String baseIndent) {
134+
String indentUnit = getIndentUnit(editor, project);
135+
136+
// 检查是否是table相关的缩进
137+
String tableIndent = calculateTableIndent(element, baseIndent, indentUnit);
138+
if (tableIndent != null) {
139+
return tableIndent;
140+
}
141+
142+
// 查找包含该元素的语句或块
143+
// PsiElement parent = element;
144+
// while (parent != null) {
145+
// if (shouldIncreaseIndent(parent)) {
146+
// return baseIndent + indentUnit;
147+
// }
148+
//
149+
// if (parent instanceof LuaBlock) {
150+
// break;
151+
// }
152+
//
153+
// parent = parent.getParent();
154+
// }
155+
156+
// 检查元素本身的类型
157+
IElementType elementType = element.getNode().getElementType();
158+
159+
// 需要增加缩进的情况
160+
if (elementType == LuaTypes.DO ||
161+
elementType == LuaTypes.THEN ||
162+
elementType == LuaTypes.ELSE ||
163+
elementType == LuaTypes.ELSEIF ||
164+
elementType == LuaTypes.FUNCTION ||
165+
elementType == LuaTypes.LOCAL ||
166+
elementType == LuaTypes.REPEAT ||
167+
elementType == LuaTypes.LCURLY ||
168+
elementType == LuaTypes.LPAREN ||
169+
elementType == LuaTypes.AND ||
170+
elementType == LuaTypes.OR
171+
) {
172+
return baseIndent + indentUnit;
173+
}
174+
return baseIndent;
175+
}
176+
177+
private boolean shouldIncreaseIndent(PsiElement element) {
178+
if (element instanceof LuaBlock) {
179+
// 检查是否是新开始的块
180+
PsiElement parent = element.getParent();
181+
return parent instanceof LuaIfStat ||
182+
parent instanceof LuaWhileStat ||
183+
parent instanceof LuaForAStat ||
184+
parent instanceof LuaForBStat ||
185+
parent instanceof LuaRepeatStat ||
186+
parent instanceof LuaFuncBody ||
187+
parent instanceof LuaDoStat;
188+
}
189+
190+
// 对于table,只有在开始新的table时才增加缩进
191+
if (element instanceof LuaTableExpr) {
192+
return true;
193+
}
194+
195+
return false;
196+
}
197+
198+
private @Nullable String calculateTableIndent(PsiElement element, String baseIndent, String indentUnit) {
199+
IElementType elementType = element.getNode().getElementType();
200+
201+
// 如果是在左大括号后,增加缩进
202+
if (elementType == LuaTypes.LCURLY || elementType == LuaTypes.LPAREN) {
203+
return baseIndent + indentUnit;
204+
}
205+
206+
// 如果是逗号或分号,并且它们直接在table内(不在嵌套的function等结构中)
207+
if (elementType == LuaTypes.COMMA || elementType == LuaTypes.SEMI) {
208+
if (isDirectTableSeparator(element)) {
209+
return baseIndent; // 在table field分隔符后保持当前缩进
210+
}
211+
}
212+
213+
return null;
214+
}
215+
216+
private boolean isDirectTableSeparator(PsiElement separator) {
217+
// 向上查找,看是否是直接在table内的分隔符
218+
PsiElement current = separator.getParent();
219+
while (current != null) {
220+
if (current instanceof LuaTableExpr) {
221+
return true;
222+
}
223+
// 如果遇到了这些复杂结构,说明分隔符不是直接在table内
224+
if (current instanceof LuaFuncBody ||
225+
current instanceof LuaIfStat ||
226+
current instanceof LuaWhileStat ||
227+
current instanceof LuaForAStat ||
228+
current instanceof LuaForBStat ||
229+
current instanceof LuaRepeatStat ||
230+
current instanceof LuaDoStat) {
231+
return false;
232+
}
233+
current = current.getParent();
234+
}
235+
return false;
236+
}
237+
238+
private String getIndentUnit(Editor editor, Project project) {
239+
var settings = editor.getSettings();
240+
if (settings.isUseTabCharacter(project)) {
241+
return "\t";
242+
} else {
243+
int indentSize = settings.getTabSize(project);
244+
if (indentSize <= 0) {
245+
indentSize = settings.getTabSize(project);
246+
}
247+
return " ".repeat(Math.max(0, indentSize));
248+
}
249+
}
250+
}

src/main/java/com/tang/intellij/lua/lang/LuaIcons.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ object LuaIcons {
4040
val ENUM = AllIcons.Nodes.Enum
4141
val SNIPPET = AllIcons.Nodes.Variable
4242
val CLASS_METHOD_OVERRIDING: Icon = RowIcon(AllIcons.Nodes.Method, AllIcons.Gutter.OverridingMethod)
43-
val GLOBAL_FUNCTION: Icon = LayeredIcon(AllIcons.Nodes.Function, AllIcons.Nodes.StaticMark)
44-
val GLOBAL_VAR: Icon = LayeredIcon(AllIcons.Nodes.Variable, AllIcons.Nodes.StaticMark)
43+
// val GLOBAL_FUNCTION: Icon = LayeredIcon(AllIcons.Nodes.Function, AllIcons.Nodes.StaticMark)
44+
// val GLOBAL_VAR: Icon = LayeredIcon(AllIcons.Nodes.Variable, AllIcons.Nodes.StaticMark)
4545
val LOCAL_VAR = AllIcons.Nodes.Variable
4646
val LOCAL_FUNCTION = AllIcons.Nodes.Function
4747
val PARAMETER = AllIcons.Nodes.Parameter

0 commit comments

Comments
 (0)