Merge: Ini of projects
authorJean Privat <jean@pryen.org>
Wed, 2 Sep 2015 00:36:09 +0000 (20:36 -0400)
committerJean Privat <jean@pryen.org>
Wed, 2 Sep 2015 00:36:09 +0000 (20:36 -0400)
Now that projects can have metadata, let's just provide them.

Commits are grouped by maintainer and directory so that selfish maintainers can review only their.

Fell free to propose better tagging or metadata. See #1655 and http://nitlanguage.org/nep/project_metadata.html for current state of the discussion.

Note that the proof on concept of a catalog is available at http://nitlanguage.org/catalog

Pull-Request: #1675
Reviewed-by: Lucas Bajolet <r4pass@hotmail.com>
Reviewed-by: Alexis Laferrière <alexis.laf@xymus.net>

117 files changed:
.mailmap
Makefile
contrib/benitlux/src/benitlux_daily.nit
contrib/benitlux/src/benitlux_model.nit
contrib/crazy_moles/src/moles_android.nit
contrib/crazy_moles/src/moles_linux.nit
contrib/friendz/README.md
contrib/friendz/src/friendz_android.nit
contrib/friendz/src/friendz_linux.nit
contrib/github_merge.nit
contrib/inkscape_tools/tests/app/src/s2pn_android.nit
contrib/inkscape_tools/tests/app/src/s2pn_linux.nit
contrib/mnit_test/src/simple_android.nit
contrib/mnit_test/src/simple_linux.nit
contrib/nitiwiki/src/wiki_base.nit
contrib/objcwrapper/.gitignore
contrib/objcwrapper/Makefile
contrib/objcwrapper/README.md [new file with mode: 0644]
contrib/objcwrapper/grammar/objc.sablecc
contrib/objcwrapper/src/header_static.nit
contrib/objcwrapper/src/objc_generator.nit
contrib/objcwrapper/src/objc_model.nit
contrib/objcwrapper/src/objc_visitor.nit
contrib/objcwrapper/src/objcwrapper.nit
contrib/tinks/src/client/android_client.nit
contrib/tinks/src/client/linux_client.nit
examples/circular_list.nit
examples/clock.nit
examples/clock_more.nit
examples/fibonacci.nit
examples/mnit_ballz/src/game_logic.nit
examples/mnit_dino/src/dino_android.nit
examples/mnit_dino/src/dino_linux.nit
examples/nitcorn/Makefile [deleted file]
examples/print_arguments.nit
examples/procedural_array.nit
examples/rosettacode/README.md
examples/shoot/README.md [new file with mode: 0644]
examples/shoot/src/shoot_android.nit
examples/shoot/src/shoot_linux.nit
lib/ai/README.md
lib/ai/examples/examples.nit [deleted file]
lib/android/input_events.nit
lib/bcm2835/examples/examples.nit [deleted file]
lib/cartesian.nit
lib/combinations.nit
lib/core/README.md
lib/core/text/abstract_text.nit
lib/core/text/flat.nit
lib/core/text/ropes.nit
lib/curl/examples/examples.nit [deleted file]
lib/deriving.nit
lib/glesv2/examples/examples.nit [deleted file]
lib/glesv2/examples/opengles2_hello_triangle.nit
lib/html/examples/examples.nit [deleted file]
lib/ini.nit
lib/linux/audio.nit
lib/mnit/android/android.nit [moved from lib/mnit_android/mnit_android.nit with 97% similarity]
lib/mnit/android/android_app.nit [moved from lib/mnit_android/android_app.nit with 99% similarity]
lib/mnit/android/android_assets.nit [moved from lib/mnit_android/android_assets.nit with 100% similarity]
lib/mnit/android/android_opengles1.nit [moved from lib/mnit_android/android_opengles1.nit with 98% similarity]
lib/mnit/assets.nit
lib/mnit/display.nit [moved from lib/mnit_display.nit with 98% similarity]
lib/mnit/input.nit [moved from lib/mnit_input.nit with 99% similarity]
lib/mnit/linux/linux.nit [moved from lib/mnit_linux/mnit_linux.nit with 97% similarity]
lib/mnit/linux/linux_app.nit [moved from lib/mnit_linux/linux_app.nit with 98% similarity]
lib/mnit/linux/linux_assets.nit [moved from lib/mnit_linux/linux_assets.nit with 100% similarity]
lib/mnit/linux/linux_opengles1.nit [moved from lib/mnit_linux/linux_opengles1.nit with 100% similarity]
lib/mnit/mnit.nit
lib/mnit/mnit_app.nit
lib/mnit/opengles1.nit
lib/mnit/tileset.nit
lib/nitcorn/examples/Makefile [new file with mode: 0644]
lib/nitcorn/examples/src/file_server_on_port_80.nit [moved from examples/nitcorn/src/file_server_on_port_80.nit with 100% similarity]
lib/nitcorn/examples/src/nitcorn_hello_world.nit [moved from examples/nitcorn/src/nitcorn_hello_world.nit with 100% similarity]
lib/nitcorn/examples/src/xymus_net.nit [moved from examples/nitcorn/src/xymus_net.nit with 98% similarity]
lib/nitcorn/examples/www/hello_world/dir/a.txt [moved from examples/nitcorn/www/hello_world/dir/a.txt with 100% similarity]
lib/nitcorn/examples/www/hello_world/dir/b.txt [moved from examples/nitcorn/www/hello_world/dir/b.txt with 100% similarity]
lib/nitcorn/examples/www/hello_world/favicon.ico [moved from examples/nitcorn/www/hello_world/favicon.ico with 100% similarity]
lib/ordered_tree.nit
lib/privileges/examples/examples.nit [deleted file]
lib/scene2d.nit
lib/sdl.nit
lib/socket/examples/examples.nit [deleted file]
lib/symbol.nit
lib/template/examples/examples.nit [deleted file]
lib/websocket/examples/examples.nit [deleted file]
src/astbuilder.nit
src/compiler/abstract_compiler.nit
src/interpreter/naive_interpreter.nit
src/loader.nit
src/nitcatalog.nit [new file with mode: 0644]
src/parser/parser_nodes.nit
src/rapid_type_analysis.nit
tests/base_import.nit
tests/base_import2.nit [new file with mode: 0644]
tests/project1/module_0.nit [new file with mode: 0644]
tests/project1/module_01.nit [new file with mode: 0644]
tests/project1/module_02.nit [new file with mode: 0644]
tests/project1/subdir/module_0.nit [new file with mode: 0644]
tests/sav/base_import2.res [new file with mode: 0644]
tests/sav/base_import2_alt1.res [new file with mode: 0644]
tests/sav/base_import2_alt2.res [new file with mode: 0644]
tests/sav/base_import2_alt3.res [new file with mode: 0644]
tests/sav/base_import_alt3.res
tests/sav/base_import_alt5.res
tests/sav/base_import_alt6.res [new file with mode: 0644]
tests/sav/dino_linux.res
tests/sav/error_needed_method_alt3.res
tests/sav/nitcatalog.res [new file with mode: 0644]
tests/sav/niti/error_needed_method_alt4.res
tests/sav/nitls_args1.res
tests/sav/nitls_args2.res
tests/sav/nitls_args3.res
tests/sav/nitls_args4.res
tests/sav/shoot_linux.res
tests/sav/xymus_net.res

index 452266a..a1e995c 100644 (file)
--- a/.mailmap
+++ b/.mailmap
@@ -26,3 +26,7 @@ Clement de Figueiredo <clement.defigueiredo@gmail.com>
 Christophe Gigax <christophe.gigax@viacesi.fr>
 
 Julien Pagès <julien.projet@gmail.com>
+
+Mehdi Ait Younes <overpex@gmail.com>
+
+Hervé Matysiak <herve.matysiak@viacesi.fr>
index 1eb78c7..cad3ffa 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -40,7 +40,7 @@ doc/stdlib/index.html: bin/nitdoc bin/nitls
        @echo '***************************************************************'
        @echo '* Generate doc for NIT standard library                       *'
        @echo '***************************************************************'
-       bin/nitdoc $$(bin/nitls lib -rs --path) -d doc/stdlib \
+       bin/nitdoc lib -d doc/stdlib \
                --custom-title "Nit Standard Library" \
                --custom-brand "<a href=\"http://nitlanguage.org/\">Nitlanguage.org</a>" \
                --custom-overview-text "<p>Documentation for the standard library of Nit<br/>Version $$(git describe)<br/>Date: $$(git show --format="%cd" | head -1)</p>" \
@@ -53,7 +53,7 @@ doc/stdlib/index.html: bin/nitdoc bin/nitls
                --piwik-site-id "2" \
 
 doc/nitc/index.html: bin/nitdoc bin/nitls
-       bin/nitdoc $$(bin/nitls lib -rs --path) src/nit*.nit src/test_*.nit -d doc/nitc \
+       bin/nitdoc lib src/nit*.nit src/test_*.nit -d doc/nitc \
                --private \
                --custom-title "Nit Compilers and Tools" \
                --custom-brand "<a href=\"http://nitlanguage.org/\">Nitlanguage.org</a>" \
index f4a3f25..d242751 100644 (file)
@@ -171,7 +171,7 @@ class Benitlux
        # Generate email and fill the attributes `email_content` and `email_title`
        fun generate_email(beer_events: BeerEvents)
        do
-               email_title = beer_events.to_email_title
+               email_title = "Benitlux {street.capitalized}{beer_events.to_email_title}"
                email_content = beer_events.to_email_content
        end
 
@@ -189,6 +189,8 @@ To unsubscribe, go to <a href="{{{unsub_link}}}">{{{unsub_link}}}</a>
                        var mail = new Mail("Benitlux <benitlux@xymus.net>", email_title, content)
                        mail.to.add email
                        mail.header["Content-Type"] = "text/html; charset=\"UTF-8\""
+                       mail.header["List-Unsubscribe"] = unsub_link
+                       mail.header["Precedence"] = "bulk"
                        mail.encrypt_with_base64
 
                        mail.send
index d3e440d..4649a5c 100644 (file)
@@ -71,16 +71,14 @@ class BeerEvents
        # Get a pretty and short version of `self`
        fun to_email_title: String
        do
-               var title = "Benelux Beer Menu"
-
                # New beers
                var new_beers_name = new Array[String]
                for beer in self.new_beers do new_beers_name.add beer.name
 
                if not new_beers_name.is_empty then
-                       title += " (+ {new_beers_name.join(", ")})"
+                       return " (+ {new_beers_name.join(", ")})"
                end
 
-               return title
+               return ""
        end
 end
index 1da39be..edf1251 100644 (file)
@@ -16,7 +16,7 @@
 
 module moles_android
 
-import mnit_android
+import mnit::android
 import android::portrait
 
 import moles
index 66a645a..c46b29b 100644 (file)
@@ -16,7 +16,7 @@
 
 module moles_linux
 
-import mnit_linux
+import mnit::linux
 
 import moles
 import effects
index 48be94e..b30803b 100644 (file)
@@ -1,6 +1,4 @@
-Chainz of Friendz
-
-A puzzle game
+Chainz of Friendz, a multi-platform puzzle game
 
 # Objectives
 
index e0dec75..4f40ca7 100644 (file)
@@ -11,7 +11,7 @@
 module friendz_android
 
 import friendz
-import mnit_android
+import mnit::android
 import android::landscape
 
 redef class App
index 7940843..e8ada20 100644 (file)
@@ -11,7 +11,7 @@
 module friendz_linux
 
 import friendz
-import mnit_linux
+import mnit::linux
 
 redef class Display
        redef fun wanted_width do return app.screen_width
index 3a98c10..52afa49 100644 (file)
@@ -68,8 +68,8 @@ redef class GithubCurl
                var res = new Array[String]
                for l in logins do
                        var u = get_and_check("https://api.github.com/users/{l}").json_as_map
-                       if not u.has_key("name") then
-                               print "No public name for user {l}"
+                       if not u.has_key("name") or u["name"] == null or not u.has_key("email")or u["email"] == null then
+                               print "No public name/email for user {l}"
                                continue
                        end
                        var r = "{u["name"].to_s} <{u["email"].to_s}>"
index d91ade5..350a5b2 100644 (file)
@@ -15,4 +15,4 @@
 # limitations under the License.
 
 import s2pn
-import mnit_android
+import mnit::android
index c7ce636..7cc5cee 100644 (file)
@@ -15,4 +15,4 @@
 # limitations under the License.
 
 import s2pn
-import mnit_linux
+import mnit::linux
index bc5e69d..6e237d6 100644 (file)
@@ -19,7 +19,7 @@ module simple_android is
        android_manifest """<uses-permission android:name="android.permission.VIBRATE" />"""
 end
 
-import mnit_android
+import mnit::android
 import android::portrait
 import serialization
 
index c0d8d0f..1ff4235 100644 (file)
@@ -17,7 +17,7 @@
 module simple_linux
 
 import simple
-import mnit_linux
+import mnit::linux
 
 import test_data_store
 
index 8257091..5f093a0 100644 (file)
@@ -616,8 +616,7 @@ class WikiConfig
 
        # Returns the config value at `key` or return `default` if no key was found.
        private fun value_or_default(key: String, default: String): String do
-               if not has_key(key) then return default
-               return self[key]
+               return self[key] or else default
        end
 
        # Site name displayed.
index 0d0b14f..36673f1 100644 (file)
@@ -2,4 +2,6 @@ src/objc_lexer.nit
 src/objc_parser.nit
 src/objc_test_parser.nit
 tests/MyClass.nit
+tests/nsarray.nit
+tests/nsalert.nit
 objc.ast.dot
index 3f39029..4fcfa3b 100644 (file)
@@ -1,4 +1,4 @@
-all: bin/objcwrapper
+all: bin/objcwrapper bin/header_static
 
 ../nitcc/src/nitcc:
        make -C ../nitcc
@@ -23,20 +23,26 @@ check: bin/objc_test_parser bin/objcwrapper
        ../../bin/nitpick tests/MyClass.nit
 
 # Test on classes of libgnustep-base-dev
-check-gnustep:
+check-gnustep: bin/objcwrapper bin/header_static
        gcc -E /usr/include/GNUstep/Foundation/NSArray.h -I /usr/include/GNUstep/ -Wno-deprecated \
        | ../header_keeper/bin/header_keeper /usr/include/GNUstep/Foundation/NSArray.h \
        | bin/header_static > tests/NSArray.pre.h
-       bin/objcwrapper tests/NSArray.pre.h
-       ../../bin/nitpick NSArray.nit
+       bin/objcwrapper tests/NSArray.pre.h -o tests/nsarray.nit
+       ../../bin/nitpick tests/nsarray.nit
 
 # Test on classes of the Apple Foundation framework
-check-apple:
+check-apple: bin/objcwrapper bin/header_static
        gcc -E /System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSArray.h \
        | ../header_keeper/bin/header_keeper /System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSArray.h \
        | bin/header_static > tests/NSArray.pre.h
-       bin/objcwrapper tests/NSArray.pre.h
-       ../../bin/nitpick NSArray.nit
+       bin/objcwrapper tests/NSArray.pre.h -o tests/nsarray.nit
+       ../../bin/nitpick tests/nsarray.nit
 
-bin/header_static:
+       gcc -E /System/Library/Frameworks/AppKit.framework/Headers/NSAlert.h \
+       | ../header_keeper/bin/header_keeper NSAlert.h \
+       | bin/header_static > tests/NSAlert.pre.h
+       bin/objcwrapper tests/NSAlert.pre.h -o tests/nsalert.nit
+       ../../bin/nitpick tests/nsalert.nit
+
+bin/header_static: $(shell ../../bin/nitls -M src/header_static.nit)
        ../../bin/nitc --dir bin src/header_static.nit
diff --git a/contrib/objcwrapper/README.md b/contrib/objcwrapper/README.md
new file mode 100644 (file)
index 0000000..b33a682
--- /dev/null
@@ -0,0 +1,31 @@
+Generator of Nit extern classes to wrap Objective-C services.
+
+# Description
+
+_objcwrapper_ is a tool to help access Objective-C classes and methods from Nit.
+It generates Nit code composed of extern classes and extern methods from the Objective-C FFI.
+The code should be valid Nit, but it may need some tweaking by hand before use.
+
+_objcwrapper_ takes as input preprocessed Objective-C header files.
+This preprocessing is usually done by combinig (or piping) calls to:
+`gcc -E`, `header_keeper` and `header_static`.
+See the check rules in the Makefile for example preprocessing.
+
+# Usage
+
+1. Compile _objcwrapper_ with: `make`
+
+2. Compile the wrapper `NSArray.nit` from the preprocessed header `NSArray.h` with:
+
+       ~~~
+       bin/objcwrapper -o NSArray.h NSArray.h
+       ~~~
+
+3. Import the generated module as usual from any Nit program.
+   It is not recommended to modify the generated file directly,
+   but you can redef the generated classes from other modules.
+
+# See also
+
+_jwrapper_ is the inspiration for this tool.
+It generate wrappers to access Java services from Nit.
index b997614..0a4bb52 100644 (file)
@@ -121,11 +121,13 @@ Parser
         {instance:} '-';
 
     parameter =
-        {named:} [left:]term ':' lpar parameter_type rpar attribute? [right:]term |
+        {named:} [left:]term ':' parameter_type_in_par? attribute? [right:]term |
         {single:} term |
         {comma:} comma '...' |
         {macro:} macro_name;
 
+    parameter_type_in_par = lpar parameter_type rpar;
+
     parameter_type =
         {normal:} type |
         {anonymous:} type anonymous |
@@ -195,7 +197,8 @@ Parser
         'long long int' |
         'float' |
         'double' |
-        'long double';
+        'long double' |
+        'size_t';
 
     classe =
         {class:} class |
index c43fe2a..19ffd72 100644 (file)
@@ -49,7 +49,10 @@ fun header_static(input: Reader, output: Writer) do
 
        while not input.eof do
                var line = input.read_line
-               if line.to_s.has("static") then static_target = true
+
+               if line.has("typedef struct") then continue
+
+               if line.has("static") then static_target = true
 
                if static_target then
                        if line.to_s.has("__attribute__") then static_attribute_target = true
index 25bd84f..845a3b1 100644 (file)
@@ -20,29 +20,69 @@ import opts
 import objc_model
 
 redef class Sys
+
        # Path to the output file
        var opt_output = new OptionString("Output file", "-o")
-end
 
-class CodeGenerator
-       # Merge the calls to `alloc` and `init...` in a single constructor?
+       # Shall `init` methods/constructors be wrapped as methods?
        #
-       # If `true`, also the default behavior, initializing an extern Objective-C object looks like:
+       # By default, these methods/constructors are wrapped as extern constructors.
+       # So initializing an extern Objective-C object looks like:
        # ~~~nitish
        # var o = new NSArray.init_with_array(some_other_array)
        # ~~~
        #
-       # If `false`, the object must first be allocated and then initialized.
+       # If this option is set, the object must first be allocated and then initialized.
        # This is closer to the Objective-C behavior:
        # ~~~nitish
        # var o = new NSArray
        # o.init_with_array(some_other_array)
        # ~~~
-       var init_with_alloc = true is writable
+       var opt_init_as_methods = new OptionBool(
+               "Wrap `init...` constructors as Nit methods instead of Nit constructors",
+               "--init-as-methods")
+
+       private var objc_to_nit_types: Map[String, String] is lazy do
+               var types = new HashMap[String, String]
+               types["char"] = "Byte"
+               types["short"] = "Int"
+               types["short int"] = "Int"
+               types["int"] = "Int"
+               types["long"] = "Int"
+               types["long int"] = "Int"
+               types["long long"] = "Int"
+               types["long long int"] = "Int"
+               types["float"] = "Float"
+               types["double"] = "Float"
+               types["long double"] = "Float"
+
+               types["NSUInteger"] = "Int"
+               types["BOOL"] = "Bool"
+               types["id"] = "NSObject"
+               types["constid"] = "NSObject"
+               types["SEL"] = "NSObject"
+               types["void"] = "Pointer"
+
+               return types
+       end
+end
+
+redef class ObjcModel
+       redef fun knows_type(objc_type) do return super or
+               objc_to_nit_types.keys.has(objc_type)
+end
+
+# Wrapper generator
+class CodeGenerator
+
+       # `ObjcModel` to wrap
+       var model: ObjcModel
 
        # Generate Nit code to wrap `classes`
-       fun generate(classes: Array[ObjcClass])
+       fun generate
        do
+               var classes = model.classes
+
                # Open specified path or stdin
                var file
                var path = opt_output.value
@@ -65,169 +105,210 @@ class CodeGenerator
                if path != null then file.close
        end
 
-       private fun type_convertor(type_word: String): String
+       private fun write_class(classe: ObjcClass, file: Writer)
        do
-               var types = new HashMap[String, String]
-               types["char"] = "Byte"
-               types["short"] = "Int"
-               types["short int"] = "Int"
-               types["int"] = "Int"
-               types["long"] = "Int"
-               types["long int"] = "Int"
-               types["long long"] = "Int"
-               types["long long int"] = "Int"
-               types["float"] = "Float"
-               types["double"] = "Float"
-               types["long double"] = "Float"
+               # Class header
+               file.write """
 
-               types["NSUInteger"] = "Int"
-               types["BOOL"] = "Bool"
-               types["id"] = "NSObject"
+extern class {{{classe.name}}} in "ObjC" `{ {{{classe.name}}} * `}
+"""
 
-               if types.has_key(type_word) then
-                       return types[type_word]
-               else
-                       return type_word
-               end
-       end
+               # Supers
+               for super_name in classe.super_names do file.write """
+       super {{{super_name}}}
+"""
+               if classe.super_names.is_empty then file.write """
+       super NSObject
+"""
 
-       private fun write_class(classe: ObjcClass, file: Writer)
-       do
-               var commented_methods = new Array[ObjcMethod]
-               file.write "extern class " + classe.name + """ in "ObjC" `{ """ + classe.name  + """ * `}\n"""
-               for super_name in classe.super_names do
-                       file.write """  super """ + super_name + "\n"
-               end
-               if classe.super_names.is_empty then file.write """      super NSObject\n"""
-               write_constructor(classe, file)
                file.write "\n"
+
+               # Constructor or constructors
+               write_constructors(classe, file)
+
+               # Attributes
                for attribute in classe.attributes do
                        write_attribute(attribute, file)
                end
+
+               # Methods
                for method in classe.methods do
-                       if method.is_commented then
-                               commented_methods.add(method)
-                       else
-                               if init_with_alloc and method.params.first.name.has("init") then continue
-                               file.write """  """
-                               write_method(method, file)
-                               file.write """ in "ObjC" `{\n           """
-                               write_objc_method_call(method, file)
-                               file.write """  `}"""
-                               if method != classe.methods.last then file.write "\n\n"
-                       end
-               end
-               for commented_method in commented_methods do
-                       if commented_method == commented_methods.first then file.write "\n"
-                       file.write """  #"""
-                       write_method(commented_method, file)
-                       if commented_method != commented_methods.last then file.write "\n"
+                       if not model.knows_all_types(method) then method.is_commented = true
+
+                       if not opt_init_as_methods.value and method.is_init then continue
+
+                       write_method_signature(method, file)
+                       write_objc_method_call(method, file)
                end
-               file.write "\nend\n"
+
+               file.write """
+end
+"""
        end
 
-       private fun write_constructor(classe: ObjcClass, file: Writer)
+       private fun write_constructors(classe: ObjcClass, file: Writer)
        do
-               if init_with_alloc then
-                       for method in classe.methods do
-                               if method.params.first.name.has("init") and not method.is_commented then
-                                       file.write """\n        """
-                                       if method.params.first.name == "init" then
-                                               file.write "new"
-                                       else
-                                               write_method(method, file)
-                                       end
-                                       file.write """ in "ObjC" `{\n"""
-                                       write_objc_init_call(classe.name, method, file)
-                                       file.write """  `}\n"""
-                               end
-                       end
-               else
-                       file.write """\n        new in "ObjC"`{\n"""
-                       file.write """          return [""" + classe.name + " alloc];\n"
-                       file.write """  `}\n"""
+               if opt_init_as_methods.value then
+                       # A single constructor for `alloc`
+                       file.write """
+       new in "ObjC" `{
+               return [{{{classe.name}}} alloc];
+       `}
+
+"""
+                       return
+               end
+
+               # A constructor per `init...` method
+               for method in classe.methods do
+                       if not method.is_init then continue
+
+                       if not model.knows_all_types(method) then method.is_commented = true
+
+                       write_method_signature(method, file)
+
+                               write_objc_init_call(classe.name, method, file)
                end
        end
 
        private fun write_attribute(attribute: ObjcAttribute, file: Writer)
        do
+               if not model.knows_type(attribute.return_type) then attribute.is_commented = true
+
                write_attribute_getter(attribute, file)
                # TODO write_attribute_setter if there is no `readonly` annotation
-               file.write "\n"
        end
 
        private fun write_attribute_getter(attribute: ObjcAttribute, file: Writer)
        do
-               file.write """  fun """ + attribute.name.to_snake_case + ": " + type_convertor(attribute.return_type)
-               file.write """ in "ObjC" `{\n"""
-               file.write """          return [self """ + attribute.name + "];\n"
-               file.write """  `}\n"""
+               var nit_attr_name = attribute.name.to_snake_case
+               var nit_attr_type = attribute.return_type.objc_to_nit_type
+
+               var c = attribute.comment_str
+
+               file.write """
+{{{c}}}        fun {{{nit_attr_name}}}: {{{nit_attr_type}}} in "ObjC" `{
+{{{c}}}                return [self {{{attribute.name}}}];
+{{{c}}}        `}
+
+"""
        end
 
        private fun write_attribute_setter(attribute: ObjcAttribute, file: Writer)
        do
-               file.write """  fun """ + attribute.name.to_snake_case + "=(value: " + type_convertor(attribute.return_type) + ")"
-               file.write " in \"ObjC\" `\{\n"
-               file.write """          self.""" + attribute.name + " = value;\n"
-               file.write """  `}\n"""
+               var nit_attr_name = attribute.name.to_snake_case
+               var nit_attr_type = attribute.return_type.objc_to_nit_type
+
+               var c = attribute.comment_str
+
+               file.write """
+{{{c}}}        fun {{{nit_attr_name}}}=(value: {{{nit_attr_type}}}) in "ObjC" `{
+{{{c}}}                return self.{{{attribute.name}}} = value;
+{{{c}}}        `}
+
+"""
        end
 
-       private fun write_method(method: ObjcMethod, file: Writer)
+       private fun write_method_signature(method: ObjcMethod, file: Writer)
        do
+               var c = method.comment_str
+
+               # Build Nit method name
                var name = ""
                for param in method.params do
                        name += param.name[0].to_upper.to_s + param.name.substring_from(1)
-                       name = name.to_snake_case
                end
-               if name.has("init") and init_with_alloc then
-                       file.write "new "
-               else
-                       if not init_with_alloc and name == "init" then name = "init_0"
-                       file.write "fun "
+               name = name.to_snake_case
+
+               if name == "init" then name = ""
+
+               # Kind of method
+               var fun_keyword = "fun"
+               if not opt_init_as_methods.value and method.is_init then
+                       fun_keyword = "new"
                end
-               file.write name
+
+               # Params
+               var params = new Array[String]
                for param in method.params do
-                       if param == method.params.first and not param.is_single then
-                               file.write "(" + param.variable_name + ": " + type_convertor(param.return_type)
-                       end
-                       if param != method.params.first and not param.is_single then
-                               file.write ", " + param.variable_name + ": " + type_convertor(param.return_type)
-                       end
-                       if param == method.params.last and not param.is_single then
-                               file.write ")"
-                       end
+                       if param.is_single then break
+                       params.add "{param.variable_name}: {param.return_type.objc_to_nit_type}"
                end
-               if method.return_type != "void" and not method.params.first.name.has("init") then
-                       file.write ": " + type_convertor(method.return_type)
+
+               var params_with_par = ""
+               if params.not_empty then params_with_par = "({params.join(", ")})"
+
+               # Return
+               var ret = ""
+               if method.return_type != "void" and fun_keyword != "new" then
+                       ret = ": {method.return_type.objc_to_nit_type}"
                end
+
+               file.write """
+{{{c}}}        {{{fun_keyword}}} {{{name}}}{{{params_with_par}}}{{{ret}}} in "ObjC" `{
+"""
        end
 
-       private fun write_objc_init_call(classe_name: String, method: ObjcMethod, file: Writer)
+       # Write a combined call to alloc and to a constructor/method
+       private fun write_objc_init_call(class_name: String, method: ObjcMethod, file: Writer)
        do
-               file.write """          return [[""" + classe_name + " alloc] "
+               # Method name and other params
+               var params = new Array[String]
                for param in method.params do
                        if not param.is_single then
-                               file.write param.name + ":" + param.variable_name
-                               if not param == method.params.last then file.write " "
-                       else
-                               file.write param.name
-                       end
+                               params.add "{param.name}: {param.variable_name}"
+                       else params.add param.name
                end
-               file.write "];\n"
+
+               var c = method.comment_str
+
+               file.write """
+{{{c}}}                return [[{{{class_name}}} alloc] {{{params.join(" ")}}}];
+{{{c}}}        `}
+
+"""
        end
 
        private fun write_objc_method_call(method: ObjcMethod, file: Writer)
        do
-               if method.return_type != "void" then file.write "return "
-               file.write "[self "
+               # Is there a value to return?
+               var ret = ""
+               if method.return_type != "void" then ret = "return "
+
+               # Method name and other params
+               var params = new Array[String]
                for param in method.params do
                        if not param.is_single then
-                               file.write param.name + ":" + param.variable_name
-                               if not param == method.params.last then file.write " "
-                       else
-                               file.write param.name
-                       end
+                               params.add "{param.name}: {param.variable_name}"
+                       else params.add param.name
                end
-               file.write "];\n"
+
+               var c = method.comment_str
+
+               file.write """
+{{{c}}}                {{{ret}}}[self {{{params.join(" ")}}}];
+{{{c}}}        `}
+
+"""
        end
 end
+
+redef class Text
+       # Nit equivalent to this type
+       private fun objc_to_nit_type: String
+       do
+               var types = sys.objc_to_nit_types
+
+               if types.has_key(self) then
+                       return types[self]
+               else
+                       return to_s
+               end
+       end
+end
+
+redef class Property
+       private fun comment_str: String do if is_commented then
+               return "#"
+       else return ""
+end
index b2465c5..a082d22 100644 (file)
@@ -19,6 +19,33 @@ module objc_model
 class ObjcModel
        # All analyzed classes
        var classes = new Array[ObjcClass]
+
+       # Is `objc_type` known by this model?
+       fun knows_type(objc_type: Text): Bool
+       do
+               for c in classes do
+                       if c.name == objc_type then return true
+               end
+
+               return imported_types.has(objc_type)
+       end
+
+       # Are all types in the signature or `method` known by this model?
+       fun knows_all_types(method: ObjcMethod): Bool
+       do
+               for param in method.params do
+                       if param.is_single then break
+                       if not knows_type(param.return_type) then return false
+               end
+
+               var r = method.return_type
+               return r == "void" or r == "id" or knows_type(r)
+       end
+
+       # Objective-C types available in imported modules
+       #
+       # TODO seach in existing wrappers
+       private var imported_types: Array[String] = ["NSObject", "NSString"]
 end
 
 # Objective-C class
@@ -48,6 +75,9 @@ class ObjcMethod
 
        # Return type as a `String`
        var return_type: String is noinit, writable
+
+       # Does this method look like a constructor/method?
+       fun is_init: Bool do return params.first.name.has_prefix("init")
 end
 
 # Attribute of an `ObjcClass`
index 3d51935..487713d 100644 (file)
@@ -167,7 +167,12 @@ redef class Nparameter_named
                var param = new Param
                param.variable_name = n_right.collect_text
                param.name = n_left.collect_text
-               param.return_type = n_parameter_type.to_type
+
+               var n_type = n_parameter_type_in_par
+               param.return_type = if n_type != null then
+                       n_type.n_parameter_type.to_type
+               else  "NSObject"
+
                return param
        end
 end
index b1abbc6..0e3cb18 100644 (file)
@@ -27,7 +27,7 @@ import objc_parser
 var opt_help = new OptionBool("Show this help message", "-h", "--help")
 
 var opts = new OptionContext
-opts.add_option(opt_help, opt_output)
+opts.add_option(opt_help, opt_output, opt_init_as_methods)
 opts.parse(args)
 
 if opts.errors.not_empty or opts.rest.is_empty or opt_help.value then
@@ -41,7 +41,6 @@ Options:"""
 end
 
 var v = new ObjcVisitor
-var g = new CodeGenerator
 
 for arg in opts.rest do
        # Read input
@@ -64,4 +63,5 @@ for arg in opts.rest do
        v.enter_visit root
 end
 
-g.generate v.model.classes
+var g = new CodeGenerator(v.model)
+g.generate
index c90e3a4..376d5a6 100644 (file)
@@ -20,7 +20,7 @@ module android_client is
        android_api_target 10
 end
 
-import mnit_android
+import mnit::android
 import android::audio
 import android::vibration
 import android::landscape
index d464cc8..6ef6b15 100644 (file)
@@ -15,7 +15,7 @@
 # GNU/Linux client with config saved to `config.json`
 module linux_client
 
-import mnit_linux
+import mnit::linux
 import linux::audio
 import json::serialization
 
index dd4e54d..2d1df4a 100644 (file)
@@ -12,7 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Implementation of circular lists
+# Example of an implementation of circular lists
 # This example shows the usage of generics and somewhat a specialisation of collections.
 module circular_list
 
index 811392b..3a081da 100644 (file)
@@ -12,8 +12,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# This module provide a simple wall clock.
-# It is an example of getters and setters.
+# Example of a simple wall clock class with generalized getters and setters.
+#
 # A beefed-up module is available in clock_more
 module clock
 
index d2ef89e..97b99c1 100644 (file)
@@ -12,8 +12,9 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# This module beef up the clock module by allowing a clock to be comparable.
-# It show the usage of class refinement
+# A beefed up example of the `clock` module that show class refinement.
+#
+# It refines clock to make them comparable.
 module clock_more
 
 import clock
index e1a72c9..d4330ba 100644 (file)
@@ -14,7 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# A simple exemple of refinement where a method is added to the integer class.
+# Simple example of refinement where a method is added to the integer class.
 module fibonacci
 
 redef class Int
index 38cf9e5..7ece167 100644 (file)
@@ -16,7 +16,7 @@
 
 module game_logic
 
-import mnit_android
+import mnit::android
 import android::sensors
 
 class Ball
index be92d49..ebf1eaf 100644 (file)
@@ -20,7 +20,7 @@ end
 
 import dino
 
-import mnit_android
+import mnit::android
 import android::portrait
 import android::vibration
 
index 757cc0b..5d7f97d 100644 (file)
@@ -18,7 +18,7 @@ module dino_linux
 
 import dino
 
-import mnit_linux
+import mnit::linux
 
 redef class ImageSet
        redef fun start_over_path do return "images/play_again.png"
diff --git a/examples/nitcorn/Makefile b/examples/nitcorn/Makefile
deleted file mode 100644 (file)
index 22113aa..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-all:
-       mkdir -p bin/
-       ../../bin/nitc --dir bin src/nitcorn_hello_world.nit src/file_server_on_port_80.nit
-
-xymus.net:
-       mkdir -p bin/
-       ../../bin/nitc --dir bin/ -I ../../contrib/tnitter/src/ -I ../../contrib/benitlux/src/ -I ../../contrib/opportunity/src/ src/xymus_net.nit
index 3bdddc6..1c26e06 100644 (file)
@@ -14,7 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# How to print arguments of the command line.
+# Example of how to print the arguments of the command line.
 module print_arguments
 
 for a in args do
index 838bda0..5f88218 100644 (file)
@@ -14,7 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# A procedural program (without explicit class definition).
+# Example of a procedural program (without explicit class definition).
 # This program manipulates arrays of integers.
 module procedural_array
 
index ade40c7..8527efa 100644 (file)
@@ -1,4 +1,4 @@
-# Rosetta Code
+# Various tasks from Rosetta Code
 
 This are Nit programs that solve some tasks from the [Rosetta Code] project.
 
diff --git a/examples/shoot/README.md b/examples/shoot/README.md
new file mode 100644 (file)
index 0000000..9dae94d
--- /dev/null
@@ -0,0 +1,3 @@
+A multi-platform shoot'em up in space
+
+This unfinished game is an example of the use of `mnit` and `scene2d` to develop a simple multi-platform game.
index 22b9b35..4de7f66 100644 (file)
@@ -16,6 +16,6 @@
 module shoot_android
 
 import shoot
-import mnit_android
+import mnit::android
 
 super
index 6b80432..1587d36 100644 (file)
@@ -16,6 +16,6 @@
 module shoot_linux
 
 import shoot
-import mnit_linux
+import mnit::linux
 
 super
index fdb2f08..f9997e1 100644 (file)
@@ -1,4 +1,4 @@
-Simple toolkit for artificial intelligence
+Simple library for basic artificial intelligence algorithms
 
 This library provides some frameworks for basic algorithms used for artificial intelligence.
 It offers simple generic classes to extends.
diff --git a/lib/ai/examples/examples.nit b/lib/ai/examples/examples.nit
deleted file mode 100644 (file)
index e69de29..0000000
index cbb10b1..c1709ae 100644 (file)
@@ -17,7 +17,7 @@
 # Pointer and hardware key events
 module input_events
 
-import mnit_input
+import mnit::input
 import android
 
 in "C header" `{
diff --git a/lib/bcm2835/examples/examples.nit b/lib/bcm2835/examples/examples.nit
deleted file mode 100644 (file)
index e69de29..0000000
index 7090050..35c65b2 100644 (file)
@@ -8,7 +8,7 @@
 # You  are  allowed  to  redistribute it and sell it, alone or is a part of
 # another product.
 
-# Cartesian products on heterogeneous collections.
+# Memory-efficient Cartesian products on heterogeneous collections.
 #
 # This module is a proof-of-concept to propose memory-efficient views on collections.
 #
@@ -19,6 +19,7 @@
 #
 # However, in Nit, there in no native *tuple* type.
 # So we need a first building block, a pair.
+module cartesian
 
 # A simple read-only pair of two elements `e` and `f`.
 class Pair[E, F]
index cb1612a..184c1ed 100644 (file)
@@ -8,7 +8,7 @@
 # You  are  allowed  to  redistribute it and sell it, alone or is a part of
 # another product.
 
-# Cartesian products, combinations and permutation on collections.
+# Memory-efficient Cartesian products, combinations and permutation on collections.
 #
 # This module offers memory-efficient views on combinatoric collections.
 # Methods of the views create objects only when needed.
index cd780d0..7833f37 100644 (file)
@@ -1,4 +1,4 @@
-# Nit Core Library
+# Nit common library of core classes and methods
 
 Core classes and methods used by default by Nit programs and libraries.
 
index 9a88cc1..5cd317c 100644 (file)
@@ -1808,6 +1808,12 @@ redef class NativeString
 
        # Returns `self` as a String of `length`.
        fun to_s_with_length(length: Int): String is abstract
+
+       # Returns `self` as a String with `bytelen` and `length` set
+       #
+       # SEE: `abstract_text::Text` for more infos on the difference
+       # between `Text::bytelen` and `Text::length`
+       fun to_s_full(bytelen, unilen: Int): String is abstract
 end
 
 redef class NativeArray[E]
index 75cf729..2343f9f 100644 (file)
@@ -626,7 +626,7 @@ class FlatBuffer
        do
                written = true
                if bytelen == 0 then items = new NativeString(1)
-               return new FlatString.with_infos(items, bytelen, 0, bytelen - 1)
+               return new FlatString.full(items, bytelen, 0, bytelen - 1, length)
        end
 
        redef fun to_cstring
@@ -737,7 +737,7 @@ class FlatBuffer
 
        redef fun times(repeats)
        do
-               var x = new FlatString.with_infos(items, bytelen, 0, bytelen - 1)
+               var x = new FlatString.full(items, bytelen, 0, bytelen - 1, length)
                for i in [1 .. repeats[ do
                        append(x)
                end
@@ -922,6 +922,10 @@ redef class NativeString
                return str
        end
 
+       redef fun to_s_full(bytelen, unilen) do
+               return new FlatString.full(self, bytelen, 0, bytelen - 1, unilen)
+       end
+
        # Returns `self` as a new String.
        redef fun to_s_with_copy: FlatString
        do
index 2b3ff28..f582292 100644 (file)
@@ -559,7 +559,7 @@ redef class FlatString
                        var ns = new NativeString(nlen + 1)
                        mits.copy_to(ns, mlen, mifrom, 0)
                        sits.copy_to(ns, slen, sifrom, mlen)
-                       return ns.to_s_with_length(nlen)
+                       return new FlatString.full(ns, nlen, 0, nlen - 1, length + s.length)
                else if s isa Concat then
                        var sl = s.left
                        var sllen = sl.bytelen
diff --git a/lib/curl/examples/examples.nit b/lib/curl/examples/examples.nit
deleted file mode 100644 (file)
index e69de29..0000000
index ae8394e..5a1336a 100644 (file)
@@ -8,7 +8,7 @@
 # You  are  allowed  to  redistribute it and sell it, alone or is a part of
 # another product.
 
-# Automatic derivable implementation of standard basic methods.
+# Automatic derivable implementations of standard basic methods.
 #
 # This module introduce `Derivable` as the main interface to implement (or auto-implement) and
 # provides additional mixin-interfaces with specific default behavior of standard basic methods based
diff --git a/lib/glesv2/examples/examples.nit b/lib/glesv2/examples/examples.nit
deleted file mode 100644 (file)
index e69de29..0000000
index 6389e09..48d49ab 100644 (file)
@@ -22,7 +22,7 @@ module opengles2_hello_triangle
 
 import glesv2
 import egl
-import mnit_linux::sdl
+import sdl
 import x11
 
 if "NIT_TESTING".environ == "true" then exit(0)
diff --git a/lib/html/examples/examples.nit b/lib/html/examples/examples.nit
deleted file mode 100644 (file)
index e69de29..0000000
index 2f81240..84b200c 100644 (file)
@@ -41,41 +41,32 @@ class ConfigTree
 
        # Get the config value for `key`
        #
-       # REQUIRE: `has_key(key)`
-       #
        #     var config = new ConfigTree("config.ini")
        #     assert config["goo"] == "goo"
        #     assert config["foo.bar"] == "foobar"
        #     assert config["foo.baz"] == "foobaz"
-       fun [](key: String): String do
-               if not has_key(key) then
-                       print "error: config key `{key}` not found"
-                       abort
-               end
-               var node = get_node(key).as(not null)
-               if node.value == null then
-                       print "error: config key `{key}` has no value"
-                       abort
-               end
-               return node.value.as(not null)
+       #     assert config["fail.fail"] == null
+       fun [](key: String): nullable String do
+               var node = get_node(key)
+               if node == null then return null
+               return node.value
        end
 
        # Get the config values under `key`
        #
-       # REQUIRE: `has_key(key)`
-       #
        #     var config = new ConfigTree("config.ini")
        #     var values = config.at("foo")
        #     assert values.has_key("bar")
        #     assert values.has_key("baz")
        #     assert not values.has_key("goo")
-       fun at(key: String): Map[String, String] do
-               if not has_key(key) then
-                       print "error: config key `{key}` not found"
-                       abort
-               end
+       #
+       # Return null if the key does not exists.
+       #
+       #     assert config.at("fail.fail") == null
+       fun at(key: String): nullable Map[String, String] do
+               var node = get_node(key)
+               if node == null then return null
                var map = new HashMap[String, String]
-               var node = get_node(key).as(not null)
                for k, child in node.children do
                        if child.value == null then continue
                        map[k] = child.value.to_s
@@ -278,7 +269,7 @@ class ConfigTree
        private fun get_node(key: String): nullable ConfigNode do
                var parts = key.split(".").reversed
                var node = get_root(parts.pop)
-               while not parts.is_empty do
+               while node != null and not parts.is_empty do
                        node = node.get_child(parts.pop)
                end
                return node
index 34470ad..30f55a4 100644 (file)
@@ -18,7 +18,7 @@
 module audio
 
 import app::audio
-import mnit_linux::linux_assets
+import mnit::linux::linux_assets
 
 # Simple audio asset
 redef class Sound
similarity index 97%
rename from lib/mnit_android/mnit_android.nit
rename to lib/mnit/android/android.nit
index 9ba623b..5ebead7 100644 (file)
@@ -15,7 +15,7 @@
 # limitations under the License.
 
 # Android support for MNit
-module mnit_android
+module android
 
 import android_app
 import android_opengles1
similarity index 99%
rename from lib/mnit_android/android_app.nit
rename to lib/mnit/android/android_app.nit
index 41ed7f8..61af9df 100644 (file)
@@ -21,8 +21,8 @@ module android_app is android_manifest_activity """
 """
 
 import mnit
-import android
 import mnit::opengles1
+import ::android
 intrude import ::android::input_events
 
 in "C" `{
similarity index 98%
rename from lib/mnit_android/android_opengles1.nit
rename to lib/mnit/android/android_opengles1.nit
index 71e59aa..b85db15 100644 (file)
@@ -19,7 +19,6 @@
 module android_opengles1 is ldflags "-lEGL -lGLESv1_CM"
 
 import android_app
-import android
 
 in "C" `{
        #include <android_native_app_glue.h>
index d46b5e4..337304a 100644 (file)
@@ -18,7 +18,7 @@
 module assets
 
 import mnit_app
-import mnit_display
+import mnit::display
 
 # General asset
 interface Asset
similarity index 98%
rename from lib/mnit_display.nit
rename to lib/mnit/display.nit
index fad361e..8c67d13 100644 (file)
@@ -15,9 +15,9 @@
 # limitations under the License.
 
 # Defines abstract display classes
-module mnit_display
+module display
 
-import mnit_input
+import mnit::input
 
 # Any class with a size
 interface Sized
similarity index 99%
rename from lib/mnit_input.nit
rename to lib/mnit/input.nit
index ea035b3..dca8107 100644 (file)
@@ -16,7 +16,7 @@
 
 # Defines abstract classes for user and general inputs to the application.
 # Implemented independantly for each platforms and technologies.
-module mnit_input
+module input
 
 # Input to the App, propagated through `App::input`.
 interface InputEvent
similarity index 97%
rename from lib/mnit_linux/mnit_linux.nit
rename to lib/mnit/linux/linux.nit
index 3596d7e..d6515d1 100644 (file)
@@ -15,7 +15,7 @@
 # limitations under the License.
 
 # Linux support for MNit
-module mnit_linux
+module linux
 
 import linux_app
 import linux_opengles1
similarity index 98%
rename from lib/mnit_linux/linux_app.nit
rename to lib/mnit/linux/linux_app.nit
index 45d15c1..938bfd8 100644 (file)
@@ -19,7 +19,7 @@ module linux_app
 import mnit
 import sdl
 import linux_opengles1
-import linux
+import ::linux
 
 in "C" `{
        #include <EGL/egl.h>
index af897a2..98641f7 100644 (file)
@@ -14,7 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# General module for cross-compatibility between multiple platforms
+# Multiplatform game framework for Nit
 module mnit
 
 import mnit_app
index 9987b8c..4ae8b89 100644 (file)
@@ -18,7 +18,7 @@
 module mnit_app
 
 import ::app
-import mnit_display
+import mnit::display
 
 # An App instance serves as base to every Mnit projects.
 #
index 3ee92fe..e2b02bc 100644 (file)
@@ -17,7 +17,7 @@
 # OpenGL ES1 general support (most of it)
 module opengles1 is pkgconfig("glesv1_cm", "egl")
 
-import mnit_display
+import mnit::display
 
 in "C header" `{
        #include <EGL/egl.h>
index c9dbb16..3193993 100644 (file)
@@ -15,7 +15,7 @@
 # Manage images that are tileset or glyphset (for bitmap fonts)
 module tileset
 
-import mnit_display
+import mnit::display
 
 # Efficiently retrieve tiles in a big image
 class TileSet
diff --git a/lib/nitcorn/examples/Makefile b/lib/nitcorn/examples/Makefile
new file mode 100644 (file)
index 0000000..e09cde1
--- /dev/null
@@ -0,0 +1,7 @@
+all:
+       mkdir -p bin/
+       ../../bin/nitc --dir bin src/nitcorn_hello_world.nit src/file_server_on_port_80.nit
+
+xymus.net:
+       mkdir -p bin/
+       ../../bin/nitc --dir bin/ src/xymus_net.nit
similarity index 98%
rename from examples/nitcorn/src/xymus_net.nit
rename to lib/nitcorn/examples/src/xymus_net.nit
index b8a869a..89b9e0b 100644 (file)
@@ -22,8 +22,8 @@ import privileges
 
 # Use actions defined by contribs
 import tnitter
-import benitlux_controller
-import opportunity_controller
+import benitlux::benitlux_controller
+import opportunity::opportunity_controller
 
 # Header for the whole site
 class MasterHeader
index b507fb3..2a6a10c 100644 (file)
@@ -14,7 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# management and display of ordered trees
+# Manipulation and presentation of ordered trees.
 module ordered_tree
 
 # Generic structure to manage and display an ordered tree
diff --git a/lib/privileges/examples/examples.nit b/lib/privileges/examples/examples.nit
deleted file mode 100644 (file)
index e69de29..0000000
index aa567e8..2fadd09 100644 (file)
@@ -12,7 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# 2D management of game elements
+# Framework for 2D management of game elements
 #
 # TODO: collision framework (with quad tree?)
 module scene2d
index db0f700..04d9fac 100644 (file)
@@ -20,7 +20,7 @@ module sdl is
        ldflags(exec("sdl-config", "--libs"), "-lSDL_image -lSDL_ttf")
 end
 
-import mnit_display
+import mnit::display
 import c
 
 in "C header" `{
diff --git a/lib/socket/examples/examples.nit b/lib/socket/examples/examples.nit
deleted file mode 100644 (file)
index e69de29..0000000
index b865017..2a26fa6 100644 (file)
@@ -10,8 +10,7 @@
 # You  are  allowed  to  redistribute it and sell it, alone or is a part of
 # another product.
 
-# Symbol classes
-# FIXME: Should be deprecated soon
+# Library for simple interning of strings
 module symbol
 
 redef class String
diff --git a/lib/template/examples/examples.nit b/lib/template/examples/examples.nit
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/lib/websocket/examples/examples.nit b/lib/websocket/examples/examples.nit
deleted file mode 100644 (file)
index e69de29..0000000
index cc7cd88..1e1a235 100644 (file)
@@ -16,8 +16,8 @@
 module astbuilder
 
 intrude import semantize::typing
-intrude import semantize::literal
-intrude import semantize::parser
+intrude import literal
+intrude import parser
 intrude import semantize::scope
 
 # General factory to build semantic nodes in the AST of expressions
index b58d4a3..2f83449 100644 (file)
@@ -1598,8 +1598,9 @@ abstract class AbstractCompilerVisitor
                var native_mtype = mmodule.native_string_type
                var nat = self.new_var(native_mtype)
                self.add("{nat} = \"{string.escape_to_c}\";")
-               var length = self.int_instance(string.bytelen)
-               self.add("{res} = {self.send(self.get_property("to_s_with_length", native_mtype), [nat, length]).as(not null)};")
+               var bytelen = self.int_instance(string.bytelen)
+               var unilen = self.int_instance(string.length)
+               self.add("{res} = {self.send(self.get_property("to_s_full", native_mtype), [nat, bytelen, unilen]).as(not null)};")
                self.add("{name} = {res};")
                self.add("\}")
                return res
index 1181cf0..23a7b0c 100644 (file)
@@ -343,7 +343,7 @@ class NaiveInterpreter
        fun string_instance(txt: String): Instance
        do
                var nat = native_string_instance(txt)
-               var res = self.send(self.force_get_primitive_method("to_s_with_length", nat.mtype), [nat, self.int_instance(txt.bytelen)])
+               var res = self.send(self.force_get_primitive_method("to_s_full", nat.mtype), [nat, self.int_instance(txt.bytelen), self.int_instance(txt.length)])
                assert res != null
                return res
        end
index bde4ae2..eb3d06d 100644 (file)
@@ -287,6 +287,20 @@ redef class ModelBuilder
                return res.first
        end
 
+       # Search groups named `name` from paths `lookpaths`.
+       private fun search_group_in_paths(name: String, lookpaths: Collection[String]): ArraySet[MGroup]
+       do
+               var res = new ArraySet[MGroup]
+               for dirname in lookpaths do
+                       # try a single group directory
+                       var mg = get_mgroup(dirname/name)
+                       if mg != null then
+                               res.add mg
+                       end
+               end
+               return res
+       end
+
        # Cache for `identify_file` by realpath
        private var identified_files_by_path = new HashMap[String, nullable ModulePath]
 
@@ -446,8 +460,7 @@ redef class ModelBuilder
                var mgroup
                if parent == null then
                        # no parent, thus new project
-                       var namekey = "project.name"
-                       if ini != null and ini.has_key(namekey) then pn = ini[namekey]
+                       if ini != null then pn = ini["project.name"] or else pn
                        var mproject = new MProject(pn, model)
                        mgroup = new MGroup(pn, mproject, null) # same name for the root group
                        mproject.root = mgroup
@@ -702,14 +715,77 @@ redef class ModelBuilder
        # This method handles qualified names as used in `AModuleName`.
        fun seach_module_by_amodule_name(n_name: AModuleName, mgroup: nullable MGroup): nullable ModulePath
        do
-               if n_name.n_quad != null then mgroup = null # Start from top level
-               for grp in n_name.n_path do
-                       var path = search_mmodule_by_name(grp, mgroup, grp.text)
-                       if path == null then return null # Forward error
-                       mgroup = path.mgroup
-               end
                var mod_name = n_name.n_id.text
-               return search_mmodule_by_name(n_name, mgroup, mod_name)
+
+               # If a quad is given, we ignore the starting group (go from path)
+               if n_name.n_quad != null then mgroup = null
+
+               # If name not qualified, just search the name
+               if n_name.n_path.is_empty then
+                       # Fast search if no n_path
+                       return search_mmodule_by_name(n_name, mgroup, mod_name)
+               end
+
+               # If qualified and in a group
+               if mgroup != null then
+                       # First search in the project
+                       var r = mgroup.mproject.root
+                       assert r != null
+                       scan_group(r)
+                       # Get all modules with the final name
+                       var res = r.mmodule_paths_by_name(mod_name)
+                       # Filter out the name that does not match the qualifiers
+                       res = [for x in res do if match_amodulename(n_name, x) then x]
+                       if res.not_empty then
+                               if res.length > 1 then
+                                       error(n_name, "Error: conflicting module files for `{mod_name}`: `{res.join(",")}`")
+                               end
+                               return res.first
+                       end
+               end
+
+               # If no module yet, then assume that the first element of the path
+               # Is to be searched in the path.
+               var root_name = n_name.n_path.first.text
+               var roots = search_group_in_paths(root_name, paths)
+               if roots.is_empty then
+                       error(n_name, "Error: cannot find `{root_name}`. Tried: {paths.join(", ")}.")
+                       return null
+               end
+
+               var res = new ArraySet[ModulePath]
+               for r in roots do
+                       # Then, for each root, collect modules that matches the qualifiers
+                       scan_group(r)
+                       var root_res = r.mmodule_paths_by_name(mod_name)
+                       for x in root_res do if match_amodulename(n_name, x) then res.add x
+               end
+               if res.not_empty then
+                       if res.length > 1 then
+                               error(n_name, "Error: conflicting module files for `{mod_name}`: `{res.join(",")}`")
+                       end
+                       return res.first
+               end
+               # If still nothing, just call a basic search that will fail and will produce an error message
+               error(n_name, "Error: cannot find module `{mod_name}` from `{root_name}`. Tried: {paths.join(", ")}.")
+               return null
+       end
+
+       # Is elements of `n_name` correspond to the group nesting of `m`?
+       #
+       # Basically it check that `bar::foo` matches `bar/foo.nit` and `bar/baz/foo.nit`
+       # but not `baz/foo.nit` nor `foo/bar.nit`
+       #
+       # Is used by `seach_module_by_amodule_name` to validate qualified names.
+       private fun match_amodulename(n_name: AModuleName, m: ModulePath): Bool
+       do
+               var g: nullable MGroup = m.mgroup
+               for grp in n_name.n_path.reverse_iterator do
+                       while g != null and grp.text != g.name do
+                               g = g.parent
+                       end
+               end
+               return g != null
        end
 
        # Analyze the module importation and fill the module_importation_hierarchy
diff --git a/src/nitcatalog.nit b/src/nitcatalog.nit
new file mode 100644 (file)
index 0000000..6aba6f1
--- /dev/null
@@ -0,0 +1,815 @@
+# 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.
+
+# Basic catalog generator for Nit projects
+#
+# See: <http://nitlanguage.org/catalog/>
+#
+# The tool scans projects and generates the HTML files of a catalog.
+#
+# ## Features
+#
+# * [X] scan projects and their `.ini`
+# * [X] generate lists of projects
+# * [X] generate a page per project with the readme and most metadata
+# * [ ] link/include/be included in the documentation
+# * [ ] propose `related projects`
+# * [ ] show directory content (a la nitls)
+# * [X] gather git information from the working directory
+# * [ ] gather git information from the repository
+# * [ ] gather project information from github
+# * [ ] gather people information from github
+# * [ ] reify people
+# * [ ] separate information gathering from rendering
+# * [ ] move up information gathering in (existing or new) service modules
+# * [ ] add command line options
+# * [ ] harden HTML (escaping, path injection, etc)
+# * [ ] nitcorn server with RESTful API
+#
+# ## Issues and limitations
+#
+# The tool works likee the other tools and expects to find valid Nit source code in the directories
+#
+# * cruft and temporary files will be collected
+# * missing source file (e.g. not yet generated by nitcc) will make information
+#   incomplete (e.g. invalid module thus partial dependency and metrics)
+#
+# How to use the tool as the basis of a Nit code archive on the web usable with a package manager is not clear.
+module nitcatalog
+
+import loader # Scan&load projects, groups and modules
+import doc::doc_down # Display mdoc
+import md5 # To get gravatar images
+import counter # For statistics
+import modelize # To process and count classes and methods
+
+redef class MProject
+       # Return the associated metadata from the `ini`, if any
+       fun metadata(key: String): nullable String
+       do
+               var ini = self.ini
+               if ini == null then return null
+               return ini[key]
+       end
+
+       # The list of maintainers
+       var maintainers = new Array[String]
+
+       # The list of contributors
+       var contributors = new Array[String]
+
+       # The date of the most recent commit
+       var last_date: nullable String = null
+
+       # The date of the oldest commit
+       var first_date: nullable String = null
+end
+
+# A HTML page in a catalog
+#
+# This is just a template with the header pre-filled and the footer injected at rendering.
+# Therefore, once instantiated, the content can just be added to it.
+class CatalogPage
+       super Template
+
+       # Placeholder to include additional things before the `</head>`.
+       var more_head = new Template
+
+       redef init
+       do
+               add """
+<!DOCTYPE html>
+<html>
+<head>
+       <meta charset="utf-8">
+       <link rel="stylesheet" media="all" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css">
+       <link rel="stylesheet" media="all" href="style.css">
+"""
+               add more_head
+
+               add """
+</head>
+<body>
+<div class='container-fluid'>
+ <div class='row'>
+  <nav id='topmenu' class='navbar navbar-default navbar-fixed-top' role='navigation'>
+   <div class='container-fluid'>
+    <div class='navbar-header'>
+     <button type='button' class='navbar-toggle' data-toggle='collapse' data-target='#topmenu-collapse'>
+      <span class='sr-only'>Toggle menu</span>
+      <span class='icon-bar'></span>
+      <span class='icon-bar'></span>
+      <span class='icon-bar'></span>
+     </button>
+     <span class='navbar-brand'><a href="http://nitlanguage.org/">Nitlanguage.org</a></span>
+    </div>
+    <div class='collapse navbar-collapse' id='topmenu-collapse'>
+     <ul class='nav navbar-nav'>
+      <li><a href="index.html">Catalog</a></li>
+     </ul>
+    </div>
+   </div>
+  </nav>
+ </div>
+"""
+       end
+
+       redef fun rendering
+       do
+               add """
+</div> <!-- container-fluid -->
+<script src='https://code.jquery.com/jquery-latest.min.js'></script>
+<script src='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js'></script>
+<script src='https://cdnjs.cloudflare.com/ajax/libs/bootstrap-table/1.8.1/bootstrap-table-all.min.js'></script>
+</body>
+</html>
+"""
+       end
+end
+
+redef class Int
+       # Returns `log(self+1)`. Used to compute score of projects
+       fun score: Float do return (self+1).to_f.log
+end
+
+# The main class of the calatog generator that has the knowledge
+class Catalog
+
+       # The modelbuilder
+       # used to access the files and count source lines of code
+       var modelbuilder: ModelBuilder
+
+       # Projects by tag
+       var tag2proj = new MultiHashMap[String, MProject]
+
+       # Projects by category
+       var cat2proj = new MultiHashMap[String, MProject]
+
+       # Projects by maintainer
+       var maint2proj = new MultiHashMap[String, MProject]
+
+       # Projects by contributors
+       var contrib2proj = new MultiHashMap[String, MProject]
+
+       # Dependency between projects
+       var deps = new POSet[MProject]
+
+       # Number of modules by project
+       var mmodules = new Counter[MProject]
+
+       # Number of classes by project
+       var mclasses = new Counter[MProject]
+
+       # Number of methods by project
+       var mmethods = new Counter[MProject]
+
+       # Number of line of code by project
+       var loc = new Counter[MProject]
+
+       # Number of commits by project
+       var commits = new Counter[MProject]
+
+       # Score by project
+       #
+       # The score is loosely computed using other metrics
+       var score = new Counter[MProject]
+
+       # Scan, register and add a contributor to a project
+       fun add_contrib(person: String, mproject: MProject, res: Template)
+       do
+               var projs = contrib2proj[person]
+               if not projs.has(mproject) then projs.add mproject
+               var name = person
+               var email = null
+               var page = null
+
+               # Regular expressions are broken, need to investigate.
+               # So split manually.
+               #
+               #var re = "([^<(]*?)(<([^>]*?)>)?(\\((.*)\\))?".to_re
+               #var m = (person+" ").search(re)
+               #print "{person}: `{m or else "?"}` `{m[1] or else "?"}` `{m[3] or else "?"}` `{m[5] or else "?"}`"
+               do
+                       var sp1 = person.split_once_on("<")
+                       if sp1.length < 2 then
+                               break
+                       end
+                       var sp2 = sp1.last.split_once_on(">")
+                       if sp2.length < 2 then
+                               break
+                       end
+                       name = sp1.first.trim
+                       email = sp2.first.trim
+                       var sp3 = sp2.last.split_once_on("(")
+                       if sp3.length < 2 then
+                               break
+                       end
+                       var sp4 = sp3.last.split_once_on(")")
+                       if sp4.length < 2 then
+                               break
+                       end
+                       page = sp4.first.trim
+               end
+
+               var e = name.html_escape
+               res.add "<li>"
+               if page != null then
+                       res.add "<a href=\"{page.html_escape}\">"
+               end
+               if email != null then
+                       # TODO get more things from github by using the email as a key
+                       # "https://api.github.com/search/users?q={email}+in:email"
+                       var md5 = email.md5.to_lower
+                       res.add "<img src=\"https://secure.gravatar.com/avatar/{md5}?size=20&amp;default=retro\">&nbsp;"
+               end
+               res.add "{e}"
+               if page != null then res.add "</a>"
+               res.add "</li>"
+       end
+
+
+       # Compute information and generate a full HTML page for a project
+       fun project_page(mproject: MProject): Writable
+       do
+               var res = new CatalogPage
+               var score = score[mproject].to_f
+               var name = mproject.name.html_escape
+               res.more_head.add """<title>{{{name}}}</title>"""
+
+               res.add """
+<div class="content">
+<h1 class="package-name">{{{name}}}</h1>
+"""
+               var mdoc = mproject.mdoc_or_fallback
+               if mdoc != null then
+                       score += 100.0
+                       res.add mdoc.html_documentation
+                       score += mdoc.content.length.score
+               end
+               res.add """
+</div>
+<div class="sidebar">
+<ul class="box">
+"""
+               var homepage = mproject.metadata("upstream.homepage")
+               if homepage != null then
+                       score += 5.0
+                       var e = homepage.html_escape
+                       res.add "<li><a href=\"{e}\">{e}</a></li>\n"
+               end
+               var maintainer = mproject.metadata("project.maintainer")
+               if maintainer != null then
+                       score += 5.0
+                       add_contrib(maintainer, mproject, res)
+                       mproject.maintainers.add maintainer
+                       var projs = maint2proj[maintainer]
+                       if not projs.has(mproject) then projs.add mproject
+               end
+               var license = mproject.metadata("project.license")
+               if license != null then
+                       score += 5.0
+                       var e = license.html_escape
+                       res.add "<li><a href=\"http://opensource.org/licenses/{e}\">{e}</a> license</li>\n"
+               end
+               res.add "</ul>\n"
+
+               res.add "<h3>Source Code</h3>\n<ul class=\"box\">\n"
+               var browse = mproject.metadata("upstream.browse")
+               if browse != null then
+                       score += 5.0
+                       var e = browse.html_escape
+                       res.add "<li><a href=\"{e}\">{e}</a></li>\n"
+               end
+               var git = mproject.metadata("upstream.git")
+               if git != null then
+                       var e = git.html_escape
+                       res.add "<li><tt>{e}</tt></li>\n"
+               end
+               var last_date = mproject.last_date
+               if last_date != null then
+                       var e = last_date.html_escape
+                       res.add "<li>most recent commit: {e}</li>\n"
+               end
+               var first_date = mproject.first_date
+               if first_date != null then
+                       var e = first_date.html_escape
+                       res.add "<li>oldest commit: {e}</li>\n"
+               end
+               var commits = commits[mproject]
+               if commits != 0 then
+                       res.add "<li>{commits} commits</li>\n"
+               end
+               res.add "</ul>\n"
+
+               res.add "<h3>Tags</h3>\n"
+               var tags = mproject.metadata("project.tags")
+               var ts2 = new Array[String]
+               var cat = null
+               if tags != null then
+                       var ts = tags.split(",")
+                       for t in ts do
+                               t = t.trim
+                               if t == "" then continue
+                               if cat == null then cat = t
+                               tag2proj[t].add mproject
+                               t = t.html_escape
+                               ts2.add "<a href=\"index.html#tag_{t}\">{t}</a>"
+                       end
+                       res.add_list(ts2, ", ", ", ")
+               end
+               if ts2.is_empty then
+                       var t = "none"
+                       cat = t
+                       tag2proj[t].add mproject
+                       res.add "<a href=\"index.html#tag_{t}\">{t}</a>"
+               end
+               if cat != null then cat2proj[cat].add mproject
+               score += ts2.length.score
+
+               var reqs = deps[mproject].greaters.to_a
+               reqs.remove(mproject)
+               alpha_comparator.sort(reqs)
+               res.add "<h3>Requirements</h3>\n"
+               if reqs.is_empty then
+                       res.add "none"
+               else
+                       var list = new Array[String]
+                       for r in reqs do
+                               var direct = deps.has_direct_edge(mproject, r)
+                               var s = "<a href=\"{r}.html\">"
+                               if direct then s += "<strong>"
+                               s += r.to_s
+                               if direct then s += "</strong>"
+                               s += "</a>"
+                               list.add s
+                       end
+                       res.add_list(list, ", ", " and ")
+               end
+
+               reqs = deps[mproject].smallers.to_a
+               reqs.remove(mproject)
+               alpha_comparator.sort(reqs)
+               res.add "<h3>Clients</h3>\n"
+               if reqs.is_empty then
+                       res.add "none"
+               else
+                       var list = new Array[String]
+                       for r in reqs do
+                               var direct = deps.has_direct_edge(r, mproject)
+                               var s = "<a href=\"{r}.html\">"
+                               if direct then s += "<strong>"
+                               s += r.to_s
+                               if direct then s += "</strong>"
+                               s += "</a>"
+                               list.add s
+                       end
+                       res.add_list(list, ", ", " and ")
+               end
+
+               score += deps[mproject].greaters.length.score
+               score += deps[mproject].direct_greaters.length.score
+               score += deps[mproject].smallers.length.score
+               score += deps[mproject].direct_smallers.length.score
+
+               var contributors = mproject.contributors
+               if not contributors.is_empty then
+                       res.add "<h3>Contributors</h3>\n<ul class=\"box\">"
+                       for c in contributors do
+                               add_contrib(c, mproject, res)
+                       end
+                       res.add "</ul>"
+               end
+               score += contributors.length.to_f
+
+               var mmodules = 0
+               var mclasses = 0
+               var mmethods = 0
+               var loc = 0
+               for g in mproject.mgroups do
+                       mmodules += g.module_paths.length
+                       for m in g.mmodules do
+                               var am = modelbuilder.mmodule2node(m)
+                               if am != null then
+                                       var file = am.location.file
+                                       if file != null then
+                                               loc += file.line_starts.length - 1
+                                       end
+                               end
+                               for cd in m.mclassdefs do
+                                       mclasses += 1
+                                       for pd in cd.mpropdefs do
+                                               if not pd isa MMethodDef then continue
+                                               mmethods += 1
+                                       end
+                               end
+                       end
+               end
+               self.mmodules[mproject] = mmodules
+               self.mclasses[mproject] = mclasses
+               self.mmethods[mproject] = mmethods
+               self.loc[mproject] = loc
+
+               #score += mmodules.score
+               score += mclasses.score
+               score += mmethods.score
+               score += loc.score
+
+               res.add """
+<h3>Stats</h3>
+<ul class="box">
+<li>{{{mmodules}}} modules</li>
+<li>{{{mclasses}}} classes</li>
+<li>{{{mmethods}}} methods</li>
+<li>{{{loc}}} lines of code</li>
+</ul>
+"""
+
+               res.add """
+</div>
+"""
+               self.score[mproject] = score.to_i
+
+               return res
+       end
+
+       # Return a short HTML sequence for a project
+       #
+       # Intended to use in lists.
+       fun li_project(p: MProject): String
+       do
+               var res = ""
+               var f = "{p.name}.html"
+               res += "<a href=\"{f}\">{p}</a>"
+               var d = p.mdoc_or_fallback
+               if d != null then res += " - {d.html_synopsis.write_to_string}"
+               return res
+       end
+
+       # List projects by group.
+       #
+       # For each key of the `map` a `<h3>` is generated.
+       # Each project is then listed.
+       #
+       # The list of keys is generated first to allow fast access to the correct `<h3>`.
+       # `id_prefix` is used to give an id to the `<h3>` element.
+       fun list_by(map: MultiHashMap[String, MProject], id_prefix: String): Template
+       do
+               var res = new Template
+               var keys = map.keys.to_a
+               alpha_comparator.sort(keys)
+               var list = [for x in keys do "<a href=\"#{id_prefix}{x.html_escape}\">{x.html_escape}</a>"]
+               res.add_list(list, ", ", " and ")
+
+               for k in keys do
+                       var projs = map[k].to_a
+                       alpha_comparator.sort(projs)
+                       var e = k.html_escape
+                       res.add "<h3 id=\"{id_prefix}{e}\">{e} ({projs.length})</h3>\n<ul>\n"
+                       for p in projs do
+                               res.add "<li>"
+                               res.add li_project(p)
+                               res.add "</li>"
+                       end
+                       res.add "</ul>"
+               end
+               return res
+       end
+
+       # List the 10 best projects from `cpt`
+       fun list_best(cpt: Counter[MProject]): Template
+       do
+               var res = new Template
+               res.add "<ul>"
+               var best = cpt.sort
+               for i in [1..10] do
+                       if i > best.length then break
+                       var p = best[best.length-i]
+                       res.add "<li>"
+                       res.add li_project(p)
+                       # res.add " ({cpt[p]})"
+                       res.add "</li>"
+               end
+               res.add "</ul>"
+               return res
+       end
+
+       # Collect more information on a project using the `git` tool.
+       fun git_info(mproject: MProject)
+       do
+               var ini = mproject.ini
+               if ini == null then return
+
+               # TODO use real git info
+               #var repo = ini.get_or_null("upstream.git")
+               #var branch = ini.get_or_null("upstream.git.branch")
+               #var directory = ini.get_or_null("upstream.git.directory")
+
+               var dirpath = mproject.root.filepath
+               if dirpath == null then return
+
+               # Collect commits info
+               var res = git_run("log", "--no-merges", "--follow", "--pretty=tformat:%ad;%aN <%aE>", "--", dirpath)
+               var contributors = new Counter[String]
+               var commits = res.split("\n")
+               if commits.not_empty and commits.last == "" then commits.pop
+               self.commits[mproject] = commits.length
+               for l in commits do
+                       var s = l.split_once_on(';')
+                       if s.length != 2 or s.last == "" then continue
+
+                       # Collect date of last and first commit
+                       if mproject.last_date == null then mproject.last_date = s.first
+                       mproject.first_date = s.first
+
+                       # Count contributors
+                       contributors.inc(s.last)
+               end
+               for c in contributors.sort.reverse_iterator do
+                       mproject.contributors.add c
+               end
+
+       end
+
+       # Produce a HTML table containig information on the projects
+       #
+       # `project_page` must have been called before so that information is computed.
+       fun table_projects(mprojects: Array[MProject]): Template
+       do
+               alpha_comparator.sort(mprojects)
+               var res = new Template
+               res.add "<table data-toggle=\"table\" data-sort-name=\"name\" data-sort-order=\"desc\" width=\"100%\">\n"
+               res.add "<thead><tr>\n"
+               res.add "<th data-field=\"name\" data-sortable=\"true\">name</th>\n"
+               res.add "<th data-field=\"maint\" data-sortable=\"true\">maint</th>\n"
+               res.add "<th data-field=\"contrib\" data-sortable=\"true\">contrib</th>\n"
+               res.add "<th data-field=\"reqs\" data-sortable=\"true\">reqs</th>\n"
+               res.add "<th data-field=\"dreqs\" data-sortable=\"true\">direct<br>reqs</th>\n"
+               res.add "<th data-field=\"cli\" data-sortable=\"true\">clients</th>\n"
+               res.add "<th data-field=\"dcli\" data-sortable=\"true\">direct<br>clients</th>\n"
+               res.add "<th data-field=\"mod\" data-sortable=\"true\">modules</th>\n"
+               res.add "<th data-field=\"cla\" data-sortable=\"true\">classes</th>\n"
+               res.add "<th data-field=\"met\" data-sortable=\"true\">methods</th>\n"
+               res.add "<th data-field=\"loc\" data-sortable=\"true\">lines</th>\n"
+               res.add "<th data-field=\"score\" data-sortable=\"true\">score</th>\n"
+               res.add "</tr></thead>"
+               for p in mprojects do
+                       res.add "<tr>"
+                       res.add "<td><a href=\"{p.name}.html\">{p.name}</a></td>"
+                       var maint = "?"
+                       if p.maintainers.not_empty then maint = p.maintainers.first
+                       res.add "<td>{maint}</td>"
+                       res.add "<td>{p.contributors.length}</td>"
+                       res.add "<td>{deps[p].greaters.length-1}</td>"
+                       res.add "<td>{deps[p].direct_greaters.length}</td>"
+                       res.add "<td>{deps[p].smallers.length-1}</td>"
+                       res.add "<td>{deps[p].direct_smallers.length}</td>"
+                       res.add "<td>{mmodules[p]}</td>"
+                       res.add "<td>{mclasses[p]}</td>"
+                       res.add "<td>{mmethods[p]}</td>"
+                       res.add "<td>{loc[p]}</td>"
+                       res.add "<td>{score[p]}</td>"
+                       res.add "</tr>\n"
+               end
+               res.add "</table>\n"
+               return res
+       end
+end
+
+# Execute a git command and return the result
+fun git_run(command: String...): String
+do
+       # print "git {command.join(" ")}"
+       var p = new ProcessReader("git", command...)
+       var res = p.read_all
+       p.close
+       p.wait
+       return res
+end
+
+var model = new Model
+var tc = new ToolContext
+
+tc.process_options(sys.args)
+tc.keep_going = true
+
+var modelbuilder = new ModelBuilder(model, tc)
+var catalog = new Catalog(modelbuilder)
+
+# Get files or groups
+for a in tc.option_context.rest do
+       modelbuilder.get_mgroup(a)
+       modelbuilder.identify_file(a)
+end
+
+# Scan projects and compute information
+for p in model.mprojects do
+       var g = p.root
+       assert g != null
+       modelbuilder.scan_group(g)
+
+       # Load the module to process importation information
+       modelbuilder.parse_group(g)
+
+       catalog.deps.add_node(p)
+       for gg in p.mgroups do for m in gg.mmodules do
+               for im in m.in_importation.direct_greaters do
+                       var ip = im.mproject
+                       if ip == null or ip == p then continue
+                       catalog.deps.add_edge(p, ip)
+               end
+       end
+
+       catalog.git_info(p)
+end
+
+# Run phases to modelize classes and properties (so we can count them)
+#modelbuilder.run_phases
+
+var out = "out"
+out.mkdir
+
+# Generate the css (hard coded)
+var css = """
+body {
+margin-top:                    15px;
+background-color:              #f8f8f8;
+}
+
+a {
+color:                         #0D8921;
+text-decoration:               none;
+}
+
+a:hover {
+color:                         #333;
+text-decoration:               none;
+}
+
+h1 {
+font-weight:                   bold;
+color:                          #0D8921;
+font-size:                     22px;
+}
+
+h2 {
+color:                          #6C6C6C;
+font-size:                     18px;
+border-bottom:                 solid 3px #CCC;
+}
+
+h3 {
+color:                          #6C6C6C;
+font-size:                     15px;
+border-bottom:                 solid 1px #CCC;
+}
+
+ul {
+list-style-type:               square;
+}
+
+dd {
+color:                         #6C6C6C;
+margin-top:                    1em;
+margin-bottom:                 1em;
+}
+
+pre {
+border:                                1px solid #CCC;
+font-family:                   Monospace;
+color:                         #2d5003;
+background-color:              rgb(250, 250, 250);
+}
+
+code {
+font-family:                   Monospace;
+color:                         #2d5003;
+}
+
+footer {
+margin-top:                    20px;
+}
+
+.container {
+margin:                                0 auto;
+padding:                       0 20px;
+}
+
+.content {
+float:                         left;
+margin-top:                    40px;
+width:                         65%;
+}
+
+.sidebar {
+float:                         right;
+margin-top:                    40px;
+width:                         30%
+}
+
+.sidebar h3 {
+color:                          #0D8921;
+font-size:                     18px;
+border-bottom:                 0px;
+}
+
+.box {
+margin:                                0;
+padding:                       0;
+}
+
+.box li {
+line-height:                   2.5;
+white-space:                   nowrap;
+overflow:                      hidden;
+text-overflow:                 ellipsis;
+padding-right:                 10px;
+border-bottom:                 1px solid rgba(0,0,0,0.2);
+}
+"""
+css.write_to_file(out/"style.css")
+
+# PAGES
+
+for p in model.mprojects do
+       # print p
+       var f = "{p.name}.html"
+       catalog.project_page(p).write_to_file(out/f)
+end
+
+# INDEX
+
+var index = new CatalogPage
+index.more_head.add "<title>Projects in Nit</title>"
+
+index.add """
+<div class="content">
+<h1>Projects in Nit</h1>
+"""
+
+index.add "<h2>Highlighted Projects</h2>\n"
+index.add catalog.list_best(catalog.score)
+
+index.add "<h2>Most Required</h2>\n"
+var reqs = new Counter[MProject]
+for p in model.mprojects do
+       reqs[p] = catalog.deps[p].smallers.length - 1
+end
+index.add catalog.list_best(reqs)
+
+index.add "<h2>By First Tag</h2>\n"
+index.add catalog.list_by(catalog.cat2proj, "cat_")
+
+index.add "<h2>By Any Tag</h2>\n"
+index.add catalog.list_by(catalog.tag2proj, "tag_")
+
+index.add """
+</div>
+<div class="sidebar">
+<h3>Stats</h3>
+<ul class="box">
+<li>{{{model.mprojects.length}}} projects</li>
+<li>{{{catalog.maint2proj.length}}} maintainers</li>
+<li>{{{catalog.contrib2proj.length}}} contributors</li>
+<li>{{{catalog.tag2proj.length}}} tags</li>
+<li>{{{catalog.mmodules.sum}}} modules</li>
+<li>{{{catalog.mclasses.sum}}} classes</li>
+<li>{{{catalog.mmethods.sum}}} methods</li>
+<li>{{{catalog.loc.sum}}} lines of code</li>
+</ul>
+</div>
+"""
+
+index.write_to_file(out/"index.html")
+
+# PEOPLE
+
+var page = new CatalogPage
+page.more_head.add "<title>People of Nit</title>"
+page.add """<div class="content">\n<h1>People of Nit</h1>\n"""
+page.add "<h2>By Maintainer</h2>\n"
+page.add catalog.list_by(catalog.maint2proj, "maint_")
+page.add "<h2>By Contributor</h2>\n"
+page.add catalog.list_by(catalog.contrib2proj, "contrib_")
+page.add "</div>\n"
+page.write_to_file(out/"people.html")
+
+# TABLE
+
+page = new CatalogPage
+page.more_head.add "<title>Projets of Nit</title>"
+page.add """<div class="content">\n<h1>People of Nit</h1>\n"""
+page.add "<h2>Table of Projets</h2>\n"
+page.add catalog.table_projects(model.mprojects)
+page.add "</div>\n"
+page.write_to_file(out/"table.html")
index 9550339..0990d6a 100644 (file)
@@ -203,6 +203,7 @@ class ANodes[E: ANode]
        private var parent: ANode
        private var items = new Array[E]
        redef fun iterator do return items.iterator
+       redef fun reverse_iterator do return items.reverse_iterator
        redef fun length do return items.length
        redef fun is_empty do return items.is_empty
        redef fun push(e)
index 9ef5320..bcdb6db 100644 (file)
@@ -581,7 +581,7 @@ redef class AStringFormExpr
        do
                var native = v.analysis.mainmodule.native_string_type
                v.add_type(native)
-               var prop = v.get_method(native, "to_s_with_length")
+               var prop = v.get_method(native, "to_s_full")
                v.add_monomorphic_send(native, prop)
        end
 end
index 8fb3e6c..8f972aa 100644 (file)
@@ -1,3 +1,23 @@
-import project1 #alt1# import project1::module1 #alt2# import project1::module2 #alt3# import project1::fail #alt4# import project1::module4 #alt5# import project1::project2
+# 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 project1 #alt1-6#
+#alt1#import project1::module1
+#alt2#import project1::module2
+#alt3#import project1::fail
+#alt4#import project1::module4
+#alt5#import project1::project2
+#alt6#import fail::module1
 
 foo
diff --git a/tests/base_import2.nit b/tests/base_import2.nit
new file mode 100644 (file)
index 0000000..1b2955e
--- /dev/null
@@ -0,0 +1,18 @@
+# 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 module_0 #alt1-3#
+#alt1#import project1::module_0
+#alt2#import project1::module_01
+#alt3#import project1::module_02
diff --git a/tests/project1/module_0.nit b/tests/project1/module_0.nit
new file mode 100644 (file)
index 0000000..5604c67
--- /dev/null
@@ -0,0 +1,18 @@
+# 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 kernel
+
+1.output
+0.output
diff --git a/tests/project1/module_01.nit b/tests/project1/module_01.nit
new file mode 100644 (file)
index 0000000..8499bb9
--- /dev/null
@@ -0,0 +1,15 @@
+# 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 module_0
diff --git a/tests/project1/module_02.nit b/tests/project1/module_02.nit
new file mode 100644 (file)
index 0000000..58207a5
--- /dev/null
@@ -0,0 +1,15 @@
+# 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 ::module_0
diff --git a/tests/project1/subdir/module_0.nit b/tests/project1/subdir/module_0.nit
new file mode 100644 (file)
index 0000000..9c6ed48
--- /dev/null
@@ -0,0 +1,19 @@
+# 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 kernel
+
+1.output
+'s'.output
+0.output
diff --git a/tests/sav/base_import2.res b/tests/sav/base_import2.res
new file mode 100644 (file)
index 0000000..573541a
--- /dev/null
@@ -0,0 +1 @@
+0
diff --git a/tests/sav/base_import2_alt1.res b/tests/sav/base_import2_alt1.res
new file mode 100644 (file)
index 0000000..2bac1c7
--- /dev/null
@@ -0,0 +1 @@
+alt/base_import2_alt1.nit:16,8--25: Error: conflicting module files for `module_0`: `./project1/module_0.nit,./project1/subdir/module_0.nit`
diff --git a/tests/sav/base_import2_alt2.res b/tests/sav/base_import2_alt2.res
new file mode 100644 (file)
index 0000000..b261da1
--- /dev/null
@@ -0,0 +1,2 @@
+1
+0
diff --git a/tests/sav/base_import2_alt3.res b/tests/sav/base_import2_alt3.res
new file mode 100644 (file)
index 0000000..573541a
--- /dev/null
@@ -0,0 +1 @@
+0
index f9cc83e..28413fe 100644 (file)
@@ -1 +1 @@
-alt/base_import_alt3.nit:1,8--21: Error: cannot find module `fail` from `project1`. Tried: ., ../lib/core, ../lib/core/collection, alt, ../lib, ../contrib.
+alt/base_import_alt3.nit:18,8--21: Error: cannot find module `fail` from `project1`. Tried: ., ../lib/core, ../lib/core/collection, alt, ../lib, ../contrib.
index d93c6ce..335fcd3 100644 (file)
@@ -1 +1 @@
-alt/base_import_alt5.nit:1,8--25: Error: cannot find module `project2` from `project1`. Tried: ., ../lib/core, ../lib/core/collection, alt, ../lib, ../contrib.
+alt/base_import_alt5.nit:20,8--25: Error: cannot find module `project2` from `project1`. Tried: ., ../lib/core, ../lib/core/collection, alt, ../lib, ../contrib.
diff --git a/tests/sav/base_import_alt6.res b/tests/sav/base_import_alt6.res
new file mode 100644 (file)
index 0000000..9cdbe5b
--- /dev/null
@@ -0,0 +1 @@
+alt/base_import_alt6.nit:21,8--20: Error: cannot find `fail`. Tried: ., ../lib/core, ../lib/core/collection, alt, ../lib, ../contrib.
index c1dad34..18bfeab 100644 (file)
@@ -1,2 +1,2 @@
-../lib/mnit_linux/linux_app.nit:29,16--31: Redef Error: a virtual type cannot be refined.
-../lib/mnit_linux/linux_app.nit:30,16--29: Redef Error: a virtual type cannot be refined.
+../lib/mnit/linux/linux_app.nit:29,16--31: Redef Error: a virtual type cannot be refined.
+../lib/mnit/linux/linux_app.nit:30,16--29: Redef Error: a virtual type cannot be refined.
index f7f479a..f06d6c6 100644 (file)
@@ -1 +1 @@
-alt/error_needed_method_alt3.nit:48,9--13: Fatal Error: `NativeString` must have a property named `to_s_with_length`.
+alt/error_needed_method_alt3.nit:48,9--13: Fatal Error: `NativeString` must have a property named `to_s_full`.
diff --git a/tests/sav/nitcatalog.res b/tests/sav/nitcatalog.res
new file mode 100644 (file)
index 0000000..e50ad70
--- /dev/null
@@ -0,0 +1,2 @@
+Usage: [OPTION]... [ARG]...
+Use --help for help
index 3626477..5b1731b 100644 (file)
@@ -1 +1 @@
-alt/error_needed_method_alt4.nit:49,10--14: Fatal Error: `NativeString` must have a property named `to_s_with_length`.
+alt/error_needed_method_alt4.nit:49,10--14: Fatal Error: `NativeString` must have a property named `to_s_full`.
index b497cd5..19b7801 100644 (file)
@@ -3,7 +3,11 @@ project1 (\e[33mproject1\e[m)
 |--\e[1mmodule1\e[m (\e[33mproject1/module1.nit\e[m)
 |--\e[1mmodule2\e[m (\e[33mproject1/module2.nit\e[m)
 |--\e[1mmodule3\e[m (\e[33mproject1/module3.nit\e[m)
+|--\e[1mmodule_0\e[m (\e[33mproject1/module_0.nit\e[m)
+|--\e[1mmodule_01\e[m (\e[33mproject1/module_01.nit\e[m)
+|--\e[1mmodule_02\e[m (\e[33mproject1/module_02.nit\e[m)
 |--\e[1mproject1\e[m (\e[33mproject1/project1.nit\e[m)
 `--subdir (\e[33mproject1/subdir\e[m)
-   `--\e[1mmodule4\e[m (\e[33mproject1/subdir/module4.nit\e[m)
+   |--\e[1mmodule4\e[m (\e[33mproject1/subdir/module4.nit\e[m)
+   `--\e[1mmodule_0\e[m (\e[33mproject1/subdir/module_0.nit\e[m)
 \e[1mproject2\e[m (\e[33mproject1/project2/project2.nit\e[m)
index b497cd5..19b7801 100644 (file)
@@ -3,7 +3,11 @@ project1 (\e[33mproject1\e[m)
 |--\e[1mmodule1\e[m (\e[33mproject1/module1.nit\e[m)
 |--\e[1mmodule2\e[m (\e[33mproject1/module2.nit\e[m)
 |--\e[1mmodule3\e[m (\e[33mproject1/module3.nit\e[m)
+|--\e[1mmodule_0\e[m (\e[33mproject1/module_0.nit\e[m)
+|--\e[1mmodule_01\e[m (\e[33mproject1/module_01.nit\e[m)
+|--\e[1mmodule_02\e[m (\e[33mproject1/module_02.nit\e[m)
 |--\e[1mproject1\e[m (\e[33mproject1/project1.nit\e[m)
 `--subdir (\e[33mproject1/subdir\e[m)
-   `--\e[1mmodule4\e[m (\e[33mproject1/subdir/module4.nit\e[m)
+   |--\e[1mmodule4\e[m (\e[33mproject1/subdir/module4.nit\e[m)
+   `--\e[1mmodule_0\e[m (\e[33mproject1/subdir/module_0.nit\e[m)
 \e[1mproject2\e[m (\e[33mproject1/project2/project2.nit\e[m)
index 52b93a0..c5ec486 100644 (file)
@@ -3,9 +3,13 @@ project1 (\e[33mproject1\e[m)
 |--\e[1mmodule1\e[m (\e[33mproject1/module1.nit\e[m)
 |--\e[1mmodule2\e[m (\e[33mproject1/module2.nit\e[m)
 |--\e[1mmodule3\e[m (\e[33mproject1/module3.nit\e[m)
+|--\e[1mmodule_0\e[m (\e[33mproject1/module_0.nit\e[m)
+|--\e[1mmodule_01\e[m (\e[33mproject1/module_01.nit\e[m)
+|--\e[1mmodule_02\e[m (\e[33mproject1/module_02.nit\e[m)
 |--\e[1mproject1\e[m (\e[33mproject1/project1.nit\e[m)
 `--subdir (\e[33mproject1/subdir\e[m)
-   `--\e[1mmodule4\e[m (\e[33mproject1/subdir/module4.nit\e[m)
+   |--\e[1mmodule4\e[m (\e[33mproject1/subdir/module4.nit\e[m)
+   `--\e[1mmodule_0\e[m (\e[33mproject1/subdir/module_0.nit\e[m)
 project2 (\e[33mproject1/project2\e[m)
 |--\e[1mfoo\e[m (\e[33mproject1/project2/foo.nit\e[m)
 `--\e[1mproject2\e[m (\e[33mproject1/project2/project2.nit\e[m)
index eeed2c7..a8b51a5 100644 (file)
@@ -3,5 +3,9 @@ project1/\e[1mmodule1\e[m (\e[33mproject1/module1.nit\e[m)
 project1/\e[1mmodule2\e[m (\e[33mproject1/module2.nit\e[m)
 project1/\e[1mmodule3\e[m (\e[33mproject1/module3.nit\e[m)
 project1/subdir/\e[1mmodule4\e[m (\e[33mproject1/subdir/module4.nit\e[m)
+project1/\e[1mmodule_0\e[m (\e[33mproject1/module_0.nit\e[m)
+project1/subdir/\e[1mmodule_0\e[m (\e[33mproject1/subdir/module_0.nit\e[m)
+project1/\e[1mmodule_01\e[m (\e[33mproject1/module_01.nit\e[m)
+project1/\e[1mmodule_02\e[m (\e[33mproject1/module_02.nit\e[m)
 project1/\e[1mproject1\e[m (\e[33mproject1/project1.nit\e[m)
 project2/\e[1mproject2\e[m (\e[33mproject1/project2/project2.nit\e[m)
index c1dad34..18bfeab 100644 (file)
@@ -1,2 +1,2 @@
-../lib/mnit_linux/linux_app.nit:29,16--31: Redef Error: a virtual type cannot be refined.
-../lib/mnit_linux/linux_app.nit:30,16--29: Redef Error: a virtual type cannot be refined.
+../lib/mnit/linux/linux_app.nit:29,16--31: Redef Error: a virtual type cannot be refined.
+../lib/mnit/linux/linux_app.nit:30,16--29: Redef Error: a virtual type cannot be refined.
index cb5a73d..e69de29 100644 (file)
@@ -1,2 +0,0 @@
-../examples/nitcorn/src/xymus_net.nit:25,8--26: Error: cannot find module `benitlux_controller` from `src`. Tried: alt, ../lib, ../contrib, ../examples.
-../examples/nitcorn/src/xymus_net.nit:26,8--29: Error: cannot find module `opportunity_controller` from `src`. Tried: alt, ../lib, ../contrib, ../examples.