diff --git a/gradle.properties b/gradle.properties index 7a57a6a1..8f9783ca 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ # https://www.jetbrains.com/intellij-repository/snapshots name='CSV Plugin' -pluginVersion=1.7.0 +pluginVersion=1.8.0 javaVersion=1.8 javaTargetVersion=1.8 downloadIntellijSources=false diff --git a/intellij-csv-validator.iml b/intellij-csv-validator.iml index 6218d5fe..28080894 100644 --- a/intellij-csv-validator.iml +++ b/intellij-csv-validator.iml @@ -1,5 +1,5 @@ - + diff --git a/src/main/java/net/seesharpsoft/intellij/plugins/csv/CsvColumnInfo.java b/src/main/java/net/seesharpsoft/intellij/plugins/csv/CsvColumnInfo.java index 9db301eb..482635ef 100644 --- a/src/main/java/net/seesharpsoft/intellij/plugins/csv/CsvColumnInfo.java +++ b/src/main/java/net/seesharpsoft/intellij/plugins/csv/CsvColumnInfo.java @@ -1,25 +1,79 @@ package net.seesharpsoft.intellij.plugins.csv; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; +import com.intellij.openapi.util.TextRange; +import org.jetbrains.annotations.NotNull; + +import java.util.*; public class CsvColumnInfo { + public class RowInfo { + RowInfo(T element, int row) { + this(element, row, -1, -1); + } + + RowInfo(@NotNull T element, @NotNull int row, int startIndex, int endIndex) { + this.element = element; + this.row = row; + if (startIndex <= endIndex && startIndex >= 0) { + this.textRange = TextRange.create(startIndex, endIndex); + } else { + this.textRange = null; + } + } + + final T element; + final int row; + final TextRange textRange; + + public T getElement() { + return element; + } + + public int getRowIndex() { + return row; + } + + public TextRange getTextRange() { + return textRange; + } + + @Override + public int hashCode() { + return element.hashCode(); + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof CsvColumnInfo.RowInfo)) { + return false; + } + return this.element.equals(((RowInfo) other).element); + } + } + + private int columnIndex; + private int maxLength; + private Map elementInfos; + private T headerElement; + private int size; + public CsvColumnInfo(int columnIndex, int maxLength) { this.columnIndex = columnIndex; this.maxLength = maxLength; - this.elements = new ArrayList<>(); + this.elementInfos = new HashMap<>(); + this.headerElement = null; + this.size = 0; + } + + public RowInfo getRowInfo(T element) { + return elementInfos.get(element); } public int getColumnIndex() { return columnIndex; } - private int columnIndex; - - private int maxLength; - public int getMaxLength() { return maxLength; } @@ -28,36 +82,53 @@ public void setMaxLength(int maxLength) { this.maxLength = maxLength; } - private List elements; + public int getSize() { + return this.size; + } public List getElements() { - return Collections.unmodifiableList(elements); + List result = new ArrayList<>(getSize()); + result.addAll(Collections.nCopies(getSize(), null)); + elementInfos.values() + .forEach(rowInfo -> result.set(rowInfo.row, rowInfo.element)); + return result; } - public boolean addElement(T element) { - return elements.add(element); + protected void put(@NotNull T element, @NotNull RowInfo rowInfo) { + RowInfo previous = elementInfos.put(element, rowInfo); + if (this.getSize() <= rowInfo.row) { + this.size = rowInfo.row + 1; + } + if (rowInfo.row == 0) { + this.headerElement = element; + } + } + + public void addElement(T element, int startIndex, int endIndex) { + this.put(element, new RowInfo(element, elementInfos.size(), startIndex, endIndex)); + } + + public void addElement(T element) { + this.addElement(element, -1, -1); + } + + public void addElement(T element, int row, int startIndex, int endIndex) { + this.put(element, new RowInfo(element, row, startIndex, endIndex)); } public void addElement(T element, int row) { - if (row == elements.size()) { - addElement(element); - } else if (row < elements.size()) { - elements.set(row, element); - } else { - elements.addAll(Collections.nCopies(row - elements.size(), null)); - addElement(element); - } + this.addElement(element, row, -1, -1); } - public boolean containsElement(T element) { - return elements.contains(element); + public boolean containsElement(T needle) { + return elementInfos.containsKey(needle); } - public boolean isHeaderElement(T element) { - return elements.indexOf(element) == 0; + public boolean isHeaderElement(@NotNull T element) { + return element.equals(getHeaderElement()); } public T getHeaderElement() { - return elements.size() > 0 ? elements.get(0) : null; + return this.headerElement; } } diff --git a/src/main/java/net/seesharpsoft/intellij/plugins/csv/CsvColumnInfoMap.java b/src/main/java/net/seesharpsoft/intellij/plugins/csv/CsvColumnInfoMap.java index 2b7566b2..85197634 100644 --- a/src/main/java/net/seesharpsoft/intellij/plugins/csv/CsvColumnInfoMap.java +++ b/src/main/java/net/seesharpsoft/intellij/plugins/csv/CsvColumnInfoMap.java @@ -31,6 +31,11 @@ public CsvColumnInfo getColumnInfo(int columnIndex) { return infoColumnMap.get(columnIndex); } + public CsvColumnInfo.RowInfo getRowInfo(T element) { + CsvColumnInfo columnInfo = getColumnInfo(element); + return columnInfo != null ? columnInfo.getRowInfo(element) : null; + } + public Map> getColumnInfos() { return Collections.unmodifiableMap(this.infoColumnMap); } diff --git a/src/main/java/net/seesharpsoft/intellij/plugins/csv/CsvHelper.java b/src/main/java/net/seesharpsoft/intellij/plugins/csv/CsvHelper.java index 3ccbfcb0..4b39550f 100644 --- a/src/main/java/net/seesharpsoft/intellij/plugins/csv/CsvHelper.java +++ b/src/main/java/net/seesharpsoft/intellij/plugins/csv/CsvHelper.java @@ -73,6 +73,64 @@ public static PsiElement getParentFieldElement(PsiElement element) { return element; } + public static PsiElement getPreviousCRLF(PsiElement recordElement) { + while (recordElement != null) { + if (CsvHelper.getElementType(recordElement) == CsvTypes.CRLF) { + break; + } + recordElement = recordElement.getPrevSibling(); + } + return recordElement; + } + + public static PsiElement getNextCRLF(PsiElement recordElement) { + while (recordElement != null) { + if (CsvHelper.getElementType(recordElement) == CsvTypes.CRLF) { + break; + } + recordElement = recordElement.getNextSibling(); + } + return recordElement; + } + + public static PsiElement getPreviousSeparator(PsiElement fieldElement) { + PsiElement current = fieldElement; + while (current != null) { + if (CsvHelper.getElementType(current) == CsvTypes.COMMA) { + break; + } + current = current.getPrevSibling(); + } + return current; + } + + public static PsiElement getNextSeparator(PsiElement fieldElement) { + PsiElement current = fieldElement; + while (current != null) { + if (CsvHelper.getElementType(current) == CsvTypes.COMMA) { + break; + } + current = current.getNextSibling(); + } + return current; + } + + public static int getFieldStartOffset(PsiElement field) { + PsiElement separator = CsvHelper.getPreviousSeparator(field); + if (separator == null) { + separator = getPreviousCRLF(field.getParent()); + } + return separator == null ? 0 :separator.getTextOffset() + separator.getTextLength(); + } + + public static int getFieldEndOffset(PsiElement field) { + PsiElement separator = CsvHelper.getNextSeparator(field); + if (separator == null) { + separator = getNextCRLF(field.getParent()); + } + return separator == null ? field.getContainingFile().getTextLength() : separator.getTextOffset(); + } + public static CsvColumnInfoMap createColumnInfoMap(CsvFile csvFile) { Map> columnInfoMap = new HashMap<>(); CsvRecord[] records = PsiTreeUtil.getChildrenOfType(csvFile, CsvRecord.class); @@ -86,7 +144,7 @@ public static CsvColumnInfoMap createColumnInfoMap(CsvFile csvFile) } else if (columnInfoMap.get(column).getMaxLength() < length) { columnInfoMap.get(column).setMaxLength(length); } - columnInfoMap.get(column).addElement(field, row); + columnInfoMap.get(column).addElement(field, row, getFieldStartOffset(field), getFieldEndOffset(field)); ++column; } ++row; diff --git a/src/main/java/net/seesharpsoft/intellij/plugins/csv/CsvAnnotator.java b/src/main/java/net/seesharpsoft/intellij/plugins/csv/annotation/CsvAnnotator.java similarity index 78% rename from src/main/java/net/seesharpsoft/intellij/plugins/csv/CsvAnnotator.java rename to src/main/java/net/seesharpsoft/intellij/plugins/csv/annotation/CsvAnnotator.java index 3cbffcb4..060f57bb 100644 --- a/src/main/java/net/seesharpsoft/intellij/plugins/csv/CsvAnnotator.java +++ b/src/main/java/net/seesharpsoft/intellij/plugins/csv/annotation/CsvAnnotator.java @@ -1,4 +1,4 @@ -package net.seesharpsoft.intellij.plugins.csv; +package net.seesharpsoft.intellij.plugins.csv.annotation; import com.intellij.lang.annotation.Annotation; import com.intellij.lang.annotation.AnnotationHolder; @@ -6,8 +6,11 @@ import com.intellij.lang.annotation.HighlightSeverity; import com.intellij.openapi.editor.markup.AttributesFlyweight; import com.intellij.openapi.editor.markup.TextAttributes; +import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiElement; import com.intellij.xml.util.XmlStringUtil; +import net.seesharpsoft.intellij.plugins.csv.CsvColumnInfo; +import net.seesharpsoft.intellij.plugins.csv.CsvHelper; import net.seesharpsoft.intellij.plugins.csv.psi.CsvFile; import net.seesharpsoft.intellij.plugins.csv.psi.CsvTypes; import org.jetbrains.annotations.NotNull; @@ -33,8 +36,12 @@ public void annotate(@NotNull final PsiElement element, @NotNull AnnotationHolde PsiElement headerElement = columnInfo.getHeaderElement(); String message = XmlStringUtil.escapeString(headerElement == null ? "" : headerElement.getText(), true); String tooltip = XmlStringUtil.wrapInHtml(String.format("%s

Header: %s
Index: %d", XmlStringUtil.escapeString(element.getText(), true), message, columnInfo.getColumnIndex())); + TextRange textRange = columnInfo.getRowInfo(element).getTextRange(); + if (textRange.getStartOffset() - csvFile.getTextLength() == 0 && textRange.getStartOffset() > 0) { + textRange = TextRange.from(textRange.getStartOffset() - 1, 1); + } - Annotation annotation = holder.createAnnotation(CSV_COLUMN_INFO_SEVERITY, element.getTextRange(), message, tooltip); + Annotation annotation = holder.createAnnotation(CSV_COLUMN_INFO_SEVERITY, textRange, message, tooltip); annotation.setEnforcedTextAttributes(EMPTY_TEXT_ATTRIBUTES); annotation.setNeedsUpdateOnTyping(false); } diff --git a/src/main/java/net/seesharpsoft/intellij/plugins/csv/highlighter/CsvHighlightUsagesHandler.java b/src/main/java/net/seesharpsoft/intellij/plugins/csv/highlighter/CsvHighlightUsagesHandler.java index 2d2ee208..99cb0339 100644 --- a/src/main/java/net/seesharpsoft/intellij/plugins/csv/highlighter/CsvHighlightUsagesHandler.java +++ b/src/main/java/net/seesharpsoft/intellij/plugins/csv/highlighter/CsvHighlightUsagesHandler.java @@ -1,11 +1,14 @@ package net.seesharpsoft.intellij.plugins.csv.highlighter; import com.intellij.codeInsight.highlighting.HighlightUsagesHandlerBase; +import com.intellij.lang.injection.InjectedLanguageManager; import com.intellij.openapi.editor.Caret; import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiElement; import com.intellij.util.Consumer; import net.seesharpsoft.intellij.plugins.csv.CsvColumnInfo; +import net.seesharpsoft.intellij.plugins.csv.CsvColumnInfoMap; import net.seesharpsoft.intellij.plugins.csv.CsvHelper; import net.seesharpsoft.intellij.plugins.csv.psi.CsvFile; import org.jetbrains.annotations.NotNull; @@ -26,13 +29,17 @@ protected CsvFile getCsvFile() { @Override public List getTargets() { Caret primaryCaret = this.myEditor.getCaretModel().getPrimaryCaret(); - PsiElement myFocusedFieldElement = CsvHelper.getParentFieldElement(this.myFile.getViewProvider().findElementAt(primaryCaret.getOffset())); + PsiElement myFocusedElement = this.myFile.getViewProvider().findElementAt(primaryCaret.getOffset()); + if (myFocusedElement == null) { + myFocusedElement = this.myFile.getLastChild(); + } + myFocusedElement = CsvHelper.getParentFieldElement(myFocusedElement); - if (myFocusedFieldElement == null) { + if (myFocusedElement == null) { return Collections.emptyList(); } - CsvColumnInfo columnInfo = getCsvFile().getMyColumnInfoMap().getColumnInfo(myFocusedFieldElement); + CsvColumnInfo columnInfo = getCsvFile().getMyColumnInfoMap().getColumnInfo(myFocusedElement); return columnInfo == null ? Collections.emptyList() : Collections.unmodifiableList(columnInfo.getElements()); } @@ -43,8 +50,21 @@ protected void selectTargets(List list, Consumer> c @Override public void computeUsages(List list) { + CsvColumnInfoMap columnInfoMap = getCsvFile().getMyColumnInfoMap(); list.forEach(element -> { - if (element != null && !element.getText().isEmpty()) { this.addOccurrence(element); } + if (element != null && !element.getText().isEmpty()) { this.addOccurrence(columnInfoMap.getRowInfo(element)); } }); } + + protected void addOccurrence(CsvColumnInfo.RowInfo rowInfo) { + if (rowInfo == null) { + return; + } + TextRange range = rowInfo.getTextRange(); + if (range != null) { + PsiElement element = rowInfo.getElement(); + range = InjectedLanguageManager.getInstance(element.getProject()).injectedToHost(element, range); + this.myReadUsages.add(range); + } + } } diff --git a/src/main/java/net/seesharpsoft/intellij/plugins/csv/intention/CsvInspectionSuppressor.java b/src/main/java/net/seesharpsoft/intellij/plugins/csv/inspection/CsvInspectionSuppressor.java similarity index 95% rename from src/main/java/net/seesharpsoft/intellij/plugins/csv/intention/CsvInspectionSuppressor.java rename to src/main/java/net/seesharpsoft/intellij/plugins/csv/inspection/CsvInspectionSuppressor.java index ef1c678a..368f4abd 100644 --- a/src/main/java/net/seesharpsoft/intellij/plugins/csv/intention/CsvInspectionSuppressor.java +++ b/src/main/java/net/seesharpsoft/intellij/plugins/csv/inspection/CsvInspectionSuppressor.java @@ -1,4 +1,4 @@ -package net.seesharpsoft.intellij.plugins.csv.intention; +package net.seesharpsoft.intellij.plugins.csv.inspection; import com.intellij.codeInspection.InspectionProfileEntry; import com.intellij.codeInspection.InspectionSuppressor; diff --git a/src/main/java/net/seesharpsoft/intellij/plugins/csv/intention/CsvValidationInspection.java b/src/main/java/net/seesharpsoft/intellij/plugins/csv/inspection/CsvValidationInspection.java similarity index 98% rename from src/main/java/net/seesharpsoft/intellij/plugins/csv/intention/CsvValidationInspection.java rename to src/main/java/net/seesharpsoft/intellij/plugins/csv/inspection/CsvValidationInspection.java index 96814d5e..b682a9c3 100644 --- a/src/main/java/net/seesharpsoft/intellij/plugins/csv/intention/CsvValidationInspection.java +++ b/src/main/java/net/seesharpsoft/intellij/plugins/csv/inspection/CsvValidationInspection.java @@ -1,4 +1,4 @@ -package net.seesharpsoft.intellij.plugins.csv.intention; +package net.seesharpsoft.intellij.plugins.csv.inspection; import com.intellij.codeInspection.*; import com.intellij.openapi.diagnostic.Logger; @@ -12,6 +12,7 @@ import com.intellij.util.IncorrectOperationException; import net.seesharpsoft.intellij.plugins.csv.CsvHelper; import net.seesharpsoft.intellij.plugins.csv.CsvLanguage; +import net.seesharpsoft.intellij.plugins.csv.intention.CsvIntentionHelper; import net.seesharpsoft.intellij.plugins.csv.psi.CsvTypes; import net.seesharpsoft.intellij.plugins.csv.settings.CsvCodeStyleSettings; import org.jetbrains.annotations.NonNls; diff --git a/src/main/java/net/seesharpsoft/intellij/plugins/csv/intention/CsvIntentionHelper.java b/src/main/java/net/seesharpsoft/intellij/plugins/csv/intention/CsvIntentionHelper.java index b7cbff27..fe0029f6 100644 --- a/src/main/java/net/seesharpsoft/intellij/plugins/csv/intention/CsvIntentionHelper.java +++ b/src/main/java/net/seesharpsoft/intellij/plugins/csv/intention/CsvIntentionHelper.java @@ -32,46 +32,6 @@ public static List getChildren(PsiElement element) { return children; } - public static PsiElement getPreviousSeparator(PsiElement fieldElement) { - while (fieldElement != null) { - if (CsvHelper.getElementType(fieldElement) == CsvTypes.COMMA) { - break; - } - fieldElement = fieldElement.getPrevSibling(); - } - return fieldElement; - } - - public static PsiElement getNextSeparator(PsiElement fieldElement) { - while (fieldElement != null) { - if (CsvHelper.getElementType(fieldElement) == CsvTypes.COMMA) { - break; - } - fieldElement = fieldElement.getNextSibling(); - } - return fieldElement; - } - - public static PsiElement getPreviousCRLF(PsiElement recordElement) { - while (recordElement != null) { - if (CsvHelper.getElementType(recordElement) == CsvTypes.CRLF) { - break; - } - recordElement = recordElement.getPrevSibling(); - } - return recordElement; - } - - public static PsiElement getNextCRLF(PsiElement recordElement) { - while (recordElement != null) { - if (CsvHelper.getElementType(recordElement) == CsvTypes.CRLF) { - break; - } - recordElement = recordElement.getNextSibling(); - } - return recordElement; - } - public static Collection getAllElements(PsiFile file) { List todo = getChildren(file); Collection elements = new HashSet(); @@ -100,7 +60,7 @@ public static void quoteAll(@NotNull Project project, @NotNull PsiFile psiFile) PsiElement separator; for (PsiElement field : fields) { if (field.getFirstChild() == null || CsvHelper.getElementType(field.getFirstChild()) != CsvTypes.QUOTE) { - separator = getPreviousSeparator(field); + separator = CsvHelper.getPreviousSeparator(field); if (separator == null) { quotePositions.add(field.getParent().getTextOffset()); } else { @@ -108,7 +68,7 @@ public static void quoteAll(@NotNull Project project, @NotNull PsiFile psiFile) } } if (field.getLastChild() == null || CsvHelper.getElementType(field.getLastChild()) != CsvTypes.QUOTE) { - separator = getNextSeparator(field); + separator = CsvHelper.getNextSeparator(field); if (separator == null) { quotePositions.add(field.getParent().getTextOffset() + field.getParent().getTextLength()); } else { diff --git a/src/main/java/net/seesharpsoft/intellij/plugins/csv/intention/CsvQuoteValueIntentionAction.java b/src/main/java/net/seesharpsoft/intellij/plugins/csv/intention/CsvQuoteValueIntentionAction.java index f896d5cf..52e49989 100644 --- a/src/main/java/net/seesharpsoft/intellij/plugins/csv/intention/CsvQuoteValueIntentionAction.java +++ b/src/main/java/net/seesharpsoft/intellij/plugins/csv/intention/CsvQuoteValueIntentionAction.java @@ -24,7 +24,7 @@ public boolean isAvailable(@NotNull Project project, Editor editor, @Nullable Ps return false; } - element = CsvHelper.getParentFieldElement(element); + element = element == null ? null : CsvHelper.getParentFieldElement(element); return element instanceof CsvField && element.getFirstChild() != null && (CsvHelper.getElementType(element.getFirstChild()) != CsvTypes.QUOTE || @@ -32,7 +32,7 @@ public boolean isAvailable(@NotNull Project project, Editor editor, @Nullable Ps } @Override - public void invoke(@NotNull Project project, Editor editor, PsiElement element) throws IncorrectOperationException { + public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) throws IncorrectOperationException { CsvIntentionHelper.quoteValue(project, CsvHelper.getParentFieldElement(element)); } diff --git a/src/main/java/net/seesharpsoft/intellij/plugins/csv/intention/CsvShiftColumnIntentionAction.java b/src/main/java/net/seesharpsoft/intellij/plugins/csv/intention/CsvShiftColumnIntentionAction.java index 05480b7f..3d570069 100644 --- a/src/main/java/net/seesharpsoft/intellij/plugins/csv/intention/CsvShiftColumnIntentionAction.java +++ b/src/main/java/net/seesharpsoft/intellij/plugins/csv/intention/CsvShiftColumnIntentionAction.java @@ -6,8 +6,9 @@ import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; import net.seesharpsoft.intellij.plugins.csv.CsvColumnInfo; -import net.seesharpsoft.intellij.plugins.csv.settings.CsvCodeStyleSettings; +import net.seesharpsoft.intellij.plugins.csv.CsvHelper; import net.seesharpsoft.intellij.plugins.csv.psi.CsvFile; +import net.seesharpsoft.intellij.plugins.csv.settings.CsvCodeStyleSettings; import org.jetbrains.annotations.NotNull; import java.util.List; @@ -24,7 +25,7 @@ protected static void changeLeftAndRightColumnOrder(@NotNull Project project, Cs } @NotNull - protected static String changeLeftAndRightColumnOrder(String text, String separator, CsvColumnInfo leftColumnInfo, CsvColumnInfo rightColumnInfo) { + protected static String changeLeftAndRightColumnOrder(String text, String separator, CsvColumnInfo leftColumnInfo, CsvColumnInfo rightColumnInfo) { List rightElements = rightColumnInfo.getElements(); List leftElements = leftColumnInfo.getElements(); int lastIndex = 0, maxRows = leftElements.size(); @@ -56,9 +57,9 @@ protected static String changeLeftAndRightColumnOrder(String text, String separa protected static TextRange findPreviousSeparatorOrCRLF(PsiElement psiElement) { TextRange textRange; - PsiElement separator = CsvIntentionHelper.getPreviousSeparator(psiElement); + PsiElement separator = CsvHelper.getPreviousSeparator(psiElement); if (separator == null) { - separator = CsvIntentionHelper.getPreviousCRLF(psiElement.getParent()); + separator = CsvHelper.getPreviousCRLF(psiElement.getParent()); if (separator == null) { separator = psiElement.getParent().getParent().getFirstChild(); textRange = TextRange.create(separator.getTextRange().getStartOffset(), separator.getTextRange().getStartOffset()); @@ -73,9 +74,9 @@ protected static TextRange findPreviousSeparatorOrCRLF(PsiElement psiElement) { protected static TextRange findNextSeparatorOrCRLF(PsiElement psiElement) { TextRange textRange; - PsiElement separator = CsvIntentionHelper.getNextSeparator(psiElement); + PsiElement separator = CsvHelper.getNextSeparator(psiElement); if (separator == null) { - separator = CsvIntentionHelper.getNextCRLF(psiElement.getParent()); + separator = CsvHelper.getNextCRLF(psiElement.getParent()); if (separator == null) { separator = psiElement.getParent().getParent().getLastChild(); textRange = TextRange.create(separator.getTextRange().getEndOffset(), separator.getTextRange().getEndOffset()); diff --git a/src/main/java/net/seesharpsoft/intellij/plugins/csv/intention/CsvUnquoteValueIntentionAction.java b/src/main/java/net/seesharpsoft/intellij/plugins/csv/intention/CsvUnquoteValueIntentionAction.java index 3485b404..5faa9bd9 100644 --- a/src/main/java/net/seesharpsoft/intellij/plugins/csv/intention/CsvUnquoteValueIntentionAction.java +++ b/src/main/java/net/seesharpsoft/intellij/plugins/csv/intention/CsvUnquoteValueIntentionAction.java @@ -24,7 +24,7 @@ public boolean isAvailable(@NotNull Project project, Editor editor, @Nullable Ps return false; } - element = CsvHelper.getParentFieldElement(element); + element = element == null ? null : CsvHelper.getParentFieldElement(element); return element instanceof CsvField && element.getFirstChild() != null && (CsvHelper.getElementType(element.getFirstChild()) == CsvTypes.QUOTE || @@ -33,7 +33,7 @@ public boolean isAvailable(@NotNull Project project, Editor editor, @Nullable Ps } @Override - public void invoke(@NotNull Project project, Editor editor, PsiElement element) throws IncorrectOperationException { + public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) throws IncorrectOperationException { CsvIntentionHelper.unquoteValue(project, CsvHelper.getParentFieldElement(element)); } diff --git a/src/main/java/net/seesharpsoft/intellij/plugins/csv/CsvSpellCheckingStrategy.java b/src/main/java/net/seesharpsoft/intellij/plugins/csv/spellchecker/CsvSpellCheckingStrategy.java similarity index 88% rename from src/main/java/net/seesharpsoft/intellij/plugins/csv/CsvSpellCheckingStrategy.java rename to src/main/java/net/seesharpsoft/intellij/plugins/csv/spellchecker/CsvSpellCheckingStrategy.java index 7c4067d2..8b6a63a5 100644 --- a/src/main/java/net/seesharpsoft/intellij/plugins/csv/CsvSpellCheckingStrategy.java +++ b/src/main/java/net/seesharpsoft/intellij/plugins/csv/spellchecker/CsvSpellCheckingStrategy.java @@ -1,4 +1,4 @@ -package net.seesharpsoft.intellij.plugins.csv; +package net.seesharpsoft.intellij.plugins.csv.spellchecker; import com.intellij.psi.PsiElement; import com.intellij.spellchecker.tokenizer.SpellcheckingStrategy; diff --git a/src/main/java/net/seesharpsoft/intellij/plugins/csv/structureview/CsvStructureViewElement.java b/src/main/java/net/seesharpsoft/intellij/plugins/csv/structureview/CsvStructureViewElement.java index 6d79d543..48ef644e 100644 --- a/src/main/java/net/seesharpsoft/intellij/plugins/csv/structureview/CsvStructureViewElement.java +++ b/src/main/java/net/seesharpsoft/intellij/plugins/csv/structureview/CsvStructureViewElement.java @@ -108,7 +108,7 @@ public TreeElement[] getChildren() { } } - private static class Header extends CsvStructureViewElement { + public static class Header extends CsvStructureViewElement { private CsvColumnInfo columnInfo; public Header(PsiElement element, CsvColumnInfo columnInfo) { @@ -124,7 +124,7 @@ public TreeElement[] getChildren() { TreeElement[] children = new TreeElement[elements.size() - 1]; for (PsiElement element : elements) { if (rowIndex > 0) { - children[rowIndex - 1] = new Field(element == null ? CsvHelper.createEmptyCsvField(element.getProject()) : element, rowIndex - 1); + children[rowIndex - 1] = new Field(element == null ? CsvHelper.createEmptyCsvField(this.element.getProject()) : element, rowIndex - 1); } ++rowIndex; } @@ -134,7 +134,7 @@ public TreeElement[] getChildren() { @Nullable @Override public String getLocationString() { - return String.format("Header (%s entries)", columnInfo.getElements().size() - 1); + return String.format("Header (%s entries)", columnInfo.getSize() - 1); } @Nullable @@ -144,7 +144,7 @@ public Icon getIcon(boolean unused) { } } - private static class Field extends CsvStructureViewElement { + public static class Field extends CsvStructureViewElement { private int rowIndex; public Field(PsiElement element, int rowIndex) { diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 537b2591..0e655854 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -37,9 +37,12 @@ ]]> NEW: Annotated values: tooltip shows the value itself, the header and the column index
- NEW: Active column (caret position) is colored differently
- FIX: 'Add separator' inspection is adding correct separator (e.g. tabs in TSV files)
+ NEW: Custom 'Wrapping' settings
+ NEW: Column highlighter takes whitespaces into account
+ FIX: CSV column info tooltip trumps spellchecker tooltip (but keeps the visualization of a typo)
+ FIX: Show tooltip even when caret is at the last position withing the CSV file
+ FIX: Support for suppressing inspections not relevant for CSV (e.g. 'Problematic Whitespace')
+ FIX: Structure View: proper handling of elements (instead of endless loading)
+ several code & performance improvements

]]> @@ -67,10 +70,10 @@ - + + implementationClass="net.seesharpsoft.intellij.plugins.csv.spellchecker.CsvSpellCheckingStrategy" /> @@ -88,8 +91,8 @@ enabledByDefault="true" groupName="CSV" shortName="CsvValidation" - implementationClass="net.seesharpsoft.intellij.plugins.csv.intention.CsvValidationInspection" /> - + implementationClass="net.seesharpsoft.intellij.plugins.csv.inspection.CsvValidationInspection" /> + net.seesharpsoft.intellij.plugins.csv.intention.CsvShiftColumnLeftIntentionAction diff --git a/src/test/java/net/seesharpsoft/intellij/plugins/csv/CsvAnnotatorTest.java b/src/test/java/net/seesharpsoft/intellij/plugins/csv/annotation/CsvAnnotatorTest.java similarity index 95% rename from src/test/java/net/seesharpsoft/intellij/plugins/csv/CsvAnnotatorTest.java rename to src/test/java/net/seesharpsoft/intellij/plugins/csv/annotation/CsvAnnotatorTest.java index 8c409374..819d9408 100644 --- a/src/test/java/net/seesharpsoft/intellij/plugins/csv/CsvAnnotatorTest.java +++ b/src/test/java/net/seesharpsoft/intellij/plugins/csv/annotation/CsvAnnotatorTest.java @@ -1,4 +1,4 @@ -package net.seesharpsoft.intellij.plugins.csv; +package net.seesharpsoft.intellij.plugins.csv.annotation; import com.intellij.codeInsight.daemon.impl.HighlightInfo; import com.intellij.codeInsight.daemon.impl.HighlightInfoType; @@ -21,7 +21,7 @@ import java.util.Iterator; import java.util.List; -import static net.seesharpsoft.intellij.plugins.csv.CsvAnnotator.CSV_COLUMN_INFO_SEVERITY; +import static net.seesharpsoft.intellij.plugins.csv.annotation.CsvAnnotator.CSV_COLUMN_INFO_SEVERITY; public class CsvAnnotatorTest extends LightCodeInsightFixtureTestCase { diff --git a/src/test/java/net/seesharpsoft/intellij/plugins/csv/highlighter/CsvHighlightUsagesHandlerTest.java b/src/test/java/net/seesharpsoft/intellij/plugins/csv/highlighter/CsvHighlightUsagesHandlerTest.java index fe5c54e1..1d0c1e98 100644 --- a/src/test/java/net/seesharpsoft/intellij/plugins/csv/highlighter/CsvHighlightUsagesHandlerTest.java +++ b/src/test/java/net/seesharpsoft/intellij/plugins/csv/highlighter/CsvHighlightUsagesHandlerTest.java @@ -19,16 +19,16 @@ public void testHighlightUsages01() { RangeHighlighter[] rangeHighlighters = myFixture.testHighlightUsages("HighlightUsagesTestData01.csv"); assertSize(2, rangeHighlighters); - assertHighlightedText(rangeHighlighters[0], "Header 2"); - assertHighlightedText(rangeHighlighters[1], "Value 2"); + assertHighlightedText(rangeHighlighters[0], " Header 2"); + assertHighlightedText(rangeHighlighters[1], " Value 2"); } public void testHighlightUsages02() { RangeHighlighter[] rangeHighlighters = myFixture.testHighlightUsages("HighlightUsagesTestData02.csv"); assertSize(2, rangeHighlighters); - assertHighlightedText(rangeHighlighters[0], "Header 2"); - assertHighlightedText(rangeHighlighters[1], "Value 2"); + assertHighlightedText(rangeHighlighters[0], " Header 2"); + assertHighlightedText(rangeHighlighters[1], " Value 2"); } public void testHighlightUsages03() { @@ -43,20 +43,28 @@ public void testHighlightUsages04() { RangeHighlighter[] rangeHighlighters = myFixture.testHighlightUsages("HighlightUsagesTestData04.csv"); assertSize(1, rangeHighlighters); - assertHighlightedText(rangeHighlighters[0], "Value 3"); + assertHighlightedText(rangeHighlighters[0], " Value 3"); } public void testHighlightUsages05() { RangeHighlighter[] rangeHighlighters = myFixture.testHighlightUsages("HighlightUsagesTestData05.csv"); assertSize(2, rangeHighlighters); - assertHighlightedText(rangeHighlighters[0], "Header 2"); - assertHighlightedText(rangeHighlighters[1], "Value 2"); + assertHighlightedText(rangeHighlighters[0], " Header 2"); + assertHighlightedText(rangeHighlighters[1], " Value 2"); } public void testHighlightUsages06() { RangeHighlighter[] rangeHighlighters = myFixture.testHighlightUsages("HighlightUsagesTestData06.csv"); - assertSize(0, rangeHighlighters); + assertSize(1, rangeHighlighters); + assertHighlightedText(rangeHighlighters[0], " Value 3"); + } + + public void testHighlightUsages07() { + RangeHighlighter[] rangeHighlighters = myFixture.testHighlightUsages("HighlightUsagesTestData07.csv"); + + assertSize(1, rangeHighlighters); + assertHighlightedText(rangeHighlighters[0], " Header 2"); } } diff --git a/src/test/java/net/seesharpsoft/intellij/plugins/csv/intention/CsvInspectionTest.java b/src/test/java/net/seesharpsoft/intellij/plugins/csv/inspection/CsvInspectionTest.java similarity index 96% rename from src/test/java/net/seesharpsoft/intellij/plugins/csv/intention/CsvInspectionTest.java rename to src/test/java/net/seesharpsoft/intellij/plugins/csv/inspection/CsvInspectionTest.java index 82ea0f93..9d1bd73f 100644 --- a/src/test/java/net/seesharpsoft/intellij/plugins/csv/intention/CsvInspectionTest.java +++ b/src/test/java/net/seesharpsoft/intellij/plugins/csv/inspection/CsvInspectionTest.java @@ -1,4 +1,4 @@ -package net.seesharpsoft.intellij.plugins.csv.intention; +package net.seesharpsoft.intellij.plugins.csv.inspection; import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase; diff --git a/src/test/java/net/seesharpsoft/intellij/plugins/csv/structureview/CsvStructureViewTest.java b/src/test/java/net/seesharpsoft/intellij/plugins/csv/structureview/CsvStructureViewTest.java new file mode 100644 index 00000000..5f574a35 --- /dev/null +++ b/src/test/java/net/seesharpsoft/intellij/plugins/csv/structureview/CsvStructureViewTest.java @@ -0,0 +1,128 @@ +package net.seesharpsoft.intellij.plugins.csv.structureview; + +import com.intellij.ide.structureView.StructureViewTreeElement; +import com.intellij.ide.util.treeView.smartTree.TreeElement; +import com.intellij.navigation.ItemPresentation; +import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase; + +public class CsvStructureViewTest extends LightCodeInsightFixtureTestCase { + + @Override + protected String getTestDataPath() { + return "./src/test/resources/structureview"; + } + + private void doCheckTreeElement(TreeElement element, Class expectedClazz, String expectedText, String expectedLocation) { + assertInstanceOf(element, expectedClazz); + assertInstanceOf(element, ItemPresentation.class); + + ItemPresentation presentation = (ItemPresentation) element; + assertEquals(expectedText, presentation.getPresentableText()); + if (expectedLocation != null) { + assertEquals(expectedLocation, presentation.getLocationString()); + } + } + + public void testStructureView() { + myFixture.configureByFile("StructureViewTestData.csv"); + myFixture.testStructureView(structureViewComponent -> { + StructureViewTreeElement root = structureViewComponent.getTreeModel().getRoot(); + doCheckTreeElement(root, CsvStructureViewElement.File.class, "FirstName, LastName\n" + + "Peter,Lustig,42\n" + + "Martin\n" + + ",Fuchs\n", null); + assertEquals(3, root.getChildren().length); + + TreeElement header = root.getChildren()[0]; + doCheckTreeElement( + header, + CsvStructureViewElement.Header.class, + "FirstName", + "Header (4 entries)" + ); + + TreeElement field = header.getChildren()[0]; + doCheckTreeElement( + field, + CsvStructureViewElement.Field.class, + "Peter", + "(1)" + ); + field = header.getChildren()[1]; + doCheckTreeElement( + field, + CsvStructureViewElement.Field.class, + "Martin", + "(2)" + ); + field = header.getChildren()[2]; + doCheckTreeElement( + field, + CsvStructureViewElement.Field.class, + "", + "(3)" + ); + field = header.getChildren()[3]; + doCheckTreeElement( + field, + CsvStructureViewElement.Field.class, + "", + "(4)" + ); + + /** + * LastName header + */ + header = root.getChildren()[1]; + doCheckTreeElement( + header, + CsvStructureViewElement.Header.class, + "LastName", + "Header (3 entries)" + ); + + field = header.getChildren()[0]; + doCheckTreeElement( + field, + CsvStructureViewElement.Field.class, + "Lustig", + "(1)" + ); + field = header.getChildren()[1]; + doCheckTreeElement( + field, + CsvStructureViewElement.Field.class, + "", + "(2)" + ); + field = header.getChildren()[2]; + doCheckTreeElement( + field, + CsvStructureViewElement.Field.class, + "Fuchs", + "(3)" + ); + + /** + * Empty header + */ + header = root.getChildren()[2]; + doCheckTreeElement( + header, + CsvStructureViewElement.Header.class, + "", + "Header (1 entries)" + ); + + field = header.getChildren()[0]; + doCheckTreeElement( + field, + CsvStructureViewElement.Field.class, + "42", + "(1)" + ); + }); + } + + +} diff --git a/src/test/java/net/seesharpsoft/intellij/plugins/tsv/intention/TsvInspectionTest.java b/src/test/java/net/seesharpsoft/intellij/plugins/tsv/intention/TsvInspectionTest.java index 704a838b..e76a511f 100644 --- a/src/test/java/net/seesharpsoft/intellij/plugins/tsv/intention/TsvInspectionTest.java +++ b/src/test/java/net/seesharpsoft/intellij/plugins/tsv/intention/TsvInspectionTest.java @@ -2,7 +2,7 @@ import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase; -import net.seesharpsoft.intellij.plugins.csv.intention.CsvValidationInspection; +import net.seesharpsoft.intellij.plugins.csv.inspection.CsvValidationInspection; public class TsvInspectionTest extends LightCodeInsightFixtureTestCase { diff --git a/src/test/resources/AnnotatorTestData.csv b/src/test/resources/AnnotatorTestData.csv index 862f591c..cfa3d732 100644 --- a/src/test/resources/AnnotatorTestData.csv +++ b/src/test/resources/AnnotatorTestData.csv @@ -1,2 +1,2 @@ -Header 1, Header 2 -Value 1, Value 2, Value 3 \ No newline at end of file +Header 1, Header 2 +Value 1, Value 2, Value 3 \ No newline at end of file diff --git a/src/test/resources/highlighter/HighlightUsagesTestData07.csv b/src/test/resources/highlighter/HighlightUsagesTestData07.csv new file mode 100644 index 00000000..6684baf6 --- /dev/null +++ b/src/test/resources/highlighter/HighlightUsagesTestData07.csv @@ -0,0 +1,2 @@ +Header 1, Header 2 +Value 1, \ No newline at end of file diff --git a/src/test/resources/structureview/StructureViewTestData.csv b/src/test/resources/structureview/StructureViewTestData.csv new file mode 100644 index 00000000..511cba22 --- /dev/null +++ b/src/test/resources/structureview/StructureViewTestData.csv @@ -0,0 +1,4 @@ +FirstName, LastName +Peter,Lustig,42 +Martin +,Fuchs