Merge: Doc down
authorJean Privat <jean@pryen.org>
Tue, 16 Dec 2014 04:16:20 +0000 (23:16 -0500)
committerJean Privat <jean@pryen.org>
Tue, 16 Dec 2014 04:16:20 +0000 (23:16 -0500)
Please disregard commits before 0a57951, they belong to #680.

This pull request remove the module `src/markdown` and use the newly introduced `markdown` parser instead.

## Major changes considering Nit comments

### Synopsys line

Synopsys line must be followed by a blank line for multilines comments according to the Markdown paragraph syntax.

Example before:

    # foo doc
    fun foo do end

    # bar doc
    # more doc or doc unit
    fun bar do end

After:

    # foo doc
    fun foo do end

    # bar doc
    #
    # more doc or doc unit
    fun bar do end

This is boring, I know, but it allows Nit comment to be properly parsed by most of the markdown parsers.
Also, most of multilne comments are already corrects.

### Code blocks

Code blocks (and doc unit) are now parsed following the correct rule: every line starting with at least 4 spaces is a code block (if not in a nested list).

Example before:

    #    assert true # was not recognized by nitdoc and nitunit
    fun foo do end

After:

    #    assert true # works in nitunit and nitdoc
    fun foo do end

Commit 0b275eb performs those changes on lib/standard as example.

## What's new?

### Full markdown syntax available

All the original markdown syntax is now recognized by the parser (link definitions, blockquotes, nested elements, escape...).
I'm working to add a Github flavoured mode and maby a custom mode for Nitdoc.

### Nitdoc annotations

Some annotations are now recognized in the comments. They do nothing but allow nitdoc to add some style.

Annotation list:

* DEPRECATED
* SEE
* REQUIRE, ENSURE
* FIXME, TODO, NOTE

Syntax:

    # Some doc
    #
    # DEPRECATED: use `bar` instead.
    fun foo do end

## TODO

Some tests maybe broken in lib/ and src/ because of the synopsys change, I will update this PR with fixes.

We now have everything we need to work on a Nit flavoured markdown allowing to control nitdoc generation. This is the next step.

Pull-Request: #683
Reviewed-by: Lucas Bajolet <r4pass@hotmail.com>
Reviewed-by: Jean Privat <jean@pryen.org>

1  2 
lib/markdown/test_markdown.nit
lib/standard/file.nit
lib/standard/kernel.nit
lib/standard/string.nit
src/doc/doc_model.nit

@@@ -1013,6 -1013,12 +1013,12 @@@ break</a> with a line-ending space.</p
                assert exp == res
        end
  
+       fun test_escape_bad_html do
+                       var test = "-1 if < , +1 if > and 0 otherwise"
+                       var exp = "<p>-1 if &lt; , +1 if > and 0 otherwise</p>\n"
+               var res = test.md_to_html.write_to_string
+               assert exp == res
+       end
  
        fun test_daring_encoding do
                var test = """
@@@ -2522,6 -2528,8 +2528,6 @@@ class TestLin
  
        var subject: MDLine
  
 -      init do end
 -
        fun test_is_empty do
                subject = new MDLine("")
                assert subject.is_empty
diff --combined lib/standard/file.nit
@@@ -6,7 -6,7 +6,7 @@@
  #
  # This file is free software, which comes along with NIT.  This software is
  # distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 -# without  even  the implied warranty of  MERCHANTABILITY or  FITNESS FOR A 
 +# without  even  the implied warranty of  MERCHANTABILITY or  FITNESS FOR A
  # PARTICULAR PURPOSE.  You can modify it is you want,  provided this header
  # is kept unaltered, and a notification of the changes is added.
  # You  are  allowed  to  redistribute it and sell it, alone or is a part of
@@@ -19,7 -19,6 +19,7 @@@ intrude import strea
  intrude import ropes
  import string_search
  import time
 +import gc
  
  in "C Header" `{
        #include <dirent.h>
@@@ -28,8 -27,6 +28,8 @@@
        #include <sys/stat.h>
        #include <unistd.h>
        #include <stdio.h>
 +      #include <poll.h>
 +      #include <errno.h>
  `}
  
  # File Abstract Stream
@@@ -42,25 -39,10 +42,25 @@@ abstract class FStrea
        private var file: nullable NativeFile = null
  
        # The status of a file. see POSIX stat(2).
 -      fun file_stat: FileStat do return _file.file_stat
 +      fun file_stat: NativeFileStat do return _file.file_stat
  
        # File descriptor of this file
        fun fd: Int do return _file.fileno
 +
 +      # Sets the buffering mode for the current FStream
 +      #
 +      # If the buf_size is <= 0, its value will be 512 by default
 +      #
 +      # The mode is any of the buffer_mode enumeration in `Sys`:
 +      #       - buffer_mode_full
 +      #       - buffer_mode_line
 +      #       - buffer_mode_none
 +      fun set_buffering_mode(buf_size, mode: Int) do
 +              if buf_size <= 0 then buf_size = 512
 +              if _file.set_buffering_type(buf_size, mode) != 0 then
 +                      last_error = new IOError("Error while changing buffering type for FStream, returned error {sys.errno.strerror}")
 +              end
 +      end
  end
  
  # File input stream
@@@ -74,14 -56,8 +74,14 @@@ class IFStrea
        # The original path is reused, therefore the reopened file can be a different file.
        fun reopen
        do
 -              if not eof then close
 +              if not eof and not _file.address_is_null then close
 +              last_error = null
                _file = new NativeFile.io_open_read(path.to_cstring)
 +              if _file.address_is_null then
 +                      last_error = new IOError("Error: Opening file at '{path.as(not null)}' failed with '{sys.errno.strerror}'")
 +                      end_reached = true
 +                      return
 +              end
                end_reached = false
                _buffer_pos = 0
                _buffer.clear
@@@ -89,8 -65,7 +89,8 @@@
  
        redef fun close
        do
 -              _file.io_close
 +              if _file.address_is_null then return
 +              var i = _file.io_close
                _buffer.clear
                end_reached = true
        end
                _buffer.length = nb
                _buffer_pos = 0
        end
 -      
 +
        # End of file?
        redef var end_reached: Bool = false
  
                self.path = path
                prepare_buffer(10)
                _file = new NativeFile.io_open_read(path.to_cstring)
 -              assert not _file.address_is_null else
 -                      print "Error: Opening file at '{path}' failed with '{sys.errno.strerror}'"
 +              if _file.address_is_null then
 +                      last_error = new IOError("Error: Opening file at '{path}' failed with '{sys.errno.strerror}'")
 +                      end_reached = true
                end
        end
  
 +      init from_fd(fd: Int) do
 +              self.path = ""
 +              prepare_buffer(1)
 +              _file = fd.fd_to_stream(read_only)
 +              if _file.address_is_null then
 +                      last_error = new IOError("Error: Converting fd {fd} to stream failed with '{sys.errno.strerror}'")
 +                      end_reached = true
 +              end
 +      end
  end
  
  # File output stream
  class OFStream
        super FStream
        super OStream
 -      
 +
        redef fun write(s)
        do
 -              assert _is_writable
 +              if last_error != null then return
 +              if not _is_writable then
 +                      last_error = new IOError("Cannot write to non-writable stream")
 +                      return
 +              end
                if s isa FlatText then
                        write_native(s.to_cstring, s.length)
                else
                        for i in s.substrings do write_native(i.to_cstring, i.length)
                end
 +              _file.flush
        end
  
        redef fun close
        do
 -              _file.io_close
 +              if _file.address_is_null then
 +                      if last_error != null then return
 +                      last_error = new IOError("Cannot close unopened write stream")
 +                      _is_writable = false
 +                      return
 +              end
 +              var i = _file.io_close
 +              if i != 0 then
 +                      last_error = new IOError("Close failed due to error {sys.errno.strerror}")
 +              end
                _is_writable = false
        end
 -
        redef var is_writable = false
 -      
 +
        # Write `len` bytes from `native`.
        private fun write_native(native: NativeString, len: Int)
        do
 -              assert _is_writable
 +              if last_error != null then return
 +              if not _is_writable then
 +                      last_error = new IOError("Cannot write to non-writable stream")
 +                      return
 +              end
 +              if _file.address_is_null then
 +                      last_error = new IOError("Writing on a null stream")
 +                      _is_writable = false
 +                      return
 +              end
                var err = _file.io_write(native, len)
                if err != len then
                        # Big problem
 -                      printn("Problem in writing : ", err, " ", len, "\n")
 +                      last_error = new IOError("Problem in writing : {err} {len} \n")
                end
        end
 -      
 +
        # Open the file at `path` for writing.
        init open(path: String)
        do
                _file = new NativeFile.io_open_write(path.to_cstring)
 -              assert not _file.address_is_null else
 -                      print "Error: Opening file at '{path}' failed with '{sys.errno.strerror}'"
 -              end
                self.path = path
                _is_writable = true
 +              if _file.address_is_null then
 +                      last_error = new IOError("Error: Opening file at '{path}' failed with '{sys.errno.strerror}'")
 +                      is_writable = false
 +              end
 +      end
 +
 +      # Creates a new File stream from a file descriptor
 +      init from_fd(fd: Int) do
 +              self.path = ""
 +              _file = fd.fd_to_stream(wipe_write)
 +              _is_writable = true
 +               if _file.address_is_null then
 +                       last_error = new IOError("Error: Opening stream from file descriptor {fd} failed with '{sys.errno.strerror}'")
 +                      _is_writable = false
 +              end
        end
  end
  
 +redef class Int
 +      # Creates a file stream from a file descriptor `fd` using the file access `mode`.
 +      #
 +      # NOTE: The `mode` specified must be compatible with the one used in the file descriptor.
 +      private fun fd_to_stream(mode: NativeString): NativeFile is extern "file_int_fdtostream"
 +end
 +
 +# Constant for read-only file streams
 +private fun read_only: NativeString do return "r".to_cstring
 +
 +# Constant for write-only file streams
 +#
 +# If a stream is opened on a file with this method,
 +# it will wipe the previous file if any.
 +# Else, it will create the file.
 +private fun wipe_write: NativeString do return "w".to_cstring
 +
  ###############################################################################
  
  # Standard input stream.
@@@ -276,68 -190,24 +276,68 @@@ redef class Streamabl
        end
  end
  
 -redef class String
 -      # return true if a file with this names exists
 -      fun file_exists: Bool do return to_cstring.file_exists
 +# Utility class to access file system services
 +#
 +# Usually created with `Text::to_path`.
 +class Path
  
 -      # The status of a file. see POSIX stat(2).
 -      fun file_stat: FileStat do return to_cstring.file_stat
 +      private var path: String
  
 -      # The status of a file or of a symlink. see POSIX lstat(2).
 -      fun file_lstat: FileStat do return to_cstring.file_lstat
 +      # Path to this file
 +      redef fun to_s do return path
  
 -      # Remove a file, return true if success
 -      fun file_delete: Bool do return to_cstring.file_delete
 +      # Name of the file name at `to_s`
 +      #
 +      # ~~~
 +      # var path = "/tmp/somefile".to_path
 +      # assert path.filename == "somefile"
 +      # ~~~
 +      var filename: String = path.basename("") is lazy
  
 -      # Copy content of file at `self` to `dest`
 -      fun file_copy_to(dest: String)
 +      # Does the file at `path` exists?
 +      fun exists: Bool do return stat != null
 +
 +      # Information on the file at `self` following symbolic links
 +      #
 +      # Returns `null` if there is no file at `self`.
 +      #
 +      # ~~~
 +      # var p = "/tmp/".to_path
 +      # var stat = p.stat
 +      # if stat != null then # Does `p` exist?
 +      #     print "It's size is {stat.size}"
 +      #     if stat.is_dir then print "It's a directory"
 +      # end
 +      # ~~~
 +      fun stat: nullable FileStat
        do
 -              var input = new IFStream.open(self)
 -              var output = new OFStream.open(dest)
 +              var stat = path.to_cstring.file_stat
 +              if stat.address_is_null then return null
 +              return new FileStat(stat)
 +      end
 +
 +      # Information on the file or link at `self`
 +      #
 +      # Do not follow symbolic links.
 +      fun link_stat: nullable FileStat
 +      do
 +              var stat = path.to_cstring.file_lstat
 +              if stat.address_is_null then return null
 +              return new FileStat(stat)
 +      end
 +
 +      # Delete a file from the file system, return `true` on success
 +      #
 +      # Require: `exists`
 +      fun delete: Bool do return path.to_cstring.file_delete
 +
 +      # Copy content of file at `path` to `dest`
 +      #
 +      # Require: `exists`
 +      fun copy(dest: Path)
 +      do
 +              var input = open_ro
 +              var output = dest.open_wo
  
                while not input.eof do
                        var buffer = input.read(1024)
                output.close
        end
  
 +      # Open this file for reading
 +      #
 +      # Require: `exists and not link_stat.is_dir`
 +      fun open_ro: IFStream
 +      do
 +              # TODO manage streams error when they are merged
 +              return new IFStream.open(path)
 +      end
 +
 +      # Open this file for writing
 +      #
 +      # Require: `not exists or not stat.is_dir`
 +      fun open_wo: OFStream
 +      do
 +              # TODO manage streams error when they are merged
 +              return new OFStream.open(path)
 +      end
 +
 +      # Lists the name of the files contained within the directory at `path`
 +      #
 +      # Require: `exists and is_dir`
 +      fun files: Array[Path]
 +      do
 +              var files = new Array[Path]
 +              for filename in path.files do
 +                      files.add new Path(path / filename)
 +              end
 +              return files
 +      end
 +
 +      # Delete a directory and all of its content, return `true` on success
 +      #
 +      # Does not go through symbolic links and may get stuck in a cycle if there
 +      # is a cycle in the file system.
 +      fun rmdir: Bool
 +      do
 +              var ok = true
 +              for file in self.files do
 +                      var stat = file.link_stat
 +                      if stat.is_dir then
 +                              ok = file.rmdir and ok
 +                      else
 +                              ok = file.delete and ok
 +                      end
 +              end
 +
 +              # Delete the directory itself
 +              if ok then path.to_cstring.rmdir
 +
 +              return ok
 +      end
 +
 +      redef fun ==(other) do return other isa Path and path.simplify_path == other.path.simplify_path
 +      redef fun hash do return path.simplify_path.hash
 +end
 +
 +# Information on a file
 +#
 +# Created by `Path::stat` and `Path::link_stat`.
 +#
 +# The information within this class is gathered when the instance is initialized
 +# it will not be updated if the targeted file is modified.
 +class FileStat
 +      super Finalizable
 +
 +      # TODO private init
 +
 +      # The low-level status of a file
 +      #
 +      # See: POSIX stat(2)
 +      private var stat: NativeFileStat
 +
 +      private var finalized = false
 +
 +      redef fun finalize
 +      do
 +              if not finalized then
 +                      stat.free
 +                      finalized = true
 +              end
 +      end
 +
 +      # Returns the last access time in seconds since Epoch
 +      fun last_access_time: Int
 +      do
 +              assert not finalized
 +              return stat.atime
 +      end
 +
 +      # Returns the last modification time in seconds since Epoch
 +      fun last_modification_time: Int
 +      do
 +              assert not finalized
 +              return stat.mtime
 +      end
 +
 +      # Size of the file at `path`
 +      fun size: Int
 +      do
 +              assert not finalized
 +              return stat.size
 +      end
 +
 +      # Is this a regular file and not a device file, pipe, socket, etc.?
 +      fun is_file: Bool
 +      do
 +              assert not finalized
 +              return stat.is_reg
 +      end
 +
 +      # Is this a directory?
 +      fun is_dir: Bool
 +      do
 +              assert not finalized
 +              return stat.is_dir
 +      end
 +
 +      # Is this a symbolic link?
 +      fun is_link: Bool
 +      do
 +              assert not finalized
 +              return stat.is_lnk
 +      end
 +
 +      # FIXME Make the following POSIX only? or implement in some other way on Windows
 +
 +      # Returns the last status change time in seconds since Epoch
 +      fun last_status_change_time: Int
 +      do
 +              assert not finalized
 +              return stat.ctime
 +      end
 +
 +      # Returns the permission bits of file
 +      fun mode: Int
 +      do
 +              assert not finalized
 +              return stat.mode
 +      end
 +
 +      # Is this a character device?
 +      fun is_chr: Bool
 +      do
 +              assert not finalized
 +              return stat.is_chr
 +      end
 +
 +      # Is this a block device?
 +      fun is_blk: Bool
 +      do
 +              assert not finalized
 +              return stat.is_blk
 +      end
 +
 +      # Is this a FIFO pipe?
 +      fun is_fifo: Bool
 +      do
 +              assert not finalized
 +              return stat.is_fifo
 +      end
 +
 +      # Is this a UNIX socket
 +      fun is_sock: Bool
 +      do
 +              assert not finalized
 +              return stat.is_sock
 +      end
 +end
 +
 +redef class Text
 +      # Access file system related services on the path at `self`
 +      fun to_path: Path do return new Path(to_s)
 +end
 +
 +redef class String
 +      # return true if a file with this names exists
 +      fun file_exists: Bool do return to_cstring.file_exists
 +
 +      # The status of a file. see POSIX stat(2).
 +      fun file_stat: NativeFileStat do return to_cstring.file_stat
 +
 +      # The status of a file or of a symlink. see POSIX lstat(2).
 +      fun file_lstat: NativeFileStat do return to_cstring.file_lstat
 +
 +      # Remove a file, return true if success
 +      fun file_delete: Bool do return to_cstring.file_delete
 +
 +      # Copy content of file at `self` to `dest`
 +      fun file_copy_to(dest: String) do to_path.copy(dest.to_path)
 +
        # Remove the trailing extension `ext`.
        #
        # `ext` usually starts with a dot but could be anything.
        # trainling "/" is removed
        #
        # Note that the method only wonrk on the string:
+       #
        #  * no I/O access is performed
        #  * the validity of the path is not checked
        #
-       #     assert "some/./complex/../../path/from/../to/a////file//".simplify_path        ==  "path/to/a/file"
-       #     assert "../dir/file".simplify_path       ==  "../dir/file"
-       #     assert "dir/../../".simplify_path        ==  ".."
-       #     assert "dir/..".simplify_path            ==  "."
-       #     assert "//absolute//path/".simplify_path ==  "/absolute/path"
-       #     assert "//absolute//../".simplify_path   ==  "/"
+       # ~~~
+       # assert "some/./complex/../../path/from/../to/a////file//".simplify_path            ==  "path/to/a/file"
+       # assert "../dir/file".simplify_path       ==  "../dir/file"
+       # assert "dir/../../".simplify_path        ==  ".."
+       # assert "dir/..".simplify_path            ==  "."
+       # assert "//absolute//path/".simplify_path ==  "/absolute/path"
+       # assert "//absolute//../".simplify_path   ==  "/"
+       # ~~~
        fun simplify_path: String
        do
                var a = self.split_with("/")
        #
        # Does not go through symbolic links and may get stuck in a cycle if there
        # is a cycle in the filesystem.
 -      fun rmdir: Bool
 -      do
 -              var ok = true
 -              for file in self.files do
 -                      var file_path = self.join_path(file)
 -                      var stat = file_path.file_lstat
 -                      if stat.is_dir then
 -                              ok = file_path.rmdir and ok
 -                      else
 -                              ok = file_path.file_delete and ok
 -                      end
 -                      stat.free
 -              end
 -
 -              # Delete the directory itself
 -              if ok then to_cstring.rmdir
 -
 -              return ok
 -      end
 +      fun rmdir: Bool do return to_path.rmdir
  
        # Change the current working directory
        #
@@@ -867,8 -568,8 +870,8 @@@ en
  
  redef class NativeString
        private fun file_exists: Bool is extern "string_NativeString_NativeString_file_exists_0"
 -      private fun file_stat: FileStat is extern "string_NativeString_NativeString_file_stat_0"
 -      private fun file_lstat: FileStat `{
 +      private fun file_stat: NativeFileStat is extern "string_NativeString_NativeString_file_stat_0"
 +      private fun file_lstat: NativeFileStat `{
                struct stat* stat_element;
                int res;
                stat_element = malloc(sizeof(struct stat));
  end
  
  # This class is system dependent ... must reify the vfs
 -extern class FileStat `{ struct stat * `}
 +extern class NativeFileStat `{ struct stat * `}
        # Returns the permission bits of file
        fun mode: Int is extern "file_FileStat_FileStat_mode_0"
        # Returns the last access time
        fun atime: Int is extern "file_FileStat_FileStat_atime_0"
 -      # Returns the last status change time 
 +      # Returns the last status change time
        fun ctime: Int is extern "file_FileStat_FileStat_ctime_0"
        # Returns the last modification time
        fun mtime: Int is extern "file_FileStat_FileStat_mtime_0"
@@@ -917,12 -618,8 +920,12 @@@ private extern class NativeFile `{ FILE
        fun io_read(buf: NativeString, len: Int): Int is extern "file_NativeFile_NativeFile_io_read_2"
        fun io_write(buf: NativeString, len: Int): Int is extern "file_NativeFile_NativeFile_io_write_2"
        fun io_close: Int is extern "file_NativeFile_NativeFile_io_close_0"
 -      fun file_stat: FileStat is extern "file_NativeFile_NativeFile_file_stat_0"
 +      fun file_stat: NativeFileStat is extern "file_NativeFile_NativeFile_file_stat_0"
        fun fileno: Int `{ return fileno(recv); `}
 +      # Flushes the buffer, forcing the write operation
 +      fun flush: Int is extern "fflush"
 +      # Used to specify how the buffering will be handled for the current stream.
 +      fun set_buffering_type(buf_length: Int, mode: Int): Int is extern "file_NativeFile_NativeFile_set_buffering_type_0"
  
        new io_open_read(path: NativeString) is extern "file_NativeFileCapable_NativeFileCapable_io_open_read_1"
        new io_open_write(path: NativeString) is extern "file_NativeFileCapable_NativeFileCapable_io_open_write_1"
@@@ -933,10 -630,6 +936,10 @@@ en
  
  redef class Sys
  
 +      init do
 +              if stdout isa FStream then stdout.as(FStream).set_buffering_mode(256, buffer_mode_line)
 +      end
 +
        # Standard input
        var stdin: PollableIStream = new Stdin is protected writable
  
        # Standard output for errors
        var stderr: OStream = new Stderr is protected writable
  
 +      # Enumeration for buffer mode full (flushes when buffer is full)
 +      fun buffer_mode_full: Int is extern "file_Sys_Sys_buffer_mode_full_0"
 +      # Enumeration for buffer mode line (flushes when a `\n` is encountered)
 +      fun buffer_mode_line: Int is extern "file_Sys_Sys_buffer_mode_line_0"
 +      # Enumeration for buffer mode none (flushes ASAP when something is written)
 +      fun buffer_mode_none: Int is extern "file_Sys_Sys_buffer_mode_none_0"
 +
 +      # returns first available stream to read or write to
 +      # return null on interruption (possibly a signal)
 +      protected fun poll( streams : Sequence[FStream] ) : nullable FStream
 +      do
 +              var in_fds = new Array[Int]
 +              var out_fds = new Array[Int]
 +              var fd_to_stream = new HashMap[Int,FStream]
 +              for s in streams do
 +                      var fd = s.fd
 +                      if s isa IFStream then in_fds.add( fd )
 +                      if s isa OFStream then out_fds.add( fd )
 +
 +                      fd_to_stream[fd] = s
 +              end
 +
 +              var polled_fd = intern_poll( in_fds, out_fds )
 +
 +              if polled_fd == null then
 +                      return null
 +              else
 +                      return fd_to_stream[polled_fd]
 +              end
 +      end
 +
 +      private fun intern_poll(in_fds: Array[Int], out_fds: Array[Int]) : nullable Int is extern import Array[Int].length, Array[Int].[], Int.as(nullable Int) `{
 +              int in_len, out_len, total_len;
 +              struct pollfd *c_fds;
 +              sigset_t sigmask;
 +              int i;
 +              int first_polled_fd = -1;
 +              int result;
 +
 +              in_len = Array_of_Int_length( in_fds );
 +              out_len = Array_of_Int_length( out_fds );
 +              total_len = in_len + out_len;
 +              c_fds = malloc( sizeof(struct pollfd) * total_len );
 +
 +              /* input streams */
 +              for ( i=0; i<in_len; i ++ ) {
 +                      int fd;
 +                      fd = Array_of_Int__index( in_fds, i );
 +
 +                      c_fds[i].fd = fd;
 +                      c_fds[i].events = POLLIN;
 +              }
 +
 +              /* output streams */
 +              for ( i=0; i<out_len; i ++ ) {
 +                      int fd;
 +                      fd = Array_of_Int__index( out_fds, i );
 +
 +                      c_fds[i].fd = fd;
 +                      c_fds[i].events = POLLOUT;
 +              }
 +
 +              /* poll all fds, unlimited timeout */
 +              result = poll( c_fds, total_len, -1 );
 +
 +              if ( result > 0 ) {
 +                      /* analyse results */
 +                      for ( i=0; i<total_len; i++ )
 +                              if ( c_fds[i].revents & c_fds[i].events || /* awaited event */
 +                                       c_fds[i].revents & POLLHUP ) /* closed */
 +                              {
 +                                      first_polled_fd = c_fds[i].fd;
 +                                      break;
 +                              }
 +
 +                      return Int_as_nullable( first_polled_fd );
 +              }
 +              else if ( result < 0 )
 +                      fprintf( stderr, "Error in Stream:poll: %s\n", strerror( errno ) );
 +
 +              return null_Int();
 +      `}
 +
  end
  
  # Print `objects` on the standard output (`stdout`).
diff --combined lib/standard/kernel.nit
@@@ -66,13 -66,13 +66,13 @@@ interface Objec
        fun is_same_instance(other: nullable Object): Bool is intern
  
        # Have `self` and `other` the same value?
-       ##
+       #
        # The exact meaning of "same value" is let to the subclasses.
        # Implicitly, the default implementation, is `is_same_instance`
        fun ==(other: nullable Object): Bool do return self.is_same_instance(other)
  
        # Have `self` and `other` different values?
-       ##
+       #
        # != is equivalent with "not ==".
        fun !=(other: nullable Object): Bool do return not (self == other)
  
@@@ -93,7 -93,7 +93,7 @@@
  
        # The hash code of the object.
        # Assuming that a == b -> a.hash == b.hash
-       ##
+       #
        # Without redefinition, it is based on the `object_id` of the instance.
        fun hash: Int do return object_id / 8
  end
@@@ -283,8 -283,10 +283,10 @@@ en
  
  # Native Booleans.
  # `true` and `false` are the only instances.
+ #
  # Boolean are manipulated trough three special operators:
- #      `and`, `or`, `not`.
+ # `and`, `or`, `not`.
+ #
  # Booleans are mainly used by conditional statement and loops.
  universal Bool
        redef fun object_id is intern
@@@ -489,8 -491,8 +491,8 @@@ universal In
  
        # The character whose ASCII value is `self`.
        #
-       #      assert 65.ascii   == 'A'
-       #      assert 10.ascii   == '\n'
+       #     assert 65.ascii   == 'A'
+       #     assert 10.ascii   == '\n'
        fun ascii: Char is intern
  
        # Number of digits of an integer in base `b` (plus one if negative)
@@@ -705,5 -707,5 +707,5 @@@ extern class Pointe
        fun address_is_null: Bool is extern "address_is_null"
  
        # Free the memory pointed by this pointer
 -      fun free `{ free(recv); `}
 +      fun free is extern "free"
  end
diff --combined lib/standard/string.nit
@@@ -385,38 -385,17 +385,38 @@@ abstract class Tex
        #     assert "\na\nb\tc\t".trim          == "a\nb\tc"
        fun trim: SELFTYPE do return (self.l_trim).r_trim
  
 -      # Returns `self` removed from its last `\n` (if any).
 +      # Returns `self` removed from its last line terminator (if any).
        #
        #    assert "Hello\n".chomp == "Hello"
        #    assert "Hello".chomp   == "Hello"
 -      #    assert "\n\n\n".chomp  == "\n\n"
        #
 -      # This method is mainly used to remove the LINE_FEED character from lines of text.
 +      #    assert "\n".chomp == ""
 +      #    assert "".chomp   == ""
 +      #
 +      # Line terminators are `"\n"`, `"\r\n"` and `"\r"`.
 +      # A single line terminator, the last one, is removed.
 +      #
 +      #    assert "\r\n".chomp     == ""
 +      #    assert "\r\n\n".chomp   == "\r\n"
 +      #    assert "\r\n\r\n".chomp == "\r\n"
 +      #    assert "\r\n\r".chomp   == "\r\n"
 +      #
 +      # Note: unlike with most IO methods like `IStream::read_line`,
 +      # a single `\r` is considered here to be a line terminator and will be removed.
        fun chomp: SELFTYPE
        do
 -              if self.chars.last != '\n' then return self
 -              return substring(0, length-1)
 +              var len = length
 +              if len == 0 then return self
 +              var l = self.chars.last
 +              if l == '\r' then
 +                      return substring(0, len-1)
 +              else if l != '\n' then
 +                      return self
 +              else if len > 1 and self.chars[len-2] == '\r' then
 +                      return substring(0, len-2)
 +              else
 +                      return substring(0, len-1)
 +              end
        end
  
        # Justify a self in a space of `length`
        # * 1.0 for right-justified (all spaces at the left)
        # * 0.5 for centered (half the spaces at the left)
        #
+       # Examples
+       #
        #     assert "hello".justify(10, 0.0)  == "hello     "
        #     assert "hello".justify(10, 1.0)  == "     hello"
        #     assert "hello".justify(10, 0.5)  == "  hello   "
        #
        #     assert "hello".justify(2, 0.0)   == "hello"
        #
-       # REQUIRE: left >= 0.0 and left <= 1.0
+       # REQUIRE: `left >= 0.0 and left <= 1.0`
        # ENSURE: `self.length <= length implies result.length == length`
-       # ENSURE: `self.length >= length implies result == self
+       # ENSURE: `self.length >= length implies result == self`
        fun justify(length: Int, left: Float): SELFTYPE
        do
                var diff = length - self.length
@@@ -968,9 -949,9 +970,9 @@@ abstract class Strin
        #
        # SEE : `Char::is_letter` for the definition of letter.
        #
-       #    assert "jAVASCRIPT".capitalized == "Javascript"
-       #    assert "i am root".capitalized == "I Am Root"
-       #    assert "ab_c -ab0c ab\nc".capitalized == "Ab_C -Ab0C Ab\nC"
+       #     assert "jAVASCRIPT".capitalized == "Javascript"
+       #     assert "i am root".capitalized == "I Am Root"
+       #     assert "ab_c -ab0c ab\nc".capitalized == "Ab_C -Ab0C Ab\nC"
        fun capitalized: SELFTYPE do
                if length == 0 then return self
  
@@@ -1445,15 -1426,15 +1447,15 @@@ abstract class Buffe
        #
        # SEE: `Char::is_letter` for the definition of a letter.
        #
-       #    var b = new FlatBuffer.from("jAVAsCriPt")
-       #    b.capitalize
-       #    assert b == "Javascript"
-       #    b = new FlatBuffer.from("i am root")
-       #    b.capitalize
-       #    assert b == "I Am Root"
-       #    b = new FlatBuffer.from("ab_c -ab0c ab\nc")
-       #    b.capitalize
-       #    assert b == "Ab_C -Ab0C Ab\nC"
+       #     var b = new FlatBuffer.from("jAVAsCriPt")
+       #     b.capitalize
+       #     assert b == "Javascript"
+       #     b = new FlatBuffer.from("i am root")
+       #     b.capitalize
+       #     assert b == "I Am Root"
+       #     b = new FlatBuffer.from("ab_c -ab0c ab\nc")
+       #     b.capitalize
+       #     assert b == "Ab_C -Ab0C Ab\nC"
        fun capitalize do
                if length == 0 then return
                var c = self[0].to_upper
@@@ -1810,7 -1791,7 +1812,7 @@@ redef class Objec
  
        # The class name of the object.
        #
-       #    assert 5.class_name == "Int"
+       #     assert 5.class_name == "Int"
        fun class_name: String do return native_class_name.to_s
  
        # Developer readable representation of `self`.
@@@ -2005,10 -1986,10 +2007,10 @@@ redef class Cha
  
        # Returns true if the char is a numerical digit
        #
-       #      assert '0'.is_numeric
-       #      assert '9'.is_numeric
-       #      assert not 'a'.is_numeric
-       #      assert not '?'.is_numeric
+       #     assert '0'.is_numeric
+       #     assert '9'.is_numeric
+       #     assert not 'a'.is_numeric
+       #     assert not '?'.is_numeric
        fun is_numeric: Bool
        do
                return self >= '0' and self <= '9'
  
        # Returns true if the char is an alpha digit
        #
-       #      assert 'a'.is_alpha
-       #      assert 'Z'.is_alpha
-       #      assert not '0'.is_alpha
-       #      assert not '?'.is_alpha
+       #     assert 'a'.is_alpha
+       #     assert 'Z'.is_alpha
+       #     assert not '0'.is_alpha
+       #     assert not '?'.is_alpha
        fun is_alpha: Bool
        do
                return (self >= 'a' and self <= 'z') or (self >= 'A' and self <= 'Z')
  
        # Returns true if the char is an alpha or a numeric digit
        #
-       #      assert 'a'.is_alphanumeric
-       #      assert 'Z'.is_alphanumeric
-       #      assert '0'.is_alphanumeric
-       #      assert '9'.is_alphanumeric
-       #      assert not '?'.is_alphanumeric
+       #     assert 'a'.is_alphanumeric
+       #     assert 'Z'.is_alphanumeric
+       #     assert '0'.is_alphanumeric
+       #     assert '9'.is_alphanumeric
+       #     assert not '?'.is_alphanumeric
        fun is_alphanumeric: Bool
        do
                return self.is_numeric or self.is_alpha
diff --combined src/doc/doc_model.nit
  module doc_model
  
  import model_utils
- import docdown
+ import doc_down
  import doc_templates
  import ordered_tree
  import model_ext
  
- ################################################################################
- # Additions to Nit entities.
- redef class MDoc
-       # Comment synopsys HTML escaped
-       fun short_comment: String do return content.first.html_escape
-       # Full comment HTML escaped
-       fun full_comment: String do return content.join("\n").html_escape
-       # Synopsys in a template
-       fun tpl_short_comment: Streamable do return short_markdown
-       # Full comment in a template
-       fun tpl_comment: Streamable do return full_markdown
- end
  redef class Location
        # Github url based on this location
        fun github(gitdir: String): String do
@@@ -112,7 -94,7 +94,7 @@@ redef class MEntit
                lnk.add tpl_link
                if mdoc != null then
                        lnk.add ": "
-                       lnk.add mdoc.short_markdown
+                       lnk.add mdoc.tpl_short_comment
                end
                return new TplListItem.with_content(lnk)
        end
@@@ -142,7 -124,7 +124,7 @@@ redef class MConcer
                lnk.add tpl_anchor
                if mdoc != null then
                        lnk.add ": "
-                       lnk.add mdoc.short_markdown
+                       lnk.add mdoc.tpl_short_comment
                end
                return new TplListItem.with_content(lnk)
        end
@@@ -237,7 -219,7 +219,7 @@@ redef class MModul
        redef fun tpl_declaration do
                var tpl = new Template
                tpl.add "<span>module "
 -              tpl.add tpl_link
 +              tpl.add tpl_namespace
                tpl.add "</span>"
                return tpl
        end
@@@ -405,10 -387,10 +387,10 @@@ redef class MClassDe
                lnk.add tpl_link
                if mdoc != null then
                        lnk.add ": "
-                       lnk.add mdoc.short_markdown
+                       lnk.add mdoc.tpl_short_comment
                else if mclass.intro.mdoc != null then
                        lnk.add ": "
-                       lnk.add mclass.intro.mdoc.short_markdown
+                       lnk.add mclass.intro.mdoc.tpl_short_comment
                end
                return new TplListItem.with_content(lnk)
        end
@@@ -552,10 -534,10 +534,10 @@@ redef class MPropDe
                lnk.add anchor
                if mdoc != null then
                        lnk.add ": "
-                       lnk.add mdoc.short_markdown
+                       lnk.add mdoc.tpl_short_comment
                else if mproperty.intro.mdoc != null then
                        lnk.add ": "
-                       lnk.add mproperty.intro.mdoc.short_markdown
+                       lnk.add mproperty.intro.mdoc.tpl_short_comment
                end
                return new TplListItem.with_content(lnk)
        end
                lnk.add anchor
                if mdoc != null then
                        lnk.add ": "
-                       lnk.add mdoc.short_markdown
+                       lnk.add mdoc.tpl_short_comment
                end
                var li = new TplListItem.with_content(lnk)
                li.css_classes.add "signature"
        end
  end
  
 +redef class MAttributeDef
 +      redef fun tpl_signature do
 +              var tpl = new Template
 +              if static_mtype != null then
 +                      tpl.add ": "
 +                      tpl.add static_mtype.tpl_signature
 +              end
 +              return tpl
 +      end
 +end
 +
  redef class MMethod
        redef fun tpl_signature do
                var tpl = new Template