Skip to content

Commit 111f668

Browse files
committed
Fixing line indentation for documentation completion
1 parent 1612ede commit 111f668

File tree

3 files changed

+255
-86
lines changed

3 files changed

+255
-86
lines changed
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package com.intellij.plugins.haxe.ide.editor;
2+
3+
import com.intellij.application.options.CodeStyle;
4+
import com.intellij.codeInsight.editorActions.enter.EnterHandlerDelegateAdapter;
5+
import com.intellij.openapi.actionSystem.DataContext;
6+
import com.intellij.openapi.editor.Document;
7+
import com.intellij.openapi.editor.Editor;
8+
import com.intellij.openapi.editor.actionSystem.EditorActionHandler;
9+
import com.intellij.openapi.util.Ref;
10+
import com.intellij.plugins.haxe.HaxeFileType;
11+
import com.intellij.plugins.haxe.ide.HaxeCommenter;
12+
import com.intellij.plugins.haxe.lang.parser.HaxePsiDocCommentImpl;
13+
import com.intellij.plugins.haxe.lang.psi.HaxeFile;
14+
import com.intellij.psi.PsiElement;
15+
import com.intellij.psi.PsiFile;
16+
import com.intellij.psi.codeStyle.CodeStyleSettings;
17+
import com.intellij.psi.util.PsiUtilCore;
18+
import org.jetbrains.annotations.NotNull;
19+
20+
import static com.intellij.plugins.haxe.ide.HaxeCommenter.DOC_COMMENT_PREFIX;
21+
22+
/// Custom logic to correctly handle completion of haxe documentation block (the default javadoc stuff does not work correctly for haxedocs)
23+
/// if block is incomplete then this handler will add the end "tag" and make sure we get correct indentation
24+
public class HaxeDocumentationEnterHandler extends EnterHandlerDelegateAdapter {
25+
26+
@Override
27+
public Result preprocessEnter(
28+
@NotNull PsiFile file,
29+
@NotNull Editor editor,
30+
@NotNull Ref<Integer> caretOffsetRef,
31+
@NotNull Ref<Integer> caretAdvance,
32+
@NotNull DataContext dataContext,
33+
EditorActionHandler originalHandler
34+
) {
35+
if (file instanceof HaxeFile) {
36+
int caretOffset = caretOffsetRef.get();
37+
38+
if (isInsideDocsWithoutCloseTag(file, caretOffset)) {
39+
40+
Document document = editor.getDocument();
41+
42+
int lineNumber = document.getLineNumber(caretOffset);
43+
int lineStart = document.getLineStartOffset(lineNumber);
44+
45+
// TODO mlo: make use of codeStyle line indent instead of manual workaround.
46+
// probably need to either create a LineIndentProvider or change the formatter logic
47+
// so FormatterBasedLineIndentProvider can handle the indentation.
48+
//String indent = CodeStyleManager.getInstance(file.getProject()).getLineIndent(file, caretOffset);
49+
50+
String text = document.getText();
51+
String lineText = text.substring(lineStart, caretOffset);
52+
53+
if(!lineText.trim().endsWith(DOC_COMMENT_PREFIX)){
54+
return Result.Continue;
55+
}
56+
String indent = getLineIndent(lineText);
57+
58+
CodeStyleSettings settings = CodeStyle.getSettings(file.getProject());
59+
CodeStyleSettings.IndentOptions indentOptions = settings.getIndentOptions(HaxeFileType.INSTANCE);
60+
61+
String docIndent = indentOptions.USE_TAB_CHARACTER ? "\t" : " ";
62+
String docNewLine = indent + docIndent;
63+
64+
String toInsert = "\n" + docNewLine + "\n" + indent + HaxeCommenter.DOC_COMMENT_SUFFIX;
65+
document.insertString(caretOffset, toInsert);
66+
67+
editor.getCaretModel().moveToOffset(caretOffset + 1 + docNewLine.length());
68+
69+
return Result.Stop;
70+
}
71+
}
72+
return Result.Continue;
73+
74+
}
75+
76+
77+
private static boolean isInsideDocsWithoutCloseTag(@NotNull PsiFile file, int caretOffset) {
78+
PsiElement elementAtOffset = PsiUtilCore.getElementAtOffset(file, caretOffset);
79+
if (elementAtOffset instanceof HaxePsiDocCommentImpl docComment) {
80+
String text = docComment.getText();
81+
if (caretOffset < elementAtOffset.getTextOffset() + DOC_COMMENT_PREFIX.length()) {
82+
return false;
83+
}
84+
if(text.endsWith(HaxeCommenter.BLOCK_COMMENT_SUFFIX) || text.endsWith(HaxeCommenter.DOC_COMMENT_SUFFIX)) {
85+
return false;
86+
}
87+
return true;
88+
}
89+
return false;
90+
}
91+
92+
93+
/**
94+
* Temp solution to find line indentation (should probably be handled by formatter)
95+
*/
96+
@NotNull
97+
private static String getLineIndent(@NotNull String lineText) {
98+
int indentLength = lineText.stripLeading().length();
99+
return lineText.substring(0, lineText.length() - indentLength);
100+
}
101+
}
102+

src/main/resources/META-INF/plugin.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,8 @@
171171
implementationClass="com.intellij.plugins.haxe.lang.psi.manipulators.HaxeRegularExpressionLiteralManipulator"/>
172172
<lang.smartEnterProcessor language="Haxe" implementationClass="com.intellij.plugins.haxe.editor.smartEnter.HaxeSmartEnterProcessor"/>
173173

174+
<enterHandlerDelegate implementation="com.intellij.plugins.haxe.ide.editor.HaxeDocumentationEnterHandler" order="first"/>
175+
174176
<copyPastePostProcessor implementation="com.intellij.plugins.haxe.editor.HaxeReferenceCopyPasteProcessor"/>
175177

176178
<moveFileHandler implementation="com.intellij.plugins.haxe.ide.refactoring.move.HaxeFileMoveHandler"/>

src/test/java/com/intellij/plugins/haxe/editor/HaxeEnterActionTest.java

Lines changed: 151 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -28,90 +28,155 @@
2828
* @author winmain
2929
*/
3030
public class HaxeEnterActionTest extends AbstractEnterActionTestCase {
31-
//private HaxeDebugLogger.HierarchyManipulator oldLogSettings;
32-
33-
public void setUp() throws Exception {
34-
//oldLogSettings = HaxeDebugLogger.mutePrimaryConfiguration();
35-
super.setUp();
36-
}
37-
38-
@Override
39-
public void tearDown() throws Exception {
40-
// Must come before super.tearDown because oldLogSettings is cleared magically (via reflection).
41-
//oldLogSettings.restore();
42-
//oldLogSettings = null;
43-
HaxeTestUtils.cleanupUnexpiredAppleUITimers(this::addSuppressedException);
44-
super.tearDown();
45-
}
46-
47-
@NotNull
48-
@Override
49-
protected String getTestDataPath() {
50-
return HaxeTestUtils.BASE_TEST_DATA_PATH;
51-
}
52-
53-
@Override
54-
protected void doTest() throws Exception {
55-
doTest("hx");
56-
}
57-
58-
@Test
59-
public void testEnterInAbstract() throws Throwable {
60-
doTextTest("hx",
61-
"abstract Test {\n" +
62-
" var a;<caret>\n" +
63-
"}",
64-
"abstract Test {\n" +
65-
" var a;\n" +
66-
" \n" +
67-
"}");
68-
}
69-
70-
@Test
71-
public void testEnterInClass() throws Throwable {
72-
doTextTest("hx",
73-
"class Test {\n" +
74-
" var a;<caret>\n" +
75-
"}",
76-
"class Test {\n" +
77-
" var a;\n" +
78-
" \n" +
79-
"}");
80-
}
81-
82-
@Test
83-
public void testEnterInEnum() throws Throwable {
84-
doTextTest("hx",
85-
"enum Test {\n" +
86-
" FOO;<caret>\n" +
87-
"}",
88-
"enum Test {\n" +
89-
" FOO;\n" +
90-
" \n" +
91-
"}");
92-
}
93-
94-
@Test
95-
public void testEnterInExternClass() throws Throwable {
96-
doTextTest("hx",
97-
"extern class Test {\n" +
98-
" var a;<caret>\n" +
99-
"}",
100-
"extern class Test {\n" +
101-
" var a;\n" +
102-
" \n" +
103-
"}");
104-
}
105-
106-
@Test
107-
public void testEnterInInterface() throws Throwable {
108-
doTextTest("hx",
109-
"interface Test {\n" +
110-
" function qwe():Void;<caret>\n" +
111-
"}",
112-
"interface Test {\n" +
113-
" function qwe():Void;\n" +
114-
" \n" +
115-
"}");
116-
}
31+
32+
public void setUp() throws Exception {
33+
super.setUp();
34+
}
35+
36+
@Override
37+
public void tearDown() throws Exception {
38+
HaxeTestUtils.cleanupUnexpiredAppleUITimers(this::addSuppressedException);
39+
super.tearDown();
40+
}
41+
42+
@NotNull
43+
@Override
44+
protected String getTestDataPath() {
45+
return HaxeTestUtils.BASE_TEST_DATA_PATH;
46+
}
47+
48+
@Override
49+
protected void doTest() throws Exception {
50+
doTest("hx");
51+
}
52+
53+
@Test
54+
public void testEnterInAbstract() throws Throwable {
55+
doTextTest("hx",
56+
"""
57+
abstract Test {
58+
var a;<caret>
59+
}""",
60+
"""
61+
abstract Test {
62+
var a;
63+
\s
64+
}""");
65+
}
66+
67+
@Test
68+
public void testEnterInClass() throws Throwable {
69+
doTextTest("hx",
70+
"""
71+
class Test {
72+
var a;<caret>
73+
}""",
74+
"""
75+
class Test {
76+
var a;
77+
\s
78+
}""");
79+
}
80+
81+
@Test
82+
public void testEnterInEnum() throws Throwable {
83+
doTextTest("hx",
84+
"""
85+
enum Test {
86+
FOO;<caret>
87+
}""",
88+
"""
89+
enum Test {
90+
FOO;
91+
\s
92+
}""");
93+
}
94+
95+
@Test
96+
public void testEnterInExternClass() throws Throwable {
97+
doTextTest("hx",
98+
"""
99+
extern class Test {
100+
var a;<caret>
101+
}""",
102+
"""
103+
extern class Test {
104+
var a;
105+
\s
106+
}""");
107+
}
108+
109+
@Test
110+
public void testEnterInInterface() throws Throwable {
111+
doTextTest("hx",
112+
"""
113+
interface Test {
114+
function qwe():Void;<caret>
115+
}""",
116+
"""
117+
interface Test {
118+
function qwe():Void;
119+
\s
120+
}""");
121+
}
122+
123+
@Test
124+
public void testEnterAfterDocumentationStart() throws Throwable {
125+
doTextTest("hx",
126+
"""
127+
class Test {
128+
/**<caret>
129+
function foo():Void {}
130+
}
131+
""",
132+
"""
133+
class Test {
134+
/**
135+
\s
136+
**/
137+
function foo():Void {}
138+
}
139+
""");
140+
}
141+
142+
@Test
143+
public void testEnterAfterDocumentationStartWhenClosed() throws Throwable {
144+
doTextTest("hx",
145+
"""
146+
class Test {
147+
/**<caret>
148+
**/
149+
function foo():Void {}
150+
}
151+
""",
152+
// TODO formatting should probably indent this the same way as testEnterAfterDocumentationStart
153+
"""
154+
class Test {
155+
/**
156+
\s
157+
**/
158+
function foo():Void {}
159+
}
160+
""");
161+
}
162+
163+
@Test
164+
public void testEnterAfterDocumentationStartOnLineWithContent() throws Throwable {
165+
doTextTest("hx",
166+
"""
167+
class Test {/**<caret>
168+
function foo():Void {}
169+
}
170+
""",
171+
// TODO formatting
172+
"""
173+
class Test {/**
174+
\s
175+
**/
176+
function foo():Void {}
177+
}
178+
""");
179+
}
180+
181+
117182
}

0 commit comments

Comments
 (0)