├── .gitignore ├── META-INF └── plugin.xml ├── README.md └── src ├── main └── java │ └── pipetableformatter │ ├── ColumnSplitter.java │ ├── DelimitersCount.java │ ├── FormatOptions.java │ ├── LineSplitter.java │ ├── PipeTable.java │ ├── PipeTableFormatter.java │ ├── PipeTableParser.java │ ├── Range.java │ ├── TableDetector.java │ ├── operation │ ├── AddColumnBefore.java │ ├── Format.java │ ├── FormatAllTables.java │ ├── Operation.java │ ├── PipeTableEditor.java │ ├── Select.java │ └── TableText.java │ └── plugin │ ├── AbstractPipeTableFormatAction.java │ ├── IdeaPipeTableEditor.java │ ├── PipeTableActionHandler.java │ ├── PipeTableAddColumnBeforeAction.java │ ├── PipeTableFormatAction.java │ ├── PipeTableFormatAllAction.java │ ├── PipeTableFormatPreservingOuterStateAction.java │ └── PipeTableSelectAction.java └── test ├── java └── pipetableformatter │ ├── ColumnSplitterTest.java │ ├── LineSplitterTest.java │ ├── PipeTableFormatterTest.java │ ├── PipeTableParserTest.java │ ├── PipeTableTest.java │ ├── TableDetectorTest.java │ ├── operation │ ├── AddColumnBeforeOperationTest.java │ ├── FormatAllTablesOperationTest.java │ ├── FormatOperationTest.java │ └── SelectOperationTest.java │ ├── plugin │ └── PipeTableFormatterFunctionalTest.java │ └── testsupport │ ├── PipeTableBuilder.java │ └── Utils.java └── resources ├── expected ├── text-with-a-formatted-pipe-table-with-a-lot-commas.txt ├── text-with-all-formatted-table.txt ├── text-with-formatted-table-and-new-column.txt ├── text-with-formatted-table-with-comment.txt ├── text-with-formatted-table-with-indentation-without-outer-pipes.txt ├── text-with-formatted-table-without-outer-pipes.txt └── text-with-formatted-table.txt └── input ├── text-with-a-few-not-formatted-table.txt ├── text-with-a-pipe-table-with-a-lot-commas.txt ├── text-with-not-formatted-table-with-comments.txt ├── text-with-not-formatted-table-with-indentation-without-outer-pipes.txt └── text-with-not-formatted-table.txt /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *~ 3 | /build 4 | /out 5 | /local.properties 6 | /.idea 7 | /captures 8 | *.iml 9 | *.jar 10 | -------------------------------------------------------------------------------- /META-INF/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | anton_dev_ua.PipeTableFormatter 3 | Pipe Table Formatter 4 | 0.5.1-SNAPSHOT 5 | 6 | 7 | 10 |
11 | 12 | Supported delimiters: pipe, comma, tab. 13 | When formatting, any of supported delimiters are replaced with pipe.
14 |
15 | 16 | Usage
17 | Select text in the editor or place caret inside a table 18 | and choose action "Pipe Table" -> "Format" in Code menu or in editor popup menu.
19 | ]]>
20 | 21 | Version 0.5.1 23 | 26 | 27 | Version 0.5.0 28 | 32 | 33 | Version 0.4.2 34 | 37 | 38 | Version 0.4.1 39 | 42 | 43 | Version 0.4 44 | 47 | 48 | Version 0.3.1 49 | 52 | 53 | Version 0.3 54 | 60 | 61 | Version 0.2.3 62 | 65 | 66 | Version 0.2.2 67 | 71 | 72 | Version 0.2.1 73 | 76 | 77 | Version 0.2 78 | 82 | 83 | Version 0.1 84 | 87 | ]]> 88 | 89 | 90 | 91 | 92 | 93 | 95 | com.intellij.modules.lang 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 114 | 115 | 118 | 119 | 122 | 123 | 126 | 127 | 130 | 131 | 132 | 133 | 134 | 135 | 136 |
137 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PipeTableFormatter 2 | ================== 3 | 4 | Small IDEA plugin for formatting text blocks that represents a table delimited by pipe (|). For example, can be used to format JBehave examples table. 5 | 6 | Thanks to Idris Ahmed ([idrisahmed251] (https://github.com/idrisahmed251)) and to Jesse Kuhnert ([jkuhnert] (https://github.com/jkuhnert)) for ideas to use comma and tab as delimeters. 7 | 8 | Features 9 | -------- 10 | 11 | - Formats block of text that represents a table: width of a column becomes constant from top to bottom by adjusting it to most wide value in the column. 12 | - Supported delimiters: pipe, comma, tab. 13 | - Any of supported delimiters, when formatting, are converted to pipe. 14 | - If text is not selected explicitly - automatically detects start and end of a table on base of caret position. 15 | - All tables in the editor can be formatted by one action 16 | 17 | Example 18 | ------- 19 | 20 | text like: 21 | 22 |
23 | |Header 1|Header 2|
24 | |val1|val2|
25 | 
26 | 27 | or like: 28 | 29 |
30 | Header 1,Header 2
31 | val1,val2
32 | 
33 | 34 | is formatted to: 35 | 36 |
37 | | Header 1 | Header 2 |
38 | | val1     | val2     |
39 | 
40 | 41 | Installation 42 | ----------- 43 | 44 | From Plugin Repository: http://plugins.jetbrains.com/plugin/7550 45 | 46 | Usage 47 | ----- 48 | 49 | 1. Select text that should be formatted or just put caret somewhere inside a table. 50 | 2. Choose action `Pipe Table -> Format` in Code menu or popup menu. 51 | -------------------------------------------------------------------------------- /src/main/java/pipetableformatter/ColumnSplitter.java: -------------------------------------------------------------------------------- 1 | package pipetableformatter; 2 | 3 | import java.util.Iterator; 4 | 5 | public class ColumnSplitter implements Iterable, Iterator { 6 | public static final String PIPE = "|"; 7 | public static final String PIPE_COMMENT_START = "|--"; 8 | public static final String PIPE_COMMENT_END = "--|"; 9 | private final int leadingSpaces; 10 | private String line; 11 | private int startIndex = 0; 12 | private int length = 0; 13 | private boolean insideQuoted = false; 14 | private boolean quoted = false; 15 | private Character delimiter; 16 | private int prevStartIndex; 17 | private int columnIndex; 18 | private boolean leadingPipe; 19 | private boolean trailingPipe; 20 | private boolean commented; 21 | 22 | public ColumnSplitter(String line, Character delimiter) { 23 | this.delimiter = delimiter; 24 | this.leadingSpaces = countLeadingSpaces(line); 25 | this.line = line.trim(); 26 | init(); 27 | } 28 | 29 | private int countLeadingSpaces(String line) { 30 | int charCount = 0; 31 | while (line.length() > charCount && line.charAt(charCount++) <= ' ') ; 32 | return charCount - 1; 33 | } 34 | 35 | private void init() { 36 | length = line.length(); 37 | if (line.startsWith(PIPE_COMMENT_START)) { 38 | startIndex = PIPE_COMMENT_START.length(); 39 | commented = true; 40 | leadingPipe = true; 41 | } else if (line.startsWith(PIPE)) { 42 | startIndex = 1; 43 | leadingPipe = true; 44 | } 45 | if (line.endsWith(PIPE_COMMENT_END)) { 46 | length -= PIPE_COMMENT_END.length(); 47 | trailingPipe = true; 48 | } else if (line.endsWith(PIPE)) { 49 | length--; 50 | trailingPipe = true; 51 | } 52 | columnIndex = -1; 53 | } 54 | 55 | @Override 56 | public String next() { 57 | int endIndex = startIndex; 58 | while (hasMoreChars(endIndex) && notDelimiter(endIndex)) { 59 | detectQuoted(endIndex++); 60 | } 61 | String value = line.substring(startIndex, endIndex); 62 | prevStartIndex = startIndex; 63 | startIndex = endIndex + 1; 64 | columnIndex++; 65 | return openQuotes(value); 66 | } 67 | 68 | private boolean hasMoreChars(int endIndex) { 69 | return endIndex < length; 70 | } 71 | 72 | private boolean notDelimiter(int index) { 73 | return insideQuoted || line.charAt(index) != delimiter; 74 | } 75 | 76 | private void detectQuoted(int index) { 77 | if (line.charAt(index) == '"') { 78 | quoted = true; 79 | insideQuoted = !insideQuoted; 80 | } 81 | } 82 | 83 | private String openQuotes(String value) { 84 | if (quoted) { 85 | quoted = false; 86 | return replaceTwoQuotesWithOne(removeOpenAndCloseQuotes(value)); 87 | } else { 88 | return value; 89 | } 90 | } 91 | 92 | private String replaceTwoQuotesWithOne(String value) { 93 | return value.replaceAll("\"\"", "\""); 94 | } 95 | 96 | private String removeOpenAndCloseQuotes(String value) { 97 | return value.replaceAll("(^ *\")|(\" *$)", ""); 98 | } 99 | 100 | public int currentColumnStartIndex() { 101 | return prevStartIndex + leadingSpaces; 102 | } 103 | 104 | public int currentColumnEndIndex() { 105 | return startIndex - 1 + leadingSpaces; 106 | } 107 | 108 | @Override 109 | public Iterator iterator() { 110 | return this; 111 | } 112 | 113 | @Override 114 | public boolean hasNext() { 115 | return startIndex < length; 116 | } 117 | 118 | @Override 119 | public void remove() { 120 | throw new UnsupportedOperationException(); 121 | } 122 | 123 | public int currentColumnIndex() { 124 | return columnIndex; 125 | } 126 | 127 | public int getLeadingSpaces() { 128 | return leadingSpaces; 129 | } 130 | 131 | public String getIndentetion() { 132 | if(leadingSpaces > 0) { 133 | return String.format("%" + leadingSpaces + "s", " "); 134 | } else { 135 | return ""; 136 | } 137 | } 138 | 139 | public boolean hasLeadingPipe() { 140 | return leadingPipe; 141 | } 142 | 143 | public boolean hasTrailingPipe() { 144 | return trailingPipe; 145 | } 146 | 147 | public boolean isCommented() { 148 | return commented; 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/main/java/pipetableformatter/DelimitersCount.java: -------------------------------------------------------------------------------- 1 | package pipetableformatter; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | public class DelimitersCount { 9 | 10 | private static final char PIPE = '|'; 11 | private static final char[] DEFAULT_DELIMITERS = new char[]{PIPE, ',', '\t'}; 12 | 13 | private String text; 14 | private char[] delimiters; 15 | Map counters = new HashMap(); 16 | 17 | private DelimitersCount(String text, Range lineRange, char... delimiters) { 18 | this.text = text; 19 | this.delimiters = delimiters; 20 | initCounters(); 21 | count(text, lineRange.getStart(), lineRange.getEnd()); 22 | } 23 | 24 | public static DelimitersCount delimitersCountIn(String text) { 25 | return new DelimitersCount(text, new Range(0, text.length()), DEFAULT_DELIMITERS); 26 | } 27 | 28 | public static DelimitersCount delimitersCountIn(String text, Range lineRange) { 29 | return new DelimitersCount(text, lineRange, DEFAULT_DELIMITERS); 30 | } 31 | 32 | public static DelimitersCount pipesCountIn(String text, Range lineRange) { 33 | return new DelimitersCount(text, lineRange, PIPE); 34 | } 35 | 36 | private void initCounters() { 37 | for (char delimiter : delimiters) { 38 | counters.put(delimiter, 0); 39 | } 40 | } 41 | 42 | public boolean isSameCount(DelimitersCount another) { 43 | if (pipeIsPresentInThisOr(another)) { 44 | return isNumberOfPipesTheSameAsIn(another); 45 | } else { 46 | return isNumberOfAnyDelimiterTheSameAsIn(another); 47 | } 48 | } 49 | 50 | private boolean isNumberOfAnyDelimiterTheSameAsIn(DelimitersCount another) { 51 | for (char delimiter : counters.keySet()) { 52 | if (counters.get(delimiter).equals(another.counters.get(delimiter)) && counters.get(delimiter) > 0) { 53 | return true; 54 | } 55 | } 56 | return false; 57 | } 58 | 59 | private boolean isNumberOfPipesTheSameAsIn(DelimitersCount another) { 60 | return counters.get(PIPE).equals(another.counters.get(PIPE)); 61 | } 62 | 63 | private boolean pipeIsPresentInThisOr(DelimitersCount another) { 64 | return counters.get(PIPE) > 0 || another.counters.get(PIPE) > 0; 65 | } 66 | 67 | public boolean isZero() { 68 | for (int count : counters.values()) { 69 | if (count != 0) return false; 70 | } 71 | return true; 72 | } 73 | 74 | private int count(String text, int start, int end) { 75 | boolean quoted = false; 76 | int count = 0; 77 | 78 | for (int index = start >= 0 ? start : 0; index < end; index++) { 79 | if (text.charAt(index) == '"') quoted = !quoted; 80 | if (quoted) continue; 81 | checkForDelimiter(text.charAt(index)); 82 | } 83 | 84 | return count; 85 | } 86 | 87 | private void checkForDelimiter(char character) { 88 | if (counters.containsKey(character)) { 89 | counters.put(character, counters.get(character) + 1); 90 | } 91 | } 92 | 93 | public Character mostFrequent() { 94 | if (atLeastTwoPipesPerLine()) { 95 | return PIPE; 96 | } else { 97 | return findMostFrequentDelimiter(); 98 | } 99 | } 100 | 101 | @NotNull 102 | private Character findMostFrequentDelimiter() { 103 | char maxDelimiter = 0; 104 | int maxCount = 0; 105 | for (char delimiter : counters.keySet()) { 106 | if (counters.get(delimiter) > maxCount) { 107 | maxCount = counters.get(delimiter); 108 | maxDelimiter = delimiter; 109 | } 110 | } 111 | 112 | return maxDelimiter; 113 | } 114 | 115 | private boolean atLeastTwoPipesPerLine() { 116 | return text.split("\n").length * 2 <= counters.get(PIPE); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/main/java/pipetableformatter/FormatOptions.java: -------------------------------------------------------------------------------- 1 | package pipetableformatter; 2 | 3 | public class FormatOptions { 4 | 5 | private boolean preserveOuterState = false; 6 | 7 | 8 | public static FormatOptions formatOptions() { 9 | return new FormatOptions(); 10 | } 11 | 12 | public FormatOptions preserveOuterState() { 13 | preserveOuterState = true; 14 | return this; 15 | } 16 | 17 | public boolean shouldPreserveOuterState() { 18 | return preserveOuterState; 19 | } 20 | 21 | public boolean shouldNotPreserveOuterState() { 22 | return !preserveOuterState; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/pipetableformatter/LineSplitter.java: -------------------------------------------------------------------------------- 1 | package pipetableformatter; 2 | 3 | import java.util.Iterator; 4 | 5 | public class LineSplitter implements Iterable, Iterator { 6 | private String notFormattedText; 7 | private int startIndex; 8 | private int endIndex; 9 | private String endOfLine; 10 | private int prevStartIndex; 11 | private int lineIndex; 12 | 13 | public LineSplitter(String notFormattedText) { 14 | this.notFormattedText = notFormattedText; 15 | startIndex = 0; 16 | endIndex = 0; 17 | endOfLine = ""; 18 | lineIndex = -1; 19 | } 20 | 21 | @Override 22 | public String next() { 23 | 24 | int winEof = notFormattedText.indexOf(PipeTableParser.WIN_EOF, startIndex); 25 | int linuxEof = notFormattedText.indexOf(PipeTableParser.LINUX_EOF, startIndex); 26 | 27 | if (winEof < 0 && linuxEof < 0) { 28 | endIndex = notFormattedText.length(); 29 | endOfLine = ""; 30 | } else if (winEof > 0 && winEof < linuxEof) { 31 | endIndex = winEof; 32 | endOfLine = PipeTableParser.WIN_EOF; 33 | } else { 34 | endIndex = linuxEof; 35 | endOfLine = PipeTableParser.LINUX_EOF; 36 | } 37 | 38 | String line = notFormattedText.substring(startIndex, endIndex); 39 | prevStartIndex = startIndex; 40 | startIndex = endIndex + endOfLine.length(); 41 | lineIndex++; 42 | 43 | return line; 44 | } 45 | 46 | public String getEndOfLine() { 47 | return endOfLine; 48 | } 49 | 50 | @Override 51 | public Iterator iterator() { 52 | return this; 53 | } 54 | 55 | @Override 56 | public boolean hasNext() { 57 | return startIndex < notFormattedText.length(); 58 | } 59 | 60 | @Override 61 | public void remove() { 62 | throw new UnsupportedOperationException(); 63 | } 64 | 65 | public int currentLineStartIndex() { 66 | return prevStartIndex; 67 | } 68 | 69 | public int currentLineEndIndex() { 70 | return startIndex - endOfLine.length(); 71 | } 72 | 73 | public int currentLineIndex() { 74 | return lineIndex; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/pipetableformatter/PipeTable.java: -------------------------------------------------------------------------------- 1 | package pipetableformatter; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | //TODO: make it immutable (use builder?) 7 | public class PipeTable { 8 | 9 | List table; 10 | private int selectedRow = -1; 11 | private int selectedColumn = -1; 12 | 13 | public PipeTable(List table) { 14 | this.table = new ArrayList(table); 15 | } 16 | 17 | public int getRowCount() { 18 | return table.size(); 19 | } 20 | 21 | public String getValue(int row, int column) { 22 | return table.get(row).get(column); 23 | } 24 | 25 | public Row[] rows() { 26 | return table.toArray(new Row[table.size()]); 27 | } 28 | 29 | public int getColumnCount() { 30 | return table.size() > 0 ? table.get(0).size() : 0; 31 | } 32 | 33 | public void addColumnBefore(int columnIndex) { 34 | for (Row row : table) { 35 | row.add(columnIndex, ""); 36 | } 37 | } 38 | 39 | public int getSelectedRow() { 40 | return selectedRow; 41 | } 42 | 43 | public void setSelectedRow(int selectedRow) { 44 | this.selectedRow = selectedRow; 45 | } 46 | 47 | public int getSelectedColumn() { 48 | return selectedColumn; 49 | } 50 | 51 | public void setSelectedColumn(int selectedColumn) { 52 | this.selectedColumn = selectedColumn; 53 | } 54 | 55 | public boolean isRowCommented(int row) { 56 | return table.get(row).isCommented(); 57 | } 58 | 59 | //TODO: make it immutable (use builder?) 60 | public static class Row { 61 | List row; 62 | private String endOfLine; 63 | private boolean commented; 64 | private String indentation; 65 | private boolean leadingPipe; 66 | private boolean trailingPipe; 67 | 68 | public Row(List columns, String endOfLine) { 69 | this.endOfLine = endOfLine != null ? endOfLine : ""; 70 | row = new ArrayList(columns); 71 | } 72 | 73 | public int size() { 74 | return row.size(); 75 | } 76 | 77 | public void add(String value) { 78 | row.add(new Cell(value)); 79 | } 80 | 81 | public void add(int column, String value) { 82 | row.add(column, new Cell(value)); 83 | } 84 | 85 | public String get(int column) { 86 | return row.get(column).getValue(); 87 | } 88 | 89 | public Cell[] columns() { 90 | return row.toArray(new Cell[row.size()]); 91 | } 92 | 93 | public String endOfLine() { 94 | return endOfLine; 95 | } 96 | 97 | public boolean isCommented() { 98 | return commented; 99 | } 100 | 101 | public void setCommented(boolean commented) { 102 | this.commented = commented; 103 | } 104 | 105 | public String getIndentation() { 106 | return indentation; 107 | } 108 | 109 | public void setIndentation(String indentation) { 110 | this.indentation = indentation; 111 | } 112 | 113 | public boolean hasLeadingPipe() { 114 | return leadingPipe; 115 | } 116 | 117 | public boolean hasTrailingPipe() { 118 | return trailingPipe; 119 | } 120 | 121 | public void setLeadingPipe(boolean leadingPipe) { 122 | this.leadingPipe = leadingPipe; 123 | } 124 | 125 | public void setTrailingPipe(boolean trailingPipe) { 126 | this.trailingPipe = trailingPipe; 127 | } 128 | } 129 | 130 | public static class Cell { 131 | private String value; 132 | 133 | public Cell(String value) { 134 | this.value = value != null ? value.trim() : ""; 135 | } 136 | 137 | public String getValue() { 138 | return value; 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/main/java/pipetableformatter/PipeTableFormatter.java: -------------------------------------------------------------------------------- 1 | package pipetableformatter; 2 | 3 | import pipetableformatter.PipeTable.Cell; 4 | 5 | import static pipetableformatter.FormatOptions.formatOptions; 6 | 7 | public class PipeTableFormatter { 8 | 9 | public static final String PIPE = "|"; 10 | public static final String PIPE_COMMENT_START = "|--"; 11 | public static final String PIPE_COMMENT_END = "--|"; 12 | private FormatOptions formatOptions; 13 | 14 | private PipeTableFormatter() { 15 | } 16 | 17 | public String format(PipeTable pipeTable) { 18 | return formatPipeTable( 19 | pipeTable, 20 | formatOptions != null ? formatOptions : formatOptions() 21 | ); 22 | } 23 | 24 | private String formatPipeTable(PipeTable table, FormatOptions options) { 25 | 26 | int[] columnsMaxLength = calculateColumnsMaxLength(table); 27 | 28 | StringBuffer buffer = new StringBuffer(); 29 | for (PipeTable.Row row : table.rows()) { 30 | appendFirstPipe(options, buffer, row); 31 | int columnIndex = 0; 32 | for (Cell cell : row.columns()) { 33 | int width = correctWidthForCommentedRow(columnsMaxLength[columnIndex], row, columnIndex); 34 | String formattedValue = padValue(width, cell.getValue()); 35 | buffer.append(formattedValue); 36 | columnIndex++; 37 | appendInternalPipe(buffer, row, columnIndex); 38 | } 39 | appendLastPipe(options, buffer, row); 40 | buffer.append(row.endOfLine()); 41 | } 42 | return buffer.toString(); 43 | } 44 | 45 | private int correctWidthForCommentedRow(int maxWidth, PipeTable.Row row, int columnIndex) { 46 | return maxWidth - (row.isCommented() && (columnIndex == 0 || columnIndex == row.size() - 1) ? 2 : 0); 47 | } 48 | 49 | private void appendFirstPipe(FormatOptions options, StringBuffer buffer, PipeTable.Row row) { 50 | String pipe = row.isCommented() ? PIPE_COMMENT_START : PIPE; 51 | if (options.shouldPreserveOuterState()) { 52 | buffer.append(row.getIndentation()); 53 | } 54 | if (options.shouldNotPreserveOuterState() || row.hasLeadingPipe()) { 55 | buffer.append(pipe).append(" "); 56 | } 57 | } 58 | 59 | private void appendLastPipe(FormatOptions options, StringBuffer buffer, PipeTable.Row row) { 60 | String pipe = row.isCommented() ? PIPE_COMMENT_END : PIPE; 61 | if (options.shouldNotPreserveOuterState() || row.hasTrailingPipe()) { 62 | buffer.append(" ").append(pipe); 63 | } 64 | } 65 | 66 | 67 | private void appendInternalPipe(StringBuffer buffer, PipeTable.Row row, int columnIndex) { 68 | if (columnIndex < row.size()) { 69 | buffer.append(" ").append(PIPE).append(" "); 70 | } 71 | } 72 | 73 | private String padValue(int width, String value) { 74 | if (width > 0) { 75 | return String.format("%-" + width + "s", value); 76 | } else { 77 | return ""; 78 | } 79 | } 80 | 81 | private int[] calculateColumnsMaxLength(PipeTable table) { 82 | int[] columnsMaxLength = new int[table.getColumnCount()]; 83 | 84 | for (PipeTable.Row row : table.rows()) { 85 | int columnIndex = 0; 86 | for (Cell cell : row.columns()) { 87 | int length = cellValueLength(row, columnIndex, cell); 88 | if (length > columnsMaxLength[columnIndex]) { 89 | columnsMaxLength[columnIndex] = length; 90 | } 91 | columnIndex++; 92 | } 93 | } 94 | return columnsMaxLength; 95 | } 96 | 97 | private int cellValueLength(PipeTable.Row row, int columnIndex, Cell cell) { 98 | return cell.getValue().length() + (row.isCommented() && (columnIndex == 0 || columnIndex == row.size() - 1) ? 2 : 0); 99 | } 100 | 101 | public static PipeTableFormatter formatter() { 102 | return new PipeTableFormatter(); 103 | } 104 | 105 | public PipeTableFormatter withOptions(FormatOptions options) { 106 | this.formatOptions = options; 107 | return this; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/pipetableformatter/PipeTableParser.java: -------------------------------------------------------------------------------- 1 | package pipetableformatter; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import static pipetableformatter.DelimitersCount.*; 7 | 8 | public class PipeTableParser { 9 | public static final String WIN_EOF = "\r\n"; 10 | public static final String LINUX_EOF = "\n"; 11 | public List tableRows = new ArrayList(); 12 | public int maxRowSize = 0; 13 | private int caretPosition = -1; 14 | private int selectedRow = -1; 15 | private Character delimiter; 16 | private int selectedColumn = -1; 17 | private int currentLineStartIndex; 18 | private String notFormattedText; 19 | 20 | public PipeTableParser(String notFormattedText) { 21 | this.notFormattedText = notFormattedText; 22 | } 23 | 24 | public PipeTableParser detectingCellByPosition(int caretPosition) { 25 | this.caretPosition = caretPosition; 26 | return this; 27 | } 28 | 29 | public PipeTable parse() { 30 | parseText(); 31 | PipeTable pipeTable = new PipeTable(tableRows); 32 | pipeTable.setSelectedRow(selectedRow); 33 | pipeTable.setSelectedColumn(selectedColumn); 34 | return pipeTable; 35 | } 36 | 37 | private void parseText() { 38 | 39 | delimiter = detectDelimiter(notFormattedText); 40 | LineSplitter lineSplitter = new LineSplitter(notFormattedText); 41 | 42 | for (String line : lineSplitter) { 43 | boolean rowWithCaret = false; 44 | if (caretPosition >= lineSplitter.currentLineStartIndex() && caretPosition <= lineSplitter.currentLineEndIndex()) { 45 | selectedRow = lineSplitter.currentLineIndex(); 46 | currentLineStartIndex = lineSplitter.currentLineStartIndex(); 47 | rowWithCaret = true; 48 | } 49 | parseLine(line, lineSplitter.getEndOfLine(), rowWithCaret); 50 | } 51 | 52 | normalizeRows(); 53 | } 54 | 55 | private void normalizeRows() { 56 | for (PipeTable.Row row : tableRows) { 57 | for (int i = row.size(); i < maxRowSize; i++) { 58 | row.add(""); 59 | } 60 | } 61 | } 62 | 63 | private Character detectDelimiter(String text) { 64 | return delimitersCountIn(text).mostFrequent(); 65 | } 66 | 67 | private void parseLine(String line, String endOfLine, boolean rowWithCaret) { 68 | PipeTable.Row row = splitForColumns(line, rowWithCaret, endOfLine); 69 | 70 | rememberMaxLength(row.size()); 71 | 72 | tableRows.add(row); 73 | } 74 | 75 | private PipeTable.Row splitForColumns(String line, boolean rowWithCaret, String endOfLine) { 76 | List cells = new ArrayList(); 77 | int rowCaretPosition = caretPosition - currentLineStartIndex; 78 | 79 | ColumnSplitter tableRow = new ColumnSplitter(line, delimiter); 80 | for (String value : tableRow) { 81 | cells.add(new PipeTable.Cell(value)); 82 | if (rowWithCaret) { 83 | if (rowCaretPosition >= tableRow.currentColumnStartIndex() && rowCaretPosition <= tableRow.currentColumnEndIndex()) { 84 | selectedColumn = tableRow.currentColumnIndex(); 85 | } else if (tableRow.currentColumnIndex() == 0 && rowCaretPosition < tableRow.currentColumnStartIndex()) { 86 | selectedColumn = 0; 87 | } 88 | } 89 | } 90 | if (rowWithCaret && selectedColumn == -1) { 91 | selectedColumn = cells.size(); 92 | } 93 | 94 | PipeTable.Row row = new PipeTable.Row(cells, endOfLine); 95 | row.setCommented(tableRow.isCommented()); 96 | row.setIndentation(tableRow.getIndentetion()); 97 | row.setLeadingPipe(tableRow.hasLeadingPipe()); 98 | row.setTrailingPipe(tableRow.hasTrailingPipe()); 99 | 100 | return row; 101 | } 102 | 103 | private void rememberMaxLength(int size) { 104 | if (size > maxRowSize) { 105 | maxRowSize = size; 106 | } 107 | } 108 | } -------------------------------------------------------------------------------- /src/main/java/pipetableformatter/Range.java: -------------------------------------------------------------------------------- 1 | package pipetableformatter; 2 | 3 | public class Range { 4 | 5 | public static final Range EMPTY = new Range(0, 0); 6 | 7 | private int start; 8 | private int end; 9 | 10 | public Range(int start, int end) { 11 | 12 | this.start = start; 13 | this.end = end; 14 | } 15 | 16 | public int getStart() { 17 | return start; 18 | } 19 | 20 | public int getEnd() { 21 | return end; 22 | } 23 | 24 | public boolean isNotEmpty() { 25 | return end - start > 0; 26 | } 27 | 28 | public boolean isEmpty() { 29 | return end - start == 0; 30 | } 31 | 32 | @Override 33 | public boolean equals(Object o) { 34 | if (this == o) return true; 35 | if (!(o instanceof Range)) return false; 36 | 37 | Range range = (Range) o; 38 | 39 | if (end != range.end) return false; 40 | if (start != range.start) return false; 41 | 42 | return true; 43 | } 44 | 45 | @Override 46 | public int hashCode() { 47 | int result = start; 48 | result = 31 * result + end; 49 | return result; 50 | } 51 | 52 | @Override 53 | public String toString() { 54 | return "Range{" + 55 | "start=" + start + 56 | ", end=" + end + 57 | '}'; 58 | } 59 | 60 | public Range plus(int offset) { 61 | return new Range(start + offset, end + offset); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/pipetableformatter/TableDetector.java: -------------------------------------------------------------------------------- 1 | package pipetableformatter; 2 | 3 | import static pipetableformatter.DelimitersCount.*; 4 | 5 | public class TableDetector { 6 | 7 | private String text; 8 | private DelimitersCount baseLineDelimitersCount; 9 | private boolean onlyPipe; 10 | 11 | private TableDetector(String text) { 12 | this.text = "\n" + text + "\n"; 13 | } 14 | 15 | public static TableDetector detectTableIn(String text) { 16 | return new TableDetector(text); 17 | } 18 | 19 | public Range around(int position) { 20 | Range baseLine = findBaseLine(position + 1); 21 | baseLineDelimitersCount = asIn(baseLine); 22 | return baseLineDelimitersCount.isZero() ? Range.EMPTY : findTableRange(baseLine); 23 | } 24 | 25 | private Range findBaseLine(int position) { 26 | return new Range(findSOL(position), findEOL(position)); 27 | } 28 | 29 | private Range findTableRange(Range baseLine) { 30 | return new Range(findStartOfTableOn(baseLine), findEndOfTableOn(baseLine) - 1); 31 | } 32 | 33 | private int findStartOfTableOn(Range line) { 34 | return tableContains(line) ? findStartOfTableOn(previous(line)) : line.getEnd(); 35 | } 36 | 37 | private int findEndOfTableOn(Range line) { 38 | return tableContains(line) ? findEndOfTableOn(next(line)) : line.getStart(); 39 | } 40 | 41 | private int findEOL(int position) { 42 | return text.indexOf("\n", position); 43 | } 44 | 45 | private int findSOL(int position) { 46 | return text.lastIndexOf("\n", position - 1); 47 | } 48 | 49 | private boolean tableContains(Range line) { 50 | return baseLineDelimitersCount.isSameCount(asIn(line)); 51 | } 52 | 53 | private DelimitersCount asIn(Range currLine) { 54 | return onlyPipe ? pipesCountIn(text, currLine) : delimitersCountIn(text, currLine); 55 | } 56 | 57 | private Range previous(Range line) { 58 | return new Range(findSOL(line.getStart() - 1), line.getStart()); 59 | } 60 | 61 | private Range next(Range line) { 62 | return new Range(line.getEnd(), findEOL(line.getEnd() + 1)); 63 | } 64 | 65 | public TableDetector usingOnlyPipe() { 66 | this.onlyPipe = true; 67 | return this; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/pipetableformatter/operation/AddColumnBefore.java: -------------------------------------------------------------------------------- 1 | package pipetableformatter.operation; 2 | 3 | import pipetableformatter.PipeTable; 4 | import pipetableformatter.PipeTableParser; 5 | 6 | import static pipetableformatter.PipeTableFormatter.formatter; 7 | 8 | public class AddColumnBefore extends Operation { 9 | 10 | private final PipeTableEditor editor; 11 | 12 | public AddColumnBefore(PipeTableEditor editor) { 13 | this.editor = editor; 14 | } 15 | 16 | @Override 17 | protected void perform() { 18 | TableText tableText = getSelectedTable(editor); 19 | if (tableText.isNotEmpty()) { 20 | PipeTable pipeTable = parseTable(tableText, editor.getCaretPosition()); 21 | pipeTable.addColumnBefore(pipeTable.getSelectedColumn()); 22 | String formattedText = formatter().format(pipeTable); 23 | editor.replaceText(formattedText, tableText.getRange()); 24 | } 25 | } 26 | 27 | private PipeTable parseTable(TableText tableText, int caretPosition) { 28 | return new PipeTableParser(tableText.getText()) 29 | .detectingCellByPosition(caretPosition - tableText.getRange().getStart()) 30 | .parse(); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/pipetableformatter/operation/Format.java: -------------------------------------------------------------------------------- 1 | package pipetableformatter.operation; 2 | 3 | import pipetableformatter.FormatOptions; 4 | import pipetableformatter.PipeTable; 5 | import pipetableformatter.PipeTableParser; 6 | 7 | import static pipetableformatter.FormatOptions.formatOptions; 8 | import static pipetableformatter.PipeTableFormatter.formatter; 9 | 10 | public class Format extends Operation { 11 | 12 | private final PipeTableEditor editor; 13 | private final FormatOptions options; 14 | 15 | public Format(PipeTableEditor editor) { 16 | this(editor, formatOptions()); 17 | } 18 | 19 | public Format(PipeTableEditor editor, FormatOptions formatOptions) { 20 | this.editor = editor; 21 | this.options = formatOptions; 22 | } 23 | 24 | protected void perform() { 25 | TableText tableText = getSelectedTable(editor); 26 | if (tableText.isNotEmpty()) { 27 | PipeTable pipeTable = new PipeTableParser(tableText.getText()).parse(); 28 | String formattedText = formatter().withOptions(options).format(pipeTable); 29 | editor.replaceText(formattedText, tableText.getRange()); 30 | } 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/pipetableformatter/operation/FormatAllTables.java: -------------------------------------------------------------------------------- 1 | package pipetableformatter.operation; 2 | 3 | import pipetableformatter.PipeTable; 4 | import pipetableformatter.PipeTableParser; 5 | import pipetableformatter.Range; 6 | 7 | import static pipetableformatter.PipeTableFormatter.formatter; 8 | import static pipetableformatter.TableDetector.detectTableIn; 9 | 10 | public class FormatAllTables extends Operation { 11 | 12 | private PipeTableEditor editor; 13 | 14 | public FormatAllTables(PipeTableEditor editor) { 15 | this.editor = editor; 16 | } 17 | 18 | @Override 19 | protected void perform() { 20 | formatNext(editor.getText(), editor.getText().length() - 1); 21 | editor.setSelection(new Range(0, 0)); 22 | } 23 | 24 | private void formatNext(String text, int position) { 25 | if (position < 0) return; 26 | 27 | Range tableRange = detectTableIn(text).usingOnlyPipe().around(position); 28 | if (tableRange.isNotEmpty()) { 29 | formatAndReplace(text, tableRange); 30 | formatNext(text, tableRange.getStart() - 1); 31 | } else { 32 | formatNext(text, text.lastIndexOf("\n", position) - 1); 33 | } 34 | } 35 | 36 | private void formatAndReplace(String text, Range tableRange) { 37 | String textToFormat = text.substring(tableRange.getStart(), tableRange.getEnd()); 38 | PipeTable pipeTable = parse(textToFormat); 39 | if (pipeTable.getRowCount() > 1) { 40 | String formattedText = formatter().format(pipeTable); 41 | editor.replaceText(formattedText, tableRange); 42 | } 43 | } 44 | 45 | private PipeTable parse(String textToFormat) { 46 | return new PipeTableParser(textToFormat).parse(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/pipetableformatter/operation/Operation.java: -------------------------------------------------------------------------------- 1 | package pipetableformatter.operation; 2 | 3 | import pipetableformatter.Range; 4 | import pipetableformatter.TableDetector; 5 | 6 | import static pipetableformatter.TableDetector.detectTableIn; 7 | 8 | public abstract class Operation implements Runnable { 9 | 10 | @Override 11 | final public void run() { 12 | perform(); 13 | } 14 | 15 | protected abstract void perform(); 16 | 17 | final protected TableText getSelectedTable(PipeTableEditor editor) { 18 | TableText tableText = editor.getSelectedText(); 19 | if(tableText.isEmpty()) { 20 | String text = editor.getText(); 21 | int caretPosition = editor.getCaretPosition(); 22 | Range range = detectTableIn(text).around(caretPosition); 23 | tableText = new TableText(text.substring(range.getStart(), range.getEnd()), range); 24 | } 25 | return tableText; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/pipetableformatter/operation/PipeTableEditor.java: -------------------------------------------------------------------------------- 1 | package pipetableformatter.operation; 2 | 3 | import pipetableformatter.Range; 4 | 5 | public interface PipeTableEditor { 6 | TableText getSelectedText(); 7 | String getText(); 8 | int getCaretPosition(); 9 | void replaceText(String formattedText, Range tableRange); 10 | void setSelection(Range range); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/pipetableformatter/operation/Select.java: -------------------------------------------------------------------------------- 1 | package pipetableformatter.operation; 2 | 3 | public class Select extends Operation { 4 | 5 | private final PipeTableEditor editor; 6 | 7 | public Select(PipeTableEditor editor) { 8 | this.editor = editor; 9 | } 10 | 11 | @Override 12 | protected void perform() { 13 | TableText tableText = getSelectedTable(editor); 14 | if(tableText.isNotEmpty()) { 15 | editor.setSelection(tableText.getRange()); 16 | } 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/pipetableformatter/operation/TableText.java: -------------------------------------------------------------------------------- 1 | package pipetableformatter.operation; 2 | 3 | import pipetableformatter.Range; 4 | 5 | public class TableText { 6 | private final String text; 7 | private final Range range; 8 | 9 | public TableText(String text, Range range) { 10 | this.text = text; 11 | this.range = range; 12 | } 13 | 14 | public String getText() { 15 | return text; 16 | } 17 | 18 | public Range getRange() { 19 | return range; 20 | } 21 | 22 | public boolean isNotEmpty() { 23 | return range.isNotEmpty(); 24 | } 25 | 26 | public boolean isEmpty() { 27 | return range.isEmpty(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/pipetableformatter/plugin/AbstractPipeTableFormatAction.java: -------------------------------------------------------------------------------- 1 | package pipetableformatter.plugin; 2 | 3 | import com.intellij.openapi.actionSystem.ActionPlaces; 4 | import com.intellij.openapi.actionSystem.AnActionEvent; 5 | import com.intellij.openapi.editor.actionSystem.EditorAction; 6 | import com.intellij.openapi.editor.actionSystem.EditorActionHandler; 7 | 8 | public class AbstractPipeTableFormatAction extends EditorAction { 9 | public AbstractPipeTableFormatAction(EditorActionHandler defaultHandler) { 10 | super(defaultHandler); 11 | } 12 | 13 | @Override 14 | public void update(AnActionEvent e) { 15 | super.update(e); 16 | if (ActionPlaces.isPopupPlace(e.getPlace())) { 17 | e.getPresentation().setVisible(e.getPresentation().isEnabled()); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/pipetableformatter/plugin/IdeaPipeTableEditor.java: -------------------------------------------------------------------------------- 1 | package pipetableformatter.plugin; 2 | 3 | import com.intellij.openapi.editor.Editor; 4 | import com.intellij.openapi.editor.SelectionModel; 5 | import pipetableformatter.Range; 6 | import pipetableformatter.operation.PipeTableEditor; 7 | import pipetableformatter.operation.TableText; 8 | 9 | public class IdeaPipeTableEditor implements PipeTableEditor { 10 | protected final Editor editor; 11 | 12 | public IdeaPipeTableEditor(Editor editor) { 13 | this.editor = editor; 14 | } 15 | 16 | private SelectionModel getSelectionModel() { 17 | return editor.getSelectionModel(); 18 | } 19 | 20 | @Override 21 | public TableText getSelectedText() { 22 | return new TableText( 23 | getSelectionModel().getSelectedText(), 24 | new Range(getSelectionModel().getSelectionStart(), getSelectionModel().getSelectionEnd()) 25 | ); 26 | } 27 | 28 | @Override 29 | public String getText() { 30 | return editor.getDocument().getText(); 31 | } 32 | 33 | @Override 34 | public int getCaretPosition() { 35 | return editor.getCaretModel().getOffset(); 36 | } 37 | 38 | @Override 39 | public void replaceText(String newText, Range tableRange) { 40 | getSelectionModel().setSelection(tableRange.getStart(), tableRange.getEnd()); 41 | editor.getDocument().replaceString(tableRange.getStart(), tableRange.getEnd(), newText); 42 | } 43 | 44 | @Override 45 | public void setSelection(Range range) { 46 | getSelectionModel().setSelection(range.getStart(), range.getEnd()); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/pipetableformatter/plugin/PipeTableActionHandler.java: -------------------------------------------------------------------------------- 1 | package pipetableformatter.plugin; 2 | 3 | import com.intellij.openapi.actionSystem.DataContext; 4 | import com.intellij.openapi.application.ApplicationManager; 5 | import com.intellij.openapi.editor.Caret; 6 | import com.intellij.openapi.editor.Editor; 7 | import com.intellij.openapi.editor.actionSystem.EditorActionHandler; 8 | import org.jetbrains.annotations.Nullable; 9 | 10 | abstract class PipeTableActionHandler extends EditorActionHandler { 11 | 12 | @Override 13 | protected void doExecute(Editor editor, @Nullable Caret caret, final DataContext dataContext) { 14 | ApplicationManager.getApplication().runWriteAction(action(editor)); 15 | } 16 | 17 | abstract protected Runnable action(Editor editor); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/pipetableformatter/plugin/PipeTableAddColumnBeforeAction.java: -------------------------------------------------------------------------------- 1 | package pipetableformatter.plugin; 2 | 3 | import com.intellij.openapi.editor.Editor; 4 | import pipetableformatter.operation.AddColumnBefore; 5 | 6 | public class PipeTableAddColumnBeforeAction extends AbstractPipeTableFormatAction { 7 | public PipeTableAddColumnBeforeAction() { 8 | super(new PipeTableActionHandler() { 9 | @Override 10 | protected Runnable action(Editor editor) { 11 | return new AddColumnBefore(new IdeaPipeTableEditor(editor)); 12 | } 13 | }); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/pipetableformatter/plugin/PipeTableFormatAction.java: -------------------------------------------------------------------------------- 1 | package pipetableformatter.plugin; 2 | 3 | import com.intellij.openapi.editor.Editor; 4 | import pipetableformatter.operation.Format; 5 | 6 | public class PipeTableFormatAction extends AbstractPipeTableFormatAction { 7 | 8 | public PipeTableFormatAction() { 9 | super(new PipeTableActionHandler() { 10 | @Override 11 | protected Runnable action(Editor editor) { 12 | return new Format(new IdeaPipeTableEditor(editor)); 13 | } 14 | }); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/pipetableformatter/plugin/PipeTableFormatAllAction.java: -------------------------------------------------------------------------------- 1 | package pipetableformatter.plugin; 2 | 3 | import com.intellij.openapi.editor.Editor; 4 | import pipetableformatter.operation.FormatAllTables; 5 | 6 | public class PipeTableFormatAllAction extends AbstractPipeTableFormatAction { 7 | 8 | public PipeTableFormatAllAction() { 9 | super(new PipeTableActionHandler() { 10 | @Override 11 | protected Runnable action(Editor editor) { 12 | return new FormatAllTables(new IdeaPipeTableEditor(editor)); 13 | } 14 | }); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/pipetableformatter/plugin/PipeTableFormatPreservingOuterStateAction.java: -------------------------------------------------------------------------------- 1 | package pipetableformatter.plugin; 2 | 3 | import com.intellij.openapi.editor.Editor; 4 | import pipetableformatter.operation.Format; 5 | 6 | import static pipetableformatter.FormatOptions.formatOptions; 7 | 8 | public class PipeTableFormatPreservingOuterStateAction extends AbstractPipeTableFormatAction { 9 | 10 | public PipeTableFormatPreservingOuterStateAction() { 11 | super(new PipeTableActionHandler() { 12 | @Override 13 | protected Runnable action(Editor editor) { 14 | return new Format(new IdeaPipeTableEditor(editor), formatOptions().preserveOuterState()); 15 | } 16 | }); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/pipetableformatter/plugin/PipeTableSelectAction.java: -------------------------------------------------------------------------------- 1 | package pipetableformatter.plugin; 2 | 3 | import com.intellij.openapi.editor.Editor; 4 | import pipetableformatter.operation.Select; 5 | 6 | public class PipeTableSelectAction extends AbstractPipeTableFormatAction { 7 | public PipeTableSelectAction() { 8 | super(new PipeTableActionHandler() { 9 | @Override 10 | protected Runnable action(Editor editor) { 11 | return new Select(new IdeaPipeTableEditor(editor)); 12 | } 13 | }); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/test/java/pipetableformatter/ColumnSplitterTest.java: -------------------------------------------------------------------------------- 1 | package pipetableformatter; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.hamcrest.CoreMatchers.is; 6 | import static org.junit.Assert.*; 7 | 8 | public class ColumnSplitterTest { 9 | 10 | @Test 11 | public void iteratesTroughColumns() { 12 | ColumnSplitter columnSplitter = new ColumnSplitter("|col1|col2|col3|", '|'); 13 | assertThat(columnSplitter.next(), is("col1")); 14 | assertThat(columnSplitter.next(), is("col2")); 15 | assertThat(columnSplitter.next(), is("col3")); 16 | } 17 | 18 | @Test 19 | public void returnsTrueIfThereAreMoreColumns() { 20 | ColumnSplitter columnSplitter = new ColumnSplitter("|col1|col2|col3|", '|'); 21 | assertThat(columnSplitter.hasNext(), is(true)); 22 | } 23 | 24 | @Test 25 | public void returnsFalseIfThereAreNoMoreColumns() { 26 | ColumnSplitter columnSplitter = new ColumnSplitter("|col1|col2|col3|", '|'); 27 | columnSplitter.next(); 28 | columnSplitter.next(); 29 | columnSplitter.next(); 30 | 31 | assertThat(columnSplitter.hasNext(), is(false)); 32 | } 33 | 34 | @Test 35 | public void returnsColumnStartIndex() { 36 | ColumnSplitter columnSplitter = new ColumnSplitter("|col1|col2|col3|", '|'); 37 | columnSplitter.next(); 38 | columnSplitter.next(); 39 | 40 | assertThat(columnSplitter.currentColumnStartIndex(), is(6)); 41 | } 42 | 43 | @Test 44 | public void returnsColumnEndIndex() { 45 | ColumnSplitter columnSplitter = new ColumnSplitter("|col1|col2|col3|", '|'); 46 | columnSplitter.next(); 47 | columnSplitter.next(); 48 | 49 | assertThat(columnSplitter.currentColumnEndIndex(), is(10)); 50 | } 51 | 52 | @Test 53 | public void incrementsColumnIndex() { 54 | ColumnSplitter columnSplitter = new ColumnSplitter("|col1|col2|col3|", '|'); 55 | int index = 0; 56 | for (String value : columnSplitter) { 57 | assertThat(columnSplitter.currentColumnIndex(), is(index)); 58 | index++; 59 | } 60 | } 61 | 62 | @Test 63 | public void includesLeadingSpaceInLineWhenDeterminesColumnStartIndex() { 64 | ColumnSplitter columnSplitter = new ColumnSplitter(" |col1|col2|col3|", '|'); 65 | columnSplitter.next(); 66 | 67 | assertThat(columnSplitter.currentColumnStartIndex(), is(5)); 68 | } 69 | 70 | @Test 71 | public void returnsLeadingSpaces() { 72 | assertThat(new ColumnSplitter(" |val|val|", '|').getLeadingSpaces(), is(4)); 73 | } 74 | 75 | @Test 76 | public void returnsPresenceLeadingPipe() { 77 | assertThat(new ColumnSplitter(" |val|val|", '|').hasLeadingPipe(), is(true)); 78 | assertThat(new ColumnSplitter(" val|val|", '|').hasLeadingPipe(), is(false)); 79 | } 80 | 81 | @Test 82 | public void returnsPresenceTrailingPipe() { 83 | assertThat(new ColumnSplitter(" |val|val|", '|').hasTrailingPipe(), is(true)); 84 | assertThat(new ColumnSplitter(" |val|val", '|').hasTrailingPipe(), is(false)); 85 | } 86 | 87 | } -------------------------------------------------------------------------------- /src/test/java/pipetableformatter/LineSplitterTest.java: -------------------------------------------------------------------------------- 1 | package pipetableformatter; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.hamcrest.core.Is.is; 6 | import static org.junit.Assert.*; 7 | 8 | public class LineSplitterTest { 9 | 10 | @Test 11 | public void iteratesThrowLines() { 12 | LineSplitter lineSplitter = new LineSplitter("row1\nrow2\n"); 13 | assertThat(lineSplitter.next(), is("row1")); 14 | assertThat(lineSplitter.next(), is("row2")); 15 | } 16 | 17 | @Test 18 | public void returnTrueIfThereAreMoreLines() { 19 | LineSplitter lineSplitter = new LineSplitter("row1\nrow2\n"); 20 | assertThat(lineSplitter.hasNext(), is(true)); 21 | } 22 | 23 | @Test 24 | public void returnFalseIfThereAreNoMoreLines() { 25 | LineSplitter lineSplitter = new LineSplitter("row1\nrow2\n"); 26 | lineSplitter.next(); 27 | lineSplitter.next(); 28 | 29 | assertThat(lineSplitter.hasNext(), is(false)); 30 | } 31 | 32 | @Test 33 | public void returnsStartLineIndex() { 34 | LineSplitter lineSplitter = new LineSplitter("row1\nrow2\nrow3\n"); 35 | lineSplitter.next(); 36 | lineSplitter.next(); 37 | 38 | assertThat(lineSplitter.currentLineStartIndex(), is(5)); 39 | } 40 | 41 | @Test 42 | public void returnsEndLineIndex() { 43 | LineSplitter lineSplitter = new LineSplitter("row1\nrow2\nrow3\n"); 44 | lineSplitter.next(); 45 | lineSplitter.next(); 46 | 47 | assertThat(lineSplitter.currentLineEndIndex(), is(9)); 48 | } 49 | 50 | @Test 51 | public void incrementsLineIndex() { 52 | LineSplitter lineSplitter = new LineSplitter("row1\nrow2\nrow3\n"); 53 | int index = 0; 54 | for(String line : lineSplitter){ 55 | assertThat(lineSplitter.currentLineIndex(), is(index)); 56 | index++; 57 | } 58 | } 59 | 60 | 61 | } -------------------------------------------------------------------------------- /src/test/java/pipetableformatter/PipeTableFormatterTest.java: -------------------------------------------------------------------------------- 1 | package pipetableformatter; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.hamcrest.CoreMatchers.is; 6 | import static org.junit.Assert.assertThat; 7 | import static pipetableformatter.FormatOptions.formatOptions; 8 | import static pipetableformatter.testsupport.PipeTableBuilder.aPipeTable; 9 | import static pipetableformatter.PipeTableFormatter.formatter; 10 | 11 | public class PipeTableFormatterTest { 12 | 13 | private static final String FORMATTED_TABLE = 14 | "" + 15 | "| Header 1 | Header 2 | Header 3 | Header 4 | Header 5 |\n" + 16 | "| val 11 | val 12 | val 13 | val 14 | val 15 |\n" + 17 | "| some value longer then previous | val 22 | val 33 | val34 | and last value with extra space |\n"; 18 | 19 | private static final String FORMATTED_TABLE_WITH_COMMENT = 20 | "" + 21 | "| Header 1 | Header 2 | Header 3 | Header 4 | Header 5 |\n" + 22 | "|-- this is commented row that should be left commented | val 12 | val 13 | val 14 | this is commented row that should be left commented --|\n" + 23 | "| some value longer then previous | val 22 | val 33 | val34 | val35 |\n"; 24 | 25 | @Test 26 | public void formatsTableDelimitingColumnsWithPipes() { 27 | PipeTable pipeTable = aPipeTable() 28 | .addRow("Header 1", "Header 2", "Header 3", "Header 4", "Header 5") 29 | .addRow("val 11", "val 12", "val 13", "val 14", "val 15") 30 | .addRow("some value longer then previous", "val 22", "val 33", "val34", "and last value with extra space") 31 | .build(); 32 | 33 | String formattedText = formatter().format(pipeTable); 34 | 35 | assertThat(formattedText, is(FORMATTED_TABLE)); 36 | } 37 | 38 | @Test 39 | public void preservesCommentedRow() { 40 | PipeTable pipeTable = aPipeTable() 41 | .addRow("Header 1", "Header 2", "Header 3", "Header 4", "Header 5") 42 | .addRow("this is commented row that should be left commented", "val 12", "val 13", "val 14", "this is commented row that should be left commented").commented() 43 | .addRow("some value longer then previous", "val 22", "val 33", "val34", "val35") 44 | .build(); 45 | 46 | String formattedText = formatter().format(pipeTable); 47 | 48 | assertThat(formattedText, is(FORMATTED_TABLE_WITH_COMMENT)); 49 | } 50 | 51 | @Test 52 | public void preservesIndentation() { 53 | PipeTable pipeTable = aPipeTable() 54 | .addRow("row1.val1", "row1.val2", "row1.val3").withIndentation(" ") 55 | .addRow("row2.val1", "row2.val2", "row2.val3").withIndentation(" ") 56 | .addRow("row3.val1", "row3.val2", "row3.val3").withIndentation(" ") 57 | .build(); 58 | 59 | String formattedText = formatter().withOptions(formatOptions().preserveOuterState()).format(pipeTable); 60 | 61 | assertThat(formattedText, is("" + 62 | " | row1.val1 | row1.val2 | row1.val3 |\n" + 63 | " | row2.val1 | row2.val2 | row2.val3 |\n" + 64 | " | row3.val1 | row3.val2 | row3.val3 |\n" 65 | )); 66 | } 67 | 68 | 69 | @Test 70 | public void preservesAbsenceOfOuterPipes() { 71 | PipeTable pipeTable = aPipeTable() 72 | .addRow("row1.val1", "row1.val2", "row1.val3").withIndentation(" ").withoutLeadingPipe().withoutTrailingPipe() 73 | .addRow("row2.val1", "row2.val2", "row2.val3").withIndentation(" ").withoutLeadingPipe().withoutTrailingPipe() 74 | .addRow("row3.val1", "row3.val2", "row3.val3").withIndentation(" ").withoutLeadingPipe().withoutTrailingPipe() 75 | .build(); 76 | 77 | String formattedText = formatter().withOptions(formatOptions().preserveOuterState()).format(pipeTable); 78 | 79 | assertThat(formattedText, is("" + 80 | " row1.val1 | row1.val2 | row1.val3\n" + 81 | " row2.val1 | row2.val2 | row2.val3\n" + 82 | " row3.val1 | row3.val2 | row3.val3\n" 83 | )); 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /src/test/java/pipetableformatter/PipeTableParserTest.java: -------------------------------------------------------------------------------- 1 | package pipetableformatter; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.hamcrest.CoreMatchers.is; 6 | import static org.junit.Assert.assertThat; 7 | 8 | public class PipeTableParserTest { 9 | 10 | private final static String TABLE = 11 | "" + 12 | " | Header 1 | Header 2 | Header 3 | Header 4 | Header 5 | \n" + 13 | " | val 11 | val 12 | val 13 | val 14 | val 15 | \n" + 14 | " | some value longer then previous | val 22 | val 33 | val34 | and last value with extra space | "; 15 | 16 | 17 | @Test 18 | public void convertsEachLineOfTextToRowOfTable() { 19 | PipeTable pipeTable = new PipeTableParser("row1\nrow2").parse(); 20 | assertThat(pipeTable.getRowCount(), is(2)); 21 | assertThat(pipeTable.getValue(0, 0), is("row1")); 22 | assertThat(pipeTable.getValue(1, 0), is("row2")); 23 | } 24 | 25 | @Test 26 | public void understandsWindowsLineDelimiter() { 27 | PipeTable pipeTable = new PipeTableParser("row1\r\nrow2").parse(); 28 | assertThat(pipeTable.getRowCount(), is(2)); 29 | assertThat(pipeTable.getValue(0, 0), is("row1")); 30 | assertThat(pipeTable.getValue(1, 0), is("row2")); 31 | } 32 | 33 | @Test 34 | public void splitsLineForColumnsByPipe() { 35 | PipeTable pipeTable = new PipeTableParser("|column1|column2|").parse(); 36 | assertThat(pipeTable.getValue(0, 0), is("column1")); 37 | assertThat(pipeTable.getValue(0, 1), is("column2")); 38 | } 39 | 40 | @Test 41 | public void splitsLineForColumnsByTab() { 42 | PipeTable pipeTable = new PipeTableParser("column1\tcolumn2").parse(); 43 | assertThat(pipeTable.getValue(0, 0), is("column1")); 44 | assertThat(pipeTable.getValue(0, 1), is("column2")); 45 | } 46 | 47 | @Test 48 | public void removesLeadingAndTrailingSpacesOfValues() { 49 | PipeTable pipeTable = new PipeTableParser("| column1 | column2 | column3 |").parse(); 50 | assertThat(pipeTable.getValue(0, 0), is("column1")); 51 | assertThat(pipeTable.getValue(0, 1), is("column2")); 52 | assertThat(pipeTable.getValue(0, 2), is("column3")); 53 | } 54 | 55 | @Test 56 | public void extendsShorterRowsToMaxSizeWithEmptyValues() { 57 | PipeTable pipeTable = new PipeTableParser("|val11|val12|\n|val21|val22|val23|").parse(); 58 | assertThat(pipeTable.getValue(0, 2), is("")); 59 | } 60 | 61 | @Test 62 | public void preservesLineEndSeparatorForEachLine() { 63 | PipeTable pipeTable = new PipeTableParser("row1\r\nrow2\nrow3").parse(); 64 | assertThat(pipeTable.rows()[0].endOfLine(), is("\r\n")); 65 | assertThat(pipeTable.rows()[1].endOfLine(), is("\n")); 66 | assertThat(pipeTable.rows()[2].endOfLine(), is("")); 67 | } 68 | 69 | @Test 70 | public void splitsLineForColumnsByComma() { 71 | PipeTable pipeTable = new PipeTableParser(",column1,column2").parse(); 72 | assertThat(pipeTable.getValue(0, 0), is("")); 73 | assertThat(pipeTable.getValue(0, 1), is("column1")); 74 | assertThat(pipeTable.getValue(0, 2), is("column2")); 75 | } 76 | 77 | @Test 78 | public void ignoresCommasInsideDoubleQuotes() { 79 | PipeTable pipeTable = new PipeTableParser(" \"col1val1,col1val2\",column2").parse(); 80 | assertThat(pipeTable.getValue(0, 0), is("col1val1,col1val2")); 81 | assertThat(pipeTable.getValue(0, 1), is("column2")); 82 | } 83 | 84 | @Test 85 | public void ignoresPipeInsideDoubleQuotes() { 86 | PipeTable pipeTable = new PipeTableParser(" |\"col1val1|col1val2\"|column2|").parse(); 87 | assertThat(pipeTable.getValue(0, 0), is("col1val1|col1val2")); 88 | assertThat(pipeTable.getValue(0, 1), is("column2")); 89 | } 90 | 91 | @Test 92 | public void treatsTwoDoubleQuotesInsideQuotesAsOne() { 93 | PipeTable pipeTable = new PipeTableParser(" |\"val1\"\"val2\"|column2|").parse(); 94 | assertThat(pipeTable.getValue(0, 0), is("val1\"val2")); 95 | } 96 | 97 | @Test 98 | public void doesNotTreatCommaAsDelimiterInPipeDelimitedTable() { 99 | PipeTable pipeTable = new PipeTableParser("|value 1|value 2.1, value 2.2|").parse(); 100 | assertThat(pipeTable.getColumnCount(), is(2)); 101 | assertThat(pipeTable.getValue(0, 1), is("value 2.1, value 2.2")); 102 | } 103 | 104 | @Test 105 | public void doesNotTreatPipeAsDelimiterInCommaDelimitedTable() { 106 | PipeTable pipeTable = new PipeTableParser("value 1, value 2.1| value 2.2, value 3").parse(); 107 | assertThat(pipeTable.getColumnCount(), is(3)); 108 | assertThat(pipeTable.getValue(0, 1), is("value 2.1| value 2.2")); 109 | } 110 | 111 | @Test 112 | public void usesPipeAsPriorityDelimiter() { 113 | PipeTable pipeTable = new PipeTableParser("|{color:red, length:10, strength:2}|{color:green, length:2, strength:5}|").parse(); 114 | assertThat(pipeTable.getColumnCount(), is(2)); 115 | assertThat(pipeTable.getValue(0, 0), is("{color:red, length:10, strength:2}")); 116 | assertThat(pipeTable.getValue(0, 1), is("{color:green, length:2, strength:5}")); 117 | } 118 | 119 | @Test 120 | public void determinesRowWhereRowIsPlaced() { 121 | PipeTableParser pipeTableParser = new PipeTableParser(TABLE).detectingCellByPosition(167); 122 | PipeTable pipeTable = pipeTableParser.parse(); 123 | 124 | assertThat(pipeTable.getSelectedRow(), is(1)); 125 | } 126 | 127 | @Test 128 | public void determinesColumnWhereRowIsPlaced() { 129 | PipeTableParser pipeTableParser = new PipeTableParser(TABLE).detectingCellByPosition(167); 130 | PipeTable pipeTable = pipeTableParser.parse(); 131 | 132 | assertThat(pipeTable.getSelectedColumn(), is(3)); 133 | } 134 | 135 | @Test 136 | public void determinesColumnIfCaretPlacedRightBeforeDelimiter() { 137 | PipeTableParser pipeTableParser = new PipeTableParser(TABLE).detectingCellByPosition(159); 138 | PipeTable pipeTable = pipeTableParser.parse(); 139 | 140 | assertThat(pipeTable.getSelectedColumn(), is(2)); 141 | } 142 | 143 | @Test 144 | public void determinesRowIfCaretPlacedRightAfterLastDelimiter() { 145 | PipeTableParser pipeTableParser = new PipeTableParser(TABLE).detectingCellByPosition(103); 146 | PipeTable pipeTable = pipeTableParser.parse(); 147 | 148 | assertThat(pipeTable.getSelectedRow(), is(0)); 149 | } 150 | 151 | @Test 152 | public void interpretsAsExtraColumnWhenCaretPlacedRightAfterLastDelimiter() { 153 | PipeTableParser pipeTableParser = new PipeTableParser(TABLE).detectingCellByPosition(103); 154 | PipeTable pipeTable = pipeTableParser.parse(); 155 | 156 | assertThat(pipeTable.getSelectedColumn(), is(5)); 157 | } 158 | 159 | @Test 160 | public void interpretsAsExtraColumnWhenCaretPlacedAtTheEndOfLine() { 161 | PipeTableParser pipeTableParser = new PipeTableParser(TABLE).detectingCellByPosition(104); 162 | PipeTable pipeTable = pipeTableParser.parse(); 163 | 164 | assertThat(pipeTable.getSelectedColumn(), is(5)); 165 | } 166 | 167 | @Test 168 | public void interpretsAsFirstColumnIfCaretPlacedRightBeforeDelimiter() { 169 | PipeTableParser pipeTableParser = new PipeTableParser(TABLE).detectingCellByPosition(106); 170 | PipeTable pipeTable = pipeTableParser.parse(); 171 | 172 | assertThat(pipeTable.getSelectedColumn(), is(0)); 173 | } 174 | 175 | @Test 176 | public void interpretsAsFirstColumnIfCaretPlacedAtStartOfLine() { 177 | PipeTableParser pipeTableParser = new PipeTableParser(TABLE).detectingCellByPosition(105); 178 | PipeTable pipeTable = pipeTableParser.parse(); 179 | 180 | assertThat(pipeTable.getSelectedColumn(), is(0)); 181 | } 182 | 183 | @Test 184 | public void interpretsAsExtraColumnIfCaretPlacedAtEndOfLastLine() { 185 | PipeTableParser pipeTableParser = new PipeTableParser(TABLE).detectingCellByPosition(314); 186 | PipeTable pipeTable = pipeTableParser.parse(); 187 | 188 | assertThat(pipeTable.getSelectedColumn(), is(5)); 189 | } 190 | 191 | @Test 192 | public void detectsCommentedRow() { 193 | PipeTable pipeTable = new PipeTableParser("" + 194 | "|row1.col1|row1.col2|\n" + 195 | "|-- row2.col1|row2.col2--|\n" + 196 | "|row3.col1|row3.col2|") 197 | .parse(); 198 | 199 | assertThat(pipeTable.isRowCommented(0), is(false)); 200 | assertThat(pipeTable.isRowCommented(1), is(true)); 201 | assertThat(pipeTable.getValue(1,0), is("row2.col1")); 202 | assertThat(pipeTable.getValue(1,1), is("row2.col2")); 203 | assertThat(pipeTable.isRowCommented(2), is(false)); 204 | 205 | } 206 | 207 | @Test 208 | public void savesOuterStates() { 209 | PipeTable pipeTable = new PipeTableParser("" + 210 | " |row1.col1|row1.col2|\n" + 211 | " row2.col1|row2.col2|\n" + 212 | " |row3.col1|row3.col2 ") 213 | .parse(); 214 | 215 | assertThat(pipeTable.rows()[1].getIndentation(), is(" ")); 216 | assertThat(pipeTable.rows()[0].hasLeadingPipe(), is(true)); 217 | assertThat(pipeTable.rows()[0].hasTrailingPipe(), is(true)); 218 | assertThat(pipeTable.rows()[1].hasLeadingPipe(), is(false)); 219 | assertThat(pipeTable.rows()[2].hasTrailingPipe(), is(false)); 220 | 221 | } 222 | 223 | @Test 224 | public void detectsPresenceOfOuterPipeInCommentedRow() { 225 | PipeTable pipeTable = new PipeTableParser("|--row2.col1|row2.col2--|").parse(); 226 | 227 | assertThat(pipeTable.rows()[0].hasLeadingPipe(), is(true)); 228 | assertThat(pipeTable.rows()[0].hasTrailingPipe(), is(true)); 229 | 230 | } 231 | 232 | } 233 | -------------------------------------------------------------------------------- /src/test/java/pipetableformatter/PipeTableTest.java: -------------------------------------------------------------------------------- 1 | package pipetableformatter; 2 | 3 | import org.junit.Before; 4 | import org.junit.Test; 5 | 6 | import static org.hamcrest.CoreMatchers.is; 7 | import static org.junit.Assert.*; 8 | import static pipetableformatter.testsupport.PipeTableBuilder.aPipeTable; 9 | 10 | public class PipeTableTest { 11 | 12 | private PipeTable pipeTable; 13 | private PipeTable expectedTable; 14 | 15 | @Before 16 | public void setUp() { 17 | pipeTable = aPipeTable() 18 | .addRow("row11", "row12", "row13") 19 | .addRow("row21", "row22", "row23") 20 | .build(); 21 | 22 | expectedTable = aPipeTable() 23 | .addRow("row11", "", "row12", "row13") 24 | .addRow("row21", "", "row22", "row23") 25 | .build(); 26 | } 27 | 28 | @Test 29 | public void addsColumnBefore() { 30 | pipeTable.addColumnBefore(1); 31 | 32 | assertThat(pipeTable.getColumnCount(), is(4)); 33 | 34 | PipeTable.Row[] rows = pipeTable.rows(); 35 | PipeTable.Row[] expectedRows = expectedTable.rows(); 36 | for (int rowIndex = 0; rowIndex < rows.length; rowIndex++) { 37 | for (int cellIndex = 0; cellIndex < rows[rowIndex].size(); cellIndex++) { 38 | assertThat(rows[rowIndex].get(cellIndex), is(expectedRows[rowIndex].get(cellIndex))); 39 | } 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /src/test/java/pipetableformatter/TableDetectorTest.java: -------------------------------------------------------------------------------- 1 | package pipetableformatter; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.hamcrest.CoreMatchers.is; 6 | import static org.junit.Assert.assertThat; 7 | import static pipetableformatter.TableDetector.detectTableIn; 8 | 9 | public class TableDetectorTest { 10 | 11 | public static final String TEXT_WITH_TABLE_PIPE = "" + 12 | "this line not in table\n" + 13 | "\n" + 14 | "|header 1|header 2|header 3|\n" + 15 | "|value 11|value 12|value 13|\n" + 16 | "|value 21|value 22|value 33|\n"; 17 | 18 | public static final String TEXT_WITH_TABLE_NO_LAST_EOL = "" + 19 | "this line not in table\n" + 20 | "\n" + 21 | "|header 1|header 2|header 3|\n" + 22 | "|value 11|value 12|value 13|\n" + 23 | "|value 21|value 22|value 33|"; 24 | 25 | public static final String TEXT_WITH_TABLE_COMMA = "" + 26 | "this line not in table\n" + 27 | "\n" + 28 | "header 1,header 2,header 3\n" + 29 | "value 11,value 12,value 13\n" + 30 | "value 21,value 22,value 33\n"; 31 | 32 | public static final String TEXT_WITH_TABLE_TAB = "" + 33 | "this line not in table\n" + 34 | "\n" + 35 | "header 1\theader 2\theader 3\n" + 36 | "value 11\tvalue 12\tvalue 13\n" + 37 | "value 21\tvalue 22\tvalue 33\n"; 38 | 39 | public static final String TEXT_WITH_COMMA_INSIDE_QUOTES = "" + 40 | "header 1,header 2,header 3\n" + 41 | "value 11,\"val1,val2\",value 13\n"; 42 | 43 | public static final String TEXT_WITH_PIPE_INSIDE_QUOTES = "" + 44 | "|header 1|header 2|header 3|\n" + 45 | "|value 11|\"val1|val2\"|value 13|\n"; 46 | 47 | public static final String PIPE_TABLE_CONTAINS_VALUE_WITH_COMMA = "" + 48 | "|header 1|header 2|header 3|\n" + 49 | "|value 11|val1,val2|value 13|\n"; 50 | 51 | public static final String TEXT_WITH_PIPES_AND_COMMAS = "" + 52 | "this, line, not, in table\n" + 53 | "|header, 1|header, 2|header, 3|\n" + 54 | "|value, 11|value, 12|value, 13|\n" + 55 | "|value, 21|value, 22|value, 33|\n" + 56 | "after, the, table"; 57 | 58 | public static final int PIPE_TABLE_START_POS = 24; 59 | public static final int PIPE_TABLE_END_POS = 110; 60 | public static final int COMMA_TABLE_START_POS = 24; 61 | public static final int COMMA_TABLE_END_POS = 104; 62 | public static final int TAB_TABLE_START_POS = 24; 63 | public static final int TAB_TABLE_END_POS = 104; 64 | 65 | @Test 66 | public void detectsStartOfTheTableDelimitedWithPipe() { 67 | 68 | Range range = detectTableIn(TEXT_WITH_TABLE_PIPE).around(67); 69 | 70 | assertThat(range.getStart(), is(PIPE_TABLE_START_POS)); 71 | } 72 | 73 | @Test 74 | public void detectsEndOfTheTableDelimitedWithPipe(){ 75 | Range range = detectTableIn(TEXT_WITH_TABLE_PIPE).around(67); 76 | 77 | assertThat(range.getEnd(), is(PIPE_TABLE_END_POS)); 78 | } 79 | 80 | @Test 81 | public void detectsStartOfTheTableDelimitedWithComma() { 82 | 83 | Range range = detectTableIn(TEXT_WITH_TABLE_COMMA).around(67); 84 | 85 | assertThat(range.getStart(), is(COMMA_TABLE_START_POS)); 86 | } 87 | 88 | @Test 89 | public void detectsEndOfTheTableDelimitedWithComma(){ 90 | Range range = detectTableIn(TEXT_WITH_TABLE_COMMA).around(67); 91 | 92 | assertThat(range.getEnd(), is(COMMA_TABLE_END_POS)); 93 | } 94 | 95 | @Test 96 | public void ignoresNonPipeTablesWhenAppropriateOptionIsSpecified() { 97 | Range range = detectTableIn(TEXT_WITH_TABLE_COMMA).usingOnlyPipe().around(67); 98 | 99 | assertThat(range.isEmpty(), is(true)); 100 | } 101 | 102 | @Test 103 | public void detectsStartOfTheTableDelimitedWithTab() { 104 | 105 | Range range = detectTableIn(TEXT_WITH_TABLE_TAB).around(67); 106 | 107 | assertThat(range.getStart(), is(TAB_TABLE_START_POS)); 108 | } 109 | 110 | @Test 111 | public void detectsEndOfTheTableDelimitedWithTab(){ 112 | Range range = detectTableIn(TEXT_WITH_TABLE_TAB).around(67); 113 | 114 | assertThat(range.getEnd(), is(TAB_TABLE_END_POS)); 115 | } 116 | 117 | @Test 118 | public void ignoresCommasInsideQuotes() { 119 | Range range = detectTableIn(TEXT_WITH_COMMA_INSIDE_QUOTES).around(37); 120 | 121 | assertThat(range.getStart(), is(0)); 122 | assertThat(range.getEnd(), is(TEXT_WITH_COMMA_INSIDE_QUOTES.length()-1)); 123 | } 124 | 125 | @Test 126 | public void ignoresPipeInsideQuotes() { 127 | Range range = detectTableIn(TEXT_WITH_PIPE_INSIDE_QUOTES).around(37); 128 | 129 | assertThat(range.getStart(), is(0)); 130 | assertThat(range.getEnd(), is(TEXT_WITH_PIPE_INSIDE_QUOTES.length()-1)); 131 | } 132 | 133 | @Test 134 | public void canDetectTableIfNoLastEOL(){ 135 | Range range = detectTableIn(TEXT_WITH_TABLE_NO_LAST_EOL).around(67); 136 | 137 | assertThat(range.getEnd(), is(TEXT_WITH_TABLE_NO_LAST_EOL.length())); 138 | } 139 | 140 | @Test 141 | public void canDetectTableIfNoLastEOLAndCaretAtLastLine(){ 142 | Range range = detectTableIn(TEXT_WITH_TABLE_NO_LAST_EOL).around(98); 143 | 144 | assertThat(range.getEnd(), is(TEXT_WITH_TABLE_NO_LAST_EOL.length())); 145 | } 146 | 147 | @Test 148 | public void ignoresCommaInsidePipeTable() { 149 | Range range = detectTableIn(PIPE_TABLE_CONTAINS_VALUE_WITH_COMMA).around(37); 150 | 151 | assertThat(range.getStart(), is(0)); 152 | assertThat(range.getEnd(), is(PIPE_TABLE_CONTAINS_VALUE_WITH_COMMA.length()-1)); 153 | } 154 | 155 | @Test 156 | public void detectsTableWhenCaretPlacedAtStartOfALine() { 157 | Range range = detectTableIn(TEXT_WITH_TABLE_PIPE).around(53); 158 | 159 | assertThat(range.getStart(), is(PIPE_TABLE_START_POS)); 160 | } 161 | 162 | @Test 163 | public void detectsTableWhenCaretPlacedAtEndOfALine() { 164 | Range range = detectTableIn(TEXT_WITH_TABLE_PIPE).around(52); 165 | 166 | assertThat(range.getStart(), is(PIPE_TABLE_START_POS)); 167 | } 168 | 169 | @Test 170 | public void usesPipesAsPrimaryDelimiter() { 171 | Range range = detectTableIn(TEXT_WITH_PIPES_AND_COMMAS).around(53); 172 | 173 | assertThat(range.getStart(), is(26)); 174 | assertThat(range.getEnd(), is(121)); 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /src/test/java/pipetableformatter/operation/AddColumnBeforeOperationTest.java: -------------------------------------------------------------------------------- 1 | package pipetableformatter.operation; 2 | 3 | import org.junit.Before; 4 | import org.junit.Test; 5 | import pipetableformatter.Range; 6 | 7 | import static org.mockito.Mockito.*; 8 | 9 | public class AddColumnBeforeOperationTest { 10 | 11 | private static final String NOT_FORMATTED_TABLE = "" + 12 | "|Country|Currency|Population|Area|\n" + 13 | "|United States of America|US dollar|316 million|9.8 million sq km|\n" + 14 | "|Canada|Canadian dollar|34.7 million|9.9 million sq km|\n" + 15 | "|United Kingdom|pound sterling|62.8 million|242,514 sq km|\n" + 16 | "|Republic of Poland|zloty|38.3 million|312,685 sq km|"; 17 | 18 | 19 | protected static final String FORMATTED_TABLE_WITH_NEW_COLUMN = "" + 20 | "| Country | | Currency | Population | Area |\n" + 21 | "| United States of America | | US dollar | 316 million | 9.8 million sq km |\n" + 22 | "| Canada | | Canadian dollar | 34.7 million | 9.9 million sq km |\n" + 23 | "| United Kingdom | | pound sterling | 62.8 million | 242,514 sq km |\n" + 24 | "| Republic of Poland | | zloty | 38.3 million | 312,685 sq km |"; 25 | 26 | PipeTableEditor utility; 27 | 28 | @Before 29 | public void before() { 30 | utility = mock(PipeTableEditor.class); 31 | when(utility.getSelectedText()).thenReturn(new TableText(NOT_FORMATTED_TABLE, new Range(1, 200))); 32 | when(utility.getCaretPosition()).thenReturn(65); 33 | } 34 | 35 | 36 | @Test 37 | public void addsColumn() { 38 | new AddColumnBefore(utility).run(); 39 | 40 | verify(utility).replaceText(FORMATTED_TABLE_WITH_NEW_COLUMN, new Range(1, 200)); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/test/java/pipetableformatter/operation/FormatAllTablesOperationTest.java: -------------------------------------------------------------------------------- 1 | package pipetableformatter.operation; 2 | 3 | import org.junit.Before; 4 | import org.junit.Test; 5 | import pipetableformatter.Range; 6 | 7 | import static org.mockito.Mockito.*; 8 | 9 | public class FormatAllTablesOperationTest { 10 | 11 | private static final String TEXT_WITH_A_FEW_NOT_FORMATTED_TABLES = "" + 12 | "Story: Countries\n" + 13 | "\n" + 14 | "Scenario: Country currency\n" + 15 | "When country is \n" + 16 | "Then currency is \n" + 17 | "\n" + 18 | "Examples:\n" + 19 | "|Country|Currency|\n" + 20 | "|United States of America|US dollar|\n" + 21 | "|Canada|Canadian dollar|\n" + 22 | "|United Kingdom|pound sterling|\n" + 23 | "|Republic of Poland|zloty|\n" + 24 | "\n" + 25 | "\n" + 26 | "Scenario: Country Population\n" + 27 | "When country is , see examples\n" + 28 | "Then currency is , see examples\n" + 29 | "\n" + 30 | "Examples:\n" + 31 | "Country|Population\n" + 32 | "United States of America|316 million\n" + 33 | "Canada|34.7 million\n" + 34 | "United Kingdom|62.8 million\n" + 35 | "Republic of Poland|38.3 million\n" + 36 | "\n" + 37 | "\n" + 38 | "Scenario: Country Area\n" + 39 | "When country is \n" + 40 | "Then currency is \n" + 41 | "\n" + 42 | "Examples:\n" + 43 | "|Country|Area|\n" + 44 | "|United States of America|9.8 million sq km|\n" + 45 | "|Canada|9.9 million sq km|\n" + 46 | "|United Kingdom|242,514 sq km|\n" + 47 | "|Republic of Poland|312,685 sq km|\n"; 48 | 49 | private static final String FORMATTED_TABLE_1 = "" + 50 | "| Country | Currency |\n" + 51 | "| United States of America | US dollar |\n" + 52 | "| Canada | Canadian dollar |\n" + 53 | "| United Kingdom | pound sterling |\n" + 54 | "| Republic of Poland | zloty |"; 55 | 56 | private static final String FORMATTED_TABLE_2 = "" + 57 | "| Country | Population |\n" + 58 | "| United States of America | 316 million |\n" + 59 | "| Canada | 34.7 million |\n" + 60 | "| United Kingdom | 62.8 million |\n" + 61 | "| Republic of Poland | 38.3 million |"; 62 | 63 | private static final String FORMATTED_TABLE_3 = "" + 64 | "| Country | Area |\n" + 65 | "| United States of America | 9.8 million sq km |\n" + 66 | "| Canada | 9.9 million sq km |\n" + 67 | "| United Kingdom | 242,514 sq km |\n" + 68 | "| Republic of Poland | 312,685 sq km |"; 69 | 70 | private static final String TEXT_WITH_SINGLE_LINE_WITH_DELIMITERS = "" + 71 | "Scenario: Country strength\n" + 72 | "\n" + 73 | "When Country has raising GDP, export over import\n" + 74 | "Then Country is prosperous\n" + 75 | "\n"; 76 | 77 | PipeTableEditor editor; 78 | 79 | @Before 80 | public void before() { 81 | editor = mock(PipeTableEditor.class); 82 | } 83 | 84 | @Test 85 | public void formatsAllTablesInTheText() { 86 | when(editor.getText()).thenReturn(TEXT_WITH_A_FEW_NOT_FORMATTED_TABLES); 87 | 88 | new FormatAllTables(editor).run(); 89 | 90 | verify(editor, times(3)).replaceText(anyString(), any(Range.class)); 91 | verify(editor).replaceText(FORMATTED_TABLE_1, new Range(110, 256)); 92 | verify(editor).replaceText(FORMATTED_TABLE_2, new Range(383, 518)); 93 | verify(editor).replaceText(FORMATTED_TABLE_3, new Range(605, 757)); 94 | } 95 | 96 | @Test 97 | public void ignoresSingleLineWithDelimiters() { 98 | when(editor.getText()).thenReturn(TEXT_WITH_SINGLE_LINE_WITH_DELIMITERS); 99 | 100 | new FormatAllTables(editor).run(); 101 | 102 | verify(editor, times(0)).replaceText(anyString(), any(Range.class)); 103 | } 104 | 105 | } -------------------------------------------------------------------------------- /src/test/java/pipetableformatter/operation/FormatOperationTest.java: -------------------------------------------------------------------------------- 1 | package pipetableformatter.operation; 2 | 3 | import org.junit.Before; 4 | import org.junit.Test; 5 | import pipetableformatter.Range; 6 | 7 | import static org.mockito.Mockito.*; 8 | import static pipetableformatter.FormatOptions.formatOptions; 9 | 10 | public class FormatOperationTest { 11 | 12 | private static final String TEXT_WITH_NOT_FORMATTED_TABLE = "" + 13 | "Story: Countries\n" + 14 | "\n" + 15 | "Scenario: Country currency\n" + 16 | "When country is \n" + 17 | "Then currency is \n" + 18 | "\n" + 19 | "Examples:\n" + 20 | "|Country|Currency|Population|Area|\n" + 21 | "|United States of America|US dollar|316 million|9.8 million sq km|\n" + 22 | "|Canada|Canadian dollar|34.7 million|9.9 million sq km|\n" + 23 | "|United Kingdom|pound sterling|62.8 million|242,514 sq km|\n" + 24 | "|Republic of Poland|zloty|38.3 million|312,685 sq km|"; 25 | 26 | private static final String NOT_FORMATTED_TABLE = "" + 27 | "|Country|Currency|Population|Area|\n" + 28 | "|United States of America|US dollar|316 million|9.8 million sq km|\n" + 29 | "|Canada|Canadian dollar|34.7 million|9.9 million sq km|\n" + 30 | "|United Kingdom|pound sterling|62.8 million|242,514 sq km|\n" + 31 | "|Republic of Poland|zloty|38.3 million|312,685 sq km|"; 32 | 33 | private static final String NOT_FORMATTED_TABLE_WITHOUT_OUTER_PIPES = "" + 34 | "Country|Currency|Population|Area\n" + 35 | "United States of America|US dollar|316 million|9.8 million sq km\n" + 36 | "Canada|Canadian dollar|34.7 million|9.9 million sq km\n" + 37 | "United Kingdom|pound sterling|62.8 million|242,514 sq km\n" + 38 | "Republic of Poland|zloty|38.3 million|312,685 sq km"; 39 | 40 | protected static final String FORMATTED_TABLE = "" + 41 | "| Country | Currency | Population | Area |\n" + 42 | "| United States of America | US dollar | 316 million | 9.8 million sq km |\n" + 43 | "| Canada | Canadian dollar | 34.7 million | 9.9 million sq km |\n" + 44 | "| United Kingdom | pound sterling | 62.8 million | 242,514 sq km |\n" + 45 | "| Republic of Poland | zloty | 38.3 million | 312,685 sq km |"; 46 | 47 | 48 | private static final String NOT_FORMATTED_CSV_TABLE = "" + 49 | "Header 1,Header 2, Header 3,Header 4 ,Header 5\n" + 50 | "val 11, val 12,val 13, val 14,val 15\n" + 51 | "some value longer then previous, val 22,val 33,val34,and last value with extra space \n"; 52 | 53 | private static final String FORMATTED_CSV_TABLE = "" + 54 | "| Header 1 | Header 2 | Header 3 | Header 4 | Header 5 |\n" + 55 | "| val 11 | val 12 | val 13 | val 14 | val 15 |\n" + 56 | "| some value longer then previous | val 22 | val 33 | val34 | and last value with extra space |\n"; 57 | 58 | protected static final String FORMATTED_TABLE_WITHOUT_OUTER_PIPES = "" + 59 | "Country | Currency | Population | Area \n" + 60 | "United States of America | US dollar | 316 million | 9.8 million sq km\n" + 61 | "Canada | Canadian dollar | 34.7 million | 9.9 million sq km\n" + 62 | "United Kingdom | pound sterling | 62.8 million | 242,514 sq km \n" + 63 | "Republic of Poland | zloty | 38.3 million | 312,685 sq km "; 64 | 65 | 66 | PipeTableEditor utility; 67 | 68 | @Before 69 | public void before() { 70 | utility = mock(PipeTableEditor.class); 71 | } 72 | 73 | @Test 74 | public void formatsSelectedTable() { 75 | when(utility.getSelectedText()).thenReturn(new TableText(NOT_FORMATTED_TABLE, new Range(110, 380))); 76 | 77 | new Format(utility).run(); 78 | 79 | verify(utility).replaceText(FORMATTED_TABLE, new Range(110, 380)); 80 | } 81 | 82 | @Test 83 | public void findsAndFormatsTable() { 84 | when(utility.getSelectedText()).thenReturn(new TableText(null, new Range(0, 0))); 85 | when(utility.getText()).thenReturn(TEXT_WITH_NOT_FORMATTED_TABLE); 86 | when(utility.getCaretPosition()).thenReturn(229); 87 | 88 | new Format(utility).run(); 89 | 90 | verify(utility).replaceText(FORMATTED_TABLE, new Range(110, 380)); 91 | } 92 | 93 | @Test 94 | public void formatsCsvTableAndReplaceCommaWithPipe() { 95 | when(utility.getSelectedText()).thenReturn(new TableText(NOT_FORMATTED_CSV_TABLE, new Range(110, 380))); 96 | 97 | new Format(utility).run(); 98 | 99 | verify(utility).replaceText(FORMATTED_CSV_TABLE, new Range(110, 380)); 100 | } 101 | 102 | @Test 103 | public void formatsTableWithoutOuterPipes() { 104 | when(utility.getSelectedText()).thenReturn(new TableText(NOT_FORMATTED_TABLE_WITHOUT_OUTER_PIPES, new Range(110, 380))); 105 | 106 | new Format(utility, formatOptions().preserveOuterState()).run(); 107 | 108 | verify(utility).replaceText(FORMATTED_TABLE_WITHOUT_OUTER_PIPES, new Range(110, 380)); 109 | } 110 | 111 | } -------------------------------------------------------------------------------- /src/test/java/pipetableformatter/operation/SelectOperationTest.java: -------------------------------------------------------------------------------- 1 | package pipetableformatter.operation; 2 | 3 | import org.junit.Before; 4 | import org.junit.Test; 5 | import pipetableformatter.Range; 6 | 7 | import static org.mockito.Mockito.*; 8 | 9 | public class SelectOperationTest { 10 | 11 | private static final String TEXT_WITH_NOT_FORMATTED_TABLE = "" + 12 | "Story: Countries\n" + 13 | "\n" + 14 | "Scenario: Country currency\n" + 15 | "When country is \n" + 16 | "Then currency is \n" + 17 | "\n" + 18 | "Examples:\n" + 19 | "|Country|Currency|Population|Area|\n" + 20 | "|United States of America|US dollar|316 million|9.8 million sq km|\n" + 21 | "|Canada|Canadian dollar|34.7 million|9.9 million sq km|\n" + 22 | "|United Kingdom|pound sterling|62.8 million|242,514 sq km|\n" + 23 | "|Republic of Poland|zloty|38.3 million|312,685 sq km|"; 24 | 25 | PipeTableEditor utility; 26 | 27 | @Before 28 | public void before() { 29 | utility = mock(PipeTableEditor.class); 30 | } 31 | 32 | @Test 33 | public void selectsTable() { 34 | when(utility.getSelectedText()).thenReturn(new TableText(null, new Range(0, 0))); 35 | when(utility.getText()).thenReturn(TEXT_WITH_NOT_FORMATTED_TABLE); 36 | when(utility.getCaretPosition()).thenReturn(229); 37 | 38 | new Select(utility).run(); 39 | 40 | verify(utility).setSelection(new Range(110, 380)); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/test/java/pipetableformatter/plugin/PipeTableFormatterFunctionalTest.java: -------------------------------------------------------------------------------- 1 | package pipetableformatter.plugin; 2 | 3 | import com.intellij.openapi.editor.SelectionModel; 4 | import com.intellij.testFramework.builders.JavaModuleFixtureBuilder; 5 | import com.intellij.testFramework.fixtures.*; 6 | import org.jetbrains.annotations.NotNull; 7 | import org.junit.After; 8 | import org.junit.Before; 9 | import org.junit.Test; 10 | 11 | import static org.hamcrest.CoreMatchers.is; 12 | import static org.junit.Assert.assertThat; 13 | import static pipetableformatter.testsupport.Utils.loadFile; 14 | 15 | public class PipeTableFormatterFunctionalTest { 16 | 17 | private static final String TEXT_WITH_NOT_FORMATTED_TABLE = loadFile("input/text-with-not-formatted-table.txt"); 18 | private static final String TEXT_WITH_NOT_FORMATTED_TABLE_WITH_COMMENT = loadFile("input/text-with-not-formatted-table-with-comments.txt"); 19 | private static final String TEXT_WITH_A_FEW_NOT_FORMATTED_TABLE = loadFile("input/text-with-a-few-not-formatted-table.txt"); 20 | private static final String TEXT_WITH_A_PIPE_TABLE_WITH_A_LOT_OF_COMMAS = loadFile("input/text-with-a-pipe-table-with-a-lot-commas.txt"); 21 | private static final String TEXT_WITH_NOT_FORMATTED_TABLE_WITH_INDENTATION_WITHOUT_OUTER_PIPES = loadFile("input/text-with-not-formatted-table-with-indentation-without-outer-pipes.txt"); 22 | 23 | private static final String TEXT_WITH_FORMATTED_TABLE = loadFile("expected/text-with-formatted-table.txt"); 24 | private static final String TEXT_WITH_FORMATTED_TABLE_WITH_COMMENT = loadFile("expected/text-with-formatted-table-with-comment.txt"); 25 | private static final String TEXT_WITH_FORMATTED_TABLE_AND_NEW_COLUMN = loadFile("expected/text-with-formatted-table-and-new-column.txt"); 26 | private static final String TEXT_WITH_FORMATTED_TABLE_WITHOUT_OUTER_PIPES = loadFile("expected/text-with-formatted-table-without-outer-pipes.txt"); 27 | private static final String TEXT_WITH_ALL_FORMATTED_TABLES = loadFile("expected/text-with-all-formatted-table.txt"); 28 | private static final String TEXT_WITH_A_FORMATTED_PIPE_TABLE_WITH_A_LOT_OF_COMMAS = loadFile("expected/text-with-a-formatted-pipe-table-with-a-lot-commas.txt"); 29 | private static final String TEXT_WITH_FORMATTED_TABLE_WITH_INDENTATION_WITHOUT_OUTER_PIPES = loadFile("expected/text-with-formatted-table-with-indentation-without-outer-pipes.txt"); 30 | 31 | private CodeInsightTestFixture myFixture; 32 | 33 | @Before 34 | public void before() throws Exception { 35 | final IdeaTestFixtureFactory fixtureFactory = IdeaTestFixtureFactory.getFixtureFactory(); 36 | final TestFixtureBuilder testFixtureBuilder = fixtureFactory.createFixtureBuilder("PipeTableFormatter"); 37 | myFixture = JavaTestFixtureFactory.getFixtureFactory().createCodeInsightFixture(testFixtureBuilder.getFixture()); 38 | final JavaModuleFixtureBuilder builder = testFixtureBuilder.addModule(JavaModuleFixtureBuilder.class); 39 | 40 | builder.addContentRoot(myFixture.getTempDirPath()).addSourceRoot(""); 41 | builder.setMockJdkLevel(JavaModuleFixtureBuilder.MockJdkLevel.jdk15); 42 | 43 | myFixture.setUp(); 44 | } 45 | 46 | @After 47 | public void after() throws Exception { 48 | myFixture.tearDown(); 49 | } 50 | 51 | @Test 52 | public void formatsTableAroundCaret() throws Exception { 53 | myFixture.configureByText("test.story", TEXT_WITH_NOT_FORMATTED_TABLE); 54 | 55 | myFixture.performEditorAction("PipeTableFormatter.Format"); 56 | 57 | assertThat(textAfterActionApplied(), is(TEXT_WITH_FORMATTED_TABLE)); 58 | } 59 | 60 | @Test 61 | public void formatsTableAroundCaretWithComments() throws Exception { 62 | myFixture.configureByText("test.story", TEXT_WITH_NOT_FORMATTED_TABLE_WITH_COMMENT); 63 | 64 | myFixture.performEditorAction("PipeTableFormatter.Format"); 65 | 66 | assertThat(textAfterActionApplied(), is(TEXT_WITH_FORMATTED_TABLE_WITH_COMMENT)); 67 | } 68 | 69 | 70 | @Test 71 | public void formatsPipeTableIgnoringCommas() { 72 | myFixture.configureByText("test.story", TEXT_WITH_A_PIPE_TABLE_WITH_A_LOT_OF_COMMAS); 73 | 74 | myFixture.performEditorAction("PipeTableFormatter.Format"); 75 | 76 | assertThat(textAfterActionApplied(), is(TEXT_WITH_A_FORMATTED_PIPE_TABLE_WITH_A_LOT_OF_COMMAS)); 77 | } 78 | 79 | @Test 80 | public void selectsTableAroundCaret() throws Exception { 81 | myFixture.configureByText("test.story", TEXT_WITH_NOT_FORMATTED_TABLE); 82 | 83 | myFixture.performEditorAction("PipeTableFormatter.SelectTable"); 84 | 85 | SelectionModel selection = myFixture.getEditor().getSelectionModel(); 86 | assertThat(selection.getSelectionStart(), is(110)); 87 | assertThat(selection.getSelectionEnd(), is(380)); 88 | } 89 | 90 | @Test 91 | public void addsColumn() { 92 | myFixture.configureByText("test.story", TEXT_WITH_NOT_FORMATTED_TABLE); 93 | 94 | myFixture.performEditorAction("PipeTableFormatter.AddColumnBefore"); 95 | 96 | assertThat(textAfterActionApplied(), is(TEXT_WITH_FORMATTED_TABLE_AND_NEW_COLUMN)); 97 | } 98 | 99 | @Test 100 | public void formatsTablePreservingOuterStates() throws Exception { 101 | myFixture.configureByText("test.story", TEXT_WITH_NOT_FORMATTED_TABLE_WITH_INDENTATION_WITHOUT_OUTER_PIPES); 102 | 103 | myFixture.performEditorAction("PipeTableFormatter.FormatPreservingOuterState"); 104 | 105 | assertThat(textAfterActionApplied(), is(TEXT_WITH_FORMATTED_TABLE_WITH_INDENTATION_WITHOUT_OUTER_PIPES)); 106 | } 107 | 108 | @Test 109 | public void formatsAllTablesInEditor() throws Exception { 110 | myFixture.configureByText("test.story", TEXT_WITH_A_FEW_NOT_FORMATTED_TABLE); 111 | 112 | myFixture.performEditorAction("PipeTableFormatter.FormatAllTables"); 113 | 114 | assertThat(textAfterActionApplied(), is(TEXT_WITH_ALL_FORMATTED_TABLES)); 115 | } 116 | 117 | @NotNull 118 | private String textAfterActionApplied() { 119 | return myFixture.getEditor().getDocument().getText(); 120 | } 121 | } -------------------------------------------------------------------------------- /src/test/java/pipetableformatter/testsupport/PipeTableBuilder.java: -------------------------------------------------------------------------------- 1 | package pipetableformatter.testsupport; 2 | 3 | import com.sun.jna.platform.win32.WinBase; 4 | import pipetableformatter.PipeTable; 5 | import pipetableformatter.PipeTable.Row; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | public class PipeTableBuilder { 11 | 12 | 13 | private List rows = new ArrayList(); 14 | 15 | public static PipeTableBuilder aPipeTable() { 16 | return new PipeTableBuilder(); 17 | } 18 | 19 | public RowBuilder addRow(String... values) { 20 | Row row = row(values); 21 | rows.add(row); 22 | return new RowBuilder(row); 23 | } 24 | 25 | public PipeTable build() { 26 | return new PipeTable(rows); 27 | } 28 | 29 | private Row row(String... values) { 30 | List cellList = new ArrayList(); 31 | for (String cell : values) { 32 | cellList.add(new PipeTable.Cell(cell)); 33 | } 34 | Row row = new Row(cellList, "\n"); 35 | return row; 36 | } 37 | 38 | public class RowBuilder { 39 | private Row row; 40 | 41 | private RowBuilder(Row row) { 42 | this.row = row; 43 | row.setIndentation(""); 44 | row.setLeadingPipe(true); 45 | row.setTrailingPipe(true); 46 | } 47 | 48 | public RowBuilder commented() { 49 | row.setCommented(true); 50 | return this; 51 | } 52 | 53 | public RowBuilder withIndentation(String indentation) { 54 | row.setIndentation(indentation); 55 | return this; 56 | } 57 | 58 | public RowBuilder withoutLeadingPipe() { 59 | row.setLeadingPipe(false); 60 | return this; 61 | } 62 | 63 | public RowBuilder withoutTrailingPipe() { 64 | row.setTrailingPipe(false); 65 | return this; 66 | } 67 | 68 | public RowBuilder addRow(String... values) { 69 | return PipeTableBuilder.this.addRow(values); 70 | } 71 | 72 | public PipeTable build() { 73 | return PipeTableBuilder.this.build(); 74 | } 75 | 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/test/java/pipetableformatter/testsupport/Utils.java: -------------------------------------------------------------------------------- 1 | package pipetableformatter.testsupport; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.nio.charset.Charset; 6 | 7 | public class Utils { 8 | 9 | public static String loadFile(String fileName) { 10 | InputStream inputStream = ClassLoader.getSystemResourceAsStream(fileName); 11 | 12 | StringBuilder stringBuilder = new StringBuilder(); 13 | try { 14 | byte[] chunk = new byte[512]; 15 | for (int count = inputStream.read(chunk); count >= 0; count = inputStream.read(chunk)) { 16 | stringBuilder.append(new String(chunk, 0, count, Charset.defaultCharset())); 17 | } 18 | } catch (IOException e) { 19 | throw new RuntimeException(e); 20 | } finally { 21 | try { 22 | inputStream.close(); 23 | } catch (IOException e) { 24 | throw new RuntimeException(e); 25 | } 26 | } 27 | 28 | return stringBuilder.toString(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/test/resources/expected/text-with-a-formatted-pipe-table-with-a-lot-commas.txt: -------------------------------------------------------------------------------- 1 | | header1 | header2 | 2 | | value1 | [{a:1, b:1}, {a:1, b:1}, {a:1, b:1}] | 3 | | value2 | [{a:2, b:2}, {a:2, b:2}, {a:2, b:2}] | 4 | a,s,d,f,g,h -------------------------------------------------------------------------------- /src/test/resources/expected/text-with-all-formatted-table.txt: -------------------------------------------------------------------------------- 1 | Story: Countries, currencies, etc 2 | 3 | Scenario: Country currency 4 | When country is 5 | Then currency is 6 | 7 | Examples: 8 | | Country | Currency | 9 | | United States of America | US dollar | 10 | | Canada | Canadian dollar | 11 | | United Kingdom | pound sterling | 12 | | Republic of Poland | zloty | 13 | 14 | 15 | Scenario: Country Population 16 | When country is , see examples 17 | Then currency is , see examples 18 | 19 | Examples: 20 | | Country | Population | 21 | | United States of America | 316 million | 22 | | Canada | 34.7 million | 23 | | United Kingdom | 62.8 million | 24 | | Republic of Poland | 38.3 million | 25 | 26 | 27 | Scenario: Country Area 28 | When country is 29 | Then currency is 30 | 31 | Examples: 32 | | Country | Area | 33 | | United States of America | 9.8 million sq km | 34 | | Canada | 9.9 million sq km | 35 | | United Kingdom | 242,514 sq km | 36 | | Republic of Poland | 312,685 sq km | -------------------------------------------------------------------------------- /src/test/resources/expected/text-with-formatted-table-and-new-column.txt: -------------------------------------------------------------------------------- 1 | Story: Countries 2 | 3 | Scenario: Country currency 4 | When country is 5 | Then currency is 6 | 7 | Examples: 8 | | Country | | Currency | Population | Area | 9 | | United States of America | | US dollar | 316 million | 9.8 million sq km | 10 | | Canada | | Canadian dollar | 34.7 million | 9.9 million sq km | 11 | | United Kingdom | | pound sterling | 62.8 million | 242,514 sq km | 12 | | Republic of Poland | | zloty | 38.3 million | 312,685 sq km | -------------------------------------------------------------------------------- /src/test/resources/expected/text-with-formatted-table-with-comment.txt: -------------------------------------------------------------------------------- 1 | Story: Countries 2 | 3 | Scenario: Country currency 4 | When country is 5 | Then currency is 6 | 7 | Examples: 8 | | Country | Currency | Population | Area | 9 | |-- comment | | | --| 10 | | United States of America | US dollar | 316 million | 9.8 million sq km | 11 | | Canada | Canadian dollar | 34.7 million | 9.9 million sq km | 12 | | United Kingdom | pound sterling | 62.8 million | 242,514 sq km | 13 | | Republic of Poland | zloty | 38.3 million | 312,685 sq km | -------------------------------------------------------------------------------- /src/test/resources/expected/text-with-formatted-table-with-indentation-without-outer-pipes.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | table: 4 | Country | Currency | Population | Area 5 | United States of America | US dollar | 316 million | 9.8 million sq km 6 | Canada | Canadian dollar | 34.7 million | 9.9 million sq km 7 | United Kingdom | pound sterling | 62.8 million | 242,514 sq km 8 | Republic of Poland | zloty | 38.3 million | 312,685 sq km -------------------------------------------------------------------------------- /src/test/resources/expected/text-with-formatted-table-without-outer-pipes.txt: -------------------------------------------------------------------------------- 1 | Story: Countries 2 | 3 | Scenario: Country currency 4 | When country is 5 | Then currency is 6 | 7 | Examples: 8 | Country | Currency | Population | Area 9 | United States of America | US dollar | 316 million | 9.8 million sq km 10 | Canada | Canadian dollar | 34.7 million | 9.9 million sq km 11 | United Kingdom | pound sterling | 62.8 million | 242,514 sq km 12 | Republic of Poland | zloty | 38.3 million | 312,685 sq km -------------------------------------------------------------------------------- /src/test/resources/expected/text-with-formatted-table.txt: -------------------------------------------------------------------------------- 1 | Story: Countries 2 | 3 | Scenario: Country currency 4 | When country is 5 | Then currency is 6 | 7 | Examples: 8 | | Country | Currency | Population | Area | 9 | | United States of America | US dollar | 316 million | 9.8 million sq km | 10 | | Canada | Canadian dollar | 34.7 million | 9.9 million sq km | 11 | | United Kingdom | pound sterling | 62.8 million | 242,514 sq km | 12 | | Republic of Poland | zloty | 38.3 million | 312,685 sq km | -------------------------------------------------------------------------------- /src/test/resources/input/text-with-a-few-not-formatted-table.txt: -------------------------------------------------------------------------------- 1 | Story: Countries, currencies, etc 2 | 3 | Scenario: Country currency 4 | When country is 5 | Then currency is 6 | 7 | Examples: 8 | |Country|Currency| 9 | |United States of America|US dollar| 10 | |Canada|Canadian dollar| 11 | |United Kingdom|pound sterling| 12 | |Republic of Poland|zloty| 13 | 14 | 15 | Scenario: Country Population 16 | When country is , see examples 17 | Then currency is , see examples 18 | 19 | Examples: 20 | |Country|Population| 21 | |United States of America|316 million| 22 | |Canada|34.7 million| 23 | |United Kingdom|62.8 million| 24 | |Republic of Poland|38.3 million| 25 | 26 | 27 | Scenario: Country Area 28 | When country is 29 | Then currency is 30 | 31 | Examples: 32 | |Country|Area| 33 | |United States of America|9.8 million sq km| 34 | |Canada|9.9 million sq km| 35 | |United Kingdom|242,514 sq km| 36 | |Republic of Poland|312,685 sq km| -------------------------------------------------------------------------------- /src/test/resources/input/text-with-a-pipe-table-with-a-lot-commas.txt: -------------------------------------------------------------------------------- 1 | |header1|header2| 2 | |value1|[{a:1, b:1}, {a:1, b:1}, {a:1, b:1}]| 3 | |value2|[{a:2, b:2}, {a:2, b:2}, {a:2, b:2}]| 4 | a,s,d,f,g,h -------------------------------------------------------------------------------- /src/test/resources/input/text-with-not-formatted-table-with-comments.txt: -------------------------------------------------------------------------------- 1 | Story: Countries 2 | 3 | Scenario: Country currency 4 | When country is 5 | Then currency is 6 | 7 | Examples: 8 | |Country|Currency|Population|Area| 9 | |-- comment | | | --| 10 | |United States of America|US dollar|316 million|9.8 million sq km| 11 | |Canada|Canadian dollar|34.7 million|9.9 million sq km| 12 | |United Kingdom|pound sterling|62.8 million|242,514 sq km| 13 | |Republic of Poland|zloty|38.3 million|312,685 sq km| -------------------------------------------------------------------------------- /src/test/resources/input/text-with-not-formatted-table-with-indentation-without-outer-pipes.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | table: 4 | Country|Currency|Population|Area 5 | United States of America|US dollar|316 million|9.8 million sq km 6 | Canada|Canadian dollar|34.7 million|9.9 million sq km 7 | United Kingdom|pound sterling|62.8 million|242,514 sq km 8 | Republic of Poland|zloty|38.3 million|312,685 sq km -------------------------------------------------------------------------------- /src/test/resources/input/text-with-not-formatted-table.txt: -------------------------------------------------------------------------------- 1 | Story: Countries 2 | 3 | Scenario: Country currency 4 | When country is 5 | Then currency is 6 | 7 | Examples: 8 | |Country|Currency|Population|Area| 9 | |United States of America|US dollar|316 million|9.8 million sq km| 10 | |Canada|Canadian dollar|34.7 million|9.9 million sq km| 11 | |United Kingdom|pound sterling|62.8 million|242,514 sq km| 12 | |Republic of Poland|zloty|38.3 million|312,685 sq km| --------------------------------------------------------------------------------