Using heuristics to know what is a docunit and what is a raw block of code is not nice because syntax-errors in docunits happen and we do not want to silence them.
This PR introduces (and documents) the usage of tagged fences to force the programmer to explicit the role of the blocks of code.
By default (with indented block or untagged fences) code blocks are considered docunits to be checked by nitunit.
The tag `nit` does the same.
Any other tag cause docunit to ignore the whole block.
The special tag `nitish` makes docunit ignore the block but allows nitdoc to highlight it.
~~~~
~~~~
Pull-Request: #941
Reviewed-by: Alexandre Terrasa <alexandre@moz-code.org>
# #
# C L A S S H E A D E R #
# #
+
redef class Nclass_header
redef fun accept_visitor(v)
do
#
# The generated tree will be something like this:
#
- # <ul>
- # <li>section 1</li>
- # <li>section 2
- # <ul>
- # <li>section 2.1</li>
- # <li>section 2.2</li>
- # </ul>
- # </li>
- # </ul>
+ # ~~~html
+ # <ul>
+ # <li>section 1</li>
+ # <li>section 2
+ # <ul>
+ # <li>section 2.1</li>
+ # <li>section 2.2</li>
+ # </ul>
+ # </li>
+ # </ul>
+ # ~~~
fun tpl_tree(limit: Int): Template do
return tpl_tree_intern(limit, 1)
end
# 2. Apply the method `run`, that will search and return a solution.
# 3. Retrieve information from the solution.
#
-# ~~~~
+# ~~~~nitish
# var p: BacktrackProblem = new MyProblem
# var solver = p.solve
# var res = solver.run
# The constraint is that two queens cannot be on the same row, column or diagonal.
#
# Eg. a solution to the 8-queens problem is
-# ~~~
+# ~~~raw
# +--------+
# |Q.......|
# |....Q...|
# 2. Apply the method `run`, that will search and return a solution.
# 3. Retrieve information from the solution.
#
-# ~~~~
+# ~~~~nitish
# var p: SearchProblem = new MyProblem
# var res = p.astar.run
# if res != null then print "Found plan with {res.depth} actions, that cost {res.cost}: {res.plan.join(", ")}"
# The general action to be performed
#
- # Example :
- # ~~~
+ # ~~~nitish
+ # # TODO better example
# intent.action = intent_action.view.to_s
# ~~~
fun action=(action: String)
# Add category to the intent
# Only activities providing all of the requested categories will be used
#
- # Example :
- # ~~~
+ # ~~~nitish
+ # # TODO better example
# intent.add_category(intent_category.home.to_s)
# ~~~
# Returns `self` allowing fluent programming
# Add a flag to be used by the intent
#
- # Example :
- # ~~~
+ # ~~~nitish
+ # # TODO better example
# intent.add_flags(intent_flag.activity_new_task)
# ~~~
# Returns `self` allowing fluent programming
# Services to show notification in the Android status bar
#
-# ~~~~
+# ~~~~nitish
# # Create and show a notification
# var notif = new Notification("My Title", "Some content")
# notif.ticker = "Ticker text"
# # Update the notification
# notif.text = "New content!"
# notif.ongoing = true # Make it un-dismissable
-# nofif.show
+# notif.show
#
# # Hide the notification
# notif.cancel
# The sensor support is implemented in android_app module, so the user can enable the type of sensor he wants to use.
# There is an example of how you can use the android sensors in nit/examples/mnit_ballz :
#
-# ~~~~
+# ~~~~nitish
+# #FIXME rewrite the example
# var app = new MyApp
# app.sensors_support_enabled = true
# app.accelerometer.enabled = true
#
# User has to manage local stack deallocation himself
#
- # Example :
- # ~~~
+ # ~~~nitish
# var foo = new HashMap[JavaString, JavaObject]
# # ...
# for key, value in foo do
# key.delete_local_ref
# value.delete_local_ref
# end
- # ~~~
+ # ~~~
# *You should use Nit getters instead and get each value one by one*
fun all: nullable HashMap[JavaString, JavaObject]
do
#
# User has to manage local stack deallocation himself
#
- # Example :
- # ~~~
- # var a_hash_set = shared_preferences.string_set("A key")
- # ...
+ # ~~~nitish
+ # var a_hash_set = app.shared_preferences.string_set("A key")
+ # # ...
# for element in a_hash_set do element.delete_local_ref
# ~~~
fun string_set(key: String): HashSet[JavaString]
#
# User has to manage local stack deallocation himself
#
- # Example :
- # ~~~
+ # ~~~nitish
# var foo = new HashSet[JavaString]
- # shared_preferences.add_string_set("A key", foo)
+ # app.shared_preferences.add_string_set("A key", foo)
# for element in foo do element.delete_local_ref
# ~~~
fun add_string_set(key: String, value: HashSet[JavaString]): SharedPreferences
# methods of the main thread to customize the response to a given event.
#
# This graph shows the path of a button click:
-# ~~~
+# ~~~raw
# UI Thread # Main thread
#
# User
self.top >= other.bottom and other.top >= self.bottom
end
- # Create a bounding box that englobes the actual bounding box.
+ # Create a bounding box that encloses the actual bounding box.
# `dist` is the distance between the inner boundaries and the outer boundaries.
# ~~~
# var p = new Point[Int](5,10)
# var b = p.padded(3)
- # assert b.top == 2 and b.bot = 8 and b.left == 7 and b.right == 13
+ # assert b.left == 2 and b.right == 8 and b.top == 13 and b.bottom == 7
# ~~~
fun padded(dist: N): Box[N] do return new Box[N].lrtb(left - dist, right + dist, top + dist, bottom - dist)
end
#
# require: `self.is_numeric`
#
- # assert "1.234".to_json_value.to_numeric = 1.234
- # assert "1234".to_json_value.to_numeric = 1234
+ # assert "1.234".to_json_value.to_numeric == 1.234
+ # assert "1234".to_json_value.to_numeric == 1234
fun to_numeric: Numeric
do
if is_int then return to_i
# Utility to select options to create the VM using `create_jvm`
#
# Usage example:
-# ~~~~
+#
+# ~~~~nitish
# var builder = new JavaVMBuilder
# builder.options.add "-Djava.class.path=."
# var jvm = builder.create_jvm
# A Link Reference.
# Links that are specified somewhere in the mardown document to be reused as shortcuts.
#
-# Example:
-#
-# [1]: http://example.com/ "Optional title"
+# ~~~raw
+# [1]: http://example.com/ "Optional title"
+# ~~~
class LinkRef
# Link href
#
# The input event file is made of event descriptions, one event by line.
#
-# ~~~
+# ~~~raw
# 10 click 10.0 20.0
# 20 quit
# ~~~
# Draw image by specifying the positon of each image corners
# Corners are in counter-clockwise order stating top left
# a is top left, b is bottom left, c is bottom right and d is top right
- # ~~~
+ # ~~~raw
# a-d
# | |
# b-c
# 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.
-#
-# Targets the PNaCl platform
+
+# Provides PNaCl support for Nit.
#
# To use this module and compile for PNaCl, you must install the
# NaCl SDK (This file is based on Pepper 33).
# If NACL_SDK_ROOT is not set in your PATH, you have to work in
# 'nacl_sdk/pepper_your_pepper_version/getting_started/your_project_folder'.
-#
-# Provides PNaCl support for Nit.
module pnacl is platform
import standard
#
# For instance, one common usage is to add a specific attribute for each poset a class belong.
#
-# class Thing
-# var in_some_relation: POSetElement[Thing]
-# var in_other_relation: POSetElement[Thing]
-# end
-# var t: Thing # ...
-# t.in_some_relation.greaters
-#
+# ~~~nitish
+# class Thing
+# var in_some_relation: POSetElement[Thing]
+# var in_other_relation: POSetElement[Thing]
+# end
+# var t: Thing
+# # ...
+# t.in_some_relation.greaters
+# ~~~
class POSetElement[E: Object]
# The poset self belong to
var poset: POSet[E]
# can use it to make a persistent snapshot of a locator at any
# point during a document parse:
#
-# module example
-# #
-# import sax::helpers::SAXLocatorImpl
-# import sax::ContentHandler
-# #
+# import sax::helpers::sax_locator_impl
+# import sax::content_handler
+#
# class Example super ContentHandler
-# private var _locator: nullable SAXLocator = null
+# private var locator: SAXLocator
# private var start_loc: nullable SAXLocator = null
-# #
-# fun locator=(Locator locator) do
-# # note the locator
-# _locator = locator
-# end
-# #
-# fun start_document do
+#
+# redef fun start_document do
# # save the location of the start of the document
# # for future use.
-# start_loc = new SAXLocatorImpl.from(locator)
+# start_loc = new SAXLocatorImpl.with(locator)
# end
# end
#
# document from a system identifier. It is the exact
# equivalent of the following:
#
- # var source = new InputSouce
- # source.system_id = system_id
- # parse(source)
+ # ~~~nitish
+ # var source = new InputSouce
+ # source.system_id = system_id
+ # parse(source)
+ # ~~~
#
# If the system identifier is a URL, it must be fully resolved
# by the application before it is passed to the parser.
# This method must be implemented for each specific view.
# A traditional way of implementation is to use a double-dispatch mechanism
#
- # Exemple:
# class MyView
+ # super View
# redef fun draw_sprite(s) do s.draw_on_myview(self)
# end
# redef class Sprite
# r.handle_signal(sigint, true)
# r.handle_signal(sigalarm, true)
#
-# Ask system to receive a `sigalarm` signal in 1 second
+# # Ask system to receive a `sigalarm` signal in 1 second
# set_alarm(1)
#
# loop
# Subclasses often provide a more efficient implementation.
#
# Because of the `iterator` method, Collections instances can use
-# the `for` control structure:
+# the `for` control structure.
#
-# var x: Collection[U]
-# # ...
-# for u in x do
-# # u is a U
-# # ...
-# end
+# ~~~nitish
+# var x: Collection[U]
+# # ...
+# for u in x do
+# # u is a U
+# # ...
+# end
+# ~~~
#
-# that is equivalent with
+# that is equivalent with the following:
#
-# var x: Collection[U]
-# # ...
-# var i = x.iterator
-# while i.is_ok do
-# var u = i.item # u is a U
-# # ...
-# i.next
-# end
+# ~~~nitish
+# var x: Collection[U]
+# # ...
+# var i = x.iterator
+# while i.is_ok do
+# var u = i.item # u is a U
+# # ...
+# i.next
+# end
+# ~~~
interface Collection[E]
# Get a new iterator on the collection.
fun iterator: Iterator[E] is abstract
#
# SEE: `Char::is_letter` for the definition of a letter.
#
- # var b = new FlatBuffer.from("jAVAsCriPt")"
+ # var b = new FlatBuffer.from("jAVAsCriPt")
# b.capitalize
# assert b == "Javascript"
# b = new FlatBuffer.from("i am root")
#
# As per the specification :
#
+ # ~~~raw
# Length | UTF-8 octet sequence
# | (binary)
# ---------+-------------------------------------------------
# 2 | 110xxxxx 10xxxxxx
# 3 | 1110xxxx 10xxxxxx 10xxxxxx
# 4 | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+ # ~~~
private fun len: Int `{
char* ns = recv->ns;
int pos = recv->pos;
#
# As per the specification :
#
+ # ~~~raw
# Length | UTF-8 octet sequence
# | (binary)
# ---------+-------------------------------------------------
# 2 | 110xxxxx 10xxxxxx
# 3 | 1110xxxx 10xxxxxx 10xxxxxx
# 4 | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+ # ~~~
fun len: Int `{
uint32_t s = *recv;
if(s <= 127) {return 1;}
#
# In external file `example.tpl`:
#
-# <!DOCTYPE html>
-# <html lang="en">
-# <head>
-# <title>%TITLE%</title>
-# </head>
-# <body>
-# <h1>%TITLE%</h1>
-# <p>%ARTICLE%</p>
-# </body>
-# </html>
+# ~~~html
+# <!DOCTYPE html>
+# <html lang="en">
+# <head>
+# <title>%TITLE%</title>
+# </head>
+# <body>
+# <h1>%TITLE%</h1>
+# <p>%ARTICLE%</p>
+# </body>
+# </html>
+# ~~~
#
# Loading the template file using `TemplateString`:
#
# Perform left rotation on `node`
#
+ # ~~~raw
# N Y
# / \ > / \
# a Y N c
# / \ < / \
# b c a b
+ # ~~~
#
protected fun rotate_left(node: N) do
var y = node.right
# Perform right rotation on `node`
#
+ # ~~~raw
# N Y
# / \ > / \
# a Y N c
# / \ < / \
# b c a b
+ # ~~~
#
protected fun rotate_right(node: N) do
var y = node.left
# Red-Black Tree Map
# Properties of a Red-Black tree map:
-# * every node is either red or black
-# * root is black
-# * every leaf (null) is black
-# * if a node is red, then both its children are black
-# * for each node, all simple path from the node to descendant
-# leaves contain the same number of black nodes
+# * every node is either red or black
+# * root is black
+# * every leaf (null) is black
+# * if a node is red, then both its children are black
+# * for each node, all simple path from the node to descendant
+# leaves contain the same number of black nodes
#
# Operations:
-# * search average O(lg n) worst O(lg n)
-# * insert average O(lg n) worst O(lg n)
-# * delete average O(lg n) worst O(lg n)
+# * search average O(lg n) worst O(lg n)
+# * insert average O(lg n) worst O(lg n)
+# * delete average O(lg n) worst O(lg n)
class RBTreeMap[K: Comparable, E]
super BinTreeMap[K, E]
## Working with `DocUnits`
-With DocUnits, executable code can be placed in comments of modules, classes and properties.
-The execution can be verified using `assert`
+DocUnits are blocks of executable code placed in comments of modules, classes and properties.
+The execution can be verified using `assert`.
Example with a class:
fun baz(a, b: Int) do return a + b
end
+In a single piece of documentation, each docunit is considered a part of a single module, thus regrouped when
+tested.
+Therefore, it is possible (and recommended) to split docunits in small parts if it make the explanation easier.
+
+~~~~
+# Some example of grouped docunits
+#
+# Declare and initialize a variable `a`.
+#
+# var a = 1
+#
+# So the value of `a` can be used
+#
+# assert a == 1
+#
+# even in complex operations
+#
+# assert a + 1 == 2
+fun foo do end
+~~~~
+
+Sometime, some blocks of code has to be included in documentation but not considered by `nitunit`.
+Those blocks are distinguished by their tagged fences (untagged fences or fences tagged `nit` are considered to be docunits).
+
+~~~~
+# Some ASCII drawing
+#
+# ~~~~raw
+# @<
+# <__)
+# ~~~~
+fun foo do end
+~~~~
+
+The special fence-tag `nitish` could also be used to indicate pseudo-nit that will be ignored by nitunit but highlighted by nitdoc.
+Such `nitish` piece of code can be used to enclose examples that cannot compile or that one do not want to be automatically executed.
+
+~~~~
+# Some pseudo-nit
+#
+# ~~~~nitish
+# var a: Int = someting
+# # ...
+# if a == 1 then something else something-else
+# ~~~~
+#
+# Some code to not try to execute automatically
+#
+# ~~~~nitish
+# system("rm -rf /")
+# ~~~~
+~~~~
+
The `nitunit` command is used to test Nit files:
$ nitunit foo.nit
# To create the new node `n`, we need to attach the child to it.
# But, to put `n` where `c` was in `p`, the place has to be remembered.
#
- # var p: AExpr
- # var c = p.c
- # var h = c.detach_with_placeholder
- # var n = astbuilder.make_XXX(c)
- # h.replace_with(n)
+ # ~~~nitish
+ # var p: AExpr
+ # var c = p.c
+ # var h = c.detach_with_placeholder
+ # var n = astbuilder.make_XXX(c)
+ # h.replace_with(n)
+ # ~~~
fun detach_with_placeholder: AExpr
do
var h = new APlaceholderExpr.make
# Two elements from a POSet cannot have the same color if they share common subelements
#
# Example:
+#
+# ~~~raw
# A
# / | \
# / | \
# E F G
# |
# H
+# ~~~
+#
# Conflicts:
-# A: {B, C, D, E, F, G, H}
-# B: {A, C, E, H}
-# C: {A, E, H, F}
-# D: {A, G}
-# E: {A, B, C, H}
-# F: {A, C}
-# G: {A, D}
-# H: {A, B, C, E}
+#
+# * A: {B, C, D, E, F, G, H}
+# * B: {A, C, E, H}
+# * C: {A, E, H, F}
+# * D: {A, G}
+# * E: {A, B, C, H}
+# * F: {A, C}
+# * G: {A, D}
+# * H: {A, B, C, E}
+#
# Possible colors:
-# A:0, B:1, C: 2, D: 1, E: 3, F:3, G:2, H:4
#
-# see:
-# Ducournau, R. (2011).
-# Coloring, a versatile technique for implementing object-oriented languages.
-# Software: Practice and Experience, 41(6), 627–659.
+# * A:0, B:1, C: 2, D: 1, E: 3, F:3, G:2, H:4
+#
+# see: Ducournau, R. (2011).
+# Coloring, a versatile technique for implementing object-oriented languages.
+# Software: Practice and Experience, 41(6), 627–659.
class POSetColorer[E: Object]
# Is the poset already colored?
# Count empty lines between code blocks
var empty_lines = 0
+ # Optional tag for a fence
+ var fence_tag = ""
+
fun work(mdoc: MDoc): HTMLTag
do
var root = new HTMLTag("div")
empty_lines = 0
# to allows 4 spaces including the one that follows the #
curblock.add(text)
+ fence_tag = ""
continue
end
var l = 3
while l < text.length and text.chars[l] == '~' do l += 1
in_fence = text.substring(0, l)
+ while l < text.length and (text.chars[l] == '.' or text.chars[l] == ' ') do l += 1
+ fence_tag = text.substring_from(l)
continue
end
# Code part
var n2 = new HTMLTag("code")
n.add(n2)
- process_code(n2, part)
+ process_code(n2, part, null)
end
is_text = not is_text
end
# add the node
var n = new HTMLTag("pre")
root.add(n)
- process_code(n, btext.to_s)
+ process_code(n, btext.to_s, fence_tag)
curblock.clear
end
end
- fun process_code(n: HTMLTag, text: String)
+ fun process_code(n: HTMLTag, text: String, tag: nullable String)
do
+ # Do not try to highlight non-nit code.
+ if tag != null and tag != "" and tag != "nit" and tag != "nitish" then
+ n.append text
+ n.add_class("rawcode")
+ return
+ end
+
# Try to parse it
var ast = toolcontext.parse_something(text)
# types to their bounds.
#
# Example
+ #
# class A end
# class B super A end
# class X end
# super G[B]
# redef type U: Y
# end
+ #
# Map[T,U] anchor_to H #-> Map[B,Y]
#
# Explanation of the example:
# In Nit, for each super-class of a type, there is a equivalent super-type.
#
# Example:
+ #
+ # ~~~nitish
# class G[T, U] end
# class H[V] super G[V, Bool] end
+ #
# H[Int] supertype_to G #-> G[Int, Bool]
+ # ~~~
#
# REQUIRE: `super_mclass` is a super-class of `self`
# REQUIRE: `self.need_anchor implies anchor != null and self.can_resolve_for(anchor, null, mmodule)`
#
# ## Example 1
#
- # class G[E] end
- # class H[F] super G[F] end
- # class X[Z] end
+ # ~~~
+ # class G[E] end
+ # class H[F] super G[F] end
+ # class X[Z] end
+ # ~~~
#
# * Array[E].resolve_for(H[Int]) #-> Array[Int]
# * Array[E].resolve_for(G[Z], X[Int]) #-> Array[Z]
#
# ## Example 2
#
- # class A[E]
- # fun foo(e:E):E is abstract
- # end
- # class B super A[Int] end
+ # ~~~
+ # class A[E]
+ # fun foo(e:E):E is abstract
+ # end
+ # class B super A[Int] end
+ # ~~~
#
# The signature on foo is (e: E): E
# If we resolve the signature for B, we get (e:Int):Int
#
# ## Example 3
#
- # class A[E]
- # fun foo(e:E) is abstract
- # end
- # class B[F]
- # var a: A[Array[F]]
- # fun bar do a.foo(x) # <- x is here
- # end
+ # ~~~nitish
+ # class A[E]
+ # fun foo(e:E):E is abstract
+ # end
+ # class C[F]
+ # var a: A[Array[F]]
+ # fun bar do a.foo(x) # <- x is here
+ # end
+ # ~~~
#
# The first question is: is foo available on `a`?
#
# The static type of a is `A[Array[F]]`, that is an open type.
# in order to find a method `foo`, whe must look at a resolved type.
#
- # A[Array[F]].anchor_to(B[nullable Object]) #-> A[Array[nullable Object]]
+ # A[Array[F]].anchor_to(C[nullable Object]) #-> A[Array[nullable Object]]
#
# the method `foo` exists in `A[Array[nullable Object]]`, therefore `foo` exists for `a`.
#
#
# the signature of `foo` is `foo(e:E)`, thus we must resolve the type E
#
- # E.resolve_for(A[Array[F]],B[nullable Object]) #-> Array[F]
+ # E.resolve_for(A[Array[F]],C[nullable Object]) #-> Array[F]
#
# The resolution can be done because `E` make sense for the class A (see `can_resolve_for`)
#
# class B[F]
# end
#
- # * E.can_resolve_for(A[Int]) #-> true, E make sense in A
- # * E.can_resolve_for(B[Int]) #-> false, E does not make sense in B
- # * B[E].can_resolve_for(A[F], B[Object]) #-> true,
- # B[E] is a red hearing only the E is important,
- # E make sense in A
+ # ~~~nitish
+ # E.can_resolve_for(A[Int]) #-> true, E make sense in A
+ #
+ # E.can_resolve_for(B[Int]) #-> false, E does not make sense in B
+ #
+ # B[E].can_resolve_for(A[F], B[Object]) #-> true,
+ # # B[E] is a red hearing only the E is important,
+ # # E make sense in A
+ # ~~~
#
# REQUIRE: `anchor != null implies not anchor.need_anchor`
# REQUIRE: `mtype.need_anchor implies anchor != null and mtype.can_resolve_for(anchor, null, mmodule)`
# directly to the parameter types of the super-classes.
#
# Example:
+#
# class A[E]
# fun e: E is abstract
# end
# class B[F]
# super A[Array[F]]
# end
+#
# In the class definition B[F], `F` is a valid type but `E` is not.
# However, `self.e` is a valid method call, and the signature of `e` is
# declared `e: E`.
#
# Comparison is made with the formula:
#
-# a.concern_rank + a.booster_rank <=> b.concern_rank + b.booster_ran
+# ~~~nitish
+# a.concern_rank + a.booster_rank <=> b.concern_rank + b.booster_ran
+# ~~~
#
# If both `a` and `b` have the same ranking,
# ordering is based on lexicographic comparison of `a.name` and `b.name`
#
# For example, if the source code contains:
#
+# ~~~nitish
# fun foo(a: A, b: B, c: C)
+# ~~~
#
# The `MSignature` node will contain a property
# `parameter_names = ["a", "b", "c"]` so the MSignature can be reconstructed
# `Licence comments` are attached to the top of the file
# no blank line before, one after.
#
-# # This is a licence comment
+# ~~~nitish
+# # This is a licence comment
#
-# # Documentation for module `foo`
-# module foo
+# # Documentation for module `foo`
+# module foo
+# ~~~
#
# `ADoc` are documentation comments attached to a `AModule`, `AClassdef`, `APropdef`.
#
# They are printed before the definition with a blank line before and no after
# at the same indentation level than the definition.
#
-# # Documentation for module `foo`
-# module foo
+# ~~~nitish
+# # Documentation for module `foo`
+# module foo
#
-# # Documentation for class `Bar`
-# class Bar
-# # Documentation for method `baz`
-# fun baz do end
-# end
+# # Documentation for class `Bar`
+# class Bar
+# # Documentation for method `baz`
+# fun baz do end
+# end
+# ~~~
#
# `Block comments` are comments composed of one or more line rattached to nothing.
# They are displayed with one blank line before and after at current indent level.
#
-# <blank>
-# # block
-# # comment
-# <blank>
+# ~~~nitish
+# <blank>
+# # block
+# # comment
+# <blank>
+# ~~~
#
# `Attached comments` are comments attached to a production.
# They are printed as this.
#
-# fun foo do # attached comment
-# end
+# ~~~nitish
+# fun foo do # attached comment
+# end
+# ~~~
#
# `nitpretty` automatically remove multiple blanks between comments:
#
-# # Licence
-# # ...
-# <blank>
-# # Block comment
+# ~~~nitish
+# # Licence
+# # ...
+# <blank>
+# # Block comment
+# ~~~
#
# ### Inlining
#
# * There is a blank between each class definition
# * There is no blank line at the end of the module
#
-# # Documentation for module `foo`
-# module foo
+# ~~~nitish
+# # Documentation for module `foo`
+# module foo
#
-# import a
-# # import b
-# import c
+# import a
+# # import b
+# import c
#
-# # Documentation for class `Bar`
-# class Bar end
+# # Documentation for class `Bar`
+# class Bar end
#
-# class Baz end # not a `ADoc` comment
+# class Baz end # not a `ADoc` comment
+# ~~~
#
#
# ### Classes
# * There is a blank between each block definition
# * There no blank line at the end of the class definition
#
-# # Documentation for class `Bar`
-# class Bar end
+# ~~~nitish
+# # Documentation for class `Bar`
+# class Bar end
#
-# class Baz
-# super Bar
+# class Baz
+# super Bar
#
-# fun a is abstract
-# private fun b do end
+# fun a is abstract
+# private fun b do end
#
-# fun c do
-# # ...
-# end
-# end
+# fun c do
+# # ...
+# end
+# end
+# ~~~
#
-# Generic types have no espace after or before brackets and are separated by a comma and a space:
+# Generic types have no space after or before brackets and are separated by a comma and a space:
#
-# class A[E: Type1, F: Type1] do end
+# ~~~nitish
+# class A[E: Type1, F: Type1] end
+# ~~~
#
# ### Blocks
#
# * Inlined productions have no blank lines between them
# * Block productions have a blank before and after
#
-# var a = 10
-# var b = 0
+# ~~~nitish
+# var a = 10
+# var b = 0
#
-# if a > b then
-# # is positive
-# print "positive"
-# end
+# if a > b then
+# # is positive
+# print "positive"
+# end
#
-# print "end"
+# print "end"
+# ~~~
#
# ### Calls and Binary Ops
#
# Arguments are always printed separated with a comma and a space:
#
-# foo(a, b, c)
+# ~~~nitish
+# foo(a, b, c)
+# ~~~
#
# Binary ops are always printed wrapped with spaces:
#
-# var c = 1 + 2
+# ~~~nitish
+# var c = 1 + 2
+# ~~~
#
# Calls and binary ops can be splitted to fit the `max-size` constraint.
# Breaking priority is given to arguments declaration after the comma.
#
-# return foo("aaaaaaaaaaaaaaaaaaaaaaaaaa", "bbbbbbbbbbbbbbbbbbbbbbbbbbb",
-# "cccccccccccccccccccccccccc")
+# ~~~nitish
+# return foo("aaaaaaaaaaaaaaaaaaaaaaaaaa", "bbbbbbbbbbbbbbbbbbbbbbbbbbb",
+# "cccccccccccccccccccccccccc")
+# ~~~
#
# Binary ops can also be broken to fit the `max-size` limit:
#
-# return "aaaaaaaaaaaaaaaaaaaaaaaaaa" + "bbbbbbbbbbbbbbbbbbbbbbbbbbb" +
-# "cccccccccccccccccccccccccc"
-#
+# ~~~nitish
+# return "aaaaaaaaaaaaaaaaaaaaaaaaaa" + "bbbbbbbbbbbbbbbbbbbbbbbbbbb" +
+# "cccccccccccccccccccccccccc"
+# ~~~
module nitpretty
import template
# All blocks of code from a same `ADoc`
var blocks = new Array[Array[String]]
- redef fun process_code(n: HTMLTag, text: String)
+ # All failures from a same `ADoc`
+ var failures = new Array[String]
+
+ redef fun process_code(n: HTMLTag, text: String, tag: nullable String)
do
# Skip non-blocks
if n.tag != "pre" then return
+ # Skip strict non-nit
+ if tag != null and tag != "nit" and tag != "" then
+ return
+ end
+
# Try to parse it
var ast = toolcontext.parse_something(text)
+ # Skip pure comments
+ if ast isa TComment then return
+
# We want executable code
if not (ast isa AModule or ast isa ABlockExpr or ast isa AExpr) then
- if ndoc != null and n.tag == "pre" and toolcontext.opt_warn.value > 1 then
- toolcontext.warning(ndoc.location, "invalid-block", "Warning: There is a block of code that is not valid Nit, thus not considered a nitunit")
- if ast isa AError then toolcontext.warning(ast.location, "syntax-error", ast.message)
- ndoc = null # To avoid multiple warning in the same node
- end
- return
- end
-
- # Search `assert` in the AST
- var v = new SearchAssertVisitor
- v.enter_visit(ast)
- if not v.foundit then
- if ndoc != null and n.tag == "pre" and toolcontext.opt_warn.value > 1 then
- toolcontext.warning(ndoc.location, "invalid-block", "Warning: There is a block of Nit code without `assert`, thus not considered a nitunit")
- ndoc = null # To avoid multiple warning in the same node
- end
+ var message = ""
+ if ast isa AError then message = " At {ast.location}: {ast.message}."
+ toolcontext.warning(ndoc.location, "invalid-block", "Error: There is a block of code that is not valid Nit, thus not considered a nitunit. To suppress this warning, enclose the block with a fence tagged `nitish` or `raw` (see `man nitdoc`).{message}")
+ failures.add("{ndoc.location}: Invalid block of code.{message}")
return
end
fun extract(ndoc: ADoc, tc: HTMLTag)
do
blocks.clear
+ failures.clear
self.ndoc = ndoc
work(ndoc.to_mdoc)
+
toolcontext.check_errors
+ if not failures.is_empty then
+ for msg in failures do
+ var ne = new HTMLTag("failure")
+ ne.attr("message", msg)
+ tc.add ne
+ toolcontext.modelbuilder.failed_entities += 1
+ end
+ if blocks.is_empty then testsuite.add(tc)
+ end
+
if blocks.is_empty then return
for block in blocks do test_block(ndoc, tc, block)
test_file = "{include_dir}/{test_file}"
end
if not test_file.file_exists then
- toolcontext.info("Skip test for {mmodule}, no file {test_file} found", 1)
+ toolcontext.info("Skip test for {mmodule}, no file {test_file} found", 2)
return ts
end
var tester = new NitUnitTester(self)
var res = tester.test_module_unit(test_file)
if res == null then
- toolcontext.info("Skip test for {mmodule}, no test suite found", 1)
+ toolcontext.info("Skip test for {mmodule}, no test suite found", 2)
return ts
end
return res.to_xml
redef class AArrayExpr
# `[x,y]` is replaced with
#
- # var t = new Array[X].with_capacity(2)
- # t.add(x)
- # t.add(y)
- # t
+ # ~~~nitish
+ # var t = new Array[X].with_capacity(2)
+ # t.add(x)
+ # t.add(y)
+ # t
+ # ~~~
redef fun accept_transform_visitor(v)
do
var nblock = v.builder.make_block
redef class ASendReassignFormExpr
# `x.foo(y)+=z` is replaced with
#
- # x.foo(y) = x.foo(y) + z
+ # ~~~nitish
+ # x.foo(y) = x.foo(y) + z
+ # ~~~
#
# witch is, in reality:
#
- # x."foo="(y, x.foo(y)."+"(z))
+ # ~~~nitish
+ # x."foo="(y, x.foo(y)."+"(z))
+ # ~~~
redef fun accept_transform_visitor(v)
do
var nblock = v.builder.make_block
end
# Return the attribute value in `instance` with a sequence of perfect_hashing
- # `instance` is the attributes array of the receiver
- # `vtable` is the pointer to the virtual table of the class (of the receiver)
- # `mask` is the perfect hashing mask of the class
- # `id` is the identifier of the class
- # `offset` is the relative offset of this attribute
+ # * `instance` is the attributes array of the receiver
+ # * `vtable` is the pointer to the virtual table of the class (of the receiver)
+ # * `mask` is the perfect hashing mask of the class
+ # * `id` is the identifier of the class
+ # * `offset` is the relative offset of this attribute
private fun read_attribute_ph(instance: Pointer, vtable: Pointer, mask: Int, id: Int, offset: Int): Instance `{
// Perfect hashing position
int hv = mask & id;
end
# Replace the value of an attribute in an instance
- # `instance` is the attributes array of the receiver
- # `vtable` is the pointer to the virtual table of the class (of the receiver)
- # `mask` is the perfect hashing mask of the class
- # `id` is the identifier of the class
- # `offset` is the relative offset of this attribute
- # `value` is the new value for this attribute
+ # * `instance` is the attributes array of the receiver
+ # * `vtable` is the pointer to the virtual table of the class (of the receiver)
+ # * `mask` is the perfect hashing mask of the class
+ # * `id` is the identifier of the class
+ # * `offset` is the relative offset of this attribute
+ # * `value` is the new value for this attribute
private fun write_attribute_ph(instance: Pointer, vtable: Pointer, mask: Int, id: Int, offset: Int, value: Instance) `{
// Perfect hashing position
int hv = mask & id;
end
# Allocate a single vtable
- # `ids : Array of superclasses identifiers
- # `nb_methods : Array which contain the number of introduced methods for each class in ids
- # `nb_attributes : Array which contain the number of introduced attributes for each class in ids
- # `offset_attributes : Offset from the beginning of the table of the group of attributes
- # `offset_methods : Offset from the beginning of the table of the group of methods
+ # * `ids : Array of superclasses identifiers
+ # * `nb_methods : Array which contain the number of introduced methods for each class in ids
+ # * `nb_attributes : Array which contain the number of introduced attributes for each class in ids
+ # * `offset_attributes : Offset from the beginning of the table of the group of attributes
+ # * `offset_methods : Offset from the beginning of the table of the group of methods
private fun allocate_vtable(v: VirtualMachine, ids: Array[Int], nb_methods: Array[Int], nb_attributes: Array[Int],
offset_attributes: Int, offset_methods: Int)
do
end
# Fill the vtable with methods of `self` class
- # `v` : Current instance of the VirtualMachine
- # `table` : the table of self class, will be filled with its methods
+ # * `v` : Current instance of the VirtualMachine
+ # * `table` : the table of self class, will be filled with its methods
private fun fill_vtable(v:VirtualMachine, table: VTable, cl: MClass)
do
var methods = new Array[MMethodDef]
# Computes delta for each class
# A delta represents the offset for this group of attributes in the object
- # `nb_attributes` : number of attributes for each class (classes are linearized from Object to current)
- # return deltas for each class
+ # *`nb_attributes` : number of attributes for each class (classes are linearized from Object to current)
+ # * return deltas for each class
private fun calculate_delta(nb_attributes: Array[Int]): Array[Int]
do
var deltas = new Array[Int]
end
# A kind of Depth-First-Search for superclasses ordering
- # `v` : the current executed instance of VirtualMachine
- # `res` : Result Array, ie current superclasses ordering
+ # *`v` : the current executed instance of VirtualMachine
+ # * `res` : Result Array, ie current superclasses ordering
private fun dfs(v: VirtualMachine, res: Array[MClass]): Array[MClass]
do
# Add this class at the beginning
end
# Update positions of self class in `parent`
- # `attributes_offset`: absolute offset of introduced attributes
- # `methods_offset`: absolute offset of introduced methods
+ # * `attributes_offset`: absolute offset of introduced attributes
+ # * `methods_offset`: absolute offset of introduced methods
private fun update_positions(attributes_offsets: Int, methods_offset:Int, parent: MClass)
do
parent.positions_attributes[self] = attributes_offsets
`}
# Put implementation of methods of a class in `vtable`
- # `vtable` : Pointer to the C-virtual table
- # `mask` : perfect-hashing mask of the class corresponding to the vtable
- # `id` : id of the target class
- # `methods` : array of MMethodDef of the target class
+ # * `vtable` : Pointer to the C-virtual table
+ # * `mask` : perfect-hashing mask of the class corresponding to the vtable
+ # * `id` : id of the target class
+ # * `methods` : array of MMethodDef of the target class
fun put_methods(vtable: Pointer, mask: Int, id: Int, methods: Array[MMethodDef])
import Array[MMethodDef].length, Array[MMethodDef].[] `{
nitg_args5
nitg_args6
nitg_args8
-test_markdown_args1
+nitunit_args
+test_docdown_args
pep8analysis
emscripten
nitserial_args
test_nitunit.nit --gen-suite --only-show
test_nitunit.nit --gen-suite --only-show --private
test_nitunit2.nit -o $WRITE
+test_doc2.nit --no-color -o $WRITE
--- /dev/null
+DocUnits:
+DocUnits Success
+Entities: 6; Documented ones: 5; With nitunits: 3; Failures: 0
+
+TestSuites:
+No test cases found
+Class suites: 0; Test Cases: 0; Failures: 0
+<testsuites><testsuite package="test_doc2"><testcase classname="nitunit.test_doc2.standard::kernel::Object" name="test_doc2::Object::foo1"><system-err></system-err><system-out>assert true # tested
+</system-out></testcase><testcase classname="nitunit.test_doc2.standard::kernel::Object" name="test_doc2::Object::foo2"><system-err></system-err><system-out>assert true # tested
+</system-out></testcase><testcase classname="nitunit.test_doc2.standard::kernel::Object" name="test_doc2::Object::foo3"><system-err></system-err><system-out>assert true # tested
+</system-out></testcase></testsuite><testsuite></testsuite></testsuites>
\ No newline at end of file
h5 {font-weight:bold;}
.nitcode a { color: inherit; cursor:pointer; }
.nitcode .popupable:hover { text-decoration: underline; cursor:help; } /* underline titles */
-pre.nitcode .foldable { display: block } /* for block productions*/
-pre.nitcode .line{ display: block } /* for lines */
-pre.nitcode .line:hover{ background-color: #FFFFE0; } /* current line */
+.nitcode .foldable { display: block } /* for block productions*/
+.nitcode .line{ display: block } /* for lines */
+.nitcode .line:hover{ background-color: #FFFFE0; } /* current line */
.nitcode :target { background-color: #FFF3C2 } /* target highlight*/
/* lexical raw tokens. independent of usage or semantic: */
.nitcode .nc_c { color: gray; font-style: italic; } /* comment */
--- /dev/null
+<html><head>
+<meta charset="utf-8">
+<link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
+<style type="text/css">
+code {margin: 0 2px;
+padding: 0px 5px;
+border: 1px solid #ddd;
+background-color: #f8f8f8;
+border-radius: 3px;}
+pre {
+background-color: #f8f8f8;
+border: 1px solid #ddd;
+font-size: 13px;
+line-height: 19px;
+overflow: auto;
+padding: 6px 6px;
+border-radius: 3px;
+}
+.rawcode[title] {
+border-color: red;
+}
+h5 {font-weight:bold;}
+.nitcode a { color: inherit; cursor:pointer; }
+.nitcode .popupable:hover { text-decoration: underline; cursor:help; } /* underline titles */
+.nitcode .foldable { display: block } /* for block productions*/
+.nitcode .line{ display: block } /* for lines */
+.nitcode .line:hover{ background-color: #FFFFE0; } /* current line */
+.nitcode :target { background-color: #FFF3C2 } /* target highlight*/
+/* lexical raw tokens. independent of usage or semantic: */
+.nitcode .nc_c { color: gray; font-style: italic; } /* comment */
+.nitcode .nc_d { color: #3D8127; font-style: italic; } /* documentation comments */
+.nitcode .nc_k { font-weight: bold; } /* keyword */
+.nitcode .nc_o {} /* operator */
+.nitcode .nc_i {} /* standard identifier */
+.nitcode .nc_t { color: #445588; font-weight: bold; } /* type/class identifier */
+.nitcode .nc_a { color: #445588; font-style: italic; } /* old style attribute identifier */
+.nitcode .nc_l { color: #009999; } /* char and number literal */
+.nitcode .nc_s { color: #8F1546; } /* string literal */
+/* syntactic token usage. added because of their position in the AST */
+.nitcode .nc_ast { color: blue; } /* assert label */
+.nitcode .nc_la { color: blue; } /* break/continue label */
+.nitcode .nc_m { color: #445588; } /* module name */
+/* syntactic groups */
+.nitcode .nc_def { font-weight: bold; color: blue; } /* name used in a definition */
+ .nitcode .nc_def.nc_a { color: blue; } /* name used in a attribute definition */
+ .nitcode .nc_def.nc_t { color: blue; } /* name used in a class or vt definition */
+.nitcode .nc_ss { color: #9E6BEB; } /* superstrings */
+.nitcode .nc_cdef {} /* A whole class definition */
+.nitcode .nc_pdef {} /* A whole property definition */
+/* semantic token usage */
+.nitcode .nc_v { font-style: italic; } /* local variable or parameter */
+.nitcode .nc_vt { font-style: italic; } /* virtual type or formal type */
+
+.nitcode .nc_error { border: 1px red solid;} /* not used */
+.popover { max-width: 800px !important; }
+
+</style>
+</head><body><h3 id='test_doc2'>module test_doc2</h1><h5 id='test_doc2#Object#foo1'>prop test_doc2#Object#foo1</h3><div class="nitdoc"><p class="synopsys">Test code</p><pre class="nitcode"><span class="nitcode"><span class="line" id="L1"><span class="nc_k">assert</span> <span class="nc_k">true</span> <span># tested
+</span></span><span class="line" id="L2"><span></span></span></span></pre></div><h5 id='test_doc2#Object#foo2'>prop test_doc2#Object#foo2</h3><div class="nitdoc"><p class="synopsys">Test code</p><pre class="nitcode"><span class="nitcode"><span class="line" id="L1"><span class="nc_k">assert</span> <span class="nc_k">true</span> <span># tested
+</span></span><span class="line" id="L2"><span></span></span></span></pre></div><h5 id='test_doc2#Object#foo3'>prop test_doc2#Object#foo3</h3><div class="nitdoc"><p class="synopsys">Test code</p><pre class="nitcode"><span class="nitcode"><span class="line" id="L1"><span class="nc_k">assert</span> <span class="nc_k">true</span> <span># tested
+</span></span><span class="line" id="L2"><span></span></span></span></pre></div><h5 id='test_doc2#Object#foo4'>prop test_doc2#Object#foo4</h3><div class="nitdoc"><p class="synopsys">Test code</p><pre class="rawcode">assert false # not tested (and not highlighted)
+</pre></div><h5 id='test_doc2#Object#foo5'>prop test_doc2#Object#foo5</h3><div class="nitdoc"><p class="synopsys">Test code</p><pre class="nitcode"><span class="nitcode"><span class="line" id="L1"><span class="nc_k">assert</span> <span class="nc_k">false</span> <span># not tested (but highlighted)
+</span></span><span class="line" id="L2"><span></span></span></span></pre></div><script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
+<script src="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
+<script>$(".popupable").popover({html:true, placement:'top'})/*initialize bootstrap popover*/</script></body></html>
\ 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.
+
+# Test code
+#
+# assert true # tested
+fun foo1 do end
+
+# Test code
+#
+# ~~~~
+# assert true # tested
+# ~~~~
+fun foo2 do end
+
+# Test code
+#
+# ~~~~nit
+# assert true # tested
+# ~~~~
+fun foo3 do end
+
+# Test code
+#
+# ~~~~raw
+# assert false # not tested (and not highlighted)
+# ~~~~
+fun foo4 do end
+
+# Test code
+#
+# ~~~~nitish
+# assert false # not tested (but highlighted)
+# ~~~~
+fun foo5 do end
--- /dev/null
+test_doc.nit
+test_doc2.nit
+++ /dev/null
-test_doc.nit