!752 sS text = [[ Yo Spain! I said: "Let's do that." And he said "Yeah sure" And they said "Yeah nice" And we said "That's cool!" ]]; static Color markedColor = Color.green; static Color unmarkedColor = Color.white; concepts. concept Marking { S text; new BitSet marking; } static JTextPane pane, guessPane; static Marking marking; static Guesser guesser = new MarkLineByFirstChar; sinterface Guesser { BitSet guess(S text, BitSet initial); } p-substance { loadAndAutoSaveConcepts(); text = text.trim(); marking = uniqueConcept(Marking.class, "text", text); pane = new JTextPane; guessPane = new JTextPane; showFrame(hgrid( withTitle("Your marking", pane), withTitle("My guess", guessPane))); update(); addButtonsToWindow(pane, textComponentDependButton(pane, jbutton("Mark quote", "mark")), textComponentDependButton(pane, jbutton("Unmark quote", "unmark"))); } svoid mark { mark(true); } svoid unmark { mark(false); } svoid mark(bool b) { int from = pane.getSelectionStart(), to = pane.getSelectionEnd(); if (from < 0) ret; for (int i = from; i < to; i++) marking.marking.set(i, b); change(); update(); } svoid update { showMarkedText(pane, marking.marking); BitSet guess = guess(); showMarkedText(guessPane, guess); } static BitSet guess() { try { if (guesser != null) ret unnull(guesser.guess(text, marking.marking)); } catch e { print(e); } ret new BitSet; } svoid showMarkedText(JTextPane pane, BitSet marking) { new L mtext; Color c = unmarkedColor; new StringBuilder buf; for i over text: { Color cc = marking.get(i) ? markedColor : unmarkedColor; if (neq(cc, c)) { if (nempty(buf)) mtext.addAll(ll(c, str(buf))); buf.setLength(0); c = cc; } buf.append(text.charAt(i)); } if (nempty(buf)) mtext.addAll(ll(c, str(buf))); // Update JTextPane with new colors, preserve selection int from = pane.getSelectionStart(), to = pane.getSelectionEnd(); showColoredText(pane, mtext); pane.setSelectionStart(from); pane.setSelectionEnd(to); } sclass DummyGuesser implements Guesser { public BitSet guess(S text, BitSet initial) { BitSet bs = cloneBitSet(initial); for (int i = 0; i < l(text); i += 2) bs.set(i); ret bs; } } sclass LineStartsTest implements Guesser { public BitSet guess(S text, BitSet initial) { L l = lineStartIndexes(text); ret bitSet(l); } } sclass MarkLineByFirstChar implements Guesser { public BitSet guess(S text, BitSet initial) { L lines = filterNempty(extractMarkedLines(text, initial)); S pref = commonPrefixMulti(lines); if (empty(pref)) ret null; pref = substring(pref, 0, 1); print("Prefix: " + pref); new BitSet bs; for (int i : lineStartIndexes(text)) { S line = extractLine(text, i); if (startsWith(line, pref)) setBitRange(bs, i, min(l(text), smartIndexOf(text, "\n", i)+1)); } ret bs; } }