Skip to content

Commit afd08f6

Browse files
committed
[FEATURE] Intentions to shift a column left/right => v1.5.0
1 parent b9ec948 commit afd08f6

File tree

30 files changed

+334
-34
lines changed

30 files changed

+334
-34
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package net.seesharpsoft.intellij.plugins.csv;
2+
3+
import com.intellij.psi.PsiElement;
4+
import com.intellij.psi.util.PsiTreeUtil;
5+
import net.seesharpsoft.intellij.plugins.csv.psi.CsvField;
6+
import net.seesharpsoft.intellij.plugins.csv.psi.CsvFile;
7+
import net.seesharpsoft.intellij.plugins.csv.psi.CsvRecord;
8+
9+
import java.util.HashMap;
10+
import java.util.Map;
11+
12+
public class CsvHelper {
13+
14+
public static Map<Integer, CsvColumnInfo<PsiElement>> createColumnInfoMap(CsvFile csvFile) {
15+
Map<Integer, CsvColumnInfo<PsiElement>> columnInfoMap = new HashMap<>();
16+
CsvRecord[] records = PsiTreeUtil.getChildrenOfType(csvFile, CsvRecord.class);
17+
int row = 0;
18+
for (CsvRecord record : records) {
19+
int column = 0;
20+
for (CsvField field : record.getFieldList()) {
21+
Integer length = field.getTextLength();
22+
if (!columnInfoMap.containsKey(column)) {
23+
columnInfoMap.put(column, new CsvColumnInfo(column, length));
24+
} else if (columnInfoMap.get(column).getMaxLength() < length) {
25+
columnInfoMap.get(column).setMaxLength(length);
26+
}
27+
columnInfoMap.get(column).addElement(field, row);
28+
++column;
29+
}
30+
++row;
31+
}
32+
return columnInfoMap;
33+
}
34+
35+
}

src/main/java/net/seesharpsoft/intellij/plugins/csv/CsvStructureViewElement.java

+1-26
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,12 @@
1616
import com.intellij.psi.impl.source.DummyHolderFactory;
1717
import com.intellij.psi.impl.source.tree.FileElement;
1818
import com.intellij.psi.tree.IElementType;
19-
import com.intellij.psi.util.PsiTreeUtil;
20-
import net.seesharpsoft.intellij.plugins.csv.psi.CsvField;
2119
import net.seesharpsoft.intellij.plugins.csv.psi.CsvFile;
22-
import net.seesharpsoft.intellij.plugins.csv.psi.CsvRecord;
2320
import net.seesharpsoft.intellij.plugins.csv.psi.CsvTypes;
2421
import org.jetbrains.annotations.NotNull;
2522
import org.jetbrains.annotations.Nullable;
2623

2724
import javax.swing.*;
28-
import java.util.HashMap;
2925
import java.util.List;
3026
import java.util.Map;
3127

@@ -117,7 +113,7 @@ public File(PsiFile element) {
117113
@Override
118114
public TreeElement[] getChildren() {
119115
if (element instanceof CsvFile) {
120-
Map<Integer, CsvColumnInfo<PsiElement>> columnInfoMap = createColumnInfoMap((CsvFile) element);
116+
Map<Integer, CsvColumnInfo<PsiElement>> columnInfoMap = CsvHelper.createColumnInfoMap((CsvFile) element);
121117
TreeElement[] children = new TreeElement[columnInfoMap.size()];
122118
for (Map.Entry<Integer, CsvColumnInfo<PsiElement>> entry : columnInfoMap.entrySet()) {
123119
CsvColumnInfo<PsiElement> columnInfo = entry.getValue();
@@ -132,27 +128,6 @@ public TreeElement[] getChildren() {
132128
return EMPTY_ARRAY;
133129
}
134130
}
135-
136-
private Map<Integer, CsvColumnInfo<PsiElement>> createColumnInfoMap(CsvFile csvFile) {
137-
Map<Integer, CsvColumnInfo<PsiElement>> columnInfoMap = new HashMap<>();
138-
CsvRecord[] records = PsiTreeUtil.getChildrenOfType(csvFile, CsvRecord.class);
139-
int row = 0;
140-
for (CsvRecord record : records) {
141-
int column = 0;
142-
for (CsvField field : record.getFieldList()) {
143-
Integer length = field.getTextLength();
144-
if (!columnInfoMap.containsKey(column)) {
145-
columnInfoMap.put(column, new CsvColumnInfo(column, length));
146-
} else if (columnInfoMap.get(column).getMaxLength() < length) {
147-
columnInfoMap.get(column).setMaxLength(length);
148-
}
149-
columnInfoMap.get(column).addElement(field, row);
150-
++column;
151-
}
152-
++row;
153-
}
154-
return columnInfoMap;
155-
}
156131
}
157132

158133
private static class Header extends CsvStructureViewElement {

src/main/java/net/seesharpsoft/intellij/plugins/csv/intention/CsvIntentionHelper.java

+24-2
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ public static PsiElement getParentFieldElement(PsiElement element) {
4343
element = element.getParent();
4444
} else if (getElementType(element.getPrevSibling()) == CsvTypes.FIELD) {
4545
element = element.getPrevSibling();
46+
} else if (getElementType(element.getNextSibling()) == CsvTypes.FIELD) {
47+
element = element.getNextSibling();
4648
} else {
4749
element = null;
4850
}
@@ -54,7 +56,7 @@ public static PsiElement getParentFieldElement(PsiElement element) {
5456
return element;
5557
}
5658

57-
private static PsiElement getPreviousSeparator(PsiElement fieldElement) {
59+
public static PsiElement getPreviousSeparator(PsiElement fieldElement) {
5860
while (fieldElement != null) {
5961
if (getElementType(fieldElement) == CsvTypes.COMMA) {
6062
break;
@@ -64,7 +66,7 @@ private static PsiElement getPreviousSeparator(PsiElement fieldElement) {
6466
return fieldElement;
6567
}
6668

67-
private static PsiElement getNextSeparator(PsiElement fieldElement) {
69+
public static PsiElement getNextSeparator(PsiElement fieldElement) {
6870
while (fieldElement != null) {
6971
if (getElementType(fieldElement) == CsvTypes.COMMA) {
7072
break;
@@ -74,6 +76,26 @@ private static PsiElement getNextSeparator(PsiElement fieldElement) {
7476
return fieldElement;
7577
}
7678

79+
public static PsiElement getPreviousCRLF(PsiElement recordElement) {
80+
while (recordElement != null) {
81+
if (getElementType(recordElement) == CsvTypes.CRLF) {
82+
break;
83+
}
84+
recordElement = recordElement.getPrevSibling();
85+
}
86+
return recordElement;
87+
}
88+
89+
public static PsiElement getNextCRLF(PsiElement recordElement) {
90+
while (recordElement != null) {
91+
if (getElementType(recordElement) == CsvTypes.CRLF) {
92+
break;
93+
}
94+
recordElement = recordElement.getNextSibling();
95+
}
96+
return recordElement;
97+
}
98+
7799
public static Collection<PsiElement> getAllElements(PsiFile file) {
78100
List<PsiElement> todo = getChildren(file);
79101
Collection<PsiElement> elements = new HashSet();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package net.seesharpsoft.intellij.plugins.csv.intention;
2+
3+
import com.intellij.openapi.editor.Document;
4+
import com.intellij.openapi.project.Project;
5+
import com.intellij.openapi.util.TextRange;
6+
import com.intellij.psi.PsiDocumentManager;
7+
import com.intellij.psi.PsiElement;
8+
import net.seesharpsoft.intellij.plugins.csv.CsvColumnInfo;
9+
import net.seesharpsoft.intellij.plugins.csv.psi.CsvFile;
10+
import org.jetbrains.annotations.NotNull;
11+
12+
import java.util.List;
13+
14+
public abstract class CsvShiftColumnIntentionAction extends CsvIntentionAction {
15+
16+
protected CsvShiftColumnIntentionAction(String text) {
17+
super(text);
18+
}
19+
20+
protected static void changeLeftAndRightColumnOrder(@NotNull Project project, CsvFile psiFile, CsvColumnInfo<PsiElement> leftColumnInfo, CsvColumnInfo<PsiElement> rightColumnInfo) {
21+
Document document = PsiDocumentManager.getInstance(project).getDocument(psiFile);
22+
document.setText(changeLeftAndRightColumnOrder(document.getText(), leftColumnInfo, rightColumnInfo));
23+
}
24+
25+
@NotNull
26+
protected static String changeLeftAndRightColumnOrder(String text, CsvColumnInfo<PsiElement> leftColumnInfo, CsvColumnInfo<PsiElement> rightColumnInfo) {
27+
List<PsiElement> rightElements = rightColumnInfo.getElements();
28+
List<PsiElement> leftElements = leftColumnInfo.getElements();
29+
int lastIndex = 0, maxRows = leftElements.size();
30+
StringBuilder newText = new StringBuilder();
31+
32+
for (int row = 0; row < maxRows; ++row) {
33+
PsiElement leftElement = leftElements.get(row);
34+
if (leftElement == null) {
35+
continue;
36+
}
37+
PsiElement rightElement = rightElements.size() > row ? rightElements.get(row) : null;
38+
39+
TextRange leftSeparator = findPreviousSeparatorOrCRLF(leftElement);
40+
TextRange middleSeparator = findNextSeparatorOrCRLF(leftElement);
41+
TextRange rightSeparator = rightElement == null ?
42+
TextRange.create(middleSeparator.getEndOffset(), middleSeparator.getEndOffset()) :
43+
findNextSeparatorOrCRLF(rightElement);
44+
45+
newText.append(text.substring(lastIndex, leftSeparator.getEndOffset()))
46+
.append(text.substring(middleSeparator.getEndOffset(), rightSeparator.getStartOffset()))
47+
.append(",")
48+
.append(text.substring(leftSeparator.getEndOffset(), middleSeparator.getStartOffset()));
49+
50+
lastIndex = rightSeparator.getStartOffset();
51+
}
52+
newText.append(text.substring(lastIndex));
53+
return newText.toString();
54+
}
55+
56+
protected static TextRange findPreviousSeparatorOrCRLF(PsiElement psiElement) {
57+
TextRange textRange;
58+
PsiElement separator = CsvIntentionHelper.getPreviousSeparator(psiElement);
59+
if (separator == null) {
60+
separator = CsvIntentionHelper.getPreviousCRLF(psiElement.getParent());
61+
if (separator == null) {
62+
separator = psiElement.getParent().getParent().getFirstChild();
63+
textRange = TextRange.create(separator.getTextRange().getStartOffset(), separator.getTextRange().getStartOffset());
64+
} else {
65+
textRange = separator.getTextRange();
66+
}
67+
} else {
68+
textRange = separator.getTextRange();
69+
}
70+
return textRange;
71+
}
72+
73+
protected static TextRange findNextSeparatorOrCRLF(PsiElement psiElement) {
74+
TextRange textRange;
75+
PsiElement separator = CsvIntentionHelper.getNextSeparator(psiElement);
76+
if (separator == null) {
77+
separator = CsvIntentionHelper.getNextCRLF(psiElement.getParent());
78+
if (separator == null) {
79+
separator = psiElement.getParent().getParent().getLastChild();
80+
textRange = TextRange.create(separator.getTextRange().getEndOffset(), separator.getTextRange().getEndOffset());
81+
} else {
82+
textRange = TextRange.create(separator.getTextRange().getStartOffset(), separator.getTextRange().getStartOffset());
83+
}
84+
} else {
85+
textRange = separator.getTextRange();
86+
}
87+
return textRange;
88+
}
89+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package net.seesharpsoft.intellij.plugins.csv.intention;
2+
3+
import com.intellij.openapi.editor.Editor;
4+
import com.intellij.openapi.project.Project;
5+
import com.intellij.psi.PsiElement;
6+
import com.intellij.util.IncorrectOperationException;
7+
import net.seesharpsoft.intellij.plugins.csv.CsvColumnInfo;
8+
import net.seesharpsoft.intellij.plugins.csv.CsvHelper;
9+
import net.seesharpsoft.intellij.plugins.csv.psi.CsvFile;
10+
import org.jetbrains.annotations.NotNull;
11+
12+
import java.util.Map;
13+
14+
public class CsvShiftColumnLeftIntentionAction extends CsvShiftColumnIntentionAction {
15+
16+
protected CsvShiftColumnLeftIntentionAction() {
17+
super("Shift Column Left");
18+
}
19+
20+
@Override
21+
public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement psiElement) throws IncorrectOperationException {
22+
CsvFile psiFile = (CsvFile)psiElement.getContainingFile();
23+
24+
psiElement = CsvIntentionHelper.getParentFieldElement(psiElement);
25+
26+
Map<Integer, CsvColumnInfo<PsiElement>> columnInfoMap = CsvHelper.createColumnInfoMap(psiFile);
27+
28+
CsvColumnInfo<PsiElement> rightColumnInfo = null;
29+
for (CsvColumnInfo columnInfo : columnInfoMap.values()) {
30+
if (columnInfo.containsElement(psiElement)) {
31+
rightColumnInfo = columnInfo;
32+
break;
33+
}
34+
}
35+
36+
// column must be at least index 1 to be shifted left
37+
if (rightColumnInfo == null || rightColumnInfo.getColumnIndex() < 1) {
38+
return;
39+
}
40+
41+
CsvColumnInfo<PsiElement> leftColumnInfo = columnInfoMap.get(rightColumnInfo.getColumnIndex() - 1);
42+
43+
changeLeftAndRightColumnOrder(project, psiFile, leftColumnInfo, rightColumnInfo);
44+
}
45+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package net.seesharpsoft.intellij.plugins.csv.intention;
2+
3+
import com.intellij.openapi.editor.Editor;
4+
import com.intellij.openapi.project.Project;
5+
import com.intellij.psi.PsiElement;
6+
import com.intellij.util.IncorrectOperationException;
7+
import net.seesharpsoft.intellij.plugins.csv.CsvColumnInfo;
8+
import net.seesharpsoft.intellij.plugins.csv.CsvHelper;
9+
import net.seesharpsoft.intellij.plugins.csv.psi.CsvFile;
10+
import org.jetbrains.annotations.NotNull;
11+
12+
import java.util.Map;
13+
14+
public class CsvShiftColumnRightIntentionAction extends CsvShiftColumnIntentionAction {
15+
16+
protected CsvShiftColumnRightIntentionAction() {
17+
super("Shift Column Right");
18+
}
19+
20+
@Override
21+
public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement psiElement) throws IncorrectOperationException {
22+
CsvFile psiFile = (CsvFile)psiElement.getContainingFile();
23+
24+
psiElement = CsvIntentionHelper.getParentFieldElement(psiElement);
25+
26+
Map<Integer, CsvColumnInfo<PsiElement>> columnInfoMap = CsvHelper.createColumnInfoMap(psiFile);
27+
28+
CsvColumnInfo<PsiElement> leftColumnInfo = null;
29+
for (CsvColumnInfo columnInfo : columnInfoMap.values()) {
30+
if (columnInfo.containsElement(psiElement)) {
31+
leftColumnInfo = columnInfo;
32+
break;
33+
}
34+
}
35+
36+
// column must be at least index 1 to be shifted left
37+
if (leftColumnInfo == null || leftColumnInfo.getColumnIndex() + 1 >= columnInfoMap.size()) {
38+
return;
39+
}
40+
41+
CsvColumnInfo<PsiElement> rightColumnInfo = columnInfoMap.get(leftColumnInfo.getColumnIndex() + 1);
42+
43+
changeLeftAndRightColumnOrder(project, psiFile, leftColumnInfo, rightColumnInfo);
44+
}
45+
}

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

+15-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<idea-plugin>
22
<id>net.seesharpsoft.intellij.plugins.csv</id>
33
<name>CSV Plugin</name>
4-
<version>1.4.1</version>
4+
<version>1.5.0</version>
55
<vendor url="https://github.com/SeeSharpSoft/intellij-csv-validator">
66
Martin Sommer
77
</vendor>
@@ -15,7 +15,7 @@
1515
<li>syntax highlighting (configurable)</li>
1616
<li>content formatting (configurable)</li>
1717
<li>quick fix inspections</li>
18-
<li>intentions, e.g. Quote/Unquote (all)</li>
18+
<li>intentions (Alt+Enter), e.g. Quote/Unquote (all), Shift Column Left/Right</li>
1919
<li>structure view (header-entry layout)</li>
2020
<li>support for ',' or ';' as value separator</li>
2121
</ul>
@@ -29,8 +29,7 @@
2929
]]></description>
3030

3131
<change-notes><![CDATA[
32-
Handle tabs as 1-length character - fixes 'Tabularize' for csv files with tabs<br>
33-
Fix default settings initialization
32+
NEW: intentions to shift a whole column left/right<br>
3433
]]>
3534
</change-notes>
3635

@@ -70,8 +69,18 @@
7069
groupName="CSV"
7170
shortName="CsvValidation"
7271
implementationClass="net.seesharpsoft.intellij.plugins.csv.intention.CsvValidationInspection" />
73-
74-
<intentionAction id="CsvQuoteValue" order="FIRST">
72+
73+
<intentionAction id="CsvShiftColumnLeft" order="FIRST">
74+
<className>net.seesharpsoft.intellij.plugins.csv.intention.CsvShiftColumnLeftIntentionAction</className>
75+
<category>CSV</category>
76+
<descriptionDirectoryName>ShiftColumnLeft</descriptionDirectoryName>
77+
</intentionAction>
78+
<intentionAction id="CsvShiftColumnRight" order="AFTER CsvShiftColumnLeft">
79+
<className>net.seesharpsoft.intellij.plugins.csv.intention.CsvShiftColumnRightIntentionAction</className>
80+
<category>CSV</category>
81+
<descriptionDirectoryName>ShiftColumnRight</descriptionDirectoryName>
82+
</intentionAction>
83+
<intentionAction id="CsvQuoteValue" order="AFTER CsvShiftColumnRight">
7584
<className>net.seesharpsoft.intellij.plugins.csv.intention.CsvQuoteValueIntentionAction</className>
7685
<category>CSV</category>
7786
<descriptionDirectoryName>QuoteValue</descriptionDirectoryName>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<spot> "Header, 2"</spot>,Header 1
2+
<spot> Value 2 </spot>,Value 1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Header 1 ,<spot> "Header, 2"</spot>
2+
Value 1,<spot> Value 2</spot>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<html>
2+
<body>
3+
This intention shifts a column to the left (switches two columns). <br>
4+
</body>
5+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Header 1 ,<spot> "Header, 2"</spot>
2+
Value 1,<spot> Value 2</spot>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<spot> "Header, 2"</spot>,Header 1
2+
<spot> Value 2 </spot>,Value 1

0 commit comments

Comments
 (0)