fun measureText(str: String, height: Int): Int
do
var font = app.game.font
- return str.length * (app.game.font.width + app.game.font.hspace)
+ return str.length * (font.width + font.hspace.to_i)
end
# displays a debug rectangle
fun headless_run
do
+ srand_from 0
print "Headless run"
# Only run the playscene
var scene = new PlayScene(80000,60000)
# ~~~
module a_star
-redef class Object
- protected fun debug_a_star: Bool do return false
- private fun debug(msg: String) do if debug_a_star then
- sys.stderr.write "a_star debug: {msg}\n"
- end
-end
-
# General graph node
class Node
type N: Node
var current_bucket = buckets[cost % nbr_buckets]
if current_bucket.is_empty then # move to next bucket
- debug "b {cost} {cost % nbr_buckets} {buckets[cost % nbr_buckets].hash}"
cost += 1
if cost > max_cost then return null
bucket_searched += 1
if bucket_searched > nbr_buckets then break
else # found a node
- debug "c {cost}"
frontier_node = current_bucket.pop
if frontier_node.open then break
# at destination
else if frontier_node == destination or
(alt_targets != null and alt_targets.accept(frontier_node)) then
- debug "picked {frontier_node}, is destination"
var path = new Path[N](cost)
else
frontier_node.open = false
- debug "w exploring adjacents of {frontier_node}"
-
for link in frontier_node.links do
var peek_node = link.to
- debug "v {context.is_blocked(link)} {peek_node.last_pathfinding_evocation != graph.pathfinding_current_evocation} {peek_node.best_cost_up_to > frontier_node.best_cost_up_to + context.cost(link)}, {peek_node.best_cost_up_to} > {frontier_node.best_cost_up_to} + {context.cost(link)}"
if not context.is_blocked(link) and
(peek_node.last_pathfinding_evocation != graph.pathfinding_current_evocation or
(peek_node.open and
var at_bucket = buckets[est_cost % nbr_buckets]
at_bucket.add(peek_node)
-
- debug "u putting {peek_node} at {est_cost} -> {est_cost % nbr_buckets} {at_bucket.hash}, {cost}+{context.cost(link)}"
end
end
end
var current_bucket = buckets[current_bucket_key]
var next_bucket = new HashSet[Bucketable[G]]
+ buckets[current_bucket_key] = next_bucket
+ self.next_bucket = next_bucket
for e in current_bucket do
var act_at = e.act_at
end
end
end
-
- self.next_bucket = next_bucket
- buckets[current_bucket_key] = next_bucket
end
end
# A counter counts occurrences of things
# Use this instead of a `HashMap[E, Int]`
+#
+# ~~~
+# var c = new Counter[String].from(["a", "a", "b", "b", "b", "c"])
+# assert c["a"] == 2
+# assert c["b"] == 3
+# assert c["c"] == 1
+# assert c["d"] == 0
+# ~~~
+#
+# The counter class can also be used to gather statistical informations.
+#
+# ~~~~
+# assert c.length == 3 # because 3 distinct values
+# assert c.max == "b" # because "b" has the most count (3)
+# assert c.avg == 2.0 # because it is the mean of the counts
+# ~~~~
class Counter[E: Object]
super Map[E, Int]
# Total number of counted occurrences
+ #
+ # ~~~
+ # var c = new Counter[String]
+ # assert c.sum == 0
+ # c.inc_all(["a", "a", "b", "b", "b", "c"])
+ # assert c.sum == 6
+ # ~~~
var sum: Int = 0
private var map = new HashMap[E, Int]
sum += 1
end
+ # Count one more for each element of `es`
+ fun inc_all(es: Collection[E])
+ do
+ for e in es do inc(e)
+ end
+
+ # A new Counter initialized with `inc_all`.
+ init from(es: Collection[E])
+ do
+ inc_all(es)
+ end
+
# Return an array of elements sorted by occurrences
+ #
+ # ~~~
+ # var c = new Counter[String].from(["a", "a", "b", "b", "b", "c"])
+ # assert c.sort == ["c", "a", "b"]
+ # ~~~
fun sort: Array[E]
do
var res = map.keys.to_a
# @toimplement by default just call `to_s` on the element
protected fun element_to_s(e: E): String
do
- do return e.to_s
+ return e.to_s
end
# Display statistical information
end
end
- # Return the element with the highest value
+ # Return the element with the highest value (aka. the mode)
+ #
+ # ~~~
+ # var c = new Counter[String].from(["a", "a", "b", "b", "b", "c"])
+ # assert c.max == "b"
+ # ~~~
+ #
+ # If more than one max exists, the first one is returned.
fun max: nullable E do
var max: nullable Int = null
var elem: nullable E = null
end
# Return the couple with the lowest value
+ #
+ # ~~~
+ # var c = new Counter[String].from(["a", "a", "b", "b", "b", "c"])
+ # assert c.min == "c"
+ # ~~~
+ #
+ # If more than one min exists, the first one is returned.
fun min: nullable E do
var min: nullable Int = null
var elem: nullable E = null
return elem
end
- # Values average
+ # Values average (aka. arithmetic mean)
+ #
+ # ~~~
+ # var c = new Counter[String].from(["a", "a", "b", "b", "b", "c"])
+ # assert c.avg == 2.0
+ # ~~~
fun avg: Float do
if values.is_empty then return 0.0
return (sum / values.length).to_f
end
# The standard derivation of the counter values
+ #
+ # ~~~
+ # var c = new Counter[String].from(["a", "a", "b", "b", "b", "c"])
+ # assert c.std_dev > 0.81
+ # assert c.std_dev < 0.82
+ # ~~~
fun std_dev: Float do
var avg = self.avg
var sum = 0.0
JavaVM_jni_error(NULL, "Could not attach current thread to Java VM", res);
return NULL;
}
+ return env;
`}
end
# Call a method on `obj` designed by `method_id` with an array `args` of argument returning a JavaObject
fun call_object_method(obj: JavaObject, method_id: JMethodID, args: nullable Array[nullable Object]): JavaObject import convert_args_to_jni `{
jvalue * args_tab = JniEnv_convert_args_to_jni(recv, args);
- (*recv)->CallObjectMethod(recv, obj, method_id, args_tab);
+ jobject res = (*recv)->CallObjectMethod(recv, obj, method_id, args_tab);
free(args_tab);
+ return res;
`}
# Call a method on `obj` designed by `method_id` with an array `args` of arguments returning a Bool
fun call_boolean_method(obj: JavaObject, method_id: JMethodID, args: nullable Array[nullable Object]): Bool import convert_args_to_jni `{
jvalue * args_tab = JniEnv_convert_args_to_jni(recv, args);
- return (*recv)->CallBooleanMethod(recv, obj, method_id, args_tab);
+ jboolean res = (*recv)->CallBooleanMethod(recv, obj, method_id, args_tab);
free(args_tab);
+ return res;
`}
# Call a method on `obj` designed by `method_id` with an array `args` of arguments returning a Char
fun call_char_method(obj: JavaObject, method_id: JMethodID, args: nullable Array[nullable Object]): Char import convert_args_to_jni `{
jvalue * args_tab = JniEnv_convert_args_to_jni(recv, args);
- return (*recv)->CallCharMethod(recv, obj, method_id, args_tab);
+ jchar res = (*recv)->CallCharMethod(recv, obj, method_id, args_tab);
free(args_tab);
+ return res;
`}
# Call a method on `obj` designed by `method_id` with an array `args` of arguments returning an Int
fun call_int_method(obj: JavaObject, method_id: JMethodID, args: nullable Array[nullable Object]): Int import convert_args_to_jni `{
jvalue * args_tab = JniEnv_convert_args_to_jni(recv, args);
- return (*recv)->CallIntMethod(recv, obj, method_id, args_tab);
+ jint res = (*recv)->CallIntMethod(recv, obj, method_id, args_tab);
free(args_tab);
+ return res;
`}
# Call a method on `obj` designed by `method_id` with an array `args` of arguments returning a Float
fun call_float_method(obj: JavaObject, method_id: JMethodID, args: nullable Array[nullable Object]): Float import convert_args_to_jni `{
jvalue * args_tab = JniEnv_convert_args_to_jni(recv, args);
- return (*recv)->CallFloatMethod(recv, obj, method_id, args_tab);
+ jfloat res = (*recv)->CallFloatMethod(recv, obj, method_id, args_tab);
free(args_tab);
+ return res;
`}
# Call a method on `obj` designed by `method_id` with an array `args` of arguments returning a NativeString
# SEE: `String::md_to_html` for a shortcut.
class MarkdownProcessor
+ # `MarkdownEmitter` used for ouput.
var emitter: MarkdownEmitter is noinit
init do self.emitter = new MarkdownEmitter(self)
return new LineOther
end
+ # Get the token kind at `pos`.
+ fun token_at(text: Text, pos: Int): Token do
+ var c0: Char
+ var c1: Char
+ var c2: Char
+
+ if pos > 0 then
+ c0 = text[pos - 1]
+ else
+ c0 = ' '
+ end
+ var c = text[pos]
+
+ if pos + 1 < text.length then
+ c1 = text[pos + 1]
+ else
+ c1 = ' '
+ end
+ if pos + 2 < text.length then
+ c2 = text[pos + 2]
+ else
+ c2 = ' '
+ end
+
+ if c == '*' then
+ if c1 == '*' then
+ if c0 != ' ' or c2 != ' ' then
+ return new TokenStrongStar(pos, c)
+ else
+ return new TokenEmStar(pos, c)
+ end
+ end
+ if c0 != ' ' or c1 != ' ' then
+ return new TokenEmStar(pos, c)
+ else
+ return new TokenNone(pos, c)
+ end
+ else if c == '_' then
+ if c1 == '_' then
+ if c0 != ' ' or c2 != ' 'then
+ return new TokenStrongUnderscore(pos, c)
+ else
+ return new TokenEmUnderscore(pos, c)
+ end
+ end
+ if c0 != ' ' or c1 != ' ' then
+ return new TokenEmUnderscore(pos, c)
+ else
+ return new TokenNone(pos, c)
+ end
+ else if c == '!' then
+ if c1 == '[' then return new TokenImage(pos, c)
+ return new TokenNone(pos, c)
+ else if c == '[' then
+ return new TokenLink(pos, c)
+ else if c == ']' then
+ return new TokenNone(pos, c)
+ else if c == '`' then
+ if c1 == '`' then
+ return new TokenCodeDouble(pos, c)
+ else
+ return new TokenCodeSingle(pos, c)
+ end
+ else if c == '\\' then
+ if c1 == '\\' or c1 == '[' or c1 == ']' or c1 == '(' or c1 == ')' or c1 == '{' or c1 == '}' or c1 == '#' or c1 == '"' or c1 == '\'' or c1 == '.' or c1 == '<' or c1 == '>' or c1 == '*' or c1 == '+' or c1 == '-' or c1 == '_' or c1 == '!' or c1 == '`' or c1 == '~' or c1 == '^' then
+ return new TokenEscape(pos, c)
+ else
+ return new TokenNone(pos, c)
+ end
+ else if c == '<' then
+ return new TokenHTML(pos, c)
+ else if c == '&' then
+ return new TokenEntity(pos, c)
+ else if c == '^' then
+ if c0 == '^' or c1 == '^' then
+ return new TokenNone(pos, c)
+ else
+ return new TokenSuper(pos, c)
+ end
+ else
+ return new TokenNone(pos, c)
+ end
+ end
+
+ # Find the position of a `token` in `self`.
+ fun find_token(text: Text, start: Int, token: Token): Int do
+ var pos = start
+ while pos < text.length do
+ if token_at(text, pos).is_same_type(token) then
+ return pos
+ end
+ pos += 1
+ end
+ return -1
+ end
end
# Emit output corresponding to blocks content.
# Default is `HTMLDecorator`
var decorator: Decorator = new HTMLDecorator is writable
- # Create a new `MardownEmitter` using the default `HTMLDecorator`
- init(processor: MarkdownProcessor) do
- self.processor = processor
- end
-
# Create a new `MarkdownEmitter` using a custom `decorator`.
init with_decorator(processor: MarkdownProcessor, decorator: Decorator) do
init processor
current_text = text
current_pos = start
while current_pos < text.length do
- var mt = text.token_at(current_pos)
+ var mt = processor.token_at(text, current_pos)
if (token != null and not token isa TokenNone) and
(mt.is_same_type(token) or
(token isa TokenEmStar and mt isa TokenStrongStar) or
var next_empty: Bool = false is writable
# Initialize a new MDLine from its string value
- init(value: String) do
- self.value = value
+ init do
self.leading = process_leading
if leading != value.length then
self.is_empty = false
redef fun emit(v) do
var a = pos + next_pos + 1
- var b = v.current_text.find_token(a, self)
+ var b = v.processor.find_token(v.current_text.as(not null), a, self)
if b > 0 then
v.current_pos = b + next_pos
while a < b and v.current_text[a] == ' ' do a += 1
redef class Text
- # Get the token kind at `pos`.
- private fun token_at(pos: Int): Token do
- var c0: Char
- var c1: Char
- var c2: Char
-
- if pos > 0 then
- c0 = self[pos - 1]
- else
- c0 = ' '
- end
- var c = self[pos]
-
- if pos + 1 < length then
- c1 = self[pos + 1]
- else
- c1 = ' '
- end
- if pos + 2 < length then
- c2 = self[pos + 2]
- else
- c2 = ' '
- end
-
- if c == '*' then
- if c1 == '*' then
- if c0 != ' ' or c2 != ' ' then
- return new TokenStrongStar(pos, c)
- else
- return new TokenEmStar(pos, c)
- end
- end
- if c0 != ' ' or c1 != ' ' then
- return new TokenEmStar(pos, c)
- else
- return new TokenNone(pos, c)
- end
- else if c == '_' then
- if c1 == '_' then
- if c0 != ' ' or c2 != ' 'then
- return new TokenStrongUnderscore(pos, c)
- else
- return new TokenEmUnderscore(pos, c)
- end
- end
- if c0 != ' ' or c1 != ' ' then
- return new TokenEmUnderscore(pos, c)
- else
- return new TokenNone(pos, c)
- end
- else if c == '!' then
- if c1 == '[' then return new TokenImage(pos, c)
- return new TokenNone(pos, c)
- else if c == '[' then
- return new TokenLink(pos, c)
- else if c == ']' then
- return new TokenNone(pos, c)
- else if c == '`' then
- if c1 == '`' then
- return new TokenCodeDouble(pos, c)
- else
- return new TokenCodeSingle(pos, c)
- end
- else if c == '\\' then
- if c1 == '\\' or c1 == '[' or c1 == ']' or c1 == '(' or c1 == ')' or c1 == '{' or c1 == '}' or c1 == '#' or c1 == '"' or c1 == '\'' or c1 == '.' or c1 == '<' or c1 == '>' or c1 == '*' or c1 == '+' or c1 == '-' or c1 == '_' or c1 == '!' or c1 == '`' or c1 == '~' or c1 == '^' then
- return new TokenEscape(pos, c)
- else
- return new TokenNone(pos, c)
- end
- else if c == '<' then
- return new TokenHTML(pos, c)
- else if c == '&' then
- return new TokenEntity(pos, c)
- else if c == '^' then
- if c0 == '^' or c1 == '^' then
- return new TokenNone(pos, c)
- else
- return new TokenSuper(pos, c)
- end
- else
- return new TokenNone(pos, c)
- end
- end
-
- # Find the position of a `token` in `self`.
- private fun find_token(start: Int, token: Token): Int do
- var pos = start
- while pos < length do
- if token_at(pos).is_same_type(token) then
- return pos
- end
- pos += 1
- end
- return -1
- end
-
# Get the position of the next non-space character.
private fun skip_spaces(start: Int): Int do
var pos = start
var env = "MNIT_SRAND".environ
if env != "" then
srand_from(env.to_i)
+ else
+ srand_from(0)
end
var input = "MNIT_READ_INPUT".environ
# Additional space to insert horizontally between characters
# A negave value will display tile overlaped
- var hspace: Int = 0 is writable
+ var hspace: Numeric = 0.0 is writable
# Additional space to insert vertically between characters
# A negave value will display tile overlaped
- var vspace: Int = 0 is writable
+ var vspace: Numeric = 0.0 is writable
# The glyph (tile) associated to the caracter `c` according to `chars`
# Returns null if `c` is not in `chars`
# '\n' are rendered as carriage return
fun text(text: String, font: TileSetFont, x, y: Numeric)
do
+ x = x.to_f
var cx = x
- var cy = y
- var sw = font.width + font.hspace
- var sh = font.height + font.vspace
+ var cy = y.to_f
+ var sw = font.width.to_f + font.hspace.to_f
+ var sh = font.height.to_f + font.vspace.to_f
for c in text.chars do
if c == '\n' then
cx = x
int has_alpha;
unsigned int row_bytes;
- png_bytepp row_pointers;
- unsigned char *pixels;
+ png_bytepp row_pointers = NULL;
+ unsigned char *pixels = NULL;
unsigned int i;
unsigned char sig[8];
png_get_IHDR( png_ptr, info_ptr, &width, &height,
&depth, &color_type, NULL, NULL, NULL);
- if (color_type == PNG_COLOR_TYPE_RGBA)
- has_alpha = 1;
- else if (color_type == PNG_COLOR_TYPE_RGB)
- has_alpha = 0;
- else {
- LOGW("unknown color_type");
- goto close_png_ptr;
+ has_alpha = color_type & PNG_COLOR_MASK_ALPHA;
+
+ // If we get gray and alpha only, standardize the format of the pixels.
+ // GA is not supported by OpenGL ES 1.
+ if (!(color_type & PNG_COLOR_MASK_COLOR)) {
+ png_set_gray_to_rgb(png_ptr);
+ png_set_palette_to_rgb(png_ptr);
+ png_read_update_info(png_ptr, info_ptr);
}
LOGW("w: %i, h: %i", width, height);
row_bytes = png_get_rowbytes(png_ptr, info_ptr);
pixels = malloc(row_bytes * height);
- row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * height);
+ row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * height);
- for (i=0; i<height; i++)
- row_pointers[i] = (png_byte*) malloc(row_bytes);
+ for (i=0; i<height; i++)
+ row_pointers[i] = (png_byte*) malloc(row_bytes);
png_read_image(png_ptr, row_pointers);
else
png_destroy_read_struct(&png_ptr, NULL, NULL);
+ if (pixels != NULL)
+ free(pixels);
+
+ if (row_pointers != NULL) {
+ for (i=0; i<height; i++)
+ free(row_pointers[i]);
+ free(row_pointers);
+ }
+
close_stream:
return recv;
`}
# The node in the hiearchical structure for each element
private var nodes = new HashMap[E, DisjointSetNode]
+ # The number of subsets in the partition
+ #
+ # var s = new DisjointSet[Int]
+ # s.add_all([1,2,3,4,5])
+ # assert s.number_of_subsets == 5
+ # s.union_all([1,4,5])
+ # assert s.number_of_subsets == 3
+ # s.union(4,5)
+ # assert s.number_of_subsets == 3
+ var number_of_subsets: Int = 0
+
# Get the root node of an element
# require: `has(e)`
private fun find(e:E): DisjointSetNode
if nodes.has_key(e) then return
var ne = new DisjointSetNode
nodes[e] = ne
+ number_of_subsets += 1
end
# Are two elements in the same subset?
ne.rank = er+1
end
end
+ number_of_subsets -= 1
end
# Combine the subsets of all elements of `es`
end
end
+redef class Sys
+ init
+ do
+ srand
+ end
+end
+
fun atan2(x: Float, y: Float): Float is extern "kernel_Any_Any_atan2_2"
fun pi: Float is extern "kernel_Any_Any_pi_0"
+
+# Initialize the pseudo-random generator with the given seed.
+# The pseudo-random generator is used by the method `rand` and other to generate sequence of numbers.
+# These sequences are repeatable by calling `srand_from` with a same seed value.
+#
+# ~~~~
+# srand_from(0)
+# var a = 10.rand
+# var b = 100.rand
+# srand_from(0)
+# assert 10.rand == a
+# assert 100.rand == b
+# ~~~~
fun srand_from(x: Int) is extern "kernel_Any_Any_srand_from_1"
+
+# Reinitialize the pseudo-random generator used by the method `rand` and other.
+# This method is automatically invoked at the begin of the program, so usually, there is no need to manually invoke it.
+# The only exception is in conjunction with `srand_from` to reset the pseudo-random generator.
fun srand is extern "kernel_Any_Any_srand_0"
// Integer to NativeString method
char* native_int_to_s(long recv){
int len = snprintf(NULL, 0, "%ld", recv);
- char* str = malloc(len);
+ char* str = malloc(len+1);
sprintf(str, "%ld", recv);
return str;
}
return v.native_string_instance(txt)
else if pname == "get_time" then
return v.int_instance(get_time)
+ else if pname == "srand" then
+ srand
+ return null
else if pname == "srand_from" then
srand_from(args[1].to_i)
return null
# The lines of the current code block, empty is no current code block
var curblock = new Array[String]
+ # Count empty lines between code blocks
+ var empty_lines = 0
+
fun work(mdoc: MDoc): HTMLTag
do
var root = new HTMLTag("div")
# Is codeblock? Then just collect them
if indent >= 3 then
+ for i in [0..empty_lines[ do curblock.add("")
+ empty_lines = 0
# to allows 4 spaces including the one that follows the #
curblock.add(text)
continue
end
- # Was a codblock just before the current line ?
- close_codeblock(n or else root)
-
# fence opening
if text.substring(0,3) == "~~~" then
+ # Was a codblock just before the current line ?
+ close_codeblock(n or else root)
+
var l = 3
while l < text.length and text.chars[l] == '~' do l += 1
in_fence = text.substring(0, l)
if text.is_empty or indent < lastindent then
n = null
ul = null
- if text.is_empty then continue
+ if text.is_empty then
+ if not curblock.is_empty then empty_lines += 1
+ continue
+ end
end
+ # Was a codblock just before the current line ?
+ close_codeblock(n or else root)
+
# Special first word: new paragraph
if text.has_prefix("TODO") or text.has_prefix("FIXME") then
n = new HTMLTag("p")
do
# Is there a codeblock to manage?
if not curblock.is_empty then
+ empty_lines = 0
+
# determine the smalest indent
var minindent = -1
for text in curblock do
var indent = 0
while indent < text.length and text.chars[indent] == ' ' do indent += 1
+ # skip white lines
+ if indent >= text.length then continue
if minindent == -1 or indent < minindent then
minindent = indent
end
end
+ if minindent < 0 then minindent = 0
# Generate the text
var btext = new FlatBuffer
self.toolcontext.info("{mmodule} imports {imported_modules.join(", ")}", 3)
mmodule.set_imported_mmodules(imported_modules)
+ # Force standard to be public if imported
+ for sup in mmodule.in_importation.greaters do
+ if sup.name == "standard" then
+ mmodule.set_visibility_for(sup, public_visibility)
+ end
+ end
+
# TODO: Correctly check for useless importation
# It is even doable?
var directs = mmodule.in_importation.direct_greaters
redef fun process_code(n: HTMLTag, text: String)
do
+ # Skip non-blocks
+ if n.tag != "pre" then return
+
# Try to parse it
var ast = toolcontext.parse_something(text)
toolcontext.modelbuilder.unit_entities += 1
cpt += 1
- var file = "{prefix}{cpt}.nit"
+ var file = "{prefix}-{cpt}.nit"
toolcontext.info("Execute doc-unit {tc.attrs["name"]} in {file}", 1)
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+private import template
+
+print 1
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import base_import_standard
+
+print 2
test_nitunit.nit --no-color -o $WRITE
test_nitunit.nit --gen-suite --only-show
test_nitunit.nit --gen-suite --only-show --private
+test_nitunit2.nit -o $WRITE
-test_nitunit.nit:20,1--22,0: ERROR: nitunit.test_nitunit.test_nitunit::X.<class> (in .nitunit/test_nitunit2.nit): Runtime error: Assert failed (.nitunit/test_nitunit2.nit:5)
+test_nitunit.nit:20,1--22,0: ERROR: nitunit.test_nitunit.test_nitunit::X.<class> (in .nitunit/test_nitunit-2.nit): Runtime error: Assert failed (.nitunit/test_nitunit-2.nit:5)
-test_nitunit.nit:23,2--25,0: FAILURE: nitunit.test_nitunit.test_nitunit::X.test_nitunit::X::foo (in .nitunit/test_nitunit3.nit): .nitunit/test_nitunit3.nit:5,8--27: Error: Method or variable 'undefined_identifier' unknown in Sys.
+test_nitunit.nit:23,2--25,0: FAILURE: nitunit.test_nitunit.test_nitunit::X.test_nitunit::X::foo (in .nitunit/test_nitunit-3.nit): .nitunit/test_nitunit-3.nit:5,8--27: Error: Method or variable 'undefined_identifier' unknown in Sys.
test_test_nitunit.nit:36,2--40,4: ERROR: test_foo1 (in file .nitunit/test_test_nitunit_TestX_test_foo1.nit): Runtime error: Assert failed (test_test_nitunit.nit:39)
Class suites: 1; Test Cases: 3; Failures: 1
<testsuites><testsuite package="test_nitunit"><testcase classname="nitunit.test_nitunit.<module>" name="<module>"><system-err></system-err><system-out>assert true
</system-out></testcase><testcase classname="nitunit.test_nitunit.test_nitunit::X" name="<class>"><system-err></system-err><system-out>assert false
-</system-out><error message="Runtime error: Assert failed (.nitunit/test_nitunit2.nit:5)
+</system-out><error message="Runtime error: Assert failed (.nitunit/test_nitunit-2.nit:5)
"></error></testcase><testcase classname="nitunit.test_nitunit.test_nitunit::X" name="test_nitunit::X::foo"><system-err></system-err><system-out>assert undefined_identifier
-</system-out><failure message=".nitunit/test_nitunit3.nit:5,8--27: Error: Method or variable 'undefined_identifier' unknown in Sys.
+</system-out><failure message=".nitunit/test_nitunit-3.nit:5,8--27: Error: Method or variable 'undefined_identifier' unknown in Sys.
"></failure></testcase></testsuite><testsuite package="test_test_nitunit"><testcase classname="nitunit.test_test_nitunit.test_test_nitunit::TestX" name="test_test_nitunit::TestX::test_foo"><system-err></system-err><system-out>out</system-out></testcase><testcase classname="nitunit.test_test_nitunit.test_test_nitunit::TestX" name="test_test_nitunit::TestX::test_foo1"><system-err></system-err><system-out>out</system-out><error message="Runtime error: Assert failed (test_test_nitunit.nit:39)
"></error></testcase><testcase classname="nitunit.test_test_nitunit.test_test_nitunit::TestX" name="test_test_nitunit::TestX::test_foo2"><system-err></system-err><system-out>out</system-out></testcase></testsuite></testsuites>
\ No newline at end of file
--- /dev/null
+DocUnits:
+DocUnits Success
+Entities: 4; Documented ones: 3; With nitunits: 3; Failures: 0
+
+TestSuites:
+No test cases found
+Class suites: 0; Test Cases: 0; Failures: 0
+<testsuites><testsuite package="test_nitunit2"><testcase classname="nitunit.test_nitunit2.standard::kernel::Object" name="test_nitunit2::Object::foo1"><system-err></system-err><system-out>if true then
+
+ assert true
+
+end
+</system-out></testcase><testcase classname="nitunit.test_nitunit2.standard::kernel::Object" name="test_nitunit2::Object::bar2"><system-err></system-err><system-out>if true then
+
+ assert true
+
+end
+</system-out></testcase><testcase classname="nitunit.test_nitunit2.standard::kernel::Object" name="test_nitunit2::Object::foo3"><system-err></system-err><system-out>var a = 1
+assert a == 1
+assert a == 1
+</system-out></testcase></testsuite><testsuite></testsuite></testsuites>
\ No newline at end of file
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# a fence unit
+#
+# ~~~~
+# if true then
+#
+# assert true
+#
+# end
+# ~~~~
+fun foo1 do end
+
+# a block unit
+#
+# if true then
+#
+# assert true
+#
+# end
+fun bar2 do end
+
+# a context continuation
+#
+# var a = 1
+# assert a == 1
+#
+# bla bla
+#
+# assert a == 1
+fun foo3 do end