Skip to content

Commit 35d5c7a

Browse files
author
Julien Letrouit
committed
Better console display, titles for Koans
1 parent 6d99c94 commit 35d5c7a

45 files changed

Lines changed: 1196 additions & 550 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -117,19 +117,21 @@ Next, we can see these lines, colored in green:
117117

118118
```java
119119
/**
120+
* # Displaying some text in the console
121+
*
120122
* Display 'Hello!' in the console.
121123
*
122124
* --------- TIPS --------------
123125
*
124126
* All lines of code in Java must end with the ';' character. Ex:
125127
*
126-
* System.out.println("Apple");
128+
* System.out.println("Apple");
127129
*
128-
* You can use the method System.out.println([some value]) to write something in the console.
130+
* You can use the method System.out.println([some value]) to display a value in the console.
129131
*
130132
* You can tell Java that some value is text by enclosing it between double quotes. Ex:
131133
*
132-
* "This is text"
134+
* "This is text"
133135
*
134136
* -------------------------------
135137
*
@@ -140,7 +142,11 @@ Next, we can see these lines, colored in green:
140142
*/
141143
```
142144

143-
These lines are forming what's called a 'comment'. All the text between a `/*` and a `*/` is a comment. A comment is a piece of information that is ignored by Java. It is not code. It is very useful for documenting your code while you are writing Java. The master is putting each Koan's instructions for you in such a comment. You can find the goal of the koan at the top of each such comment: `Display 'Hello!' in the console.`. The console is the simplest way for a Java program to communicate with you by displaying simple text in a terminal. Remember what was displayed when running the Koans? You saw this bit:
145+
These lines are forming what's called a 'comment'. All the text between a `/*` and a `*/` is a comment. A comment is a piece of information that is ignored by Java. It is not code. It is very useful for documenting your code while you are writing Java. The master is putting each Koan's instructions for you in such a comment.
146+
147+
The first line of the comment tells you the title of the Koan: `# Displaying some text in the console`.
148+
149+
Then comes the goal of the koan at the top of each such comment: `Display 'Hello!' in the console.`. The console is the simplest way for a Java program to communicate with you by displaying simple text in a terminal. Remember what was displayed when running the Koans? You saw this bit:
144150

145151
```
146152
Console:
@@ -387,6 +393,8 @@ Ensuite, nous pouvons voir ces lignes, en vert:
387393

388394
```java
389395
/**
396+
* # Afficher du texte dans la console
397+
*
390398
* Afficher 'Hello!' dans la console.
391399
*
392400
* --------- INDICES --------------
@@ -412,7 +420,9 @@ Ensuite, nous pouvons voir ces lignes, en vert:
412420

413421
Ces lignes forment ce qu'on appelle un 'commentaire'. Tout le texte vert entre `/**` et `*/` est un commentaire. Un commentaire est une information destinée à des humains, et est ignoré par Java. Ce n'est pas du code. C'est très utile pour documenter ton code quand tu écris du Java. Le maître se sert d'un de ces commentaires pour placer les instructions de chacun de ses koans.
414422

415-
Tu peux trouver l'objectif du koan en haut du commentaire: `Afficher 'Hello!' dans la console.`. La console est la façon la plus simple pour un programme Java d'afficher quelque chose dans un terminal. Tu te souviens de ce qui a été affiché lorsque tu as exécuté les koans? Tu as vu ce bout de texte:
423+
La première ligne du commentaire te donne le titre du Koan: `# Afficher du texte dans la console`.
424+
425+
Ensuite, tu peux trouver l'objectif du koan en haut du commentaire: `Afficher 'Hello!' dans la console.`. La console est la façon la plus simple pour un programme Java d'afficher quelque chose dans un terminal. Tu te souviens de ce qui a été affiché lorsque tu as exécuté les koans? Tu as vu ce bout de texte:
416426

417427
```
418428
Console:

src/main/java/engine/Assertions.java

Lines changed: 75 additions & 64 deletions
Large diffs are not rendered by default.

src/main/java/engine/Color.java

Lines changed: 0 additions & 36 deletions
This file was deleted.
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package engine;
2+
3+
import java.util.Arrays;
4+
5+
/**
6+
* Console text coloring.
7+
*/
8+
public final class ConsoleFmt {
9+
private static final String NO_FORMAT = "\033[0m";
10+
11+
private static final String RED = "\033[0;31m";
12+
private static final String GREEN = "\033[0;32m";
13+
private static final String CYAN = "\033[0;36m";
14+
private static final String DODGERBLUE2="\033[38;5;27m";
15+
16+
private static final String BOLD= "\033[1m";
17+
private static final String RED_BOLD= RED + BOLD;
18+
19+
public static enum Formats {
20+
None(NO_FORMAT),
21+
Red(RED),
22+
Green(GREEN),
23+
Cyan(CYAN),
24+
Code(DODGERBLUE2),
25+
Strong(BOLD),
26+
StrongRed(RED_BOLD);
27+
28+
public final String tags;
29+
30+
private Formats(String tags) {
31+
this.tags = tags;
32+
}
33+
}
34+
35+
public record Formatted<T>(T text, Formats fmt) {}
36+
37+
public static <T> Formatted<T> fmt(T text, Formats fmt) {
38+
return new Formatted<T>(text, fmt);
39+
}
40+
41+
public static <T> Formatted<T> code(T text) {
42+
return new Formatted<T>(text, Formats.Code);
43+
}
44+
45+
public static <T> Formatted<T> strong(T text) {
46+
return new Formatted<T>(text, Formats.Strong);
47+
}
48+
49+
public static <T> Formatted<T> strongRed(T text) {
50+
return new Formatted<T>(text, Formats.StrongRed);
51+
}
52+
53+
public static Localizable<String> red(final Localizable<String> text) {
54+
return text.map(ConsoleFmt::red);
55+
}
56+
57+
public static Localizable<String> green(final Localizable<String> text) {
58+
return text.map(ConsoleFmt::green);
59+
}
60+
61+
public static Localizable<String> cyan(final Localizable<String> text) {
62+
return text.map(ConsoleFmt::cyan);
63+
}
64+
65+
public static Localizable<String> strongRed(final Localizable<String> text) {
66+
return text.map(ConsoleFmt::strongRed);
67+
}
68+
69+
public static Localizable<String> strong(final Localizable<String> text) {
70+
return text.map(ConsoleFmt::strong);
71+
}
72+
73+
public static String red(final String text) {
74+
return String.format("%s%s%s", RED, text, NO_FORMAT);
75+
}
76+
77+
public static String green(final String text) {
78+
return String.format("%s%s%s", GREEN, text, NO_FORMAT);
79+
}
80+
81+
public static String cyan(final String text) {
82+
return String.format("%s%s%s", CYAN, text, NO_FORMAT);
83+
}
84+
85+
public static String strong(final String text) {
86+
return String.format("%s%s%s", BOLD, text, NO_FORMAT);
87+
}
88+
89+
public static String strongRed(final String text) {
90+
return String.format("%s%s%s", RED_BOLD, text, NO_FORMAT);
91+
}
92+
93+
public static String format(final String template, Formats fmt, final Object... params) {
94+
final var formattedParams = Arrays
95+
.stream(params)
96+
.map(param -> param instanceof Formatted<?> fmted ? String.format("%s%s%s", fmted.fmt.tags, fmted.text, fmt.tags) : param)
97+
.toArray(Object[]::new);
98+
return String.format("%s%s%s", fmt.tags, String.format(template, formattedParams) , NO_FORMAT);
99+
}
100+
101+
public static Localizable<String> format(final Localizable<String> template, final Formats fmt, final Object... params) {
102+
return template.map(txt -> format(txt, fmt, params));
103+
}
104+
}

src/main/java/engine/Helpers.java

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import static engine.Texts.AND;
44

5-
import java.lang.reflect.Modifier;
65
import java.util.Arrays;
76
import java.util.Random;
87
import java.util.Scanner;
@@ -57,15 +56,4 @@ static String formatSequence(final Locale locale, final Object[] toFormat) {
5756

5857
return result.toString();
5958
}
60-
61-
static boolean isInstantiable(final Class<?> clasz) {
62-
final int modifiers = clasz.getModifiers();
63-
return
64-
Modifier.isPublic(modifiers) &&
65-
!clasz.isInterface() &&
66-
!Modifier.isAbstract(modifiers) &&
67-
!clasz.isArray() &&
68-
!clasz.isPrimitive() &&
69-
!clasz.equals(void.class);
70-
}
7159
}

src/main/java/engine/Koan.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,20 @@
66
import java.util.function.Function;
77

88
import engine.script.Expression;
9+
import engine.script.Type;
910

1011
/**
1112
* Stores all the information required to execute and assess the result of a koan.
1213
*/
1314
public class Koan {
1415
final Localizable<Type> koanClass;
15-
final String koanName;
16+
final Localizable<String> koanName;
1617
final KoanTest[] tests;
1718
final boolean usesConsole;
1819
final boolean showStdInInputs;
1920
final BeforeTestAssertion[] beforeAssertions;
2021

21-
public Koan(final Localizable<Class<?>> koanClass, final String koanName) {
22+
public Koan(final Localizable<Class<?>> koanClass, final Localizable<String> koanName) {
2223
this(
2324
koanClass.map(Type::new),
2425
koanName,
@@ -31,7 +32,7 @@ public Koan(final Localizable<Class<?>> koanClass, final String koanName) {
3132

3233
private Koan(
3334
final Localizable<Type> koanClass,
34-
final String koanName,
35+
final Localizable<String> koanName,
3536
final KoanTest[] tests,
3637
final boolean usesConsole,
3738
final boolean showStdInInputs,

src/main/java/engine/Sensei.java

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,15 @@
44
import java.util.List;
55
import java.util.concurrent.atomic.AtomicBoolean;
66

7+
import engine.ConsoleFmt.Formats;
78
import engine.script.Expression;
89
import engine.script.ScriptExecutionException;
910
import engine.script.ScriptRunner;
1011

12+
import static engine.ConsoleFmt.code;
13+
import static engine.ConsoleFmt.format;
14+
import static engine.ConsoleFmt.strong;
15+
import static engine.ConsoleFmt.strongRed;
1116
import static engine.Texts.*;
1217

1318
/**
@@ -79,24 +84,24 @@ private boolean offerTest(KoanTest test, int successfulCount) {
7984
// Special case: since the executeCall() method did not complete, the console conclusion was not displayed.
8085
concludeConsole(koan);
8186
if (see.getCause() instanceof InvocationTargetException ite && ite.getTargetException() instanceof StackOverflowError) {
82-
p.println(Color.red(THE_METHOD_SEEMS_TO_RECURSE_INFINITELY), see.methodName);
87+
p.println(ConsoleFmt.red(THE_METHOD_SEEMS_TO_RECURSE_INFINITELY), see.methodName);
8388
} else if (see.getCause() instanceof InvocationTargetException ite) {
84-
p.println(Color.red(THE_METHOD_APPEARS_TO_PRODUCE_AN_ERROR), see.methodName, ite.getCause().getMessage());
89+
p.println(ConsoleFmt.red(THE_METHOD_APPEARS_TO_PRODUCE_AN_ERROR), see.methodName, ite.getCause().getMessage());
8590
} else {
8691
throw see; // Serious bug
8792
}
8893
} catch (KoanBugException kbe) {
8994
// Special case: since the executeCall() method did not complete, the console conclusion was not displayed.
9095
concludeConsole(koan);
91-
p.println(Color.red(BUG_FOUND), kbe.getMessage());
96+
p.println(ConsoleFmt.red(BUG_FOUND), kbe.getMessage());
9297
}
9398
});
9499

95100
thread.setDaemon(true);
96101
thread.start();
97102
try {
98-
//thread.join(TIMEOUT_INFINITE_LOOPS_MS);
99-
thread.join();
103+
thread.join(TIMEOUT_INFINITE_LOOPS_MS);
104+
//thread.join();
100105
}
101106
catch(InterruptedException ie) {
102107
throw new IllegalStateException(String.format("Something very weird happened. We should not have been interrupted: %s.", ie.getMessage()));
@@ -105,7 +110,7 @@ private boolean offerTest(KoanTest test, int successfulCount) {
105110
if (thread.isAlive()) {
106111
StdStreamsInterceptor.reset();
107112
concludeConsole(koan);
108-
p.println(Color.red(THE_CODE_TRIED_BY_THE_SENSEI_SEEMS_TO_NOT_FINISH), Expression.formatSourceCode(test.script, locale));
113+
p.println(ConsoleFmt.red(THE_CODE_TRIED_BY_THE_SENSEI_SEEMS_TO_NOT_FINISH), Expression.formatSourceCode(test.script, locale));
109114
}
110115

111116
offerToMeditate(koan);
@@ -146,9 +151,7 @@ private boolean executeCall(final KoanTest test) {
146151
}
147152

148153
private void introduceConsole(final KoanTest test) {
149-
String testedExpression = test.script[test.script.length - 1].formatSourceCode(locale);
150-
151-
p.println();
154+
final var testedExpression = code(test.script[test.script.length - 1].formatSourceCode(locale));
152155

153156
if (test.script.length > 1 || test.koan.showStdInInputs) {
154157
p.println(THE_MASTER_SENSED_AN_HARMONY_BREACH_WHEN); // Seulement si code de prep ou stdin input
@@ -158,13 +161,13 @@ private void introduceConsole(final KoanTest test) {
158161
if (test.script.length > 1) {
159162
p.println(WHEN_EXECUTING);
160163
p.println();
161-
p.println(Expression.formatPreparationSourceCode(test.script, locale, " "));
162-
p.println(AND_FINALLY_LOOKING_THE_RESULT_OF, test.script[test.script.length - 1].formatSourceCode(locale));
164+
p.println(format(Expression.formatPreparationSourceCode(test.script, locale, " "), Formats.Code));
165+
p.println(format(AND_FINALLY_LOOKING_THE_RESULT_OF, Formats.None, testedExpression));
163166
} else {
164-
p.println(WHEN_LOOKING_THE_RESULT_OF, testedExpression);
167+
p.println(format(WHEN_LOOKING_THE_RESULT_OF, Formats.None, testedExpression));
165168
}
166169
} else {
167-
p.println(THE_MASTER_SENSED_AN_HARMONY_BREACH_WHEN_LOOKING_AT, testedExpression);
170+
p.println(format(THE_MASTER_SENSED_AN_HARMONY_BREACH_WHEN_LOOKING_AT, Formats.None, testedExpression));
168171
}
169172

170173
p.println();
@@ -187,10 +190,10 @@ private void concludeConsole(final Koan koan) {
187190
private void encourage(final Koan koan) {
188191
p.println();
189192
p.println(THINKING, koan.koanClass.get(locale).simpleClassName);
190-
p.println(Color.red(HAS_DAMAGED_YOUR_KARMA), koan.koanName);
193+
p.println(format(HAS_DAMAGED_YOUR_KARMA, Formats.Red, strongRed(koan.koanName.get(locale))));
191194
p.println();
192195
p.println(THE_MASTER_SAYS);
193-
p.println(Color.cyan(YOU_HAVE_NOT_REACHED_ENLIGHTMENT));
196+
p.println(ConsoleFmt.cyan(YOU_HAVE_NOT_REACHED_ENLIGHTMENT));
194197
p.println();
195198
p.println("---------");
196199
p.println();
@@ -201,9 +204,12 @@ private void encourage(final Koan koan) {
201204
private void offerToMeditate(final Koan koan) {
202205
p.println();
203206
p.println(
204-
PLEASE_MEDITATE_ON,
205-
koan.koanName,
206-
koan.koanClass.get(locale).simpleClassName
207+
format(
208+
PLEASE_MEDITATE_ON,
209+
Formats.None,
210+
strong(koan.koanName.get(locale)),
211+
koan.koanClass.get(locale).simpleClassName
212+
)
207213
);
208214
p.println();
209215
}
@@ -216,9 +222,9 @@ private void showProgress(final int successfulCount) {
216222

217223
final var bar = String.format(
218224
"%s%s%s",
219-
Color.green(repeat(".", successfulCount)),
220-
Color.red("X"),
221-
Color.cyan(repeat("_", allKoans.size() - successfulCount - 1)));
225+
ConsoleFmt.green(repeat(".", successfulCount)),
226+
ConsoleFmt.red("X"),
227+
ConsoleFmt.cyan(repeat("_", allKoans.size() - successfulCount - 1)));
222228
p.println(YOUR_PROGRESS_THUS_FAR, bar, successfulCount, allKoans.size());
223229
p.println();
224230
}

0 commit comments

Comments
 (0)