Some useful services for games and other geometry heavy applications.
I must say that I'm getting tired of the points API. Dealing with both the numeric complexity and the 2/3D is unpleasant. I will probably remove/separate the 3D part in the near future.
Pull-Request: #1557
Reviewed-by: Jean Privat <jean@pryen.org>
Reviewed-by: Lucas Bajolet <r4pass@hotmail.com>
-all: bins tests
-
-bins:
+all:
mkdir -p bin
../../bin/nitc --dir bin src/svg_to_png_and_nit.nit src/svg_to_icons.nit
-check: tests
-tests: test-dino test-app
+check: test-dino test-app
test-app: bin/svg_to_png_and_nit
make -C tests/app
end
svg_file.close
- assert page_width != -1
- assert page_height != -1
+ if page_width == -1 or page_height == -1 then
+ stderr.write "Source drawing file '{drawing}' doesn't look like an SVG file\n"
+ exit 1
+ end
# Query Inkscape
var prog = "inkscape"
# Article used to contextualize links.
var context: WikiEntry
- redef fun add_wikilink(v, link, name, comment) do
+ redef fun add_wikilink(v, token) do
+ var wiki = v.processor.as(NitiwikiMdProcessor).wiki
+ var target: nullable WikiEntry = null
var anchor: nullable String = null
- v.add "<a "
- if not link.has_prefix("http://") and not link.has_prefix("https://") then
- var wiki = v.processor.as(NitiwikiMdProcessor).wiki
- var target: nullable WikiEntry = null
- if link.has("#") then
- var parts = link.split_with("#")
- link = parts.first
- anchor = parts.subarray(1, parts.length - 1).join("#")
- end
- if link.has("/") then
- target = wiki.lookup_entry_by_path(context, link.to_s)
- else
- target = wiki.lookup_entry_by_name(context, link.to_s)
- if target == null then
- target = wiki.lookup_entry_by_title(context, link.to_s)
- end
- end
- if target != null then
- if name == null then name = target.title
- link = target.href_from(context)
- else
- var loc = context.src_path or else context.name
- wiki.message("Warning: unknown wikilink `{link}` (in {loc})", 0)
- v.add "class=\"broken\" "
+ var link = token.link
+ if link == null then return
+ if link.has("#") then
+ var parts = link.split_with("#")
+ link = parts.first
+ anchor = parts.subarray(1, parts.length - 1).join("#")
+ end
+ if link.has("/") then
+ target = wiki.lookup_entry_by_path(context, link.to_s)
+ else
+ target = wiki.lookup_entry_by_name(context, link.to_s)
+ if target == null then
+ target = wiki.lookup_entry_by_title(context, link.to_s)
end
end
+ v.add "<a "
+ var name = token.name
+ if target != null then
+ if name == null then name = target.title
+ link = target.url
+ else
+ wiki.message("Warning: unknown wikilink `{link}` (in {context.src_path.as(not null)})", 0)
+ v.add "class=\"broken\" "
+ end
v.add "href=\""
append_value(v, link)
if anchor != null then append_value(v, "#{anchor}")
v.add "\""
+ var comment = token.comment
if comment != null and not comment.is_empty then
v.add " title=\""
append_value(v, comment)
--- /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.
+
+# Serialize and deserialize Nit objects to binary streams
+#
+# The serialized data format uses a dictionary structure similar to BSON:
+#
+# ~~~raw
+# object = 0x01 # null
+# | 0x02 id attributes # New object
+# | 0x03 id # Ref to object
+# | 0x04 int64 # Int
+# | 0x05 int8 # Bool (int8 != 0)
+# | 0x06 int8 # Char
+# | 0x07 double(64 bits) # Float
+# | 0x08 block # String
+# | 0x09 block # NativeString
+# | 0x0A flat_array; # Array[nullable Object]
+#
+# block = int64 int8*;
+# cstring = int8* 0x00;
+# id = int64;
+#
+# attributes = attribute* 0x00;
+# attribute = cstring object;
+# ~~~
+module serialization
+
+import ::serialization::caching
+private import ::serialization::engine_tools
+import binary
+import more_collections
+
+# ---
+# Special bytes, marking the kind of objects in the stream and the end on an object
+
+private fun kind_null: Int do return 0x01
+private fun kind_object_new: Int do return 0x02
+private fun kind_object_ref: Int do return 0x03
+private fun kind_int: Int do return 0x04
+private fun kind_bool: Int do return 0x05
+private fun kind_char: Int do return 0x06
+private fun kind_float: Int do return 0x07
+private fun kind_string: Int do return 0x08
+private fun kind_native_string: Int do return 0x09
+private fun kind_flat_array: Int do return 0x0A
+
+private fun new_object_end: Int do return 0x00
+
+#---
+# Engines
+
+# Writes Nit objects to the binary `stream`
+#
+# The output can be deserialized with `BinaryDeserializer`.
+class BinarySerializer
+ super CachingSerializer
+
+ # Target writing stream
+ var stream: Writer is writable
+
+ redef fun serialize(object)
+ do
+ if object == null then
+ stream.write_byte kind_null
+ else serialize_reference(object)
+ end
+
+ redef fun serialize_attribute(name, value)
+ do
+ stream.write_string name
+ super
+ end
+
+ redef fun serialize_reference(object)
+ do
+ if cache.has_object(object) then
+ # if already serialized, add local reference
+ var id = cache.id_for(object)
+ stream.write_byte kind_object_ref
+ stream.write_int64 id
+ else
+ # serialize here
+ object.serialize_to_binary self
+ end
+ end
+
+ # Write `collection` as a simple list of objects
+ private fun serialize_flat_array(collection: Collection[nullable Object])
+ do
+ stream.write_byte kind_flat_array
+ stream.write_int64 collection.length
+ for e in collection do
+ if not try_to_serialize(e) then
+ assert e != null
+ warn "Element of {collection} is not serializable, it is a {e}"
+ serialize null
+ end
+ end
+ end
+end
+
+# Deserialize Nit objects from a binary `stream`
+#
+# Used with `BinarySerializer`.
+class BinaryDeserializer
+ super CachingDeserializer
+
+ # Source `Reader` stream
+ var stream: Reader
+
+ # Last encountered object reference id.
+ #
+ # See `cache.received`.
+ private var just_opened_id: nullable Int = null
+
+ # Tree of attributes, deserialized but not yet claimed
+ private var unclaimed_attributes = new UnrolledList[HashMap[String, nullable Object]]
+
+ # Read and deserialize the next attribute name and value
+ #
+ # A `peeked_char` can suffix the next attribute name.
+ #
+ # Returns `null` on error.
+ private fun deserialize_next_attribute(peeked_char: nullable Char):
+ nullable Couple[String, nullable Object]
+ do
+ # Try the next attribute
+ var next_attribute_name = stream.read_string
+ var next_object = deserialize_next_object
+
+ if stream.last_error != null then return null
+
+ if peeked_char != null then
+ # Replace a char peeked to find an object end
+ next_attribute_name = "{peeked_char}{next_attribute_name}"
+ end
+
+ return new Couple[String, nullable Object](next_attribute_name, next_object)
+ end
+
+ redef fun deserialize_attribute(name)
+ do
+ if unclaimed_attributes.last.keys.has(name) then
+ # Pick in already deserialized attributes
+ var value = unclaimed_attributes.last[name]
+ unclaimed_attributes.last.keys.remove(name)
+ return value
+ end
+
+ # Read attributes until we find the wanted one named `name`
+ loop
+ var next = deserialize_next_attribute
+ if next == null then
+ # Error was already logged
+ return null
+ end
+
+ var next_attribute_name = next.first
+ var next_object = next.second
+
+ # Got the wanted object
+ if next_attribute_name == name then return next_object
+
+ # An invalid attribute name is an heuristic for invalid data.
+ # Hitting an object end marker will result in an empty string.
+ assert next_attribute_name.is_valid_id else
+
+ var error
+ if next_attribute_name.is_empty then
+ # Reached the end of the object
+ error = new Error("Deserialization Error: Attributes '{name}' not in stream.")
+ else
+ error = new Error("Deserialization Error: Got an invalid attribute name '{next_attribute_name}', expected '{name}'")
+ # TODO this is invalid data, break even on keep_going
+ end
+ errors.add error
+ return null
+ end
+
+ # It's not the next attribute, put it aside
+ unclaimed_attributes.last[next_attribute_name] = next_object
+ end
+ end
+
+ redef fun notify_of_creation(new_object)
+ do
+ var id = just_opened_id
+ if id == null then return
+ cache[id] = new_object
+ end
+
+ # Convert from simple Json object to Nit object
+ private fun deserialize_next_object: nullable Object
+ do
+ var kind = stream.read_byte
+ assert kind isa Int else
+ # TODO break even on keep_going
+ return null
+ end
+
+ # After this point, all stream reading errors are caught later
+
+ if kind == kind_null then return null
+ if kind == kind_int then return stream.read_int64
+ if kind == kind_bool then return stream.read_bool
+ if kind == kind_float then return stream.read_double
+ if kind == kind_char then return (stream.read_byte or else 0).ascii
+ if kind == kind_string then return stream.read_block
+ if kind == kind_native_string then return stream.read_block.to_cstring
+
+ if kind == kind_flat_array then
+ # An array
+ var length = stream.read_int64
+ var array = new Array[nullable Object]
+ for i in length.times do
+ array.add deserialize_next_object
+ end
+ return array
+ end
+
+ if kind == kind_object_ref then
+ # A reference
+ var id = stream.read_int64
+ if stream.last_error != null then return null
+
+ if not cache.has_id(id) then
+ errors.add new Error("Deserialization Error: Unknown reference to id #{id}")
+ return null
+ end
+ return cache.object_for(id)
+ end
+
+ if kind == kind_object_new then
+ # A new object
+ var id = stream.read_int64
+ if stream.last_error != null then return null
+
+ if cache.has_id(id) then
+ errors.add new Error("Deserialization Error: Duplicated use of reference #{id}")
+ return null
+ end
+
+ var class_name = stream.read_string
+
+ if stream.last_error != null then return null
+
+ # Use the validity of the `class_name` as heuristic to detect invalid data
+ if not class_name.is_valid_id then
+ errors.add new Error("Deserialization Error: got an invalid class name '{class_name}'")
+ return null
+ end
+
+ # Prepare opening a new object
+ just_opened_id = id
+ unclaimed_attributes.push new HashMap[String, nullable Object]
+
+ var value = deserialize_class(class_name)
+
+ # Check for the attributes end marker
+ loop
+ var next_byte = stream.read_byte
+ if next_byte == new_object_end then break
+
+ # Fetch an additional attribute, even if it isn't expected
+ deserialize_next_attribute((next_byte or else 0).ascii)
+ end
+
+ # Close object
+ unclaimed_attributes.pop
+ just_opened_id = null
+
+ return value
+ end
+
+ errors.add new Error("Deserialization Error: Unknown binary object kind `{kind}`")
+ # TODO fatal error and break even on keep_going
+ return null
+ end
+
+ redef fun deserialize
+ do
+ errors.clear
+
+ var value = deserialize_next_object
+
+ var error = stream.last_error
+ if error != null then
+ errors.add error
+ return true
+ end
+
+ return value
+ end
+end
+
+# ---
+# Services
+
+redef class Text
+ # Is `self` a valid identifier for a Nit class or property?
+ private fun is_valid_id: Bool
+ do
+ if trim.is_empty then return false
+
+ for c in chars do
+ if not (c.is_letter or c.is_numeric or c == '[' or c == ']' or
+ c == ' ' or c == ',' or c == '_') then return false
+ end
+
+ return true
+ end
+end
+
+# ---
+# Per class serialization behavior
+
+redef class Serializable
+ # Write the binary serialization header
+ #
+ # The header for a normal object is:
+ # 1. The kind of object on 8 bits, `0x01` for a new object.
+ # 2. The id of this object so it is not serialized more than once.
+ # 3. The name of the object type as a null terminated string.
+ private fun serialize_header_to_binary(v: BinarySerializer)
+ do
+ var id = v.cache.new_id_for(self)
+ v.stream.write_byte kind_object_new # is object intro
+ v.stream.write_int64 id
+ v.stream.write_string class_name
+ end
+
+ # Write a normal object to binary
+ private fun serialize_to_binary(v: BinarySerializer)
+ do
+ serialize_header_to_binary v
+ core_serialize_to v
+ v.stream.write_byte new_object_end
+ end
+end
+
+redef class Int
+ redef fun serialize_to_binary(v)
+ do
+ v.stream.write_byte kind_int
+ v.stream.write_int64 self
+ end
+end
+
+redef class Float
+ redef fun serialize_to_binary(v)
+ do
+ v.stream.write_byte kind_float
+ v.stream.write_double self
+ end
+end
+
+redef class Bool
+ redef fun serialize_to_binary(v)
+ do
+ v.stream.write_byte kind_bool
+ v.stream.write_bool self
+ end
+end
+
+redef class Char
+ redef fun serialize_to_binary(v)
+ do
+ v.stream.write_byte kind_char
+ v.stream.write_byte self.ascii
+ end
+end
+
+redef class String
+ redef fun serialize_to_binary(v)
+ do
+ v.stream.write_byte kind_string
+ v.stream.write_block self
+ end
+end
+
+redef class NativeString
+ redef fun serialize_to_binary(v)
+ do
+ v.stream.write_byte kind_native_string
+ v.stream.write_block to_s
+ end
+end
+
+redef class SimpleCollection[E]
+
+ redef fun serialize_to_binary(v)
+ do
+ serialize_header_to_binary v
+
+ v.stream.write_string "items"
+ v.serialize_flat_array self
+
+ v.stream.write_byte new_object_end
+ end
+
+ redef init from_deserializer(v)
+ do
+ # Give a chance to other engines, and defs
+ super
+
+ if v isa BinaryDeserializer then
+ v.notify_of_creation self
+ init
+
+ var items = v.deserialize_attribute("items")
+ assert items isa Array[nullable Object]
+ for item in items do
+ assert item isa E else
+ var item_type = "null"
+ if item != null then item_type = item.class_name
+
+ v.errors.add new Error("Deserialization Error: invalid type '{item_type}' for the collection '{class_name}'")
+ continue
+ end
+
+ add item
+ end
+ end
+ end
+end
+
+redef class Map[K, V]
+ redef fun serialize_to_binary(v)
+ do
+ serialize_header_to_binary v
+
+ core_serialize_to v
+
+ v.stream.write_string "keys"
+ v.serialize_flat_array keys
+
+ v.stream.write_string "values"
+ v.serialize_flat_array values
+
+ v.stream.write_byte new_object_end
+ end
+
+ # Instantiate a new `Array` from its serialized representation.
+ redef init from_deserializer(v)
+ do
+ # Give a chance to other engines, and defs
+ super
+
+ if v isa BinaryDeserializer then
+ v.notify_of_creation self
+
+ init
+
+ var keys = v.deserialize_attribute("keys")
+ var values = v.deserialize_attribute("values")
+ assert keys isa Array[nullable Object]
+ assert values isa Array[nullable Object]
+
+ for i in keys.length.times do
+ var key = keys[i]
+ var value = values[i]
+
+ if not key isa K then
+ var item_type = "null"
+ if key != null then item_type = key.class_name
+
+ v.errors.add new Error("Deserialization Error: Invalid key type '{item_type}' for '{class_name}'")
+ continue
+ end
+
+ if not value isa V then
+ var item_type = "null"
+ if value != null then item_type = value.class_name
+
+ v.errors.add new Error("Deserialization Error: Invalid value type '{item_type}' for '{class_name}'")
+ continue
+ end
+
+ self[key] = value
+ end
+ end
+ end
+end
--- /dev/null
+# Nit wrapper for Github API
+
+This module provides a Nit object oriented interface to access the Github api.
+
+## Accessing the API
+
+[[doc: GithubAPI]]
+
+### Authentification
+
+[[doc: GithubAPI::auth]]
+
+Token can also be recovered from user config with `get_github_oauth`.
+
+[[doc: get_github_oauth]]
+
+### Retrieving user data
+
+[[doc: load_user]]
+[[doc: User]]
+[[list: User]]
+
+### Retrieving repo data
+
+[[doc: load_repo]]
+[[doc: Repo]]
+[[list: Repo]]
+
+### Other data
+
+[[list: api]]
+
+### Advanced uses
+
+#### Caching
+
+[[doc: cache]]
+
+#### Custom requests
+
+[[doc: GithubAPI::get]]
+
+#### Change the user agent
+
+[[doc: GithubAPI::user_agent]]
+
+#### Debugging
+
+[[doc: verbose_lvl]]
+
+#### Using with GitLab
+
+If URL scheme of GitLab API follows the one of Github API, it may be possible to
+configure this wrapper to use a custom URL.
+
+[[doc: api_url]]
+
+## Creating hooks
+
+Using this API you can create Github hooks able to respond to actions performed
+on a repository.
+
+[[doc: hooks]]
+
+## Dealing with events
+
+GithubAPI can trigger different events depending on the hook configuration.
+
+[[doc: GithubEvent]]
+
+[[list: github::events]]
# See the License for the specific language governing permissions and
# limitations under the License.
-# Nit object oriented interface to Github api.
+# Nit object oriented interface to [Github api](https://developer.github.com/v3/).
#
# This modules reifies Github API elements as Nit classes.
#
import github_curl
-# Interface to Github REST API.
+# Client to Github API
#
-# Used by all `GithubEntity` to perform requests.
-#
-# Usage:
+# To access the API you need an instance of a `GithubAPI` client.
#
# ~~~
# # Get Github authentification token.
# var api = new GithubAPI(token)
# ~~~
#
-# The API client allows to get Github API entities:
+# The API client allows you to get Github API entities.
#
# ~~~
# var repo = api.load_repo("privat/nit")
# ~~~
class GithubAPI
- # Github API OAuth token.
+ # Github API OAuth token
+ #
+ # To access your private ressources, you must
+ # [authenticate](https://developer.github.com/guides/basics-of-authentication/).
+ #
+ # For client applications, Github recommands to use the
+ # [OAuth tokens](https://developer.github.com/v3/oauth/) authentification method.
+ #
+ #
#
- # This token is used to authenticate the application on Github API.
# Be aware that there is [rate limits](https://developer.github.com/v3/rate_limit/)
# associated to the key.
var auth: String
return res.as(JsonObject)
end
- # Get the Github user with `login`.
+ # Get the Github user with `login`
#
- # Returns `null` if the user cannot be found.
+ # Loads the `User` from the API or returns `null` if the user cannot be found.
#
# var api = new GithubAPI(get_github_oauth)
# var user = api.load_user("Morriar")
# Get the Github repo with `full_name`.
#
- # Returns `null` if the repo cannot be found.
+ # Loads the `Repo` from the API or returns `null` if the repo cannot be found.
#
# var api = new GithubAPI(get_github_oauth)
# var repo = api.load_repo("privat/nit")
fun html_url: String do return json["html_url"].to_s
end
-# A Github user.
+# A Github user
#
+# Provides access to [Github user data](https://developer.github.com/v3/users/).
# Should be accessed from `GithubAPI::load_user`.
-#
-# See <https://developer.github.com/v3/users/>.
class User
super GithubEntity
# A Github repository.
#
+# Provides access to [Github repo data](https://developer.github.com/v3/repos/).
# Should be accessed from `GithubAPI::load_repo`.
-#
-# See <https://developer.github.com/v3/repos/>.
class Repo
super GithubEntity
# See the License for the specific language governing permissions and
# limitations under the License.
-# Github API related features.
+# Nit wrapper for Github API
+#
+# This module provides a Nit object oriented interface to access the
+# [Github api](https://developer.github.com/v3/).
module github
import cache
redef fun to_s do return "[{name}] {super}"
end
+# Gets the Github token from `git` configuration
+#
# Return the value of `git config --get github.oauthtoken`
-# return "" if no such a key
+# or `""` if no key exists.
fun get_github_oauth: String
do
var p = new ProcessReader("git", "config", "--get", "github.oauthtoken")
c2 = ' '
end
- var loc = text.pos_to_loc(pos)
+ var loc = new MDLocation(
+ current_loc.line_start,
+ current_loc.column_start + pos,
+ current_loc.line_start,
+ current_loc.column_start + pos)
if c == '*' then
if c1 == '*' then
end
return -1
end
+
+ # Location used for next parsed token.
+ #
+ # This location can be changed by the emitter to adjust with `\n` found
+ # in the input.
+ private fun current_loc: MDLocation do return emitter.current_loc
end
# Emit output corresponding to blocks content.
# Transform and emit mardown text
fun emit_text(text: Text) do emit_text_until(text, 0, null)
- # Transform and emit mardown text starting at `from` and
+ # Transform and emit mardown text starting at `start` and
# until a token with the same type as `token` is found.
- # Go until the end of text if `token` is null.
+ # Go until the end of `text` if `token` is null.
fun emit_text_until(text: Text, start: Int, token: nullable Token): Int do
var old_text = current_text
var old_pos = current_pos
current_text = text
current_pos = start
while current_pos < text.length do
+ if text[current_pos] == '\n' then
+ current_loc.line_start += 1
+ current_loc.column_start = -current_pos
+ end
var mt = processor.token_at(text, current_pos)
if (token != null and not token isa TokenNone) and
(mt.is_same_type(token) or
return buffer_stack.last
end
+ # Stacked locations.
+ private var loc_stack = new List[MDLocation]
+
+ # Push a new MDLocation on the stack.
+ private fun push_loc(location: MDLocation) do loc_stack.add location
+
+ # Pop the last buffer.
+ private fun pop_loc: MDLocation do return loc_stack.pop
+
+ # Current output buffer.
+ private fun current_loc: MDLocation do
+ assert not loc_stack.is_empty
+ return loc_stack.last
+ end
+
# Append `e` to current buffer.
fun add(e: Writable) do
if e isa Text then
var column_end: Int
redef fun to_s do return "{line_start},{column_start}--{line_end},{column_end}"
+
+ # Return a copy of `self`.
+ fun copy: MDLocation do
+ return new MDLocation(line_start, column_start, line_end, column_end)
+ end
end
# A block of markdown lines.
fun emit_blocks(v: MarkdownEmitter) do
var block = self.block.first_block
while block != null do
+ v.push_loc(block.location)
block.kind.emit(v)
+ v.pop_loc
block = block.next
end
end
class BlockHeadline
super Block
- redef fun emit(v) do v.decorator.add_headline(v, self)
+ redef fun emit(v) do
+ var loc = block.location.copy
+ loc.column_start += start
+ v.push_loc(loc)
+ v.decorator.add_headline(v, self)
+ v.pop_loc
+ end
+
+ private var start = 0
# Depth of the headline used to determine the headline level.
var depth = 0
line.leading = 0
line.trailing = 0
end
+ self.start = start
depth = level.min(6)
end
end
return null
end
- # Init a `MDLocation` instance at `pos` in `self`.
- private fun pos_to_loc(pos: Int): MDLocation do
- assert pos <= length
- var line = 1
- var col = 0
- var i = 0
- while i <= pos do
- col += 1
- var c = self[i]
- if c == '\n' then
- line +=1
- col = 0
- end
- i +=1
- end
- return new MDLocation(line, col, line, col)
- end
-
# Is `self` an unsafe HTML element?
private fun is_html_unsafe: Bool do return html_unsafe_tags.has(self.write_to_string)
"TokenLink at 4,1--4,1"]
(new TestTokenProcessor(stack)).process(string)
end
+
+ fun test_token_location4 do
+ var string = "**Hello**\n\n`World`"
+ var stack = [
+ "TokenStrongStar at 1,1--1,1",
+ "TokenStrongStar at 1,8--1,8",
+ "TokenCodeSingle at 3,1--3,1",
+ "TokenCodeSingle at 3,7--3,7"]
+ (new TestTokenProcessor(stack)).process(string)
+ end
+
+ fun test_token_location5 do
+ var string = "# *Title1*\n\n# *Title2*"
+ var stack = [
+ "TokenEmStar at 1,3--1,3",
+ "TokenEmStar at 1,10--1,10",
+ "TokenEmStar at 3,3--3,3",
+ "TokenEmStar at 3,10--3,10"]
+ (new TestTokenProcessor(stack)).process(string)
+ end
end
class TestTokenProcessor
var token = super
if token isa TokenNone then return token
var res = "{token.class_name} at {token.location}"
- print res
var exp = test_stack.shift
+ print ""
+ print "EXP {exp}"
+ print "RES {res}"
assert exp == res
return token
end
proc.emitter.decorator = new TestBlockDecorator(stack)
proc.process(string)
end
+
+ fun test_block_location3 do
+ var stack = [
+ "BlockHeadline: 1,1--1,8",
+ "BlockHeadline: 3,1--3,10"]
+ var string ="""# Title\n\n## Title 2"""
+ proc.emitter.decorator = new TestBlockDecorator(stack)
+ proc.process(string)
+ end
end
class TestBlockDecorator
--- /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 suites for module `markdown`
+module test_wikilinks is test_suite
+
+import test_markdown
+import wikilinks
+
+class TestTokenWikilink
+ super TestSuite
+
+ fun test_token_location1 do
+ var string = "[[wikilink]]"
+ var stack = ["TokenWikiLink at 1,1--1,1"]
+ (new TestTokenProcessor(stack)).process(string)
+ end
+
+ fun test_token_location2 do
+ var string = "Hello [[World]]"
+ var stack = ["TokenWikiLink at 1,7--1,7"]
+ (new TestTokenProcessor(stack)).process(string)
+ end
+
+ fun test_token_location3 do
+ var string = "\nHello\nworld [[wikilink]] !"
+ var stack = ["TokenWikiLink at 3,7--3,7"]
+ (new TestTokenProcessor(stack)).process(string)
+ end
+
+ fun test_token_location4 do
+ var string = "[[link1]]\n\n[[link2]]"
+ var stack = [
+ "TokenWikiLink at 1,1--1,1",
+ "TokenWikiLink at 3,1--3,1"]
+ (new TestTokenProcessor(stack)).process(string)
+ end
+
+ fun test_token_location5 do
+ var string = "[[link1]]\n[[link2]]"
+ var stack = [
+ "TokenWikiLink at 1,1--1,1",
+ "TokenWikiLink at 2,1--2,1"]
+ (new TestTokenProcessor(stack)).process(string)
+ end
+
+ fun test_token_location6 do
+ var string = """
+[[doc: github]]
+
+[[loollll]]
+
+## Accessing the API
+
+[[doc: GithubAPI]]"""
+ var stack = [
+ "TokenWikiLink at 1,1--1,1",
+ "TokenWikiLink at 3,1--3,1",
+ "TokenWikiLink at 7,1--7,1"]
+ (new TestTokenProcessor(stack)).process(string)
+ end
+end
redef class Decorator
# Renders a `[[wikilink]]` item.
- fun add_wikilink(v: EMITTER, link: Text, name, comment: nullable Text) do
- if name != null then
- v.add "[[{name}|{link}]]"
+ fun add_wikilink(v: EMITTER, token: TokenWikiLink) do
+ if token.name != null then
+ v.add "[[{token.name.to_s}|{token.link.to_s}]]"
else
- v.add "[[{link}]]"
+ v.add "[[{token.link.to_s}]]"
end
end
end
super TokenLink
redef fun emit_hyper(v) do
- v.decorator.add_wikilink(v, link.as(not null), name, comment)
+ v.decorator.add_wikilink(v, self)
end
redef fun check_link(v, out, start, token) do
EGL_RED_SIZE, 8,
EGL_NONE
};
- EGLint w, h, dummy, format;
+ EGLint w, h, format;
EGLint numConfigs;
EGLConfig config;
EGLSurface surface;
fun is_down: Bool is abstract
# Key is currently up?
- fun is_up: Bool is abstract
+ fun is_up: Bool do return not is_down
# Key is the up arrow key?
fun is_arrow_up: Bool is abstract
level2.keys.remove(k2)
end
+ # Is there a value at `k1, k2`?
+ fun has(k1: K1, k2: K2): Bool
+ do
+ if not level1.keys.has(k1) then return false
+ return level1[k1].keys.has(k2)
+ end
+
# Remove all items
fun clear do level1.clear
end
level2.remove_at(k2, k3)
end
+ # Is there a value at `k1, k2, k3`?
+ fun has(k1: K1, k2: K2, k3: K3): Bool
+ do
+ if not level1.keys.has(k1) then return false
+ return level1[k1].has(k2, k3)
+ end
+
# Remove all items
fun clear do level1.clear
end
--- /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.
+
+# Services to gather information on the performance of events by categories
+#
+# Provides `PerfMap` to manage all the categories and
+# `PerfEntry` for per-category statistics.
+#
+# ~~~
+# for i in 100.times do
+# var clock = new Clock
+#
+# # Do some "work" here
+# nanosleep(0, 1000000)
+#
+# # Register the perf
+# sys.perfs["sleep 1ms"].add clock.lapse
+#
+# # Do some other "work" here
+# nanosleep(0, 5000000)
+#
+# # Register the perf
+# sys.perfs["sleep 5ms"].add clock.lapse
+# end
+#
+# assert sys.perfs["sleep 1ms"].count == 100
+# assert sys.perfs["sleep 1ms"].avg.is_approx(0.001, 0.0001)
+# assert sys.perfs["sleep 5ms"].avg.is_approx(0.005, 0.0005)
+# ~~~
+module performance_analysis
+
+import realtime
+
+redef class Sys
+ # Main `PerfMap` available by default
+ var perfs = new PerfMap
+end
+
+# Collection of statistics on many events
+class PerfMap
+ super HashMap[String, PerfEntry]
+
+ redef fun provide_default_value(key)
+ do
+ if not key isa String then return super
+
+ var ts = new PerfEntry(key)
+ self[key] = ts
+ return ts
+ end
+
+ redef fun to_s do return "* " + join(": ", "\n* ")
+end
+
+# Statistics on wall clock execution time of a category of events by `name`
+class PerfEntry
+
+ # Name of the category
+ var name: String
+
+ # Shortest execution time of registered events
+ var min = 0.0
+
+ # Longest execution time of registered events
+ var max = 0.0
+
+ # Average execution time of registered events
+ var avg = 0.0
+
+ # Number of registered events
+ var count = 0
+
+ # Register a new event execution time with a `Timespec`
+ fun add(lapse: Timespec) do add_float lapse.to_f
+
+ # Register a new event execution time in seconds using a `Float`
+ fun add_float(time: Float)
+ do
+ if time.to_f < min.to_f or count == 0 then min = time
+ if time.to_f > max.to_f then max = time
+
+ avg = (avg * count.to_f + time) / (count+1).to_f
+ count += 1
+ end
+
+ redef fun to_s do return "min {min}, max {max}, avg {avg}, count {count}"
+end
private extern class NativePthreadMutex in "C" `{ pthread_mutex_t * `}
new (attr: NativePthreadMutexAttr) `{
pthread_mutex_t *mutex = malloc(sizeof(pthread_mutex_t));
- int res = pthread_mutex_init(mutex, attr);
+ int r = pthread_mutex_init(mutex, attr);
+ if (r != 0) {
+ free(mutex);
+ return NULL;
+ }
return mutex;
`}
private extern class NativePthreadMutexAttr in "C" `{ pthread_mutexattr_t * `}
new `{
pthread_mutexattr_t *attr = malloc(sizeof(pthread_mutexattr_t));
- int res = pthread_mutexattr_init(attr);
+ int r = pthread_mutexattr_init(attr);
+ if (r != 0) {
+ free(attr);
+ return NULL;
+ }
return attr;
`}
private extern class NativePthreadKey in "C" `{ pthread_key_t * `}
new `{
pthread_key_t *key = malloc(sizeof(pthread_key_t));
- int res = pthread_key_create(key, NULL);
+ int r = pthread_key_create(key, NULL);
+ if (r != 0) {
+ free(key);
+ return NULL;
+ }
return key;
`}
return self->tv_nsec;
`}
- # Seconds in Float
- # Loss of precision but great to print
+ # Elapsed time in microseconds, with both whole seconds and the rest
+ #
+ # May cause an `Int` overflow, use only with a low number of seconds.
+ fun microsec: Int `{
+ return self->tv_sec*1000000 + self->tv_nsec/1000;
+ `}
+
+ # Elapsed time in milliseconds, with both whole seconds and the rest
+ #
+ # May cause an `Int` overflow, use only with a low number of seconds.
+ fun millisec: Int `{
+ return self->tv_sec*1000 + self->tv_nsec/1000000;
+ `}
+
+ # Number of seconds as a `Float`
+ #
+ # Incurs a loss of precision, but the result is pretty to print.
fun to_f: Float do return sec.to_f + nanosec.to_f / 1000000000.0
redef fun to_s do return "{to_f}s"
# This method should be redefined for each custom subclass of `Serializable`.
# All refinement should look for a precise `class_name` and call super
# on unsupported classes.
- protected fun deserialize_class(class_name: String): Object do
+ protected fun deserialize_class(class_name: String): nullable Object do
return deserialize_class_intern(class_name)
end
# Refinements to this method will be generated by the serialization phase.
# To avoid conflicts, there should not be any other refinements to this method.
# You can instead use `deserialize_class`.
- protected fun deserialize_class_intern(class_name: String): Object do
- print "Error: doesn't know how to deserialize class \"{class_name}\""
- abort
+ protected fun deserialize_class_intern(class_name: String): nullable Object do
+ errors.add new Error("Deserialization Error: Doesn't know how to deserialize class \"{class_name}\"")
+ return null
end
# Should `self` keep trying to deserialize an object after an error?
var id: String is writable
# Item title if any.
- var title: nullable String
+ var title: nullable String is writable
# Does `self` have a `parent`?
fun is_root: Bool do return parent == null
--- /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.
+
+# Parsing of commands understood by documentation tools.
+#
+# This can be through:
+# * `nitx` commands like `code: MEntity::name`
+# * `nitdoc` wikilinks like `[[doc: MEntity::name]]`
+module doc_commands
+
+import doc_base
+
+# A command aimed at a documentation tool like `nitdoc` or `nitx`.
+#
+# `DocCommand` are generally of the form `command: args`.
+interface DocCommand
+
+ # Original command string.
+ fun string: String is abstract
+
+ # Command name.
+ fun name: String is abstract
+
+ # Command arguments.
+ #
+ # FIXME: define a syntax
+ fun args: Array[String] is abstract
+
+ # Command factory.
+ #
+ # Returns a concrete instance of `DocCommand` depending on the string.
+ new(command_string: String) do
+ if command_string.has_prefix("doc:") then
+ return new ArticleCommand(command_string)
+ else if command_string.has_prefix("comment:") then
+ return new CommentCommand(command_string)
+ else if command_string.has_prefix("list:") then
+ return new ListCommand(command_string)
+ else if command_string.has_prefix("param:") then
+ return new ParamCommand(command_string)
+ else if command_string.has_prefix("return:") then
+ return new ReturnCommand(command_string)
+ else if command_string.has_prefix("new:") then
+ return new NewCommand(command_string)
+ else if command_string.has_prefix("call:") then
+ return new CallCommand(command_string)
+ else if command_string.has_prefix("code:") then
+ return new CodeCommand(command_string)
+ end
+ return new UnknownCommand(command_string)
+ end
+
+ redef fun to_s do return string
+end
+
+# Used to factorize initialization of DocCommands.
+abstract class AbstractDocCommand
+ super DocCommand
+
+ redef var string
+ redef var name is noinit
+ redef var args = new Array[String]
+
+ init do
+ # parse command
+ var str = new FlatBuffer
+ var i = 0
+ while i < string.length do
+ var c = string[i]
+ i += 1
+ if c == ':' then break
+ str.add c
+ end
+ name = str.write_to_string
+ # parse args
+ args.add string.substring_from(i).trim
+ end
+end
+
+# A `DocCommand` not recognized by documentation tools.
+#
+# Used to provide warnings or any other behavior for unexisting commands.
+class UnknownCommand
+ super AbstractDocCommand
+end
+
+# A `DocCommand` that includes the documentation article of a `MEntity`.
+#
+# Syntax: `doc: MEntity::name`.
+class ArticleCommand
+ super AbstractDocCommand
+end
+
+# A `DocCommand` that includes the MDoc of a `MEntity`.
+#
+# Syntax: `comment: MEntity::name`.
+class CommentCommand
+ super AbstractDocCommand
+end
+
+# A `DocCommand` that includes a list of something.
+#
+# Syntax: `list:kind: <arg>`.
+class ListCommand
+ super AbstractDocCommand
+end
+
+# A `DocCommand` that includes the list of methods tanking a `MType` as parameter.
+#
+# Syntax: `param: MType`.
+class ParamCommand
+ super AbstractDocCommand
+end
+
+# A `DocCommand` that includes the list of methods returning a `MType` as parameter.
+#
+# Syntax: `param: MType`.
+class ReturnCommand
+ super AbstractDocCommand
+end
+
+# A `DocCommand` that includes the list of methods creating new instances of a specific `MType`
+#
+# Syntax: `new: MType`.
+class NewCommand
+ super AbstractDocCommand
+end
+
+# A `DocCommand` that includes the list of methods calling a specific `MProperty`.
+#
+# Syntax: `call: MEntity::name`.
+class CallCommand
+ super AbstractDocCommand
+end
+
+# A `DocCommand` that includes the source code of a `MEntity`.
+#
+# Syntax:
+# * `code: MEntity::name`
+# * `./src/file.nit` to include source code from a file.
+# * `./src/file.nit:1,2--3,4` to select code between positions.
+class CodeCommand
+ super AbstractDocCommand
+end
private import parser_util
redef class MDoc
- # Comment synopsys HTML escaped
- var short_comment: String is lazy do return content.first.html_escape
- # Full comment HTML escaped
- var full_comment: String is lazy do return content.join("\n").html_escape
+ # Synopsis HTML escaped.
+ var synopsis: String is lazy do return content.first.html_escape
+
+ # Comment without synopsis HTML escaped
+ var comment: String is lazy do
+ var lines = content.to_a
+ if not lines.is_empty then lines.shift
+ return content.join("\n").html_escape
+ end
+
+ # Full comment HTML escaped.
+ var documentation: String is lazy do return content.join("\n").html_escape
private var markdown_proc: MarkdownProcessor is lazy do
return original_mentity.model.nitdoc_md_processor
return original_mentity.model.nitdoc_inline_processor
end
- # Synopsys in a template
- var tpl_short_comment: Writable is lazy do
+ # Renders the synopsis as a HTML comment block.
+ var html_synopsis: Writable is lazy do
var res = new Template
var syn = inline_proc.process(content.first)
res.add "<span class=\"synopsys nitdoc\">{syn}</span>"
end
- # Full comment in a template
- var tpl_comment: Writable is lazy do
- var res = new Template
+ # Renders the comment without the synopsis as a HTML comment block.
+ var html_comment: Writable is lazy do
var lines = content.to_a
+ if not lines.is_empty then lines.shift
+ return lines_to_html(lines)
+ end
+
+ # Renders the synopsis and the comment as a HTML comment block.
+ var html_documentation: Writable is lazy do return lines_to_html(content.to_a)
+
+ # Renders markdown line as a HTML comment block.
+ private fun lines_to_html(lines: Array[String]): Writable do
+ var res = new Template
res.add "<div class=\"nitdoc\">"
# do not use DocUnit as synopsys
- if not content.first.has_prefix(" ") and
- not content.first.has_prefix("\t") then
- # parse synopsys
- var syn = inline_proc.process(lines.shift)
- res.add "<p class=\"synopsys\">{syn}</p>"
+ if not lines.is_empty then
+ if not lines.first.has_prefix(" ") and
+ not lines.first.has_prefix("\t") then
+ # parse synopsys
+ var syn = inline_proc.process(lines.shift)
+ res.add "<p class=\"synopsys\">{syn}</p>"
+ end
end
# check for annotations
for i in [0 .. lines.length[ do
res.add markdown_proc.process(lines.join("\n"))
res.add "</div>"
return res
+
end
end
redef class Model
# Get a markdown processor for Nitdoc comments.
- private var nitdoc_md_processor: MarkdownProcessor is lazy do
+ var nitdoc_md_processor: MarkdownProcessor is lazy do
var proc = new MarkdownProcessor
proc.emitter.decorator = new NitdocDecorator
return proc
# Get a markdown inline processor for Nitdoc comments.
#
# This processor is specificaly designed to inlinable doc elements like synopsys.
- private var nitdoc_inline_processor: MarkdownProcessor is lazy do
+ var nitdoc_inline_processor: MarkdownProcessor is lazy do
var proc = new MarkdownProcessor
proc.emitter.decorator = new InlineDecorator
return proc
module doc_console
import semantize
+import doc_commands
import doc_extract
import doc_poset
import doc::console_templates
# Processes the query string and performs it.
fun do_query(str: String) do
- var query = new NitxQuery(str)
+ var query = new DocCommand(str)
if query isa NitxCommand then
query.execute(self)
return
end
end
-# A query performed on Nitx.
-#
-# Queries are responsible to collect matching results and render them as a
-# DocPage.
-#
-# Used as a factory to concrete instances.
-interface NitxQuery
-
- # Original query string.
- fun query_string: String is abstract
+redef interface DocCommand
- # Query factory.
- #
- # Will return a concrete instance of NitxQuery.
- new(query_string: String) do
+ redef new(query_string) do
if query_string == ":q" then
return new NitxQuit
else if query_string == ":h" then
return new NitxHelp
- else if query_string.has_prefix("comment:") then
- return new CommentQuery(query_string)
- else if query_string.has_prefix("doc:") then
- return new DocQuery(query_string)
- else if query_string.has_prefix("param:") then
- return new ParamQuery(query_string)
- else if query_string.has_prefix("return:") then
- return new ReturnQuery(query_string)
- else if query_string.has_prefix("new:") then
- return new NewQuery(query_string)
- else if query_string.has_prefix("call:") then
- return new CallQuery(query_string)
- else if query_string.has_prefix("code:") then
- return new CodeQuery(query_string)
- else if query_string.has_prefix("parents:") then
- return new ParentsQuery(query_string)
- else if query_string.has_prefix("ancestors:") then
- return new AncestorsQuery(query_string)
- else if query_string.has_prefix("children:") then
- return new ChildrenQuery(query_string)
- else if query_string.has_prefix("descendants:") then
- return new DescendantsQuery(query_string)
end
- return new CommentQuery("comment: {query_string}")
+ var cmd = super(query_string)
+ if cmd isa UnknownCommand then
+ return new CommentCommand("comment: {query_string}")
+ end
+ return cmd
end
# Looks up the `doc` model and returns possible matches.
# Pretty prints the results for the console.
fun make_results(nitx: Nitx, results: Array[NitxMatch]): DocPage do
var page = new DocPage("results", "Results")
- page.root.add_child(new QueryResultArticle("results.article", "Results", self, results))
+ page.root.add_child(new QueryResultArticle("results", "Results", self, results))
return page
end
-
- redef fun to_s do return query_string
end
-# Something that matches a `NitxQuery`.
+# Something that matches a `DocCommand`.
abstract class NitxMatch
# Query matched by `self`.
- var query: NitxQuery
+ var query: DocCommand
# Pretty prints `self` for console.
fun make_list_item: String is abstract
end
-# A query that contains a meta command.
-#
-# In Nitx, commands are written such as `command: args...`.
-abstract class MetaQuery
- super NitxQuery
-
- redef var query_string
-
- # Meta command used.
- var command: String is noinit
-
- # Arguments passed to the `command`.
- var args = new Array[String]
-
- init do
- # parse command
- var str = new FlatBuffer
- var i = 0
- while i < query_string.length do
- var c = query_string[i]
- i += 1
- if c == ':' then break
- str.add c
- end
- command = str.write_to_string
- # parse args
- args.add query_string.substring_from(i).trim
- end
-end
-
-# A match between a `NitxQuery` and a `MEntity`.
+# A match between a `DocCommand` and a `MEntity`.
class MEntityMatch
super NitxMatch
redef fun make_list_item do return mentity.cs_list_item
end
-# A query to search a `MEntity` comment by its name or namespace.
-class CommentQuery
- super MetaQuery
-
+redef class CommentCommand
redef fun perform(nitx, doc) do
var name = args.first
var res = new Array[NitxMatch]
if len == 1 then
var res = results.first.as(MEntityMatch)
var mentity = res.mentity
- var page = new DocPage("results", "Results")
- var article = new DefinitionArticle("results.article", "Results", mentity)
+ var page = new DocPage("resultats", "Results")
+ var article = new DefinitionArticle("results", "Results", mentity)
article.cs_title = mentity.name
article.cs_subtitle = mentity.cs_declaration
page.root.add_child article
end
# A query to search signatures using a specific `MType` as parameter.
-class ParamQuery
- super MetaQuery
-
+redef class ParamCommand
redef fun perform(nitx, doc) do
var res = new Array[NitxMatch]
var mtype_name = args.first
end
# A query to search signatures using a specific `MType` as return.
-class ReturnQuery
- super MetaQuery
-
+redef class ReturnCommand
redef fun perform(nitx, doc) do
var res = new Array[NitxMatch]
var mtype_name = args.first
end
# A query to search methods creating new instances of a specific `MType`.
-class NewQuery
- super MetaQuery
-
+redef class NewCommand
redef fun perform(nitx, doc) do
var res = new Array[NitxMatch]
var mtype_name = args.first
end
# A query to search methods calling a specific `MProperty`.
-class CallQuery
- super MetaQuery
-
+redef class CallCommand
redef fun perform(nitx, doc) do
var res = new Array[NitxMatch]
var mprop_name = args.first
end
# A query to search a Nitdoc documentation page by its name.
-class DocQuery
- super MetaQuery
-
+redef class ArticleCommand
redef fun perform(nitx, doc) do
var res = new Array[NitxMatch]
var name = args.first
# It actually searches for pages about the mentity and extracts the
# pre-calculated hierarchies by the `doc_post` phase.
abstract class HierarchiesQuery
- super DocQuery
+ super DocCommand
redef fun make_results(nitx, results) do
var page = new DocPage("hierarchy", "Hierarchy")
end
# A query to search source code from a file name.
-class CodeQuery
- super MetaQuery
-
+redef class CodeCommand
# FIXME refactor this!
redef fun perform(nitx, doc) do
var res = new Array[NitxMatch]
redef fun make_results(nitx, results) do
var page = new DocPage("results", "Code Results")
for res in results do
- page.add new CodeQueryArticle("results.article", "Results", self, res.as(CodeMatch))
+ page.add new CodeQueryArticle("results", "Results", self, res.as(CodeMatch))
end
return page
end
# These commands are prefixed with `:` and are used to control the execution of
# `nitx` like displaying the help or quiting.
interface NitxCommand
- super NitxQuery
+ super DocCommand
# Executes the command.
fun execute(nitx: Nitx) is abstract
do
node.visit_all(self)
if not node isa ASendExpr then return
- calls.add node.callsite.mproperty
+ calls.add node.callsite.as(not null).mproperty
end
end
super DocArticle
# Query linked to the results to display.
- var query: NitxQuery
+ var query: DocCommand
# Results to display.
var results: Array[NitxMatch]
redef fun render_title do
var len = results.length
if len == 0 then
- add "No result found for '{query.query_string}'..."
+ add "No result found for '{query.string}'..."
else
- add "# {len} result(s) for '{query.query_string}'".green.bold
+ add "# {len} result(s) for '{query.string}'".green.bold
end
end
super DocArticle
# The query linked to the result to display.
- var query: NitxQuery
+ var query: DocCommand
# The result to display.
var result: CodeMatch
end
redef class MEntityPage
- redef var html_url is lazy do return mentity.nitdoc_url
+ redef var html_url is lazy do
+ if mentity isa MGroup and mentity.mdoc != null then
+ return "api_{mentity.nitdoc_url}"
+ end
+ return mentity.nitdoc_url
+ end
+
redef fun init_title(v, doc) do title = mentity.html_name
end
# doc phases. This is to preserve the compatibility with the current
# `doc_templates` module.
+redef class ReadmePage
+ redef var html_url is lazy do return mentity.nitdoc_url
+
+ redef fun init_topmenu(v, doc) do
+ super
+ var mproject = mentity.mproject
+ if not mentity.is_root then
+ topmenu.add_li new ListItem(new Link(mproject.nitdoc_url, mproject.html_name))
+ end
+ topmenu.add_li new ListItem(new Link(html_url, mproject.html_name))
+ topmenu.active_item = topmenu.items.last
+ end
+
+ redef fun init_sidebar(v, doc) do
+ super
+ var api_lnk = """<a href="api_{{{mentity.nitdoc_url}}}">Go to API</a>"""
+ sidebar.boxes.unshift new DocSideBox(api_lnk, "")
+ end
+end
+
redef class MGroupPage
redef fun init_topmenu(v, doc) do
super
redef fun init_sidebar(v, doc) do
super
+ # README link
+ if mentity.mdoc != null then
+ var doc_lnk = """<a href="{{{mentity.nitdoc_url}}}">Go to README</a>"""
+ sidebar.boxes.unshift new DocSideBox(doc_lnk, "")
+ end
+ # MClasses list
var mclasses = new HashSet[MClass]
mclasses.add_all intros
mclasses.add_all redefs
var def_url = "{cls_url}#{mprop.nitdoc_id}.definition"
var lnk = new Link(def_url, mprop.html_name)
var mdoc = mprop.intro.mdoc_or_fallback
- if mdoc != null then lnk.title = mdoc.short_comment
+ if mdoc != null then lnk.title = mdoc.synopsis
var item = new Template
item.add new DocHTMLLabel.with_classes(classes)
item.add lnk
doc.add_page new OverviewPage("overview", "Overview")
doc.add_page new SearchPage("search", "Index")
for mgroup in doc.mgroups do
+ doc.add_page new ReadmePage(mgroup)
doc.add_page new MGroupPage(mgroup)
end
for mmodule in doc.mmodules do
redef var title is lazy do return mentity.nitdoc_name
end
+# A page that displays a `MGroup` README.
+class ReadmePage
+ super MEntityPage
+
+ redef type MENTITY: MGroup
+ redef var id is lazy do return "readme_{mentity.nitdoc_id}"
+end
+
# A documentation page about a MGroup.
class MGroupPage
super MEntityPage
--- /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.
+
+# This phase parses README files.
+module doc_readme
+
+import markdown::decorators
+intrude import markdown::wikilinks
+import doc_commands
+import doc_down
+import doc_intros_redefs
+
+# Generate content of `ReadmePage`.
+#
+# This phase extracts the structure of a `ReadmePage` from the markdown content
+# of the README file.
+# It also resolves Wikilinks and commands.
+class ReadmePhase
+ super DocPhase
+
+ redef fun apply do
+ for page in doc.pages.values do page.build_content(self, doc)
+ end
+
+ # Display a warning about something wrong in the readme file.
+ fun warning(location: nullable MDLocation, page: ReadmePage, message: String) do
+ var loc = null
+ if location != null then
+ loc = location.to_location(page.mentity.mdoc.location.file)
+ end
+ ctx.warning(loc, "readme-warning", message)
+ end
+end
+
+redef class DocPage
+ # Build content of `ReadmePage` based on the content of the readme file.
+ private fun build_content(v: ReadmePhase, doc: DocModel) do end
+end
+
+redef class ReadmePage
+ redef fun build_content(v, doc) do
+ if mentity.mdoc == null then
+ v.warning(null, self, "Empty README for group `{mentity}`")
+ return
+ end
+ var proc = new MarkdownProcessor
+ proc.emitter = new ReadmeMdEmitter(proc, self, v)
+ proc.emitter.decorator = new ReadmeDecorator
+ var md = mentity.mdoc.content.join("\n")
+ proc.process(md)
+ end
+end
+
+# Markdown emitter used to produce the `ReadmeArticle`.
+class ReadmeMdEmitter
+ super MarkdownEmitter
+
+ # Readme page being decorated.
+ var page: ReadmePage
+
+ # Phase used to access doc model and toolcontext.
+ var phase: ReadmePhase
+
+ init do open_article
+
+ # Push the article template on top of the buffer stack.
+ #
+ # Subsequent markdown writting will be done in the article template.
+ #
+ # See `ReadmeArticle::md`.
+ private fun push_article(article: ReadmeArticle) do
+ buffer_stack.add article.md
+ end
+
+ private var context = new Array[DocComposite]
+
+ # Creates a new ReadmeSection in `self.toc.page`.
+ #
+ # Called from `add_headline`.
+ private fun open_section(lvl: Int, title: String) do
+ var section = new ReadmeSection(title.escape_to_c, title, lvl, processor)
+ if current_section == null then
+ page.root.add_child(section)
+ else
+ current_section.add_child(section)
+ end
+ current_section = section
+ context.add section
+ end
+ private var current_section: nullable ReadmeSection is noinit
+
+ # Close the current section.
+ #
+ # Ensure `context.last isa ReadmeSection`.
+ private fun close_section do
+ assert context.last isa ReadmeSection
+ context.pop
+ if context.is_empty then
+ current_section = null
+ else
+ current_section = context.last.as(ReadmeSection)
+ end
+ end
+
+ # Add an article at current location.
+ #
+ # This closes the current article, inserts `article` then opens a new article.
+ private fun add_article(article: DocArticle) do
+ close_article
+ if current_section == null then
+ page.root.add_child(article)
+ else
+ current_section.add_child(article)
+ end
+ open_article
+ end
+
+ # Creates a new ReadmeArticle in `self.toc.page`.
+ #
+ # Called from `add_headline`.
+ private fun open_article do
+ var section: DocComposite = page.root
+ if current_section != null then section = current_section.as(not null)
+ var article = new ReadmeArticle("mdarticle-{section.children.length}", null, processor)
+ section.add_child(article)
+ context.add article
+ push_article article
+ end
+
+ # Close the current article.
+ #
+ # Ensure `context.last isa ReadmeArticle`.
+ fun close_article do
+ assert context.last isa ReadmeArticle
+ context.pop
+ pop_buffer
+ end
+end
+
+# MarkdownDecorator used to decorated the Readme file with links between doc entities.
+class ReadmeDecorator
+ super MdDecorator
+
+ redef type EMITTER: ReadmeMdEmitter
+
+ redef fun add_headline(v, block) do
+ var txt = block.block.first_line.value
+ var lvl = block.depth
+ if not v.context.is_empty then
+ v.close_article
+ while v.current_section != null do
+ if v.current_section.depth < lvl then break
+ v.close_section
+ end
+ end
+ v.open_section(lvl, txt)
+ v.open_article
+ end
+
+ redef fun add_wikilink(v, token) do
+ var link = token.link.to_s
+ var cmd = new DocCommand(link)
+ if cmd isa UnknownCommand then
+ # search MEntities by name
+ var res = v.phase.doc.mentities_by_name(link.to_s)
+ # no match, print warning and display wikilink as is
+ if res.is_empty then
+ v.phase.warning(token.location, v.page, "Link to unknown entity `{link}`")
+ super
+ else
+ add_mentity_link(v, res.first, token.name, token.comment)
+ end
+ return
+ end
+ cmd.render(v, token)
+ end
+
+ # Renders a link to a mentity.
+ private fun add_mentity_link(v: EMITTER, mentity: MEntity, name, comment: nullable Text) do
+ # TODO real link
+ var link = mentity.full_name
+ if name == null then name = mentity.name
+ if comment == null and mentity.mdoc != null then
+ comment = mentity.mdoc.synopsis
+ end
+ add_link(v, link, name, comment)
+ end
+end
+
+redef interface DocCommand
+
+ # Render the content of the doc command.
+ fun render(v: ReadmeMdEmitter, token: TokenWikiLink) is abstract
+
+ # Search `doc` model for mentities match `string`.
+ fun search_model(doc: DocModel, string: String): Array[MEntity] do
+ var res
+ if string.has("::") then
+ res = doc.mentities_by_namespace(string).to_a
+ else
+ res = doc.mentities_by_name(string).to_a
+ end
+ return res
+ end
+end
+
+redef class ArticleCommand
+ redef fun render(v, token) do
+ var string = args.first
+ var res = search_model(v.phase.doc, string)
+ res = filter_results(res)
+ if res.is_empty then
+ v.phase.warning(
+ token.location, v.page,
+ "Try to include documentation of unknown entity `{args.first}`")
+ return
+ end
+ if res.length > 1 then
+ v.phase.warning(token.location, v.page, "conflicting article for `{args.first}` (choices : {res.join(", ")})")
+ end
+ v.add_article new DocumentationArticle("readme", "Readme", res.first)
+ end
+
+ private fun filter_results(res: Array[MEntity]): Array[MEntity] do
+ var out = new Array[MEntity]
+ for e in res do
+ if e isa MProject then continue
+ if e isa MGroup then continue
+ out.add e
+ end
+ return out
+ end
+end
+
+redef class ListCommand
+ redef fun render(v, token) do
+ var string = args.first
+ var res = search_model(v.phase.doc, string)
+ if res.is_empty then
+ v.phase.warning(token.location, v.page, "include article for unknown entity `{args.first}`")
+ return
+ end
+ if res.length > 1 then
+ v.phase.warning(token.location, v.page, "conflicting article for `{args.first}` (choices : {res.join(", ")})")
+ end
+ var mentity = res.first
+ if mentity isa MModule then
+ v.add_article new MEntitiesListArticle("Classes", mentity.mclassdefs)
+ else if mentity isa MClass then
+ var mprops = mentity.collect_intro_mproperties(public_visibility)
+ v.add_article new MEntitiesListArticle("Methods", mprops.to_a)
+ else if mentity isa MClassDef then
+ v.add_article new MEntitiesListArticle("Methods", mentity.mpropdefs)
+ end
+ end
+end
+
+
+# A section found in a README.
+#
+# Produced by markdown headlines like `## Section 1.1`.
+class ReadmeSection
+ super DocSection
+
+ # The depth is based on the markdown headline depth.
+ redef var depth
+
+ # Markdown processor used to process the section title.
+ var markdown_processor: MarkdownProcessor
+
+ redef var is_hidden = false
+end
+
+# An article found in a README file.
+#
+# Basically, everything found in a README that is not a headline.
+class ReadmeArticle
+ super DocArticle
+
+ # Markdown processor used to process the article content.
+ var markdown_processor: MarkdownProcessor
+
+ # Markdown content of this article extracted from the README file.
+ var md = new FlatBuffer
+
+ redef fun is_hidden do return super and md.trim.is_empty
+end
+
+# Documentation Article to introduce from the directive `doc: Something`.
+#
+# TODO merge with DefinitionArticle once the html is simplified
+class DocumentationArticle
+ super MEntityArticle
+
+ redef var is_hidden = false
+end
+
+redef class MDLocation
+ # Translate a Markdown location in Nit location.
+ private fun to_location(file: nullable SourceFile): Location do
+ return new Location(file, line_start, line_end, column_start, column_end)
+ end
+end
var tpl = new Link(nitdoc_url, html_name)
var mdoc = mdoc_or_fallback
if mdoc != null then
- tpl.title = mdoc.short_comment
+ tpl.title = mdoc.synopsis
end
return tpl
end
var tpl = new Link("#{nitdoc_id}", html_name)
var mdoc = mdoc_or_fallback
if mdoc != null then
- tpl.title = mdoc.short_comment
+ tpl.title = mdoc.synopsis
end
return tpl
end
# * MPropdef: `mclassdef:mpropdef`
fun html_namespace: Template is abstract
- # Returns the comment of this MEntity formatted as HTML.
- var html_comment: nullable Writable is lazy do
+ # Returns the synopsis and the comment of this MEntity formatted as HTML.
+ var html_documentation: nullable Writable is lazy do
+ var mdoc = mdoc_or_fallback
+ if mdoc == null then return null
+ return mdoc.html_documentation
+ end
+
+ # Returns the synopsis of this MEntity formatted as HTML.
+ var html_synopsis: nullable Writable is lazy do
var mdoc = mdoc_or_fallback
if mdoc == null then return null
- return mdoc.tpl_comment
+ return mdoc.html_synopsis
end
- # Returns the comment of this MEntity formatted as HTML.
- var html_short_comment: nullable Writable is lazy do
+ # Returns the the comment without the synopsis formatted as HTML.
+ var html_comment: nullable Writable is lazy do
var mdoc = mdoc_or_fallback
if mdoc == null then return null
- return mdoc.tpl_short_comment
+ return mdoc.html_comment
end
# Icon that will be displayed before the title
var tpl = new Template
tpl.add new DocHTMLLabel.with_classes(css_classes)
tpl.add html_link
- var comment = html_short_comment
+ var comment = html_synopsis
if comment != null then
tpl.add ": "
tpl.add comment
var lnk = html_link
var tpl = new Template
tpl.add new Link.with_title("#{nitdoc_id}.concern", lnk.text, lnk.title)
- var comment = html_short_comment
+ var comment = html_synopsis
if comment != null then
tpl.add ": "
tpl.add comment
import doc_phases::doc_graphs
import doc_phases::doc_intros_redefs
import doc_phases::doc_lin
+import doc_phases::doc_readme
+intrude import doc_down
# Renders the page as HTML.
redef class DocPage
if html_title != null then
var header = new Header(hlvl, html_title.write_to_string)
header.css_classes.add "signature"
- if hlvl == 2 then header.css_classes.add "well well-sm"
addn header
end
if html_subtitle != null then
redef fun render_body do
var tabs = new DocTabs("{html_id}.tabs", "")
- var comment = mentity.html_comment
+ var comment = mentity.html_documentation
+ if mentity isa MProject then
+ comment = mentity.html_synopsis
+ end
if comment != null then
tabs.add_panel new DocTabPanel("{html_tab_id}-comment", "Comment", comment)
end
var tabs = new DocTabs("{html_id}.tabs", "")
if not is_no_body then
var comment
- if is_short_comment then
- comment = mentity.html_short_comment
+ if is_short_comment or mentity isa MProject then
+ comment = mentity.html_synopsis
else
- comment = mentity.html_comment
+ comment = mentity.html_documentation
end
if comment != null then
tabs.add_panel new DocTabPanel("{html_tab_id}-comment", "Comment", comment)
if not mentity isa MPropDef then continue # TODO handle all mentities
var tpl = new Template
tpl.add mentity.mclassdef.html_namespace
- var comment = mentity.mclassdef.html_short_comment
+ var comment = mentity.mclassdef.html_synopsis
if comment != null then
tpl.add ": "
tpl.add comment
addn "</div>"
end
end
+
+redef class ReadmeSection
+ redef var html_id is lazy do
+ return markdown_processor.emitter.decorator.strip_id(html_title.as(not null).to_s)
+ end
+
+ redef var html_title is lazy do
+ return markdown_processor.process(title.as(not null))
+ end
+end
+
+redef class ReadmeArticle
+ redef var html_id = ""
+ redef var html_title = null
+ redef var is_toc_hidden = true
+
+ redef fun render_body do
+ add markdown_processor.process(md.trim.write_to_string)
+ end
+end
+
+redef class DocumentationArticle
+ redef var html_title is lazy do
+ var synopsis = mentity.html_synopsis
+ if synopsis == null then return mentity.html_link
+ return "{mentity.html_link.write_to_string} – {synopsis.write_to_string}"
+ end
+
+ redef var html_subtitle is lazy do return null
+ redef var html_toc_title is lazy do return mentity.html_name
+ redef var is_toc_hidden is lazy do return depth > 3
+
+ redef fun render_body do
+ var tabs = new DocTabs("{html_id}.tabs", "")
+ var comment = mentity.html_comment
+ if comment != null then
+ tabs.add_panel new DocTabPanel("{html_tab_id}-comment", "Comment", comment)
+ end
+ for child in children do
+ if child.is_hidden then continue
+ var title = child.html_toc_title or else child.toc_title or else ""
+ tabs.add_panel new DocTabPanel(child.html_tab_id, title, child)
+ end
+ addn tabs
+ end
+end
do
if not nclassdef isa AStdClassdef then return
- # Is there a declaration on the classdef or the module?
- var serialize = nclassdef.is_serialize
-
- if not serialize and not nclassdef.is_noserialize then
- # Is the module marked serialize?
- serialize = nclassdef.parent.as(AModule).is_serialize
- end
+ var serialize_by_default = nclassdef.how_serialize
- var per_attribute = false
- if not serialize then
- # Is there an attribute marked serialize?
- for npropdef in nclassdef.n_propdefs do
- if npropdef.is_serialize then
- serialize = true
- per_attribute = true
- break
- end
- end
- end
+ if serialize_by_default != null then
- if serialize then
# Add `super Serializable`
var sc = toolcontext.parse_superclass("Serializable")
sc.location = nclassdef.location
nclassdef.n_propdefs.add sc
# Add services
+ var per_attribute = not serialize_by_default
generate_serialization_method(nclassdef, per_attribute)
generate_deserialization_init(nclassdef, per_attribute)
end
# collect all classes
var auto_serializable_nclassdefs = new Array[AStdClassdef]
for nclassdef in nmodule.n_classdefs do
- if nclassdef isa AStdClassdef and nclassdef.is_serialize then
+ if nclassdef isa AStdClassdef and nclassdef.how_serialize != null then
auto_serializable_nclassdefs.add nclassdef
end
end
for nclassdef in nclassdefs do
var name = nclassdef.n_id.text
if nclassdef.n_formaldefs.is_empty and
- not nclassdef.n_classkind isa AAbstractClasskind then
+ nclassdef.n_classkind isa AConcreteClasskind then
code.add " if name == \"{name}\" then return new {name}.from_deserializer(self)"
end
return null
end
+
+ # Is this classed marked `serialize`? in part or fully?
+ #
+ # This method returns 3 possible values:
+ # * `null`, this class is not to be serialized.
+ # * `true`, the attributes of this class are to be serialized by default.
+ # * `false`, the attributes of this class are to be serialized on demand only.
+ fun how_serialize: nullable Bool
+ do
+ # Is there a declaration on the classdef or the module?
+ var serialize = is_serialize
+
+ if not serialize and not is_noserialize then
+ # Is the module marked serialize?
+ serialize = parent.as(AModule).is_serialize
+ end
+
+ if serialize then return true
+
+ if not serialize then
+ # Is there an attribute marked serialize?
+ for npropdef in n_propdefs do
+ if npropdef.is_serialize then
+ return false
+ end
+ end
+ end
+
+ return null
+ end
end
# Load a markdown file as a documentation object
fun load_markdown(filepath: String): MDoc
do
- var mdoc = new MDoc(new Location(new SourceFile.from_string(filepath, ""),0,0,0,0))
var s = new FileReader.open(filepath)
+ var lines = new Array[String]
+ var line_starts = new Array[Int]
+ var len = 1
while not s.eof do
- mdoc.content.add(s.read_line)
- end
+ var line = s.read_line
+ lines.add(line)
+ line_starts.add(len)
+ len += line.length + 1
+ end
+ s.close
+ var source = new SourceFile.from_string(filepath, lines.join("\n"))
+ source.line_starts.add_all line_starts
+ var mdoc = new MDoc(new Location(source, 1, lines.length, 0, 0))
+ mdoc.content.add_all(lines)
return mdoc
end
line_starts[0] = 0
end
- # Position of each line start
+ # Offset of each line start in the content `string`.
+ #
+ # Used for fast access to each line when rendering parts of the `string`.
var line_starts = new Array[Int]
end
new InheritanceListsPhase(toolcontext, doc),
new IntroRedefListPhase(toolcontext, doc),
new LinListPhase(toolcontext, doc),
- new GraphPhase(toolcontext, doc): DocPhase]
+ new GraphPhase(toolcontext, doc),
+ new ReadmePhase(toolcontext, doc): DocPhase]
if not toolcontext.opt_test.value then
phases.add new RenderHTMLPhase(toolcontext, doc)
--- /dev/null
+# Src:
+<A: true a 0.123 1234 asdf false p4ssw0rd>
+# Dst:
+<A: true a 0.123 1234 asdf false p4ssw0rd>
+
+# Src:
+<B: <A: false b 123.123 2345 hjkl false p4ssw0rd> 1111 qwer>
+# Dst:
+<B: <A: false b 123.123 2345 hjkl false p4ssw0rd> 1111 qwer>
+
+# Src:
+<C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl false p4ssw0rd> 1111 qwer>>
+# Dst:
+<C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl false p4ssw0rd> 1111 qwer>>
+
+# Src:
+<D: <B: <A: false b 123.123 2345 new line ->
+<- false p4ssw0rd> 1111 f"\r\/> true>
+# Dst:
+<D: <B: <A: false b 123.123 2345 new line ->
+<- false p4ssw0rd> 1111 f"\r\/> true>
+
+Deserialization Error: Doesn't know how to deserialize class "Array", Deserialization Error: Wrong type on `E::a` expected `PlaceHolderTypeWhichShouldNotExist`, got `null`, Deserialization Error: Doesn't know how to deserialize class "Array", Deserialization Error: Wrong type on `E::b` expected `PlaceHolderTypeWhichShouldNotExist`, got `null`
+# Src:
+<E: a: hello, 1234, 123.4; b: hella, 2345, 234.5>
+# Dst:
+<E: a: hello, 1234, 123.4; b: hella, 2345, 234.5>
+
+Deserialization Error: Doesn't know how to deserialize class "F"
+Deserialization Error: Doesn't know how to deserialize class "F"
+Deserialization Error: Doesn't know how to deserialize class "HashSet", Deserialization Error: Wrong type on `G::hs` expected `PlaceHolderTypeWhichShouldNotExist`, got `null`, Deserialization Error: Doesn't know how to deserialize class "ArraySet", Deserialization Error: Wrong type on `G::s` expected `Set[String]`, got `null`, Deserialization Error: Doesn't know how to deserialize class "HashMap", Deserialization Error: Wrong type on `G::hm` expected `PlaceHolderTypeWhichShouldNotExist`, got `null`, Deserialization Error: Doesn't know how to deserialize class "ArrayMap", Deserialization Error: Wrong type on `G::am` expected `PlaceHolderTypeWhichShouldNotExist`, got `null`
+# Src:
+<G: hs: -1, 0; s: one, two; hm: one. 1, two. 2; am: three. 3, four. 4>
+# Dst:
+<G: hs: ; s: ; hm: ; am: >
+
-Runtime error: Aborted (../lib/serialization/serialization.nit:120)
# Nit:
<A: true a 0.123 1234 asdf false p4ssw0rd>
<D: <B: <A: false b 123.123 2345 new line ->
<- false p4ssw0rd> 1111 f"\r\/> true>
-Error: doesn't know how to deserialize class "Array"
+# Nit:
+<E: a: hello, 1234, 123.4; b: hella, 2345, 234.5>
+
+# Json:
+{"__kind": "obj", "__id": 0, "__class": "E", "a": {"__kind": "obj", "__id": 1, "__class": "Array", "__items": ["hello", 1234, 123.4]}, "b": {"__kind": "obj", "__id": 2, "__class": "Array", "__items": ["hella", 2345, 234.5]}}
+
+# Back in Nit:
+<E: a: hello, 1234, 123.4; b: hella, 2345, 234.5>
+
+# Nit:
+<E: 2222>
+
+# Json:
+{"__kind": "obj", "__id": 0, "__class": "F", "n": 2222}
+
+# Back in Nit:
+null
+
+# Nit:
+<E: 33.33>
+
+# Json:
+{"__kind": "obj", "__id": 0, "__class": "F", "n": 33.33}
+
+# Back in Nit:
+null
+
+# Nit:
+<G: hs: -1, 0; s: one, two; hm: one. 1, two. 2; am: three. 3, four. 4>
+
+# Json:
+{"__kind": "obj", "__id": 0, "__class": "G", "hs": {"__kind": "obj", "__id": 1, "__class": "HashSet", "__items": [-1, 0]}, "s": {"__kind": "obj", "__id": 2, "__class": "ArraySet", "__items": ["one", "two"]}, "hm": {"__kind": "obj", "__id": 3, "__class": "HashMap", "__length": 2, "__keys": ["one", "two"], "__values": [1, 2]}, "am": {"__kind": "obj", "__id": 4, "__class": "ArrayMap", "__length": 2, "__keys": ["three", "four"], "__values": ["3", "4"]}}
+
+# Back in Nit:
+<G: hs: ; s: ; hm: ; am: >
+
+Empty README for group `module_1` (readme-warning)
+Empty README for group `module_0` (readme-warning)
+Errors: 0. Warnings: 2.
class_module_95d0-__Int.html
class_module_95d0-__Object.html
class_module_95d0-__Sys.html
+Empty README for group `base_attr_nullable` (readme-warning)
+Errors: 0. Warnings: 1.
class_base_attr_nullable-__Bar.html
class_base_attr_nullable-__Bool.html
class_base_attr_nullable-__Foo.html
+Empty README for group `base_attr_nullable` (readme-warning)
+Errors: 0. Warnings: 1.
class_base_attr_nullable-__Bar.html
class_base_attr_nullable-__Bool.html
class_base_attr_nullable-__Foo.html
## projects.section
### test_prog.definition
+ReadmePage test_prog
+ # mdarticle-0
+
+ReadmePage game
+ # mdarticle-0
+
+ReadmePage platform
+ # mdarticle-0
+
+ReadmePage rpg
+ # mdarticle-0
+
SearchPage Index
# index.article
#### test_prog__rpg__rpg.imports
#### test_prog__rpg__rpg.clients
-Generated 81 pages
+Generated 85 pages
list:
- MPropertyPage: 47 (58.02%)
- MClassPage: 20 (24.69%)
- MModulePage: 8 (9.87%)
- MGroupPage: 4 (4.93%)
- SearchPage: 1 (1.23%)
- OverviewPage: 1 (1.23%)
+ MPropertyPage: 47 (55.29%)
+ MClassPage: 20 (23.52%)
+ MModulePage: 8 (9.41%)
+ MGroupPage: 4 (4.70%)
+ ReadmePage: 4 (4.70%)
+ SearchPage: 1 (1.17%)
+ OverviewPage: 1 (1.17%)
Found 160 mentities
list:
MMethodDef: 57 (35.62%)
-test_nitunit3/README.md: Error: there is a block of invalid Nit code, thus not considered a nitunit. To suppress this warning, enclose the block with a fence tagged `nitish` or `raw` (see `man nitdoc`). At 1,2--4: Syntax Error: unexpected malformed character '\]..
-test_nitunit3/README.md: ERROR: nitunit.test_nitunit3.<group> (in .nitunit/test_nitunit3-0.nit): Runtime error: Assert failed (.nitunit/test_nitunit3-0.nit:7)
+test_nitunit3/README.md:1,0--13,0: Error: there is a block of invalid Nit code, thus not considered a nitunit. To suppress this warning, enclose the block with a fence tagged `nitish` or `raw` (see `man nitdoc`). At 1,2--4: Syntax Error: unexpected malformed character '\]..
+test_nitunit3/README.md:1,0--13,0: ERROR: nitunit.test_nitunit3.<group> (in .nitunit/test_nitunit3-0.nit): Runtime error: Assert failed (.nitunit/test_nitunit3-0.nit:7)
DocUnits:
Entities: 2; Documented ones: 2; With nitunits: 3; Failures: 2
TestSuites:
No test cases found
Class suites: 0; Test Cases: 0; Failures: 0
-<testsuites><testsuite package="test_nitunit3"><testcase classname="nitunit.test_nitunit3" name="<group>"><failure message="test_nitunit3/README.md: Invalid block of code. At 1,2--4: Syntax Error: unexpected malformed character '\].."></failure><system-err></system-err><system-out>assert false
+<testsuites><testsuite package="test_nitunit3"><testcase classname="nitunit.test_nitunit3" name="<group>"><failure message="test_nitunit3/README.md:1,0--13,0: Invalid block of code. At 1,2--4: Syntax Error: unexpected malformed character '\].."></failure><system-err></system-err><system-out>assert false
assert true
</system-out><error message="Runtime error: Assert failed (.nitunit/test_nitunit3-0.nit:7)
"></error></testcase></testsuite><testsuite package="test_nitunit3"><testcase classname="nitunit.test_nitunit3.<module>" name="<module>"><system-err></system-err><system-out>assert true
-test_nitunit_md.md: ERROR: nitunit.<file>.test_nitunit_md.md (in .nitunit/file-0.nit): Runtime error: Assert failed (.nitunit/file-0.nit:8)
+test_nitunit_md.md:1,0--15,0: ERROR: nitunit.<file>.test_nitunit_md.md:1,0--15,0 (in .nitunit/file-0.nit): Runtime error: Assert failed (.nitunit/file-0.nit:8)
DocUnits:
Entities: 1; Documented ones: 1; With nitunits: 1; Failures: 1
TestSuites:
No test cases found
Class suites: 0; Test Cases: 0; Failures: 0
-<testsuites><testsuite package="test_nitunit_md.md"><testcase classname="nitunit.<file>" name="test_nitunit_md.md"><system-err></system-err><system-out>var a = 1
+<testsuites><testsuite package="test_nitunit_md.md:1,0--15,0"><testcase classname="nitunit.<file>" name="test_nitunit_md.md:1,0--15,0"><system-err></system-err><system-out>var a = 1
assert 1 == 1
assert false
</system-out><error message="Runtime error: Assert failed (.nitunit/file-0.nit:8)
--- /dev/null
+# Src:
+<A: true a 0.123 1234 asdf false p4ssw0rd>
+# Dst:
+<A: true a 0.123 1234 asdf false p4ssw0rd>
+
+# Src:
+<B: <A: false b 123.123 2345 hjkl false p4ssw0rd> 1111 qwer>
+# Dst:
+<B: <A: false b 123.123 2345 hjkl false p4ssw0rd> 1111 qwer>
+
+# Src:
+<C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl false p4ssw0rd> 1111 qwer>>
+# Dst:
+<C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl false p4ssw0rd> 1111 qwer>>
+
+# Src:
+<D: <B: <A: false b 123.123 2345 new line ->
+<- false p4ssw0rd> 1111 f"\r\/> true>
+# Dst:
+<D: <B: <A: false b 123.123 2345 new line ->
+<- false p4ssw0rd> 1111 f"\r\/> true>
+
--- /dev/null
+# Src:
+<A: true a 0.123 1234 asdf false p4ssw0rd>
+# Dst:
+<A: true a 0.123 1234 asdf false p4ssw0rd>
+
+# Src:
+<B: <A: false b 123.123 2345 hjkl false p4ssw0rd> 1111 qwer>
+# Dst:
+<B: <A: false b 123.123 2345 hjkl false p4ssw0rd> 1111 qwer>
+
+# Src:
+<C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl false p4ssw0rd> 1111 qwer>>
+# Dst:
+<C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl false p4ssw0rd> 1111 qwer>>
+
+# Src:
+<D: <B: <A: false b 123.123 2345 new line ->
+<- false p4ssw0rd> 1111 f"\r\/> true>
+# Dst:
+<D: <B: <A: false b 123.123 2345 new line ->
+<- false p4ssw0rd> 1111 f"\r\/> true>
+
+# Src:
+<E: a: hello, 1234, 123.4; b: hella, 2345, 234.5>
+# Dst:
+<E: a: hello, 1234, 123.4; b: hella, 2345, 234.5>
+
+# Src:
+<E: 2222>
+# Dst:
+<E: 2222>
+
+# Src:
+<E: 33.33>
+# Dst:
+<E: 33.33>
+
+# Src:
+<G: hs: -1, 0; s: one, two; hm: one. 1, two. 2; am: three. 3, four. 4>
+# Dst:
+<G: hs: -1, 0; s: one, two; hm: one. 1, two. 2; am: three. 3, four. 4>
+
--- /dev/null
+Deserialization Error: Doesn't know how to deserialize class "NoSerializeClass"
--- /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 test_deserialization
+import binary::serialization
+#alt1# import test_deserialization_serial
+
+class NoSerializeClass
+ super Serializable
+
+ var some_attribute: String
+end
+
+var entities = new TestEntities
+
+var tests = entities.without_generics#alt1##alt2#
+#alt1#var tests = entities.with_generics
+#alt2#var tests = [new NoSerializeClass("will not deserialize")]
+
+var dir = "out/test_binary_deserialization"
+if not dir.file_exists then dir.mkdir
+
+var path = dir / "alt0"#alt1##alt2#
+#alt1#var path = dir / "alt1"
+#alt2#var path = dir / "alt2"
+
+var writer = new FileWriter.open(path)
+var serializer = new BinarySerializer(writer)
+for o in tests do
+ serializer.serialize o
+end
+writer.close
+
+var reader = new FileReader.open(path)
+var deserializer = new BinaryDeserializer(reader)
+for o in tests do
+ var dst = deserializer.deserialize
+
+ if deserializer.errors.not_empty then
+ print deserializer.errors.join(", ")
+ end
+
+ if dst != null then
+ assert o.is_same_type(dst)
+
+ print "# Src:\n{o}"
+ print "# Dst:\n{dst}\n"
+ end
+end
+reader.close