+ mark_done(du)
+ end
+
+ # Create and fill the header of a unit file `file`.
+ #
+ # A unit file is a Nit source file generated from one
+ # or more docunits that will be compiled and executed.
+ #
+ # The handled on the file is returned and must be completed and closed.
+ #
+ # `file` should be a valid filepath for a Nit source file.
+ private fun create_unitfile(file: String): Writer
+ do
+ var dir = file.dirname
+ if dir != "" then dir.mkdir
+ var f
+ f = new FileWriter.open(file)
+ f.write("# GENERATED FILE\n")
+ f.write("# Docunits extracted from comments\n")
+ if mmodule != null then
+ f.write("import {mmodule.name}\n")
+ end
+ f.write("\n")
+ return f
+ end
+
+ # Compile an unit file and return the compiler return code
+ #
+ # Can terminate the program if the compiler is not found
+ private fun compile_unitfile(file: String): Int
+ do
+ var nitc = toolcontext.find_nitc
+ var opts = new Array[String]
+ if mmodule != null then
+ opts.add "-I {mmodule.filepath.dirname}"
+ end
+ var cmd = "{nitc} --ignore-visibility --no-color '{file}' {opts.join(" ")} >'{file}.out1' 2>&1 </dev/null -o '{file}.bin'"
+ var res = toolcontext.safe_exec(cmd)
+ return res
+ end
+end
+
+private class NitunitDecorator
+ super HTMLDecorator
+
+ var executor: NitUnitExecutor
+
+ redef fun add_code(v, block) do
+ var code = block.raw_content
+ var meta = block.meta or else "nit"
+ # Do not try to test non-nit code.
+ if meta != "nit" then return
+ # Try to parse code blocks
+ var ast = executor.toolcontext.parse_something(code)
+
+ var mdoc = executor.mdoc
+ assert mdoc != null
+
+ # Skip pure comments
+ if ast isa TComment then return
+
+ # The location is computed according to the starts of the mdoc and the block
+ # Note, the following assumes that all the comments of the mdoc are correctly aligned.
+ var loc = block.block.location
+ var line_offset = loc.line_start + mdoc.location.line_start - 2
+ var column_offset = loc.column_start + mdoc.location.column_start
+ # Hack to handle precise location in blocks
+ # TODO remove when markdown is more reliable
+ if block isa BlockFence then
+ # Skip the starting fence
+ line_offset += 1
+ else
+ # Account a standard 4 space indentation
+ column_offset += 4
+ end
+
+ # We want executable code
+ if not (ast isa AModule or ast isa ABlockExpr or ast isa AExpr) then
+ var message
+ var l = ast.location
+ # Get real location of the node (or error)
+ var location = new Location(mdoc.location.file,
+ l.line_start + line_offset,
+ l.line_end + line_offset,
+ l.column_start + column_offset,
+ l.column_end + column_offset)
+ if ast isa AError then
+ message = ast.message
+ else
+ message = "Error: Invalid Nit code."
+ end
+
+ var du = new_docunit
+ du.block += code
+ du.error_location = location
+ du.error = message
+ executor.toolcontext.modelbuilder.failed_entities += 1
+ return
+ end
+
+ # Create a first block
+ # Or create a new block for modules that are more than a main part
+ var last_docunit = executor.last_docunit
+ if last_docunit == null or ast isa AModule then
+ last_docunit = new_docunit
+ executor.last_docunit = last_docunit
+ end
+
+ # Add it to the file
+ last_docunit.block += code
+
+ # In order to retrieve precise positions,
+ # the real position of each line of the raw_content is stored.
+ # See `DocUnit::real_location`
+ line_offset -= loc.line_start - 1
+ for i in [loc.line_start..loc.line_end] do
+ last_docunit.lines.add i + line_offset
+ last_docunit.columns.add column_offset
+ end
+ end