package me.august.lumen.common; import me.august.lumen.compile.scanner.pos.Span; import org.fusesource.jansi.Ansi; import java.util.Map; import java.util.TreeMap; import static org.fusesource.jansi.Ansi.Attribute.*; import static org.fusesource.jansi.Ansi.Color.*; import static org.fusesource.jansi.Ansi.ansi; public class TextualSnippet { private TreeMap<Integer, String> lines; private Span selected; public TextualSnippet(TreeMap<Integer, String> lines, Span selected) { this.lines = lines; this.selected = selected; } public void printError(String message) { Ansi ansi; ansi = ansi() .a(INTENSITY_BOLD).fg(RED) .a("ERROR: " + message).reset(); System.out.println(ansi); int ljust = String.valueOf(lines.lastKey()).length(); String format = "%" + ljust + "s"; int count = 0; for (Map.Entry<Integer, String> line : lines.entrySet()) { String content = line.getValue(); String first; String middle; String last; String extra = null; if (count == 0) { first = content.substring(0, selected.getStart()); if (count == lines.size() - 1) { middle = content.substring(selected.getStart(), selected.getEnd()); last = content.substring(selected.getEnd()); extra = underline( ljust + 3 + selected.getStart(), ljust + 4 + selected.getEnd() ).toString(); } else { middle = content.substring(selected.getStart()); last = ""; } } else if (count == lines.size() - 1) { first = ""; middle = content.substring(0, selected.getEnd()); last = content.substring(selected.getEnd()); } else { first = ""; middle = content; last = ""; } String justified = String.format(format, line.getKey()); ansi = ansi(). a(INTENSITY_BOLD).a(justified + ": ").reset() .a(first) .a(INTENSITY_BOLD).fg(RED).a(middle).reset() .a(last); System.out.println(ansi); if (extra != null) { System.out.println(extra); } count++; } } /** * Creates a {@code TextualSnippet} by selecting lines * within the range {@code start} to {@code end}. * @param str The source string * @param start The starting index of the selection (inclusive) * @param end The ending index of the selection (exclusive) * @return A {@code TextualSnippet} based on the selection range */ public static TextualSnippet selectLines(String str, int start, int end) { TreeMap<Integer, String> lines = new TreeMap<>(); int currentLine = 1; int lineStartPos = 0; int relativeStartPos = 0; for (int i = 0; i < start; i++) { if (str.charAt(i) == '\n') { currentLine++; lineStartPos = i + 1; } if (i == start - 1) { relativeStartPos = i - lineStartPos + 1; } } int relativeEndPos = 0; for (int i = start; i < str.length(); i++) { if (i == end) { relativeEndPos = i - lineStartPos; } if (str.charAt(i) == '\n' || i == str.length() - 1) { String sub = str.substring(lineStartPos, i); lines.put(currentLine, sub); currentLine++; lineStartPos = i + 1; if (i >= end) { break; } } } Span selected = new Span(relativeStartPos, relativeEndPos); return new TextualSnippet(lines, selected); } private static Ansi underline(int start, int end) { String lead = StringUtil.repeat(' ', start - 1); String underline = StringUtil.repeat('^', end - start - 1); return ansi().a(lead).a(INTENSITY_BOLD).fg(RED).a(underline).reset(); } }