intention to put args/parameters on single line: IDEA-209924

GitOrigin-RevId: d603666f0de7fb1f3be09a2f3616c6fa552102d2
This commit is contained in:
Roman.Ivanov
2019-07-02 06:52:16 +03:00
committed by intellij-monorepo-bot
parent 002e7efd27
commit a411f20f37
30 changed files with 513 additions and 69 deletions
@@ -1908,9 +1908,19 @@
</intentionAction>
<intentionAction>
<className>com.intellij.codeInsight.intention.impl.lists.JavaChopParametersAction</className>
<category>Java/Other</category>
</intentionAction>
<intentionAction>
<className>com.intellij.codeInsight.intention.impl.lists.JavaChopArgumentsAction</className>
<category>Java/Other</category>
</intentionAction>
<intentionAction>
<className>com.intellij.codeInsight.intention.impl.lists.JavaJoinParametersAction</className>
<category>Java/Other</category>
</intentionAction>
<intentionAction>
<className>com.intellij.codeInsight.intention.impl.lists.JavaJoinArgumentsAction</className>
<category>Java/Other</category>
</intentionAction>
<externalAnnotationsArtifactsResolver implementation="com.intellij.jarRepository.ExternalAnnotationsRepositoryResolver"/>
<errorQuickFixProvider implementation="com.intellij.codeInsight.daemon.impl.analysis.JavaErrorQuickFixProvider"/>
@@ -30,24 +30,24 @@ public abstract class AbstractChopListAction<L extends PsiElement, E extends Psi
int size = elements.size();
for (int i = elements.size() - 1; i >= 0; i--) {
E el = elements.get(i);
if (!hasBreakAfter(el)) {
int offset = findPlaceForBreakAfter(el);
if (nextBreak(el) == null) {
int offset = findOffsetForBreakAfter(el);
if (i == size - 1 && !needTailBreak(el)) continue;
document.insertString(offset, "\n");
}
}
E first = elements.get(0);
if (needHeadBreak(first)){
document.insertString(getOffsetOfBreakBeforeFirstElement(first), "\n");
document.insertString(findOffsetOfBreakBeforeFirst(first), "\n");
}
PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project);
documentManager.commitDocument(document);
CodeStyleManager.getInstance(project).adjustLineIndent(context.list.getContainingFile(), context.list.getParent().getTextRange());
}
abstract int findPlaceForBreakAfter(E element);
abstract int findOffsetForBreakAfter(E element);
protected int getOffsetOfBreakBeforeFirstElement(@NotNull E element) {
protected int findOffsetOfBreakBeforeFirst(@NotNull E element) {
return element.getTextRange().getStartOffset();
}
@@ -78,9 +78,9 @@ public abstract class AbstractChopListAction<L extends PsiElement, E extends Psi
for (int i = 0; i < size; i++) {
E current = elements.get(i);
if (i == 0) {
if (needHeadBreak(current) && !hasBreakBefore(current)) return true;
if (needHeadBreak(current) && prevBreak(current) == null) return true;
}
if (!hasBreakAfter(current)) {
if (nextBreak(current) == null) {
if (i == size - 1 && !needTailBreak(current)) continue;
return true;
}
@@ -1,41 +1,30 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.codeInsight.intention.impl.lists;
import com.intellij.psi.*;
import com.intellij.psi.JavaTokenType;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiJavaToken;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static com.intellij.util.ObjectUtils.tryCast;
public abstract class AbstractJavaChopListAction<L extends PsiElement, E extends PsiElement> extends AbstractChopListAction<L, E> {
@Override
boolean hasBreakBefore(@NotNull E element) {
PsiElement current = element.getPrevSibling();
while (current != null && isValidIntermediateElement(current)) {
if (current instanceof PsiWhiteSpace && current.textContains('\n')) return true;
current = current.getPrevSibling();
}
return false;
@Nullable
PsiElement prevBreak(@NotNull PsiElement element) {
return JavaListUtils.prevBreak(element);
}
@Override
boolean hasBreakAfter(@NotNull E element) {
PsiElement current = element.getNextSibling();
while (current != null && isValidIntermediateElement(current)) {
if (current instanceof PsiWhiteSpace && current.textContains('\n')) return true;
current = current.getNextSibling();
}
return false;
}
private static boolean isValidIntermediateElement(@NotNull PsiElement element) {
return element instanceof PsiWhiteSpace || element instanceof PsiComment ||
(element instanceof PsiJavaToken && ((PsiJavaToken)element).getTokenType() == JavaTokenType.COMMA);
@Nullable
PsiElement nextBreak(@NotNull PsiElement element) {
return JavaListUtils.nextBreak(element);
}
@Override
int findPlaceForBreakAfter(E element) {
int findOffsetForBreakAfter(E element) {
PsiJavaToken token = tryCast(PsiTreeUtil.skipWhitespacesAndCommentsForward(element), PsiJavaToken.class);
if (token != null && token.getTokenType() == JavaTokenType.COMMA) return token.getTextRange().getEndOffset();
return element.getTextRange().getEndOffset();
@@ -0,0 +1,20 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.codeInsight.intention.impl.lists;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public abstract class AbstractJavaJoinListAction<L extends PsiElement, E extends PsiElement> extends AbstractJoinListAction<L, E> {
@Override
@Nullable
PsiElement prevBreak(@NotNull PsiElement element) {
return JavaListUtils.prevBreak(element);
}
@Override
@Nullable
PsiElement nextBreak(@NotNull PsiElement element) {
return JavaListUtils.nextBreak(element);
}
}
@@ -0,0 +1,108 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.codeInsight.intention.impl.lists;
import com.google.common.collect.Lists;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
public abstract class AbstractJoinListAction<L extends PsiElement, E extends PsiElement> extends AbstractListIntentionAction<L, E> {
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) {
return from(element) != null;
}
@Override
public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) throws IncorrectOperationException {
Context<L> context = from(element);
if (context == null) return;
WhitespacesInfo info = context.myWhitespacesInfo;
List<PsiElement> reversedBreaks = Lists.reverse(info.myBreaks);
Document document = editor.getDocument();
deleteBreakIfPresent(document, info.myAfterLastBreak);
for (PsiElement aBreak : reversedBreaks) {
TextRange range = aBreak.getTextRange();
document.replaceString(range.getStartOffset(), range.getEndOffset(), " ");
}
deleteBreakIfPresent(document, info.myBeforeFirstBreak);
PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project);
documentManager.commitDocument(document);
CodeStyleManager.getInstance(project).adjustLineIndent(context.myList.getContainingFile(), context.myList.getParent().getTextRange());
}
private void deleteBreakIfPresent(Document document, PsiElement aBreak) {
if (aBreak != null) {
TextRange range = aBreak.getTextRange();
document.deleteString(range.getStartOffset(), range.getEndOffset());
}
}
private static class Context<L extends PsiElement> {
final @NotNull WhitespacesInfo myWhitespacesInfo;
final @NotNull L myList;
private Context(@NotNull WhitespacesInfo info, @NotNull L list) {
myWhitespacesInfo = info;
myList = list;
}
}
private Context<L> from(@NotNull PsiElement element) {
L list = extractList(element);
if (list == null) return null;
List<E> elements = getElements(list);
if (elements == null) return null;
if (elements.size() < minElementCount()) return null;
WhitespacesInfo whitespacesInfo = collectBreakWhitespaces(elements);
if (whitespacesInfo == null) return null;
return new Context<>(whitespacesInfo, list);
}
private static class WhitespacesInfo {
final @NotNull List<PsiElement> myBreaks;
final @Nullable PsiElement myBeforeFirstBreak;
final @Nullable PsiElement myAfterLastBreak;
private WhitespacesInfo(@NotNull List<PsiElement> breaks, @Nullable PsiElement beforeFirstBreak, @Nullable PsiElement afterLastBreak) {
myBreaks = breaks;
myBeforeFirstBreak = beforeFirstBreak;
myAfterLastBreak = afterLastBreak;
}
}
private WhitespacesInfo collectBreakWhitespaces(List<E> elements) {
List<PsiElement> breaks = new ArrayList<>();
PsiElement beforeFirst = null;
PsiElement afterLastToDelete = null;
int size = elements.size();
for (int i = 0; i < size; i++) {
E current = elements.get(i);
if (i == 0 && !needHeadBreak(current)) {
beforeFirst = prevBreak(current);
}
PsiElement nextBreak = nextBreak(current);
if (nextBreak == null) continue;
if (i == size - 1) {
if (!needTailBreak(current)) {
afterLastToDelete = nextBreak;
}
} else {
breaks.add(nextBreak);
}
}
if (breaks.isEmpty() && beforeFirst == null && afterLastToDelete == null) return null;
return new WhitespacesInfo(breaks, beforeFirst, afterLastToDelete);
}
}
@@ -15,9 +15,11 @@ public abstract class AbstractListIntentionAction<L extends PsiElement, E extend
@Nullable("When failed")
abstract List<E> getElements(@NotNull L list);
abstract boolean hasBreakBefore(@NotNull E element);
@Nullable
abstract PsiElement prevBreak(@NotNull PsiElement element);
abstract boolean hasBreakAfter(@NotNull E element);
@Nullable
abstract PsiElement nextBreak(@NotNull PsiElement element);
/**
* Min count of elements for intention to work
@@ -39,33 +41,4 @@ public abstract class AbstractListIntentionAction<L extends PsiElement, E extend
* @return true if it requires line break before first element
*/
abstract boolean needHeadBreak(@NotNull E first);
//boolean hasNewlineBetween(@NotNull E left, @NotNull E right) {
// PsiElement current = left.getNextSibling();
// while (current != null && current != right) {
// if (isWhitespaceWithBreak(current)) {
// return true;
// }
// current = current.getNextSibling();
// }
// return false;
//}
//
//private boolean isWhitespaceWithBreak(PsiElement element) {
// return isWhitespace(element) && element.textContains('\n');
//}
//
//boolean hasLeadingNewline(@NotNull E element) {
// PsiElement current = element.getPrevSibling();
// while (current != null) {
// current = current.getPrevSibling();
// if (isWhitespaceWithBreak(current)) {
//
// }
// }
//}
//boolean hasTrailingNewline(@NotNull E element) {
//
//}
}
@@ -12,15 +12,13 @@ import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.List;
import static com.intellij.codeInsight.intention.impl.lists.JavaListUtils.getCallArgumentsList;
public class JavaChopArgumentsAction extends AbstractJavaChopListAction<PsiExpressionList, PsiExpression> {
@Nullable("When failed")
@Override
PsiExpressionList extractList(@NotNull PsiElement element) {
PsiExpressionList expressionList = PsiTreeUtil.getParentOfType(element, PsiExpressionList.class, false, PsiCodeBlock.class);
if (expressionList == null) return null;
PsiElement parent = expressionList.getParent();
if (!(parent instanceof PsiCall)) return null;
return expressionList;
return getCallArgumentsList(element);
}
@Nullable("When failed")
@@ -0,0 +1,52 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.codeInsight.intention.impl.lists;
import com.intellij.application.options.CodeStyle;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.List;
import static com.intellij.codeInsight.intention.impl.lists.JavaListUtils.getCallArgumentsList;
public class JavaJoinArgumentsAction extends AbstractJavaJoinListAction<PsiExpressionList, PsiExpression> {
@Nullable("When failed")
@Override
PsiExpressionList extractList(@NotNull PsiElement element) {
return getCallArgumentsList(element);
}
@Nullable("When failed")
@Override
List<PsiExpression> getElements(@NotNull PsiExpressionList list) {
return Arrays.asList(list.getExpressions());
}
@Override
boolean needTailBreak(@NotNull PsiExpression last) {
return CodeStyle.getLanguageSettings(last.getContainingFile(), JavaLanguage.INSTANCE).CALL_PARAMETERS_RPAREN_ON_NEXT_LINE;
}
@Override
boolean needHeadBreak(@NotNull PsiExpression first) {
return CodeStyle.getLanguageSettings(first.getContainingFile(), JavaLanguage.INSTANCE).CALL_PARAMETERS_LPAREN_ON_NEXT_LINE;
}
@Nls(capitalization = Nls.Capitalization.Sentence)
@NotNull
@Override
public String getFamilyName() {
return "Put arguments on one line";
}
@NotNull
@Override
public String getText() {
return getFamilyName();
}
}
@@ -0,0 +1,52 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.codeInsight.intention.impl.lists;
import com.intellij.application.options.CodeStyle;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiParameter;
import com.intellij.psi.PsiParameterList;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.List;
public class JavaJoinParametersAction extends AbstractJavaJoinListAction<PsiParameterList, PsiParameter> {
@Nullable("When failed")
@Override
PsiParameterList extractList(@NotNull PsiElement element) {
return PsiTreeUtil.getParentOfType(element, PsiParameterList.class, false);
}
@Nullable("When failed")
@Override
List<PsiParameter> getElements(@NotNull PsiParameterList list) {
return Arrays.asList(list.getParameters());
}
@Override
boolean needTailBreak(@NotNull PsiParameter last) {
return CodeStyle.getLanguageSettings(last.getContainingFile(), JavaLanguage.INSTANCE).METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE;
}
@Override
boolean needHeadBreak(@NotNull PsiParameter first) {
return CodeStyle.getLanguageSettings(first.getContainingFile(), JavaLanguage.INSTANCE).METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE;
}
@Nls(capitalization = Nls.Capitalization.Sentence)
@NotNull
@Override
public String getFamilyName() {
return "Put parameters on one line";
}
@NotNull
@Override
public String getText() {
return getFamilyName();
}
}
@@ -0,0 +1,45 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.codeInsight.intention.impl.lists;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
class JavaListUtils {
private JavaListUtils() { }
@Nullable
static PsiElement prevBreak(@NotNull PsiElement element) {
PsiElement current = element.getPrevSibling();
while (current != null && isValidIntermediateElement(current)) {
if (current instanceof PsiWhiteSpace && current.textContains('\n')) return current;
current = current.getPrevSibling();
}
return null;
}
@Nullable
static PsiElement nextBreak(@NotNull PsiElement element) {
PsiElement current = element.getNextSibling();
while (current != null && isValidIntermediateElement(current)) {
if (current instanceof PsiWhiteSpace && current.textContains('\n')) return current;
current = current.getNextSibling();
}
return null;
}
private static boolean isValidIntermediateElement(@NotNull PsiElement element) {
return element instanceof PsiWhiteSpace ||
(element instanceof PsiJavaToken && ((PsiJavaToken)element).getTokenType() == JavaTokenType.COMMA);
}
@Nullable
static PsiExpressionList getCallArgumentsList(@NotNull PsiElement element) {
PsiExpressionList expressionList = PsiTreeUtil.getParentOfType(element, PsiExpressionList.class, false, PsiCodeBlock.class);
if (expressionList == null) return null;
PsiElement parent = expressionList.getParent();
if (!(parent instanceof PsiCall)) return null;
return expressionList;
}
}
@@ -1,6 +1,5 @@
<html>
<body>
<p>Put arguments on separate lines. Tail and head breaks are set according to formatting settings.
</p>
Put arguments on separate lines. Tail and head breaks are set according to formatting settings.
</body>
</html>
@@ -1,7 +1,6 @@
<html>
<body>
<p>Put parameters on separate lines. Tail and head breaks are set according to formatting settings.
</p>
Put parameters on separate lines. Tail and head breaks are set according to formatting settings.
<!-- tooltip end -->
</body>
</html>
@@ -0,0 +1,5 @@
class A {
void foo(int a1, int a2, int a3) {
foo(<spot>12, 23, 4</spot>);
}
}
@@ -0,0 +1,9 @@
class A {
void foo(int a1, int a2, int a3) {
foo(<spot>
12,
23,
4</spot>
);
}
}
@@ -0,0 +1,6 @@
<html>
<body>
Put arguments of the call on single line. Tail and head breaks are set according to formatting settings.
<!-- tooltip end -->
</body>
</html>
@@ -0,0 +1,4 @@
class A {
void foo(<spot>A a1, A a2, A a3</spot>) {
}
}
@@ -0,0 +1,8 @@
class A {
void foo(<spot>
A a1,
A a2,
A a3</spot>
) {
}
}
@@ -0,0 +1,6 @@
<html>
<body>
Put parameters of the call on single line. Tail and head breaks are set according to formatting settings.
<!-- tooltip end -->
</body>
</html>
@@ -0,0 +1,7 @@
// "Put arguments on one line" "true"
class A {
void foo(int a1, int a2, int a3) {
foo(12, 23, 4);
}
}
@@ -0,0 +1,11 @@
// "Put arguments on one line" "true"
class A {
void foo(int a1, int a2, int a3) {
foo(
12,
23,<caret>
4
);
}
}
@@ -0,0 +1,11 @@
// "Put arguments on one line" "false"
// break after lparen
// break before rparen
class A {
void foo(int a1, int a2, int a3) {
foo(
12, 23,<caret> 4
);
}
}
@@ -0,0 +1,7 @@
// "Put parameters on one line" "true"
class A {
void foo(A a1, A a2, A a3) {
}
}
@@ -0,0 +1,7 @@
// "Put parameters on one line" "true"
class A {
void foo(A a1, A a2, A a3) {
}
}
@@ -0,0 +1,11 @@
// "Put parameters on one line" "true"
class A {
void foo(
A a1,
A a2<caret>,
A a3
) {
}
}
@@ -0,0 +1,7 @@
// "Put parameters on one line" "false"
class A {
void foo(A a1, A a2<caret>, A a3) {
}
}
@@ -0,0 +1,9 @@
// "Put parameters on one line" "false"
// break after lparen
class A {
void foo(
A a1, A a2<caret>, A a3) {
}
}
@@ -0,0 +1,11 @@
// "Put parameters on one line" "false"
// break after lparen
// break before rparen
class A {
void foo(
A a1, A a2<caret>, A a3
) {
}
}
@@ -0,0 +1,8 @@
// "Put parameters on one line" "true"
class A {
void foo(A a1,
A a2<caret>, A a3) {
}
}
@@ -0,0 +1,36 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.java.codeInsight.intention;
import com.intellij.application.options.CodeStyle;
import com.intellij.codeInsight.daemon.LightIntentionActionTestCase;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
public class JavaJoinArgumentsActionTest extends LightIntentionActionTestCase {
private boolean myBreakAfterLparen;
private boolean myBreakBeforeRparen;
@Override
protected String getBasePath() {
return "/codeInsight/daemonCodeAnalyzer/quickFix/lists/joinArguments";
}
@Override
protected void beforeActionStarted(final String testName, final String contents) {
super.beforeActionStarted(testName, contents);
final CommonCodeStyleSettings settings = CodeStyle.getSettings(getProject()).getCommonSettings(JavaLanguage.INSTANCE);
myBreakAfterLparen = settings.CALL_PARAMETERS_LPAREN_ON_NEXT_LINE;
myBreakBeforeRparen = settings.CALL_PARAMETERS_RPAREN_ON_NEXT_LINE;
settings.CALL_PARAMETERS_LPAREN_ON_NEXT_LINE = contents.contains("break after lparen");
settings.CALL_PARAMETERS_RPAREN_ON_NEXT_LINE = contents.contains("break before rparen");
}
@Override
protected void afterActionCompleted(final String testName, final String contents) {
super.afterActionCompleted(testName, contents);
CommonCodeStyleSettings settings = CodeStyle.getSettings(getProject()).getCommonSettings(JavaLanguage.INSTANCE);
settings.CALL_PARAMETERS_LPAREN_ON_NEXT_LINE = myBreakAfterLparen;
settings.CALL_PARAMETERS_RPAREN_ON_NEXT_LINE = myBreakBeforeRparen;
}
}
@@ -0,0 +1,36 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.java.codeInsight.intention;
import com.intellij.application.options.CodeStyle;
import com.intellij.codeInsight.daemon.LightIntentionActionTestCase;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
public class JavaJoinParametersActionTest extends LightIntentionActionTestCase {
private boolean myBreakAfterLparen;
private boolean myBreakBeforeRparen;
@Override
protected String getBasePath() {
return "/codeInsight/daemonCodeAnalyzer/quickFix/lists/joinParameters";
}
@Override
protected void beforeActionStarted(final String testName, final String contents) {
super.beforeActionStarted(testName, contents);
final CommonCodeStyleSettings settings = CodeStyle.getSettings(getProject()).getCommonSettings(JavaLanguage.INSTANCE);
myBreakAfterLparen = settings.METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE;
myBreakBeforeRparen = settings.METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE;
settings.METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE = contents.contains("break after lparen");
settings.METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE = contents.contains("break before rparen");
}
@Override
protected void afterActionCompleted(final String testName, final String contents) {
super.afterActionCompleted(testName, contents);
CommonCodeStyleSettings settings = CodeStyle.getSettings(getProject()).getCommonSettings(JavaLanguage.INSTANCE);
settings.METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE = myBreakAfterLparen;
settings.METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE = myBreakBeforeRparen;
}
}