Merge: gamnit: customize writing with BMFont
authorJean Privat <jean@pryen.org>
Sat, 17 Jun 2017 00:24:33 +0000 (20:24 -0400)
committerJean Privat <jean@pryen.org>
Sat, 17 Jun 2017 00:24:33 +0000 (20:24 -0400)
Add format services to `TestSprites` implemented (only) by `BMFont`: `align, valign, wrap, max_width` and `max_height`. You can see the result in the screenshot of the new showcase/test program:

![screenshot from 2017-06-15 15 53 28](https://user-images.githubusercontent.com/208057/27199185-1b9a36dc-51e3-11e7-9700-8dde5724e98a.png)

Pull-Request: #2499
Reviewed-by: Jean Privat <jean@pryen.org>

100 files changed:
bin/nit-makepackage [new file with mode: 0755]
contrib/action_nitro/src/action_nitro.nit
contrib/nitrpg/package.ini
contrib/oot.sh
contrib/oot.txt
examples/calculator/package.ini
lib/actors/package.ini [new file with mode: 0644]
lib/app/http_request.nit
lib/core/file.nit
lib/dot/examples/clusters.nit [moved from lib/dot/exemples/clusters.nit with 99% similarity]
lib/dot/examples/hello.nit [moved from lib/dot/exemples/hello.nit with 100% similarity]
lib/dot/examples/undirected_clusters.nit [moved from lib/dot/exemples/undirected_clusters.nit with 100% similarity]
lib/gamnit/bmfont.nit
lib/gamnit/cameras.nit
lib/gamnit/cameras_cache.nit
lib/gamnit/depth/depth.nit
lib/gamnit/display.nit
lib/gamnit/display_linux.nit
lib/gamnit/dynamic_resolution.nit
lib/gamnit/flat.nit
lib/gamnit/gamnit.nit
lib/gamnit/gamnit_linux.nit
lib/geometry/points_and_lines.nit
lib/gmp/package.ini [new file with mode: 0644]
lib/neo4j/neo4j.nit
lib/popcorn/pop_json.nit
lib/popcorn/pop_validation.nit
lib/sdl2/events.nit
misc/docker/Dockerfile
misc/jenkins/unitrun.sh
share/man/nit-makepackage.md [new file with mode: 0644]
share/nitweb/directives/entity/defcard.html
share/nitweb/directives/entity/link.html
share/nitweb/directives/metrics/chart_properties.html
share/nitweb/directives/metrics/metrics_list.html
share/nitweb/directives/ui/search-field.html [new file with mode: 0644]
share/nitweb/index.html
share/nitweb/javascripts/docdown.js
share/nitweb/javascripts/entities.js
share/nitweb/javascripts/grades.js
share/nitweb/javascripts/index.js
share/nitweb/javascripts/metrics.js
share/nitweb/javascripts/model.js [deleted file]
share/nitweb/javascripts/nitweb.js
share/nitweb/javascripts/ui.js
share/nitweb/javascripts/users.js
share/nitweb/stylesheets/cards.css [new file with mode: 0644]
share/nitweb/stylesheets/nitlight.css [new file with mode: 0644]
share/nitweb/stylesheets/nitweb.css
share/nitweb/stylesheets/nitweb_bootstrap.css
share/nitweb/stylesheets/search.css [new file with mode: 0644]
share/nitweb/views/catalog/by_tags.html [new file with mode: 0644]
share/nitweb/views/catalog/highlighted.html [new file with mode: 0644]
share/nitweb/views/catalog/index.html [new file with mode: 0644]
share/nitweb/views/catalog/most_required.html [new file with mode: 0644]
share/nitweb/views/class.html [deleted file]
share/nitweb/views/classdef.html [deleted file]
share/nitweb/views/doc/all.html [new file with mode: 0644]
share/nitweb/views/doc/code.html [new file with mode: 0644]
share/nitweb/views/doc/defs.html [new file with mode: 0644]
share/nitweb/views/doc/doc.html [new file with mode: 0644]
share/nitweb/views/doc/entity.html [new file with mode: 0644]
share/nitweb/views/doc/graph.html [new file with mode: 0644]
share/nitweb/views/doc/index.html [new file with mode: 0644]
share/nitweb/views/doc/lin.html [new file with mode: 0644]
share/nitweb/views/doc/metrics.html [new file with mode: 0644]
share/nitweb/views/group.html [deleted file]
share/nitweb/views/index.html [deleted file]
share/nitweb/views/module.html [deleted file]
share/nitweb/views/package.html [deleted file]
share/nitweb/views/propdef.html [deleted file]
share/nitweb/views/property.html [deleted file]
src/compiler/abstract_compiler.nit
src/metrics/codesmells_metrics.nit [new file with mode: 0644]
src/metrics/mclassdef_collect.nit [new file with mode: 0644]
src/metrics/method_analyze_metrics.nit [new file with mode: 0644]
src/model/model.nit
src/modelize/modelize_class.nit
src/modelize/modelize_property.nit
src/nitsmells.nit [new file with mode: 0644]
src/web/web_base.nit
tests/TestNitsmells/FeatureEnvy/featureenvy.nit [new file with mode: 0644]
tests/TestNitsmells/LargeClass/largeclass.nit [new file with mode: 0644]
tests/TestNitsmells/LongMethod/longmethod.nit [new file with mode: 0644]
tests/TestNitsmells/LongParameterList/longparameterlist.nit [new file with mode: 0644]
tests/TestNitsmells/platform/platform.nit [new file with mode: 0644]
tests/error_formal_super.nit [new file with mode: 0644]
tests/nitsmells.args [new file with mode: 0644]
tests/sav/clusters.res [new file with mode: 0644]
tests/sav/error_formal_super.res [new file with mode: 0644]
tests/sav/hello.res [new file with mode: 0644]
tests/sav/nitsmells.res [new file with mode: 0644]
tests/sav/nitsmells_args1.res [new file with mode: 0644]
tests/sav/nitsmells_args2.res [new file with mode: 0644]
tests/sav/nitsmells_args3.res [new file with mode: 0644]
tests/sav/nitsmells_args4.res [new file with mode: 0644]
tests/sav/undirected_clusters.res [new file with mode: 0644]
tests/test_neo4j.nit
tests/test_neo4j_batch.nit
tests/tests.sh

diff --git a/bin/nit-makepackage b/bin/nit-makepackage
new file mode 100755 (executable)
index 0000000..a03af86
--- /dev/null
@@ -0,0 +1,81 @@
+#!/bin/bash
+
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This shell script creates (or overrides) a package.ini template file in the current directory.
+# The result must be examined and adapted.
+#
+# Default values are guessed from git and the file system.
+
+while true; do
+       case "$1" in
+               "")
+                       break
+                       ;;
+               -h|--help)
+                       echo "nit-makepackage: creates a package.ini in the current directory"
+                       echo "There is no option (yet)"
+                       exit
+                       ;;
+               *)
+                       echo >&2 "nit-makepackage does not accept options yet"
+                       exit 1
+                       ;;
+       esac
+done
+
+
+name=`basename $PWD`
+maintainer=`git shortlog -esn . | head -n 1 | sed 's/\s*[0-9]*\s*//'`
+dir=`git rev-parse --show-prefix`
+git=`git remote get-url origin`
+
+# Setup default keys.
+# The following values works for Github and Gitlab
+root=${git%.git}
+browse=$root
+homepage=$root
+if [ -n "$dir" ]; then
+       browse=$root/tree/master/$dir
+fi
+issues=$root/issues
+
+# Special cases to override
+case "$git" in
+       */nit.git)
+               # For projects on the main Nit tree
+               license=Apache-2.0
+               git=https://github.com/nitlang/nit.git
+               homepage=http://nitlanguage.org
+               root=https://github.com/nitlang/nit
+               browse=$root/tree/master/$dir
+               issues=$root/issues
+               ;;
+esac
+
+# Generate the `package.ini` file
+sed '/^$/d' > package.ini <<SUPERCALIFRAGILISTICEXPIALIDOCIOUS
+[package]
+name=$name
+tags=
+maintainer=$maintainer
+license=$license
+[upstream]
+browse=$browse
+git=$git
+`test -n "$dir" && echo "git.directory=$dir"`
+homepage=$homepage
+issues=$issues
+SUPERCALIFRAGILISTICEXPIALIDOCIOUS
index b8dd4f4..5a2c3bc 100644 (file)
@@ -562,7 +562,7 @@ redef class Player
                end
 
                # Display respawn instructions
-               app.ui_sprites.add new Sprite(app.texts_sheet.respawn, app.ui_camera.center)
+               app.ui_sprites.add new Sprite(app.texts_sheet.respawn, app.ui_camera.center.offset(0.0, 0.0, 0.0))
        end
 end
 
index 812edca..125e96c 100644 (file)
@@ -9,4 +9,3 @@ git=https://github.com/nitlang/nit.git
 git.directory=contrib/nitrpg/
 homepage=http://nitlanguage.org
 issues=https://github.com/nitlang/nit/issues
-tryit=http://nitlanguage.org/rpg/games/nitlang/nit
index ff29ba2..351e69d 100755 (executable)
@@ -21,6 +21,7 @@
 update_oot() {
        if test -d "$dir"; then
                echo "$name: git pull"
+               ../misc/jenkins/unitrun.sh "cmd-$name-remote" git --work-tree="$PWD/$dir" --git-dir="$PWD/$dir/.git" remote set-url origin "$repo"
                ../misc/jenkins/unitrun.sh "cmd-$name-pull" git --work-tree="$PWD/$dir" --git-dir="$PWD/$dir/.git" pull -f
        else
                echo "$name: git clone"
index 4ceea39..46e720b 100644 (file)
@@ -2,6 +2,8 @@
 # Format: repo name
 https://github.com/R4PaSs/brewnit.git brewnit
 https://gitlab.com/xymus/darpg.git darpg
+https://gitlab.com/jeremlvt/dawn_arrival.git dawn_arrival
 https://github.com/Morriar/Missions.git missions
+https://gitlab.com/Heavyshield/NitGains.git nitgains
 https://github.com/ppepos/pep8-dbg.git pep8dbg
 https://gitlab.com/xymus/sputnit.git sputnit
index 014014d..8237327 100644 (file)
@@ -9,4 +9,4 @@ git=https://github.com/nitlang/nit.git
 git.directory=examples/calculator/
 homepage=http://nitlanguage.org
 issues=https://github.com/nitlang/nit/issues
-apk=http://nitlanguage.org/fdroid/apk/calculator21.apk
+apk=http://nitlanguage.org/fdroid/apk/calculator.apk
diff --git a/lib/actors/package.ini b/lib/actors/package.ini
new file mode 100644 (file)
index 0000000..686b089
--- /dev/null
@@ -0,0 +1,11 @@
+[package]
+name=actors
+tags=parallelism,lib
+maintainer=Romain Chanoir <romain.chanoir@viacesi.fr>
+license=Apache-2.0
+[upstream]
+browse=https://github.com/nitlang/nit/tree/master/lib/actors/
+git=https://github.com/nitlang/nit.git
+git.directory=lib/actors/
+homepage=http://nitlanguage.org
+issues=https://github.com/nitlang/nit/issues
index 0260cab..8ab0f1d 100644 (file)
@@ -126,7 +126,7 @@ end
 #
 # Prints on communication errors and when the server returns an HTTP status code not in the 200s.
 #
-# ~~~
+# ~~~nitish
 # var request = new SimpleAsyncHttpRequest("http://example.com")
 # request.start
 # ~~~
index 09b20d9..643fe08 100644 (file)
@@ -898,9 +898,7 @@ redef class Text
 
        # return true if a file with this names exists
        fun file_exists: Bool do return to_cstring.file_exists
-end
 
-redef class String
        # The status of a file. see POSIX stat(2).
        fun file_stat: nullable FileStat
        do
@@ -943,14 +941,14 @@ redef class String
                if extension == null then
                        extension = file_extension
                        if extension == null then
-                               return self
+                               return self.to_s
                        else extension = ".{extension}"
                end
 
                if has_suffix(extension) then
-                       return substring(0, length - extension.length)
+                       return substring(0, length - extension.length).to_s
                end
-               return self
+               return self.to_s
        end
 
        # Extract the basename of a path and strip the `extension`
@@ -989,7 +987,7 @@ redef class String
 
                if extension != null then
                        return n.strip_extension(extension)
-               else return n
+               else return n.to_s
        end
 
        # Extract the dirname of a path
@@ -1018,7 +1016,7 @@ redef class String
                while l > 0 and s.chars[l] == '/' do l -= 1 # remove all trailing `/`
                var pos = s.chars.last_index_of_from('/', l)
                if pos > 0 then
-                       return s.substring(0, pos)
+                       return s.substring(0, pos).to_s
                else if pos == 0 then
                        return "/"
                else
@@ -1115,11 +1113,11 @@ redef class String
        # Note: You may want to use `simplify_path` on the result.
        #
        # Note: This method works only with POSIX paths.
-       fun join_path(path: String): String
+       fun join_path(path: Text): String
        do
-               if path.is_empty then return self
-               if self.is_empty then return path
-               if path.chars[0] == '/' then return path
+               if path.is_empty then return self.to_s
+               if self.is_empty then return path.to_s
+               if path.chars[0] == '/' then return path.to_s
                if self.last == '/' then return "{self}{path}"
                return "{self}/{path}"
        end
@@ -1134,7 +1132,7 @@ redef class String
        #     assert "".to_program_name == "./" # At least, your shell will detect the error.
        fun to_program_name: String do
                if self.has_prefix("/") then
-                       return self
+                       return self.to_s
                else
                        return "./{self}"
                end
@@ -1154,7 +1152,7 @@ redef class String
        #     var b = "/bar"
        #     var c = "baz/foobar"
        #     assert a/b/c == "/bar/baz/foobar"
-       fun /(path: String): String do return join_path(path)
+       fun /(path: Text): String do return join_path(path)
 
        # Returns the relative path needed to go from `self` to `dest`.
        #
@@ -1318,7 +1316,7 @@ redef class String
        do
                var last_slash = chars.last_index_of('.')
                if last_slash > 0 then
-                       return substring( last_slash+1, length )
+                       return substring( last_slash+1, length ).to_s
                else
                        return null
                end
similarity index 99%
rename from lib/dot/exemples/clusters.nit
rename to lib/dot/examples/clusters.nit
index 993d3ca..2361e0d 100644 (file)
@@ -66,7 +66,7 @@ graph.add_edge(b3, nend)
 
 if args.is_empty then
        print graph.to_dot
-       graph.show
+       # graph.show
 else
        graph.to_dot.write_to_file args.first
 end
index 4d6520c..fa3a197 100644 (file)
@@ -277,7 +277,8 @@ end
 # ~~~
 # redef class App
 #     var font = new BMFontAsset("arial.fnt")
-#     var ui_text = new TextSprites(font, ui_camera.top_left)
+#     var pos: Point3d[Float] = ui_camera.top_left.offset(128.0, -128.0, 0.0)
+#     var ui_text = new TextSprites(font, pos)
 #
 #     redef fun on_create
 #     do
index c9754f7..688082b 100644 (file)
@@ -29,7 +29,7 @@ abstract class Camera
        var display: GamnitDisplay
 
        # Position of this camera in world space
-       var position = new Point3d[Float](0.0, 0.0, 0.0) is writable
+       var position = new Point3d[Float](0.0, 0.0, 0.0)
 
        # The Model-View-Projection matrix created by this camera
        #
@@ -207,23 +207,21 @@ class UICamera
        # Clipping wall the farthest of the camera, defaults to -100.0
        var far: Float = -100.0 is writable
 
-       # Width in world units, defaults to the width in pixels of the screen
-       var width: Float = display.width.to_f is lazy
+       # Width in world units, calculated from `height` and the screen aspect ratio
+       fun width: Float do return height * display.aspect_ratio
 
-       # Height in world units, defaults to the height in pixels of the screen
-       var height: Float = display.height.to_f is lazy
+       # Height in world units, defaults to 1080.0
+       #
+       # Set this value using `reset_height`.
+       var height = 1080.0
 
        # Reset the camera position so that `height` world units are visible on the Y axis
        #
-       # By default, `height` is set to `display.height`.
-       #
        # This can be used to set standardized UI units independently from the screen resolution.
        fun reset_height(height: nullable Float)
        do
                if height == null then height = display.height.to_f
-
                self.height = height
-               self.width = height * display.aspect_ratio
        end
 
        # Convert the position `x, y` on screen, to UI coordinates
@@ -238,33 +236,31 @@ class UICamera
        end
 
        # Center of the screen, from the point of view of the camera, at z = 0
-       fun center: Point3d[Float] do return new Point3d[Float](position.x + width / 2.0, position.y - height / 2.0, 0.0)
+       var center: IPoint3d[Float] = new CameraAnchor(self, 0.5, -0.5)
 
        # Center of the top of the screen, at z = 0
-       fun top: Point3d[Float] do return new Point3d[Float](position.x + width / 2.0, position.y, 0.0)
+       var top: IPoint3d[Float] = new CameraAnchor(self, 0.5, 0.0)
 
        # Center of the bottom of the screen, at z = 0
-       fun bottom: Point3d[Float] do return new Point3d[Float](position.x + width / 2.0, position.y - height, 0.0)
+       var bottom: IPoint3d[Float] = new CameraAnchor(self, 0.5, -1.0)
 
        # Center of the left border of the screen, at z = 0
-       fun left: Point3d[Float] do return new Point3d[Float](position.x, position.y - height / 2.0, 0.0)
+       var left: IPoint3d[Float] = new CameraAnchor(self, 0.0, -1.0)
 
        # Center of the right border of the screen, at z = 0
-       fun right: Point3d[Float] do return new Point3d[Float](position.x + width, position.y - height / 2.0, 0.0)
+       var right: IPoint3d[Float] = new CameraAnchor(self, 1.0, -1.0)
 
        # Top left corner of the screen, at z = 0
-       fun top_left: Point3d[Float] do return new Point3d[Float](position.x, position.y, 0.0)
+       var top_left: IPoint3d[Float] = new CameraAnchor(self, 0.0, 0.0)
 
        # Top right corner of the screen, at z = 0
-       fun top_right: Point3d[Float] do return new Point3d[Float](position.x + width, position.y, 0.0)
+       var top_right: IPoint3d[Float] = new CameraAnchor(self, 1.0, 0.0)
 
        # Bottom left corner of the screen, at z = 0
-       fun bottom_left: Point3d[Float] do return new Point3d[Float](position.x, position.y - height, 0.0)
+       var bottom_left: IPoint3d[Float] = new CameraAnchor(self, 0.0, -1.0)
 
        # Bottom right corner of the screen, at z = 0
-       fun bottom_right: Point3d[Float] do return new Point3d[Float](position.x + width, position.y - height, 0.0)
-
-       # TODO cache the anchors
+       var bottom_right: IPoint3d[Float] = new CameraAnchor(self, 1.0, -1.0)
 
        redef fun mvp_matrix
        do
@@ -279,3 +275,55 @@ class UICamera
                return view * projection
        end
 end
+
+# Immutable relative anchors for reference points on `camera`
+private class CameraAnchor
+       super IPoint3d[Float]
+
+       # Reference camera
+       var camera: UICamera
+
+       # Reference position, the top left of the screen
+       var ref: Point3d[Float] = camera.position is lazy
+
+       # X position as proportion of the screen width
+       var relative_x: Float
+
+       # Y position as proportion of the screen height
+       var relative_y: Float
+
+       redef fun x do return ref.x + relative_x*camera.width
+       redef fun y do return ref.y + relative_y*camera.height
+       redef fun z do return ref.z
+
+       redef fun offset(x, y, z) do return new OffsetPoint3d(self, x.to_f, y.to_f, z.to_f)
+end
+
+# Position relative to another point or usually a `CameraAnchor`
+private class OffsetPoint3d
+       super Point3d[Float]
+
+       autoinit ref, offset_x, offset_y, offset_z
+
+       # Reference point to which the offsets are applied
+       var ref: IPoint3d[Float]
+
+       # Difference on the X axis
+       var offset_x: Float
+
+       # Difference on the X axis
+       var offset_y: Float
+
+       # Difference on the X axis
+       var offset_z: Float
+
+       redef fun x do return ref.x + offset_x
+       redef fun y do return ref.y + offset_y
+       redef fun z do return ref.z + offset_z
+
+       redef fun x=(value) do if value != null then offset_x += value - x
+       redef fun y=(value) do if value != null then offset_y += value - y
+       redef fun z=(value) do if value != null then offset_z += value - z
+
+       redef fun offset(x, y, z) do return new OffsetPoint3d(self, x.to_f, y.to_f, z.to_f)
+end
index 08adc8f..852ac9b 100644 (file)
@@ -110,12 +110,6 @@ redef class UICamera
                mvp_matrix_cache = null
        end
 
-       redef fun width=(value)
-       do
-               super
-               mvp_matrix_cache = null
-       end
-
        redef fun height=(value)
        do
                super
index f18f2eb..9b4be47 100644 (file)
@@ -45,6 +45,7 @@ redef class App
        # Draw all elements of `actors` and then call `frame_core_flat`
        protected fun frame_core_depth(display: GamnitDisplay)
        do
+               glViewport(0, 0, display.width, display.height)
                frame_core_dynamic_resolution_before display
 
                # Update cameras on both our programs
index 6e7c58c..2b694c6 100644 (file)
@@ -34,7 +34,7 @@ class GamnitDisplay
        fun height: Int is abstract
 
        # Aspect ratio of the screen, `width / height`
-       var aspect_ratio: Float is lazy do return width.to_f / height.to_f
+       fun aspect_ratio: Float do return width.to_f / height.to_f
 
        # Is the cursor locked et the center of the screen?
        var lock_cursor = false is writable
index cb6bdf0..e3ab243 100644 (file)
@@ -82,7 +82,7 @@ redef class GamnitDisplay
                        print_error "Failed to initialize SDL2 IMG: {sdl.error}"
                end
 
-               var sdl_window = new SDLWindow(window_title.to_cstring, window_width, window_height, (new SDLWindowFlags).opengl)
+               var sdl_window = new SDLWindow(window_title.to_cstring, window_width, window_height, sdl_window_flags)
                assert not sdl_window.address_is_null else
                        print_error "Failed to create SDL2 window: {sdl.error}"
                end
@@ -101,7 +101,12 @@ redef class GamnitDisplay
                return sdl_window
        end
 
-       # Initialization flags passed to SDL2 mixer
+       # SDL2 window initialization flags
+       #
+       # The default value supports OpenGL and window resizing.
+       var sdl_window_flags: SDLWindowFlags = (new SDLWindowFlags).opengl.resizable is lazy, writable
+
+       # SDL2 mixer initialization flags
        #
        # Defaults to all available formats.
        var mix_init_flags: MixInitFlags = mix.flac | mix.mod | mix.mp3 | mix.ogg is lazy, writable
index 40ea221..97efba6 100644 (file)
@@ -64,6 +64,12 @@ redef class App
                assert error == null else print_error error
        end
 
+       redef fun on_resize(display)
+       do
+               dynamic_context.resize(display, max_dynamic_resolution_ratio)
+               super
+       end
+
        # Prepare to draw to the dynamic screen if `dynamic_resolution_ratio != 1.0`
        protected fun frame_core_dynamic_resolution_before(display: GamnitDisplay)
        do
@@ -73,6 +79,7 @@ redef class App
                        # Draw directly to the screen framebuffer
                        glBindFramebuffer(gl_FRAMEBUFFER, dynamic_context.screen_framebuffer)
                        glViewport(0, 0, display.width, display.height)
+                       glClear gl_COLOR_BUFFER_BIT | gl_DEPTH_BUFFER_BIT
 
                        var gl_error = glGetError
                        assert gl_error == gl_NO_ERROR else print_error gl_error
@@ -81,13 +88,14 @@ redef class App
 
                # Draw to our dynamic framebuffer
                glBindFramebuffer(gl_FRAMEBUFFER, dynamic_context.dynamic_framebuffer)
-               glClear gl_COLOR_BUFFER_BIT | gl_DEPTH_BUFFER_BIT
 
                var ratio = dynamic_resolution_ratio
                ratio = ratio.clamp(min_dynamic_resolution_ratio, max_dynamic_resolution_ratio)
                glViewport(0, 0, (display.width.to_f*ratio).to_i,
                                                 (display.height.to_f*ratio).to_i)
 
+               glClear gl_COLOR_BUFFER_BIT | gl_DEPTH_BUFFER_BIT
+
                var gl_error = glGetError
                assert gl_error == gl_NO_ERROR else print_error gl_error
        end
@@ -176,9 +184,6 @@ private class DynamicContext
        do
                # TODO enable antialiasing.
 
-               var width = (display.width.to_f * max_dynamic_resolution_ratio).to_i
-               var height = (display.height.to_f * max_dynamic_resolution_ratio).to_i
-
                # Set aside the real screen framebuffer name
                var screen_framebuffer = glGetIntegerv(gl_FRAMEBUFFER_BINDING, 0)
                self.screen_framebuffer = screen_framebuffer
@@ -188,34 +193,19 @@ private class DynamicContext
                glBindFramebuffer(gl_FRAMEBUFFER, framebuffer)
                assert glIsFramebuffer(framebuffer)
                self.dynamic_framebuffer = framebuffer
+               var gl_error = glGetError
+               assert gl_error == gl_NO_ERROR else print_error gl_error
 
-               # Depth
+               # Depth & texture/color
                var depthbuffer = glGenRenderbuffers(1).first
-               glBindRenderbuffer(gl_RENDERBUFFER, depthbuffer)
-               assert glIsRenderbuffer(depthbuffer)
-               glRenderbufferStorage(gl_RENDERBUFFER, gl_DEPTH_COMPNENT16, width, height)
-               glFramebufferRenderbuffer(gl_FRAMEBUFFER, gl_DEPTH_ATTACHMENT, gl_RENDERBUFFER, depthbuffer)
                self.depth_renderbuffer = depthbuffer
-
-               # Texture
                var texture = glGenTextures(1).first
-               glBindTexture(gl_TEXTURE_2D, texture)
-               glTexParameteri(gl_TEXTURE_2D, gl_TEXTURE_MIN_FILTER, gl_LINEAR)
-               glTexParameteri(gl_TEXTURE_2D, gl_TEXTURE_MAG_FILTER, gl_LINEAR)
-               glTexParameteri(gl_TEXTURE_2D, gl_TEXTURE_WRAP_S, gl_CLAMP_TO_EDGE)
-               glTexParameteri(gl_TEXTURE_2D, gl_TEXTURE_WRAP_T, gl_CLAMP_TO_EDGE)
-               glTexImage2D(gl_TEXTURE_2D, 0, gl_RGB, width, height,
-                            0, gl_RGB, gl_UNSIGNED_BYTE, new Pointer.nul)
-               glFramebufferTexture2D(gl_FRAMEBUFFER, gl_COLOR_ATTACHMENT0, gl_TEXTURE_2D, texture, 0)
                self.texture = texture
-
-               var gl_error = glGetError
+               gl_error = glGetError
                assert gl_error == gl_NO_ERROR else print_error gl_error
-               assert glCheckFramebufferStatus(gl_FRAMEBUFFER) == gl_FRAMEBUFFER_COMPLETE
 
-               # Take down
-               glBindRenderbuffer(gl_RENDERBUFFER, 0)
-               glBindFramebuffer(gl_FRAMEBUFFER, 0)
+               resize(display, max_dynamic_resolution_ratio)
+               assert glCheckFramebufferStatus(gl_FRAMEBUFFER) == gl_FRAMEBUFFER_COMPLETE
 
                # Array buffer
                buffer_array = glGenBuffers(1).first
@@ -244,6 +234,46 @@ private class DynamicContext
                assert gl_error == gl_NO_ERROR else print_error gl_error
        end
 
+       # Init size or resize `depth_renderbuffer` and `texture`
+       fun resize(display: GamnitDisplay, max_dynamic_resolution_ratio: Float)
+       do
+               var width = (display.width.to_f * max_dynamic_resolution_ratio).to_i
+               var height = (display.height.to_f * max_dynamic_resolution_ratio).to_i
+
+               glBindFramebuffer(gl_FRAMEBUFFER, dynamic_framebuffer)
+
+               var depthbuffer = self.depth_renderbuffer
+               var texture = self.texture
+
+               # Depth
+               glBindRenderbuffer(gl_RENDERBUFFER, depthbuffer)
+               assert glIsRenderbuffer(depthbuffer)
+               glRenderbufferStorage(gl_RENDERBUFFER, gl_DEPTH_COMPNENT16, width, height)
+               glFramebufferRenderbuffer(gl_FRAMEBUFFER, gl_DEPTH_ATTACHMENT, gl_RENDERBUFFER, depthbuffer)
+               var gl_error = glGetError
+               assert gl_error == gl_NO_ERROR else print_error gl_error
+
+               # Texture
+               glBindTexture(gl_TEXTURE_2D, texture)
+               glTexParameteri(gl_TEXTURE_2D, gl_TEXTURE_MIN_FILTER, gl_LINEAR)
+               glTexParameteri(gl_TEXTURE_2D, gl_TEXTURE_MAG_FILTER, gl_LINEAR)
+               glTexParameteri(gl_TEXTURE_2D, gl_TEXTURE_WRAP_S, gl_CLAMP_TO_EDGE)
+               glTexParameteri(gl_TEXTURE_2D, gl_TEXTURE_WRAP_T, gl_CLAMP_TO_EDGE)
+               glTexImage2D(gl_TEXTURE_2D, 0, gl_RGB, width, height,
+                            0, gl_RGB, gl_UNSIGNED_BYTE, new Pointer.nul)
+               glFramebufferTexture2D(gl_FRAMEBUFFER, gl_COLOR_ATTACHMENT0, gl_TEXTURE_2D, texture, 0)
+
+               gl_error = glGetError
+               assert gl_error == gl_NO_ERROR else print_error gl_error
+
+               # Take down
+               glBindRenderbuffer(gl_RENDERBUFFER, 0)
+               glBindFramebuffer(gl_FRAMEBUFFER, 0)
+
+               gl_error = glGetError
+               assert gl_error == gl_NO_ERROR else print_error gl_error
+       end
+
        var destroyed = false
        fun destroy
        do
index cbcc2e7..b278029 100644 (file)
@@ -41,7 +41,8 @@ import more_collections
 import performance_analysis
 
 import gamnit
-import gamnit::cameras_cache
+intrude import gamnit::cameras
+intrude import gamnit::cameras_cache
 import gamnit::dynamic_resolution
 import gamnit::limit_fps
 import gamnit::camera_control
@@ -293,9 +294,7 @@ redef class App
        do
                texture.load
 
-               ui_camera.reset_height 1080.0
-
-               var splash = new Sprite(texture, ui_camera.center)
+               var splash = new Sprite(texture, ui_camera.center.offset(0.0, 0.0, 0.0))
                ui_sprites.add splash
 
                var display = display
@@ -373,11 +372,19 @@ redef class App
                if display != null then display.close
        end
 
-       redef fun frame_core(display)
+       redef fun on_resize(display)
        do
-               # Prepare to draw, clear buffers
-               glClear(gl_COLOR_BUFFER_BIT | gl_DEPTH_BUFFER_BIT)
+               super
+
+               world_camera.mvp_matrix_cache = null
+               ui_camera.mvp_matrix_cache = null
 
+               # Update all sprites in the UI
+               for sprite in ui_sprites do sprite.needs_update
+       end
+
+       redef fun frame_core(display)
+       do
                # Check errors
                var gl_error = glGetError
                assert gl_error == gl_NO_ERROR else print_error gl_error
@@ -585,12 +592,6 @@ private class Simple2dProgram
 end
 
 redef class Point3d[N]
-       # Get a new `Point3d[Float]` with an offset of each axis of `x, y, z`
-       fun offset(x, y, z: Numeric): Point3d[Float]
-       do
-               return new Point3d[Float](self.x.to_f+x.to_f, self.y.to_f+y.to_f, self.z.to_f+z.to_f)
-       end
-
        # ---
        # Associate each point to its sprites
 
@@ -641,6 +642,26 @@ redef class Point3d[N]
        end
 end
 
+redef class OffsetPoint3d
+       redef fun x=(v)
+       do
+               if isset _x and v != x then needs_update
+               super
+       end
+
+       redef fun y=(v)
+       do
+               if isset _y and v != y then needs_update
+               super
+       end
+
+       redef fun z=(v)
+       do
+               if isset _z and v != z then needs_update
+               super
+       end
+end
+
 # Set of sprites sorting them into different `SpriteContext`
 private class SpriteSet
        super HashSet[Sprite]
index 0d39f1e..fccf2ae 100644 (file)
@@ -77,4 +77,9 @@ redef class App
        # The instances passed as `event` may be freed (or overwritten),
        # right after this method returns. They should not be preserved.
        fun accept_event(event: InputEvent): Bool do return false
+
+       # The window has been resized by the user or system
+       #
+       # The framework handles resizing the viewport automatically.
+       fun on_resize(display: GamnitDisplay) do end
 end
index e68ee8e..b0c4241 100644 (file)
@@ -18,6 +18,7 @@ module gamnit_linux
 import sdl2::events
 
 intrude import gamnit
+intrude import display_linux
 
 redef class App
        private var sdl_event_buffer = new SDLEventBuffer.malloc
@@ -33,6 +34,12 @@ redef class App
 
                        # Convert to an SDL event with data
                        var sdl_event = sdl_event_buffer.to_event
+                       if sdl_event isa SDLWindowEvent and sdl_event.is_resized then
+                               display.width = sdl_event.data1
+                               display.height = sdl_event.data2
+                               on_resize display
+                       end
+
                        # Convert to `mnit::inputs` conforming objects
                        var gamnit_event = sdl_event.to_gamnit_event(sdl_event_buffer)
                        accept_event gamnit_event
index 16efae0..22d2c51 100644 (file)
@@ -142,6 +142,17 @@ interface IPoint3d[N: Numeric]
                var s = dist2_xy(other).add(dz.mul(dz))
                return x.value_of(s)
        end
+
+       # Get a new `Point3d[Float]` at an offset of `x, y, z` from `self`
+       #
+       # ~~~
+       # var origin = new Point3d[Float](1.0, 1.0, 1.0)
+       # assert origin.offset(1.0, 2.0, 3.0).to_s == "(2.0, 3.0, 4.0)"
+       # ~~~
+       fun offset(x, y, z: Numeric): Point3d[Float]
+       do return new Point3d[Float](self.x.to_f+x.to_f,
+                                    self.y.to_f+y.to_f,
+                                    self.z.to_f+z.to_f)
 end
 
 # 3D point with `x`, `y` and `z`
diff --git a/lib/gmp/package.ini b/lib/gmp/package.ini
new file mode 100644 (file)
index 0000000..c2fb084
--- /dev/null
@@ -0,0 +1,11 @@
+[package]
+name=gmp
+tags=maths,lib
+maintainer=PatrickBlanchette <PatrickBlanchette@users.noreply.github.com>
+license=Apache-2.0
+[upstream]
+browse=https://github.com/nitlang/nit/tree/master/lib/gmp/
+git=https://github.com/nitlang/nit.git
+git.directory=lib/gmp/
+homepage=http://nitlanguage.org
+issues=https://github.com/nitlang/nit/issues
index f054423..2489f9c 100644 (file)
@@ -415,6 +415,13 @@ class CypherQuery
                self.params = params
        end
 
+       # Pass the argument `value` as the parameter `key`.
+       #
+       # SEE: `set`
+       fun []=(key: String, value: nullable Serializable) do
+               params[key] = value
+       end
+
        # Add a `CREATE` statement to the query
        fun ncreate(query: String): CypherQuery do
                self.query = "{self.query}CREATE {query} "
@@ -451,6 +458,23 @@ class CypherQuery
                return self
        end
 
+       # Pass the argument `value` as the parameter `key`.
+       #
+       # Return `self`.
+       #
+       # ```
+       # var query = (new CypherQuery).nmatch("(n)").nwhere(
+       #               "n.key = key").set("key", "foo")
+       #
+       # assert query.params["key"] == "foo"
+       # ```
+       #
+       # SEE: `[]=`
+       fun set(key: String, value: nullable Serializable): SELF do
+               self[key] = value
+               return self
+       end
+
        # Translate the query to the body of a corresponding Neo4j REST request.
        fun to_rest: JsonObject do
                var obj = new JsonObject
index 64a06e1..8b14fc8 100644 (file)
@@ -56,6 +56,7 @@
 module pop_json
 
 import json
+import meta
 import pop_handlers
 import pop_validation
 
@@ -147,7 +148,7 @@ redef class Handler
        fun deserialize_body(req: HttpRequest, res: HttpResponse): nullable BODY do
                var body = req.body
                var deserializer = new JsonDeserializer(body)
-               var form = deserializer.deserialize(body)
+               var form = deserializer.deserialize(body_type)
                if not form isa BODY or deserializer.errors.not_empty then
                        res.json_error("Bad input", 400)
                        return null
@@ -159,6 +160,8 @@ redef class Handler
        #
        # Define it in each sub handlers depending on the kind of objects sent in request bodies.
        type BODY: Serializable
+
+       private var body_type: String is lazy do return (new GetName[BODY]).to_s
 end
 
 redef class HttpResponse
index ab92185..c3eee92 100644 (file)
@@ -517,6 +517,52 @@ class FloatField
        end
 end
 
+# Check if a field is a Bool
+#
+# ~~~
+# var validator = new ObjectValidator
+# validator.add new BoolField("field", required=false)
+# assert validator.validate("""{}""")
+# assert validator.validate("""{ "field": true }""")
+# assert validator.validate("""{ "field": false }""")
+# assert not validator.validate("""{ "field": "foo" }""")
+#
+# validator = new ObjectValidator
+# validator.add new BoolField("field")
+# assert not validator.validate("""{}""")
+# assert validator.validate("""{ "field": true }""")
+# assert validator.validate("""{ "field": false }""")
+# assert not validator.validate("""{ "field": "foo" }""")
+# ~~~
+#
+# No type conversion is applied on the input value:
+# ~~~
+# assert not validator.validate("""{ "field": "true" }""")
+# assert not validator.validate("""{ "field": 1 }""")
+# assert not validator.validate("""{ "field": [true] }""")
+# ~~~
+class BoolField
+       super RequiredField
+
+       redef fun validate_field(v, obj) do
+               if not super then return false
+               var val = obj.get_or_null(field)
+               if val == null then
+                       if required == null or required == true then
+                               v.validation.add_error(field, "Expected Bool got `null`")
+                               return false
+                       else
+                               return true
+                       end
+               end
+               if not val isa Bool then
+                       v.validation.add_error(field, "Expected Bool got `{val.class_name}`")
+                       return false
+               end
+               return true
+       end
+end
+
 # Check that a field is a JsonObject
 #
 # ~~~
index 6506dfd..2ca18aa 100644 (file)
@@ -50,6 +50,7 @@ extern class SDLEventBuffer `{SDL_Event *`}
                if is_mouse_wheel then return to_mouse_wheel
                if is_keydown then return to_keydown
                if is_keyup then return to_keyup
+               if is_window then return to_window
                return to_event_direct
        end
 
@@ -111,6 +112,14 @@ extern class SDLEventBuffer `{SDL_Event *`}
        # Require: `is_keyup`
        fun to_keyup: SDLKeyboardUpEvent `{ return self; `}
 
+       #  Is this a window event?
+       fun is_window: Bool `{ return self->type == SDL_WINDOWEVENT; `}
+
+       # Get a reference to data at `self` as a `SDLWindowEvent`
+       #
+       # Require: `is_window`
+       fun to_window: SDLWindowEvent `{ return self; `}
+
        # TODO other SDL events:
        #
        # SDL_CommonEvent common
@@ -277,3 +286,23 @@ extern class SDLKeysym `{ SDL_Keysym * `}
        fun mod: Int `{ return self->mod; `}
        # TODO related masks
 end
+
+# Window event
+extern class SDLWindowEvent
+       super SDLEvent
+
+       # Window identifier
+       fun id: Int `{ return self->window.windowID; `}
+
+       # Is `self` a resized event?
+       fun is_resized: Bool `{ return self->window.event == SDL_WINDOWEVENT_RESIZED; `}
+
+       # Is `self` a size changed event?
+       fun is_size_changed: Bool `{ return self->window.event == SDL_WINDOWEVENT_SIZE_CHANGED; `}
+
+       # `data1` field, depends on the event kind
+       fun data1: Int `{ return self->window.data1; `}
+
+       # `data2` field, depends on the event kind
+       fun data2: Int `{ return self->window.data2; `}
+end
index a1d602c..ee34149 100644 (file)
@@ -26,7 +26,7 @@ RUN git clone https://github.com/nitlang/nit.git /nit \
        && make \
        && . misc/nit_env.sh install \
        # Clean and reduce size
-       && strip c_src/nitc bin/nit* \
+       && { strip c_src/nitc bin/nit* || true; } \
        && ccache -C \
        && rm -rf .git
 
index dce0047..c1430da 100755 (executable)
@@ -25,12 +25,12 @@ name=$1
 shift
 
 # Detect a working time command
-if env time --quiet -f%U true 2>/dev/null; then
-       TIME="env time --quiet -f%U -o ${name}.t.out"
-elif env time -f%U true 2>/dev/null; then
-       TIME="env time -f%U -o ${name}.t.out"
-elif env gtime -f%U true 2>/dev/null; then
-       TIME="env gtime -f%U -o ${name}.t.out"
+if command time --quiet -f%e true 2>/dev/null; then
+       TIME="command time --quiet -f%e -o ${name}.t.out"
+elif command time -f%e true 2>/dev/null; then
+       TIME="command time -f%e -o ${name}.t.out"
+elif command gtime -f%e true 2>/dev/null; then
+       TIME="command gtime -f%e -o ${name}.t.out"
 else
        TIME=
 fi
diff --git a/share/man/nit-makepackage.md b/share/man/nit-makepackage.md
new file mode 100644 (file)
index 0000000..7569395
--- /dev/null
@@ -0,0 +1,18 @@
+# NAME
+
+nit-makepackage - helper script to setup package metadata
+
+# SYNOPSIS
+
+nit-makepackage
+
+# DESCRIPTION
+
+`nit-makepackage` creates (or overrides) a package.ini template file in the current directory.
+The result must be examined and adapted.
+
+Default values are guessed from git and the file system.
+
+# SEE ALSO
+
+The Nit language documentation and the source code of its tools and libraries may be downloaded from <http://nitlanguage.org>
index ddbef3b..2d14d83 100644 (file)
@@ -1,4 +1,4 @@
-<div class='card card-xl' ng-class='{active: isActive()}'>
+<div class='card card-xl' ng-class='{active: isActive()}' id='{{definition.full_name}}'>
        <div class='card-body'>
                <h5>
                        <span ng-if='definition.is_intro'>
index ae57696..740ea8b 100644 (file)
@@ -1,3 +1,3 @@
 <span>
-       <a ng-href='{{mentity.web_url | encodeURI}}'>{{mentity.name}}</a>
+       <a ng-href='{{mentity.web_url}}'>{{mentity.name}}</a>
 </span>
index 1a1916b..5873d28 100644 (file)
@@ -1,4 +1,4 @@
-<div class='card'>
+<div class='card' ng-if='listMetrics'>
        <div class='card-body'>
                <h4 class='card-heading'>{{listTitle}}</h4>
                <table class='table'>
diff --git a/share/nitweb/directives/ui/search-field.html b/share/nitweb/directives/ui/search-field.html
new file mode 100644 (file)
index 0000000..e0bd4fd
--- /dev/null
@@ -0,0 +1,8 @@
+<form style='margin-bottom: 0'>
+       <div class='form-group has-icon'>
+               <input placeholder='Search...' type='text' class='form-control search-input'
+                       ng-model-options='{ debounce: 300 }' ng-model='vm.query'
+                       ng-keydown='update($event)' ng-change='vm.search()'>
+               <span class='glyphicon glyphicon-search form-control-icon text-muted'></span>
+       </div>
+</form>
index 138955b..2812b99 100644 (file)
                        type='text/css' rel='stylesheet'>
 
 
-               <link href='/stylesheets/nitweb_bootstrap.css' rel='stylesheet'>
-               <link href='/stylesheets/nitweb.css' rel='stylesheet'>
+               <link rel='stylesheet' href='/stylesheets/nitweb_bootstrap.css'>
+               <link rel='stylesheet' href='/stylesheets/nitweb.css'>
+               <link rel='stylesheet' href='/stylesheets/nitlight.css'>
+               <link rel='stylesheet' href='/stylesheets/search.css'>
+               <link rel='stylesheet' href='/stylesheets/cards.css'>
 
                <script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.4.4/d3.min.js"></script>
                <script src="/vendors/d3pie.min.js"></script>
@@ -51,7 +54,7 @@
                                </div>
                        </div>
                </nav>
-               <div ng-view></div>
+               <ui-view></ui-view>
 
                <script src='https://code.jquery.com/jquery-1.12.4.min.js'
                        integrity='sha256-ZosEbRLbNQzLpnKIkEdrPv7lOy9C27hHQ+Xp8a4MxAQ='
                        crossorigin='anonymous'></script>
                <script src='https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.5/angular.min.js'>
                </script>
-               <script src='https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.5/angular-route.js'>
-               </script>
+               <script src='http://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.3.1/angular-ui-router.min.js'></script>
                <script src='https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.5/angular-sanitize.js'>
                </script>
                <script src='//cdnjs.cloudflare.com/ajax/libs/angular-loading-bar/0.9.0/loading-bar.min.js'>
                </script>
 
                <script src='/javascripts/nitweb.js'></script>
-               <script src='/javascripts/model.js'></script>
                <script src='/javascripts/entities.js'></script>
                <script src='/javascripts/ui.js'></script>
                <script src='/javascripts/index.js'></script>
index 365b8c9..2b96d19 100644 (file)
 
 (function() {
        angular
-               .module('docdown', ['model', 'ngSanitize'])
+               .module('docdown', ['ngSanitize'])
 
-               .controller('DocdownCtrl', ['$routeParams', '$sce', '$scope', '$location', 'DocDown', function($routeParams, $sce, $scope, $location, DocDown) {
+               .config(function($stateProvider, $locationProvider) {
+                       $stateProvider
+                               .state('docdown', {
+                                       url: '/docdown',
+                                       templateUrl: 'views/docdown.html',
+                                       controller: 'DocdownCtrl',
+                                       controllerAs: 'docdownCtrl'
+                               })
+               })
+
+               .factory('DocDown', [ '$http', function($http) {
+                       return {
+                               postMarkdown: function(md, cb, cbErr) {
+                                       $http.post('/api/docdown', md)
+                                               .success(cb)
+                                               .error(cbErr);
+                               }
+                       }
+               }])
+
+               .controller('DocdownCtrl', ['$sce', '$scope', '$location', 'DocDown', function($sce, $scope, $location, DocDown) {
 
                        this.updateSnippet = function() {
                                this.updateLink();
index e48b40a..d5dd3ef 100644 (file)
 
 (function() {
        angular
-               .module('entities', ['ngSanitize', 'ui', 'model'])
+               .module('entities', ['ngSanitize', 'ui'])
 
-               .controller('EntityCtrl', ['Model', 'Metrics', 'Feedback', '$routeParams', '$scope', '$sce', function(Model, Metrics, Feedback, $routeParams, $scope, $sce) {
-                       $scope.entityId = $routeParams.id;
+               /* Router */
 
-                       this.loadEntityLinearization = function() {
-                               Model.loadEntityLinearization($routeParams.id,
-                                       function(data) {
-                                               $scope.linearization = data;
-                                       }, function(err) {
-                                               $scope.error = err;
-                                       });
-                       };
+               .config(function($stateProvider, $locationProvider) {
+                       $stateProvider
+                               .state('doc', {
+                                       url: '/doc/:id',
+                                       templateUrl: 'views/doc/index.html',
+                                       resolve: {
+                                               mentity: function(Model, $q, $stateParams, $state) {
+                                                       var d = $q.defer();
+                                                       Model.loadEntity($stateParams.id, d.resolve,
+                                                               function() {
+                                                                       $state.go('404', null, { location: false })
+                                                               });
+                                                       return d.promise;
+                                               }
+                                       },
+                                       controller: function(mentity) {
+                                               this.mentity = mentity;
+                                       },
+                                       controllerAs: 'vm',
+                                       abstract: true
+                               })
+                               .state('doc.entity', {
+                                       url: '',
+                                       templateUrl: 'views/doc/entity.html',
+                                       controller: function(mentity) {
+                                               this.mentity = mentity;
+                                       },
+                                       controllerAs: 'vm',
+                                       abstract: true
+                               })
+                               .state('doc.entity.doc', {
+                                       url: '',
+                                       templateUrl: 'views/doc/doc.html',
+                                       controller: function(mentity) {
+                                               this.mentity = mentity;
+                                       },
+                                       controllerAs: 'vm',
+                               })
+                               .state('doc.entity.graph', {
+                                       url: '/graph',
+                                       templateUrl: 'views/doc/graph.html',
+                                       resolve: {
+                                               graph: function(Model, $q, $stateParams, $state) {
+                                                       var d = $q.defer();
+                                                       Model.loadEntityGraph($stateParams.id, d.resolve,
+                                                               function() {
+                                                                       $state.go('404', null, { location: false })
+                                                               });
+                                                       return d.promise;
+                                               }
+                                       },
+                                       controller: function(graph, $sce) {
+                                               this.graph = $sce.trustAsHtml(graph);
+                                       },
+                                       controllerAs: 'vm',
+                               })
+                               .state('doc.entity.metrics', {
+                                       url: '/metrics',
+                                       templateUrl: 'views/doc/metrics.html',
+                                       resolve: {
+                                               metrics: function(Metrics, $q, $stateParams, $state) {
+                                                       var d = $q.defer();
+                                                       Metrics.loadStructuralMetrics($stateParams.id, d.resolve,
+                                                               function() {
+                                                                       $state.go('404', null, { location: false })
+                                                               });
+                                                       return d.promise;
+                                               }
+                                       },
+                                       controller: function(mentity, metrics) {
+                                               this.mentity = mentity;
+                                               this.metrics = metrics;
+                                       },
+                                       controllerAs: 'vm',
+                               })
+                               .state('doc.entity.code', {
+                                       url: '/code',
+                                       templateUrl: 'views/doc/code.html',
+                                       resolve: {
+                                               code: function(Model, $q, $stateParams, $state) {
+                                                       var d = $q.defer();
+                                                       Model.loadEntityCode($stateParams.id, d.resolve,
+                                                               function() {
+                                                                       $state.go('404', null, { location: false })
+                                                               });
+                                                       return d.promise;
+                                               }
+                                       },
+                                       controller: function(mentity, code) {
+                                               this.mentity = mentity;
+                                               this.code = code;
+                                       },
+                                       controllerAs: 'vm',
+                               })
+                               .state('doc.entity.defs', {
+                                       url: '/defs',
+                                       templateUrl: 'views/doc/defs.html',
+                                       resolve: {
+                                               defs: function(Model, $q, $stateParams, $state) {
+                                                       var d = $q.defer();
+                                                       Model.loadEntityDefs($stateParams.id, d.resolve,
+                                                               function() {
+                                                                       $state.go('404', null, { location: false })
+                                                               });
+                                                       return d.promise;
+                                               }
+                                       },
+                                       controller: function(mentity, defs) {
+                                               this.mentity = mentity;
+                                               this.defs = defs;
+                                       },
+                                       controllerAs: 'vm',
+                               })
+                               .state('doc.entity.lin', {
+                                       url: '/lin',
+                                       templateUrl: 'views/doc/lin.html',
+                                       resolve: {
+                                               lin: function(Model, $q, $stateParams, $state) {
+                                                       var d = $q.defer();
+                                                       Model.loadEntityLinearization($stateParams.id, d.resolve,
+                                                               function() {
+                                                                       $state.go('404', null, { location: false })
+                                                               });
+                                                       return d.promise;
+                                               }
+                                       },
+                                       controller: function(mentity, lin, $scope, $location, $anchorScroll) {
+                                               var vm = this;
+                                               vm.focus = $location.hash() ?
+                                                       $location.hash() : mentity.intro.full_name;
+                                               vm.mentity = mentity;
+                                               vm.linearization = lin;
+                                               setTimeout(function() {
+                                                       $anchorScroll();
+                                               }, 400);
+                                               $scope.$watch(function () {
+                                                       return $location.hash();
+                                               }, function (value) {
+                                                       vm.focus = $location.hash() ?
+                                                               $location.hash() : mentity.intro.full_name;
+                                                       $anchorScroll();
+                                               });
+                                       },
+                                       controllerAs: 'vm'
+                               })
+                               .state('doc.entity.all', {
+                                       url: '/all',
+                                       templateUrl: 'views/doc/all.html',
+                                       controller: function(mentity) {
+                                               this.mentity = mentity;
+                                       },
+                                       controllerAs: 'vm',
+                               })
+               })
 
-                       this.loadEntityDefs = function() {
-                               Model.loadEntityDefs($routeParams.id,
-                                       function(data) {
-                                               $scope.defs = data;
-                                       }, function(err) {
-                                               $scope.error = err;
-                                       });
-                       };
+               /* Model */
 
-                       this.loadEntityCode = function() {
-                               Model.loadEntityCode($routeParams.id,
-                                       function(data) {
-                                               $scope.code = data;
-                                       }, function(err) {
-                                               $scope.code = err;
-                                       });
-                       };
+               .factory('Model', [ '$http', function($http) {
+                       return {
 
-                       this.loadEntityGraph = function(e) {
-                               Model.loadEntityGraph($routeParams.id,
-                                       function(data) {
-                                               $scope.graph = $sce.trustAsHtml(data);
-                                       }, function(err) {
-                                               $scope.error = err;
-                                       });
-                       };
+                               loadEntity: function(id, cb, cbErr) {
+                                       $http.get('/api/entity/' + id)
+                                               .success(cb)
+                                               .error(cbErr);
+                               },
 
-                       this.loadStructuralMetrics = function() {
-                               Metrics.loadStructuralMetrics($routeParams.id,
-                                       function(data) {
-                                               $scope.metrics = data;
-                                       }, function(message, status) {
-                                               $scope.error = {message: message, status: status};
-                                       });
-                       };
+                               loadEntityLinearization: function(id, cb, cbErr) {
+                                       $http.get('/api/linearization/' + id)
+                                               .success(cb)
+                                               .error(cbErr);
+                               },
 
-                       Model.loadEntity($routeParams.id,
-                               function(data) {
-                                       $scope.mentity = data;
-                               }, function(message, status) {
-                                       $scope.error = {message: message, status: status};
-                               });
+                               loadEntityDefs: function(id, cb, cbErr) {
+                                       $http.get('/api/defs/' + id)
+                                               .success(cb)
+                                               .error(cbErr);
+                               },
+
+                               loadEntityCode: function(id, cb, cbErr) {
+                                       $http.get('/api/code/' + id)
+                                               .success(cb)
+                                               .error(cbErr);
+                               },
+
+                               loadEntityGraph: function(id, cb, cbErr) {
+                                       $http.get('/api/graph/inheritance/' + id + '?cdepth=3')
+                                               .success(cb)
+                                               .error(cbErr);
+                               },
+
+                               search: function(q, n, cb, cbErr) {
+                                       $http.get('/api/search?q=' + q + '&n=' + n)
+                                               .success(cb)
+                                               .error(cbErr);
+                               }
+                       };
                }])
 
+               /* Directives */
+
                .directive('entityLink', function() {
                        return {
                                restrict: 'E',
                                        $scope.codeId = 'code_' + $scope.definition.full_name.replace(/[^a-zA-Z0-9]/g, '_');
 
                                        $scope.isActive = function() {
-                                               return $scope.focus.full_name == $scope.definition.full_name;
+                                               return $scope.focus == $scope.definition.full_name;
                                        }
 
                                        $scope.loadCardCode = function() {
                                                }
                                        };
 
-                                       if($scope.isActive()) $scope.loadCardCode();
+                                       $scope.$watch('focus', function() {
+                                               if($scope.isActive()) $scope.loadCardCode();
+                                       });
                                }
                        };
                }])
index 41d6647..72eadbc 100644 (file)
 
 (function() {
        angular
-               .module('grades', ['ngSanitize', 'model'])
+               .module('grades', ['ngSanitize'])
+
+               .config(function($stateProvider, $locationProvider) {
+                       $stateProvider
+                               .state('grades', {
+                                       url: '/grades',
+                                       templateUrl: 'views/grades.html',
+                                       controller: 'GradesCtrl',
+                                       controllerAs: 'gradesCtrl'
+                               })
+               })
+
+               .factory('Feedback', [ '$http', function($http) {
+                       return {
+                               loadEntityStars: function(id, cb, cbErr) {
+                                       $http.get('/api/feedback/stars/' + id)
+                                               .success(cb)
+                                               .error(cbErr);
+                               },
+                               loadEntityStarsDimension: function(id, dimension, cb, cbErr) {
+                                       $http.get('/api/feedback/stars/' + id + '/dimension/' + dimension)
+                                               .success(cb)
+                                               .error(cbErr);
+                               },
+                               postEntityStarDimension: function(id, dimension, rating, cb, cbErr) {
+                                       $http.post('/api/feedback/stars/' + id + '/dimension/' + dimension,
+                                               {rating: rating})
+                                               .success(cb)
+                                               .error(cbErr);
+                               },
+                               loadMostRated: function(cb, cbErr) {
+                                       $http.get('/api/feedback/grades/most')
+                                               .success(cb)
+                                               .error(cbErr);
+                               },
+                               loadBestRated: function(cb, cbErr) {
+                                       $http.get('/api/feedback/grades/best')
+                                               .success(cb)
+                                               .error(cbErr);
+                               },
+                               loadWorstRated: function(cb, cbErr) {
+                                       $http.get('/api/feedback/grades/worst')
+                                               .success(cb)
+                                               .error(cbErr);
+                               },
+                               loadUsersRatings: function(cb, cbErr) {
+                                       $http.get('/api/feedback/grades/users')
+                                               .success(cb)
+                                               .error(cbErr);
+                               },
+                       }
+               }])
 
                .controller('GradesCtrl', ['Feedback', '$scope', function(Feedback, $scope) {
 
index 534f462..104d627 100644 (file)
 
 (function() {
        angular
-               .module('index', ['model', 'ngSanitize'])
+               .module('index', [])
 
-               .run(['$anchorScroll', function($anchorScroll) {
-                       $anchorScroll.yOffset = 80;
+               .config(function($stateProvider, $locationProvider) {
+                       $stateProvider
+                               .state('catalog', {
+                                       url: '/',
+                                       templateUrl: 'views/catalog/index.html',
+                                       controller: 'CatalogCtrl',
+                                       controllerAs: 'vm',
+                                       abstract: true
+                               })
+                               .state('catalog.highlighted', {
+                                       url: '',
+                                       templateUrl: 'views/catalog/highlighted.html',
+                                       controller: 'CatalogHighlightedCtrl',
+                                       controllerAs: 'vm'
+                               })
+                               .state('catalog.required', {
+                                       url: 'required',
+                                       templateUrl: 'views/catalog/most_required.html',
+                                       controller: 'CatalogRequiredCtrl',
+                                       controllerAs: 'vm'
+                               })
+                               .state('catalog.tags', {
+                                       url: 'tags',
+                                       templateUrl: 'views/catalog/by_tags.html',
+                                       controller: 'CatalogTagsCtrl',
+                                       controllerAs: 'vm'
+                               })
+               })
+
+               .factory('Catalog', [ '$http', function($http) {
+                       return {
+                               loadHightlighted: function(cb, cbErr) {
+                                       $http.get('/api/catalog/highlighted')
+                                               .success(cb)
+                                               .error(cbErr);
+                               },
+
+                               loadMostRequired: function(cb, cbErr) {
+                                       $http.get('/api/catalog/required')
+                                               .success(cb)
+                                               .error(cbErr);
+                               },
+
+                               loadByTags: function(cb, cbErr) {
+                                       $http.get('/api/catalog/bytags')
+                                               .success(cb)
+                                               .error(cbErr);
+                               },
+
+                               loadStats: function(cb, cbErr) {
+                                       $http.get('/api/catalog/stats')
+                                               .success(cb)
+                                               .error(cbErr);
+                               },
+
+                               loadContributors: function(cb, cbErr) {
+                                       $http.get('/api/catalog/contributors')
+                                               .success(cb)
+                                               .error(cbErr);
+                               },
+                       }
                }])
 
-               .controller('IndexCtrl', ['Catalog', '$routeParams', '$sce', '$scope', '$location', '$anchorScroll', function(Catalog, $routeParams, $sce, $scope, $location, $anchorScroll) {
-                       this.loadHighlighted = function() {
-                               Catalog.loadHightlighted(
-                                       function(data) {
-                                               $scope.highlighted = data;
-                                       }, function(err) {
-                                               $scope.error = err;
-                                       });
-                       };
+               .controller('CatalogCtrl', function(Catalog) {
+                       var vm = this;
 
-                       this.loadMostRequired = function() {
-                               Catalog.loadMostRequired(
-                                       function(data) {
-                                               $scope.required = data;
-                                       }, function(err) {
-                                               $scope.error = err;
-                                       });
-                       };
+                       Catalog.loadContributors(
+                               function(data) {
+                                       vm.contributors = data;
+                               }, function(err) {
+                                       vm.error = err;
+                               });
 
-                       this.loadByTags = function() {
-                               Catalog.loadByTags(
-                                       function(data) {
-                                               $scope.bytags = data;
-                                       }, function(err) {
-                                               $scope.error = err;
-                                       });
-                       };
+                       Catalog.loadStats(
+                               function(data) {
+                                       vm.stats = data;
+                               }, function(err) {
+                                       vm.error = err;
+                               });
+               })
 
-                       this.loadStats = function() {
-                               Catalog.loadStats(
-                                       function(data) {
-                                               $scope.stats = data;
-                                       }, function(err) {
-                                               $scope.error = err;
-                                       });
-                       };
+               .controller('CatalogHighlightedCtrl', function(Catalog) {
+                       var vm = this;
 
-                       this.loadContributors = function() {
-                               Catalog.loadContributors(
-                                       function(data) {
-                                               $scope.contributors = data;
-                                       }, function(err) {
-                                               $scope.error = err;
-                                       });
-                       };
+                       Catalog.loadHightlighted(
+                               function(data) {
+                                       vm.highlighted = data;
+                               }, function(err) {
+                                       vm.error = err;
+                               });
+               })
 
+               .controller('CatalogRequiredCtrl', function(Catalog) {
+                       var vm = this;
 
-                       this.scrollTo = function(hash) {
-                               $anchorScroll(hash);
-                       }
+                       Catalog.loadMostRequired(
+                               function(data) {
+                                       vm.required = data;
+                               }, function(err) {
+                                       vm.error = err;
+                               });
+               })
+
+               .controller('CatalogTagsCtrl', function(Catalog, $anchorScroll, $location) {
+                       var vm = this;
+
+                       Catalog.loadByTags(
+                               function(data) {
+                                       vm.bytags = data;
+                               }, function(err) {
+                                       vm.error = err;
+                               });
 
-                       this.loadHighlighted();
-                       this.loadStats();
-                       this.loadContributors();
-               }])
 
-               .directive('contributorList', ['Model', function(Model) {
+                       vm.scrollTo = function(hash) {
+                               $location.hash(hash);
+                               $anchorScroll();
+                       }
+               })
+
+               .directive('contributorList', function(Model) {
                        return {
                                restrict: 'E',
                                scope: {
                                },
                                templateUrl: '/directives/contributor-list.html'
                        };
-               }])
+               })
 })();
index 9698e81..2fe8e15 100644 (file)
 
 (function() {
        angular
-               .module('metrics', ['model'])
+               .module('metrics', [])
+
+               .factory('Metrics', [ '$http', function($http) {
+                       return {
+                               loadStructuralMetrics: function(id, cb, cbErr) {
+                                       $http.get('/api/metrics/structural/' + id)
+                                               .success(cb)
+                                               .error(cbErr);
+                               }
+                       }
+               }])
 
                .directive('metricsList', function() {
                        return {
                                                                "content": [
                                                                        {
                                                                                "label": "Concrete classes",
-                                                                               "value": $scope.chartMetrics.mnbcc.avg,
+                                                                               "value": $scope.chartMetrics.mnbcc?
+                                                                                       $scope.chartMetrics.mnbcc.avg : 0,
                                                                                "color": "#228835"
                                                                        },
                                                                        {
                                                                                "label": "Abstract classes",
-                                                                               "value": $scope.chartMetrics.mnbac.avg,
+                                                                               "value": $scope.chartMetrics.mnbac?
+                                                                                       $scope.chartMetrics.mnbac.avg : 0,
                                                                                "color": "#103EB8"
                                                                        },
                                                                        {
                                                                                "label": "Interfaces",
-                                                                               "value": $scope.chartMetrics.mnbic.avg,
+                                                                               "value": $scope.chartMetrics.mnbic?
+                                                                                       $scope.chartMetrics.mnbic.avg : 0,
                                                                                "color": "#e65314"
                                                                        }
                                                                ]
diff --git a/share/nitweb/javascripts/model.js b/share/nitweb/javascripts/model.js
deleted file mode 100644 (file)
index fb2c008..0000000
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright 2016 Alexandre Terrasa <alexandre@moz-code.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.
- */
-
-(function() {
-       var apiUrl = '/api';
-
-       angular
-               .module('model', [])
-
-               .factory('Model', [ '$http', function($http) {
-                       return {
-
-                               loadEntity: function(id, cb, cbErr) {
-                                       $http.get(apiUrl + '/entity/' + id)
-                                               .success(cb)
-                                               .error(cbErr);
-                               },
-
-                               loadEntityLinearization: function(id, cb, cbErr) {
-                                       $http.get(apiUrl + '/linearization/' + id)
-                                               .success(cb)
-                                               .error(cbErr);
-                               },
-
-                               loadEntityDefs: function(id, cb, cbErr) {
-                                       $http.get(apiUrl + '/defs/' + id)
-                                               .success(cb)
-                                               .error(cbErr);
-                               },
-
-                               loadEntityCode: function(id, cb, cbErr) {
-                                       $http.get(apiUrl + '/code/' + id)
-                                               .success(cb)
-                                               .error(cbErr);
-                               },
-
-                               loadEntityGraph: function(id, cb, cbErr) {
-                                       $http.get(apiUrl + '/graph/inheritance/' + id + '?cdepth=3')
-                                               .success(cb)
-                                               .error(cbErr);
-                               },
-
-                               search: function(q, n, cb, cbErr) {
-                                       $http.get(apiUrl + '/search?q=' + q + '&n=' + n)
-                                               .success(cb)
-                                               .error(cbErr);
-                               }
-                       };
-               }])
-
-               .factory('Catalog', [ '$http', function($http) {
-                       return {
-                               loadHightlighted: function(cb, cbErr) {
-                                       $http.get(apiUrl + '/catalog/highlighted')
-                                               .success(cb)
-                                               .error(cbErr);
-                               },
-
-                               loadMostRequired: function(cb, cbErr) {
-                                       $http.get(apiUrl + '/catalog/required')
-                                               .success(cb)
-                                               .error(cbErr);
-                               },
-
-                               loadByTags: function(cb, cbErr) {
-                                       $http.get(apiUrl + '/catalog/bytags')
-                                               .success(cb)
-                                               .error(cbErr);
-                               },
-
-                               loadStats: function(cb, cbErr) {
-                                       $http.get(apiUrl + '/catalog/stats')
-                                               .success(cb)
-                                               .error(cbErr);
-                               },
-
-                               loadContributors: function(cb, cbErr) {
-                                       $http.get(apiUrl + '/catalog/contributors')
-                                               .success(cb)
-                                               .error(cbErr);
-                               },
-                       }
-               }])
-
-               .factory('Feedback', [ '$http', function($http) {
-                       return {
-                               loadEntityStars: function(id, cb, cbErr) {
-                                       $http.get(apiUrl + '/feedback/stars/' + id)
-                                               .success(cb)
-                                               .error(cbErr);
-                               },
-                               loadEntityStarsDimension: function(id, dimension, cb, cbErr) {
-                                       $http.get(apiUrl + '/feedback/stars/' + id + '/dimension/' + dimension)
-                                               .success(cb)
-                                               .error(cbErr);
-                               },
-                               postEntityStarDimension: function(id, dimension, rating, cb, cbErr) {
-                                       $http.post(apiUrl + '/feedback/stars/' + id + '/dimension/' + dimension,
-                                               {rating: rating})
-                                               .success(cb)
-                                               .error(cbErr);
-                               },
-                               loadMostRated: function(cb, cbErr) {
-                                       $http.get(apiUrl + '/feedback/grades/most')
-                                               .success(cb)
-                                               .error(cbErr);
-                               },
-                               loadBestRated: function(cb, cbErr) {
-                                       $http.get(apiUrl + '/feedback/grades/best')
-                                               .success(cb)
-                                               .error(cbErr);
-                               },
-                               loadWorstRated: function(cb, cbErr) {
-                                       $http.get(apiUrl + '/feedback/grades/worst')
-                                               .success(cb)
-                                               .error(cbErr);
-                               },
-                               loadUsersRatings: function(cb, cbErr) {
-                                       $http.get(apiUrl + '/feedback/grades/users')
-                                               .success(cb)
-                                               .error(cbErr);
-                               },
-                       }
-               }])
-
-               .factory('DocDown', [ '$http', function($http) {
-                       return {
-                               postMarkdown: function(md, cb, cbErr) {
-                                       $http.post(apiUrl + '/docdown', md)
-                                               .success(cb)
-                                               .error(cbErr);
-                               }
-                       }
-               }])
-
-               .factory('Metrics', [ '$http', function($http) {
-                       return {
-                               loadStructuralMetrics: function(id, cb, cbErr) {
-                                       $http.get(apiUrl + '/metrics/structural/' + id)
-                                               .success(cb)
-                                               .error(cbErr);
-                               }
-                       }
-               }])
-
-               .factory('User', [ '$http', function($http) {
-                       return {
-                               loadUser: function(cb, cbErr) {
-                                       $http.get(apiUrl + '/user')
-                                               .success(cb)
-                                               .error(cbErr);
-                               },
-                               loadUserStars: function(cb, cbErr) {
-                                       $http.get(apiUrl + '/feedback/user/stars')
-                                               .success(cb)
-                                               .error(cbErr);
-                               },
-                       }
-               }])
-})();
index 78f0f71..181da8e 100644 (file)
  */
 
 (function() {
-       angular.module('nitweb', ['ngRoute', 'ngSanitize', 'angular-loading-bar', 'entities', 'docdown', 'index', 'metrics', 'users', 'grades'])
+       angular.module('nitweb', ['ui.router', 'ngSanitize', 'angular-loading-bar', 'index', 'entities', 'docdown', 'metrics', 'users', 'grades'])
+
        .config(['cfpLoadingBarProvider', function(cfpLoadingBarProvider) {
                cfpLoadingBarProvider.includeSpinner = false;
        }])
-       .config(function($routeProvider, $locationProvider) {
-               $routeProvider
-                       .when('/', {
-                               templateUrl: 'views/index.html',
-                               controller: 'IndexCtrl',
-                               controllerAs: 'indexCtrl'
-                       })
-                       .when('/user', {
-                               templateUrl: 'views/user.html',
-                               controller: 'UserCtrl',
-                               controllerAs: 'userCtrl'
-                       })
-                       .when('/docdown', {
-                               templateUrl: 'views/docdown.html',
-                               controller: 'DocdownCtrl',
-                               controllerAs: 'docdownCtrl'
-                       })
-                       .when('/grades', {
-                               templateUrl: 'views/grades.html',
-                               controller: 'GradesCtrl',
-                               controllerAs: 'gradesCtrl'
-                       })
-                       .when('/login', {
-                               controller : function(){
-                                       window.location.replace('/login');
-                               },
-                           template : "<div></div>"
-                       })
-                       .when('/logout', {
-                               controller : function(){
-                                       window.location.replace('/logout');
-                               },
-                           template : "<div></div>"
-                       })
-                       .when('/doc/:id', {
-                               templateUrl: 'views/doc.html',
-                               controller: 'EntityCtrl',
-                               controllerAs: 'entityCtrl',
-                               reloadOnSearch: false
-                       })
-                       .otherwise({
+
+       .run(['$anchorScroll', function($anchorScroll) {
+               $anchorScroll.yOffset = 80;
+       }])
+
+       .config(function($stateProvider, $locationProvider) {
+               $stateProvider
+                       .state({
+                               name: '404',
+                               url: '*path',
                                templateUrl: 'views/error.html'
-                       });
+                       })
                $locationProvider.html5Mode(true);
        })
-
-       .filter('encodeURI', function() {
-               return encodeURIComponent;
-       });
 })();
index f6f5eca..7fc3f8a 100644 (file)
@@ -16,9 +16,9 @@
 
 (function() {
        angular
-               .module('ui', [ 'model' ])
+               .module('ui', [])
 
-               .controller('SearchCtrl', function(Model, $routeParams, $scope, $location, $document) {
+               .controller('SearchCtrl', function(Model, $scope, $location, $document) {
 
                        $scope.query = '';
 
index bd446b7..c148bbe 100644 (file)
 
 (function() {
        angular
-               .module('users', ['ngSanitize', 'model'])
+               .module('users', ['ngSanitize'])
 
-               .controller('UserCtrl', ['User', '$routeParams', '$scope', function(User, $routeParams, $scope) {
+               .config(function($stateProvider, $locationProvider) {
+                       $stateProvider
+                               .state('user', {
+                                       url: '/user',
+                                       templateUrl: 'views/user.html',
+                                       controller: 'UserCtrl',
+                                       controllerAs: 'userCtrl'
+                               })
+                               .state('login', {
+                                       url: '/login',
+                                       controller : function(){
+                                               window.location.replace('/login');
+                                       },
+                                       template : "<div></div>"
+                               })
+                               .state('logout', {
+                                       controller : function(){
+                                               window.location.replace('/logout');
+                                       },
+                                       template : "<div></div>"
+                               })
+               })
+
+               .factory('User', [ '$http', function($http) {
+                       return {
+                               loadUser: function(cb, cbErr) {
+                                       $http.get('/api/user')
+                                               .success(cb)
+                                               .error(cbErr);
+                               },
+                               loadUserStars: function(cb, cbErr) {
+                                       $http.get('/api/feedback/user/stars')
+                                               .success(cb)
+                                               .error(cbErr);
+                               },
+                       }
+               }])
+
+               .controller('UserCtrl', ['User', '$scope', function(User, $scope) {
                        this.loadUser = function() {
                                User.loadUser(
                                        function(data) {
diff --git a/share/nitweb/stylesheets/cards.css b/share/nitweb/stylesheets/cards.css
new file mode 100644 (file)
index 0000000..0d39859
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2017 Alexandre Terrasa <alexandre@moz-code.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.
+ */
+
+/* cards */
+
+.card.active {
+       border: 1px solid #1E9431;
+}
+
+.card-heading {
+    margin-top: 0;
+    margin-bottom: 5px;
+}
+
+.card {
+       display: table;
+       width: 100%;
+       background: #fff;
+       border: 1px solid #ccc;
+       margin-top: 10px;
+       box-shadow: 0 -1px 0 #e5e5e5,0 0 2px rgba(0,0,0,.12),0 2px 4px rgba(0,0,0,.24);
+}
+
+.card-body, .card-left, .card-right {
+       display: table-cell;
+       padding: 15px;
+}
+
+.card-body {
+       padding: 15px 0;
+       width: 100%
+}
+
+.card-body:first-child {
+       padding-left: 15px;
+}
+
+.card-body:last-child {
+       padding-right: 15px;
+}
+
+.card-list {
+       margin-top: 10px;
+}
+
+.card-list > .card:first-child {
+       border-top: 1px solid #ccc;
+}
+
+.card-list > .card {
+       margin-top: 0;
+       border-top: none;
+}
+
+.card-title {
+    margin-top: 0;
+}
diff --git a/share/nitweb/stylesheets/nitlight.css b/share/nitweb/stylesheets/nitlight.css
new file mode 100644 (file)
index 0000000..63a1356
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2017 Alexandre Terrasa <alexandre@moz-code.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.
+ */
+
+/*
+ * Code Highlighting
+ */
+
+.nitdoc h1, .nitdoc h2, .nitdoc h3, .nitdoc h4, .nitdoc h5, .nitdoc h6 {
+       color: #333;
+}
+
+.nitdoc .synopsys {
+       margin-top: 0;
+}
+
+.nitcode a { color: inherit; text-decoration: inherit; } /* hide links */
+.nitcode a:hover { text-decoration: underline; } /* underline links */
+.nitcode span[title]:hover { text-decoration: underline; } /* underline titles */
+/* lexical raw tokens. independent of usage or semantic: */
+.nitcode .nc_c { color: gray; font-style: italic; } /* comment */
+.nitcode .nc_d { color: #3D8127; font-style: italic; } /* documentation comments */
+.nitcode .nc_k { font-weight: bold; } /* keyword */
+.nitcode .nc_o {} /* operator */
+.nitcode .nc_i {} /* standard identifier */
+.nitcode .nc_t { color: #445588; font-weight: bold; } /* type/class identifier */
+.nitcode .nc_a { color: #445588; font-style: italic; } /* old style attribute identifier */
+.nitcode .nc_l { color: #009999; } /* char and number literal */
+.nitcode .nc_s { color: #8F1546; } /* string literal */
+/* syntactic token usage. added because of their position in the AST */
+.nitcode .nc_ast { color: blue; } /* assert label */
+.nitcode .nc_la { color: blue; } /* break/continue label */
+.nitcode .nc_m { color: #445588; } /* module name */
+/* syntactic groups */
+.nitcode .nc_def { font-weight: bold; color: blue; } /* name used in a definition */
+.nitcode .nc_def.nc_a { color: blue; } /* name used in a attribute definition */
+.nitcode .nc_def.nc_t { color: blue; } /* name used in a class or vt definition */
+.nitcode .nc_ss { color: #9E6BEB; } /* superstrings */
+.nitcode .nc_cdef {} /* A whole class definition */
+.nitcode .nc_pdef {} /* A whole property definition */
+/* semantic token usage */
+.nitcode .nc_v { font-style: italic; } /* local variable or parameter */
+.nitcode .nc_vt { font-style: italic; } /* virtual type or formal type */
+.nitcode .nc_error { border: 1px red solid;} /* not used */
index b78f244..864c296 100644 (file)
@@ -1,3 +1,38 @@
+/*
+ * Copyright 2017 Alexandre Terrasa <alexandre@moz-code.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.
+ */
+
+/* utils */
+
+.no-margin {
+       margin: 0;
+}
+
+.no-padding {
+       padding: 0;
+}
+
+.navbar-fixed-top {
+       background-color: #1E9431;
+       box-shadow: 0 0 4px rgba(0,0,0,.14),0 4px 8px rgba(0,0,0,.28);
+}
+
+#loading-bar .bar {
+       background: #FF8100;
+}
+
 /* Body */
 
 body {
@@ -14,70 +49,11 @@ a {
        cursor: pointer;
 }
 
-.nitdoc h1, .nitdoc h2, .nitdoc h3, .nitdoc h4, .nitdoc h5, .nitdoc h6 {
-       color: #333;
-}
-
 .page-header {
     margin-top: 0;
     border: none;
 }
 
-/* cards */
-
-.card.active {
-       border: 1px solid #1E9431;
-}
-
-.card-heading {
-    margin-top: 0;
-    margin-bottom: 5px;
-}
-
-.card {
-       display: table;
-       width: 100%;
-       background: #fff;
-       border: 1px solid #ccc;
-       margin-top: 10px;
-       box-shadow: 0 -1px 0 #e5e5e5,0 0 2px rgba(0,0,0,.12),0 2px 4px rgba(0,0,0,.24);
-}
-
-.card-body, .card-left, .card-right {
-       display: table-cell;
-       padding: 15px;
-}
-
-.card-body {
-       padding: 15px 0;
-       width: 100%
-}
-
-.card-body:first-child {
-       padding-left: 15px;
-}
-
-.card-body:last-child {
-       padding-right: 15px;
-}
-
-.card-list {
-       margin-top: 10px;
-}
-
-.card-list > .card:first-child {
-       border-top: 1px solid #ccc;
-}
-
-.card-list > .card {
-       margin-top: 0;
-       border-top: none;
-}
-
-.card-title {
-    margin-top: 0;
-}
-
 /* ui */
 
 .btn-bar { margin-top: -5px; float: right }
@@ -173,57 +149,10 @@ entity-list:hover .btn-filter {
        cursor: pointer;
 }
 
-/* loading bar */
-
-#loading-bar .bar {
-       background: #FF8100;
-}
-
 /* navs */
 
 .nav-tabs li { cursor: pointer; }
 
-.navbar-fixed-top {
-       background-color: #1E9431;
-       box-shadow: 0 0 4px rgba(0,0,0,.14),0 4px 8px rgba(0,0,0,.28);
-}
-
-.navbar-fixed-top .form-control:hover, .navbar-fixed-top .form-control:focus {
-       background: rgba(255, 255, 255, 0.2);
-}
-
-.navbar-fixed-top .form-control {
-       background: rgba(255, 255, 255, 0.1);
-    border: none;
-    color: #fff;
-    box-shadow: none;
-}
-
-.navbar-fixed-top .form-control-icon {
-       color: #fff;
-}
-
-.navbar-fixed-top *::-webkit-input-placeholder {
-    color: #fff;
-}
-.navbar-fixed-top *:-moz-placeholder {
-    /* FF 4-18 */
-    color: #fff;
-}
-.navbar-fixed-top *::-moz-placeholder {
-    /* FF 19+ */
-    color: #fff;
-}
-.navbar-fixed-top *:-ms-input-placeholder {
-    /* IE 10+ */
-    color: #fff;
-}
-
-.navbar-fixed-top .form-group {
-       margin-top: 8px;
-       margin-bottom: 0px;
-}
-
 /* Summary */
 
 .summary h1, .summary h2, .summary h3, .summary h4, .summary h5, .summary h6 {
@@ -260,49 +189,3 @@ entity-list:hover .btn-filter {
 .star.active {
        color: #FFC000
 }
-
-/*
- * Users
- */
-
-.avatar {
-       border-radius: 2px;
-}
-
-.avatar-icon {
-       width: 18px;
-       height: 18px;
-}
-
-/*
- * Code Highlighting
- */
-
-.nitcode a { color: inherit; text-decoration: inherit; } /* hide links */
-.nitcode a:hover { text-decoration: underline; } /* underline links */
-.nitcode span[title]:hover { text-decoration: underline; } /* underline titles */
-/* lexical raw tokens. independent of usage or semantic: */
-.nitcode .nc_c { color: gray; font-style: italic; } /* comment */
-.nitcode .nc_d { color: #3D8127; font-style: italic; } /* documentation comments */
-.nitcode .nc_k { font-weight: bold; } /* keyword */
-.nitcode .nc_o {} /* operator */
-.nitcode .nc_i {} /* standard identifier */
-.nitcode .nc_t { color: #445588; font-weight: bold; } /* type/class identifier */
-.nitcode .nc_a { color: #445588; font-style: italic; } /* old style attribute identifier */
-.nitcode .nc_l { color: #009999; } /* char and number literal */
-.nitcode .nc_s { color: #8F1546; } /* string literal */
-/* syntactic token usage. added because of their position in the AST */
-.nitcode .nc_ast { color: blue; } /* assert label */
-.nitcode .nc_la { color: blue; } /* break/continue label */
-.nitcode .nc_m { color: #445588; } /* module name */
-/* syntactic groups */
-.nitcode .nc_def { font-weight: bold; color: blue; } /* name used in a definition */
-.nitcode .nc_def.nc_a { color: blue; } /* name used in a attribute definition */
-.nitcode .nc_def.nc_t { color: blue; } /* name used in a class or vt definition */
-.nitcode .nc_ss { color: #9E6BEB; } /* superstrings */
-.nitcode .nc_cdef {} /* A whole class definition */
-.nitcode .nc_pdef {} /* A whole property definition */
-/* semantic token usage */
-.nitcode .nc_v { font-style: italic; } /* local variable or parameter */
-.nitcode .nc_vt { font-style: italic; } /* virtual type or formal type */
-.nitcode .nc_error { border: 1px red solid;} /* not used */
index e82732b..cff83cb 100644 (file)
@@ -330,7 +330,7 @@ hr {
   margin-top: 20px;\r
   margin-bottom: 20px;\r
   border: 0;\r
-  border-top: 1px solid #eeeeee;\r
+  border-top: 1px solid #ddd;\r
 }\r
 .sr-only {\r
   position: absolute;\r
diff --git a/share/nitweb/stylesheets/search.css b/share/nitweb/stylesheets/search.css
new file mode 100644 (file)
index 0000000..2f6e2db
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2017 Alexandre Terrasa <alexandre@moz-code.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.
+ */
+
+/* search */
+
+.has-icon {
+    position: relative;
+}
+
+.has-icon .form-control {
+       padding-left: 35px;
+}
+
+.form-control-icon {
+    position: absolute;
+    top: 0;
+    left: 0;
+    z-index: 2;
+    display: block;
+    width: 34px;
+    height: 34px;
+    line-height: 34px;
+    text-align: center;
+    pointer-events: none;
+}
+
+.navbar-fixed-top .form-control:hover, .navbar-fixed-top .form-control:focus {
+       background: rgba(255, 255, 255, 0.2);
+}
+
+.navbar-fixed-top .form-control {
+       background: rgba(255, 255, 255, 0.1);
+    border: none;
+    color: #fff;
+    box-shadow: none;
+}
+
+.navbar-fixed-top .form-control-icon {
+       color: #fff;
+}
+
+.navbar-fixed-top .form-group {
+       margin-top: 8px;
+       margin-bottom: 0px;
+}
+
+.navbar-fixed-top *::-webkit-input-placeholder { color: #fff; }
+.navbar-fixed-top *:-moz-placeholder { color: #fff; }
+.navbar-fixed-top *::-moz-placeholder { color: #fff; }
+.navbar-fixed-top *:-ms-input-placeholder { color: #fff; }
diff --git a/share/nitweb/views/catalog/by_tags.html b/share/nitweb/views/catalog/by_tags.html
new file mode 100644 (file)
index 0000000..31ca905
--- /dev/null
@@ -0,0 +1,14 @@
+<div>
+       <h3>Tags</h3>
+       <div class='container-fluid'>
+               <div class='col-xs-3' ng-repeat='(tag, packages) in vm.bytags'>
+                       <span class='badge'>{{packages.length}}</span>
+                       <a ng-click='vm.scrollTo(tag)'>{{tag}}</a>
+               </div>
+       </div>
+       <div ng-repeat='(tag, packages) in vm.bytags'>
+               <entity-list list-id='{{tag}}' list-title='{{tag}}'
+                       list-entities='packages'
+                       list-object-filter='{}' />
+       </div>
+</div>
diff --git a/share/nitweb/views/catalog/highlighted.html b/share/nitweb/views/catalog/highlighted.html
new file mode 100644 (file)
index 0000000..5607bd5
--- /dev/null
@@ -0,0 +1,5 @@
+<div>
+       <entity-list list-title='Highlighted packages'
+               list-entities='vm.highlighted'
+               list-object-filter='{}' />
+</div>
diff --git a/share/nitweb/views/catalog/index.html b/share/nitweb/views/catalog/index.html
new file mode 100644 (file)
index 0000000..877db4a
--- /dev/null
@@ -0,0 +1,47 @@
+<div class='container-fluid'>
+       <div class='page-header'>
+               <h2>Welcome to NitWeb!</h2>
+               <p class='text-muted'>The Nit knowledge base.</p>
+       </div>
+
+       <ul class='nav nav-tabs' role='tablist'>
+               <li role='presentation' ui-sref-active='active'>
+                       <a ui-sref='catalog.highlighted'>
+                               <span class='glyphicon glyphicon-book'/> Highlighed
+                       </a>
+               </li>
+               <li role='presentation' ui-sref-active='active'>
+                       <a ui-sref='catalog.required'>
+                               <span class='glyphicon glyphicon-book'/> Most required
+                       </a>
+               </li>
+               <li role='presentation' ui-sref-active='active'>
+                       <a ui-sref='catalog.tags'>
+                               <span class='glyphicon glyphicon-book'/> By tags
+                       </a>
+               </li>
+       </ul>
+       <table class='table'>
+               <tr>
+                       <td ng-repeat='(key, value) in vm.stats'>
+                               <h5><strong>{{value}}</strong>&nbsp;<span>{{key}}</span></h5>
+                       </td>
+               </tr>
+       </table>
+
+       <div class='container-fluid'>
+               <div class='col-xs-9'>
+                       <div class='tab-content'>
+                               <div role='tabpanel' class='tab-pane fade in active'>
+                                       <ui-view />
+                               </div>
+                       </div>
+               </div>
+               <div class='col-xs-3'>
+                       <contributor-list list-title='Maintainers'
+                                       list-contributors='vm.contributors.maintainers' />
+                       <contributor-list list-title='Contributors'
+                                       list-contributors='vm.contributors.contributors' />
+               </div>
+       </div>
+</div>
diff --git a/share/nitweb/views/catalog/most_required.html b/share/nitweb/views/catalog/most_required.html
new file mode 100644 (file)
index 0000000..93bf2e1
--- /dev/null
@@ -0,0 +1,5 @@
+<div>
+       <entity-list list-title='Most required'
+               list-entities='vm.required'
+               list-object-filter='{}' />
+</div>
diff --git a/share/nitweb/views/class.html b/share/nitweb/views/class.html
deleted file mode 100644 (file)
index 278e676..0000000
+++ /dev/null
@@ -1,129 +0,0 @@
-<ul class='nav nav-tabs'>
-       <li role='presentation' class='active'>
-               <a data-toggle='tab' data-target='#doc'>
-                       <span class='glyphicon glyphicon-book'/> Doc
-               </a>
-       </li>
-       <li role='presentation'>
-               <a data-toggle='tab' data-target='#graph' ng-click="entityCtrl.loadEntityGraph()">
-                       <span class='glyphicon glyphicon-object-align-vertical'/> Inheritance
-               </a>
-       </li>
-       <li role='presentation'>
-               <a data-toggle='tab' data-target='#all_props'>
-                       <span class='glyphicon glyphicon-tags'/> All properties
-               </a>
-       </li>
-       <li role='presentation'>
-               <a data-toggle='tab' role='tab' data-target='#linearization' aria-controls='linearization' ng-click='entityCtrl.loadEntityLinearization()'>
-                       <span class='glyphicon glyphicon-arrow-down'/> Linearization
-               </a>
-       </li>
-       <li role='presentation'>
-               <a data-toggle='tab' role='tab' data-target='#metrics' aria-controls='metrics' ng-click='entityCtrl.loadStructuralMetrics(); entityCtrl.loadPieChart()'>
-                       <span class='glyphicon glyphicon-stats'/> Metrics
-               </a>
-       </li>
-</ul>
-
-<div class='tab-content'>
-       <div role='tabpanel' class='tab-pane fade in active' id='doc'>
-               <div class='col-xs-3'>
-                       <ui-summary target='#summary-content' />
-               </div>
-               <div id='summary-content' class='col-xs-9'>
-                       <entity-card mentity='mentity' default-tab='doc' no-synopsis='true' />
-
-                       <entity-list list-title='Parents'
-                               list-entities='mentity.parents'
-                               list-object-filter='{}' />
-
-                       <entity-list list-title='Constructors'
-                               list-entities='mentity.all_mproperties'
-                               list-object-filter='{is_init: true}' />
-
-                       <entity-list list-title='Introduced properties'
-                               list-entities='mentity.intro_mproperties'
-                               list-object-filter='{is_init: "!true"}' />
-
-                       <entity-list list-title='Redefined properties'
-                               list-entities='mentity.redef_mproperties'
-                               list-object-filter='{is_init: "!true"}' />
-               </div>
-       </div>
-       <div role='tabpanel' class='tab-pane fade' id='all_props'>
-               <entity-list list-title='All properties' list-entities='mentity.all_mproperties'
-                       list-object-filter='{}' />
-       </div>
-       <div role='tabpanel' class='tab-pane fade' id='linearization'>
-               <entity-linearization
-                       list-title='Class definitions'
-                       list-entities='linearization'
-                       list-focus='mentity.intro' />
-       </div>
-       <div role='tabpanel' class='tab-pane fade' id='graph'>
-               <div class='card'>
-                       <div class='card-body text-center'>
-                               <entity-graph mentity='mentity' graph='graph' />
-                       </div>
-               </div>
-       </div>
-       <div role='tabpanel' class='tab-pane fade' id='metrics'>
-               <div class='card'>
-                       <div class='card-body container-fluid'>
-                               <h3 class='card-heading'>Class inheritance</h3>
-                               <div class='col-sm-6'>
-                                       <h4>
-                                               Inheritance kind
-                                               <small>({{metrics.mclass['cnoa'].values[mentity.full_name].value}}
-                                               ancestors)</small>
-                                       </h4>
-                                       <chart-class-inheritance-kind chart-id='chartInheritanceKind'
-                                               chart-metrics='metrics.mclass' />
-                               </div>
-                               <div class='col-sm-6'>
-                                       <h4>
-                                               Inheritance metrics
-                                       </h4>
-                                       <dl class='dl-horizontal'>
-                                               <dt>{{metrics.mclass.cnoa.values[mentity.full_name].value}}</dt>
-                                               <dd>ancestors</dd>
-                                               <dt>{{metrics.mclass.cnop.values[mentity.full_name].value}}</dt>
-                                               <dd>direct parents</dd>
-                                               <dt>{{metrics.mclass.cnoc.values[mentity.full_name].value}}</dt>
-                                               <dd>direct children</dd>
-                                               <dt>{{metrics.mclass.cnod.values[mentity.full_name].value}}</dt>
-                                               <dd>descendants</dd>
-                                       </dl>
-                                       <dl class='dl-horizontal'>
-                                               <dt>{{metrics.mclass.cdit.values[mentity.full_name].value}}</dt>
-                                               <dd>Depth in Inheritance Tree</dd>
-                                       </dl>
-                               </div>
-                       </div>
-               </div>
-               <div class='card'>
-                       <div class='card-body container-fluid'>
-                               <h3 class='card-heading'>Class properties</h3>
-                               <div class='col-sm-6'>
-                                       <h4>
-                                               Properties kind
-                                               <small>({{metrics.mclass['cnbp'].values[mentity.full_name].value}}
-                                               accessible properties)</small>
-                                       </h4>
-                                       <chart-class-properties-kind chart-id='chartPropertiesKind'
-                                               chart-metrics='metrics.mclass' />
-                               </div>
-                               <div class='col-sm-6'>
-                                       <h4>
-                                               Properties inheritance
-                                               <small>({{metrics.mclass['cnbp'].values[mentity.full_name].value}}
-                                               accessible properties)</small>
-                                       </h4>
-                                       <chart-class-properties-inh chart-id='chartPropertiesInh'
-                                               chart-metrics='metrics.mclass' />
-                               </div>
-                       </div>
-               </div>
-       </div>
-</div>
diff --git a/share/nitweb/views/classdef.html b/share/nitweb/views/classdef.html
deleted file mode 100644 (file)
index 1e1365b..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-<ul class='nav nav-tabs' ng-init='entityCtrl.loadEntityLinearization()'>
-       <li role='presentation' class='warning'>
-               <a ng-href='{{mentity.mclass.web_url}}'>
-                       <span class='glyphicon glyphicon-chevron-left'/> Go to class
-               </a>
-       </li>
-       <li role='presentation' class='active'>
-               <a data-toggle='tab' role='tab' data-target='#linearization' aria-controls='linearization'>
-                       <span class='glyphicon glyphicon-arrow-down'/> Linearization
-               </a>
-       </li>
-</ul>
-
-<div class='tab-content'>
-       <div role='tabpanel' class='tab-pane fade in active' id='linearization'>
-               <entity-linearization
-                       list-title='Class definitions'
-                       list-entities='linearization'
-                       list-focus='mentity' />
-       </div>
-</div>
diff --git a/share/nitweb/views/doc/all.html b/share/nitweb/views/doc/all.html
new file mode 100644 (file)
index 0000000..927d312
--- /dev/null
@@ -0,0 +1,4 @@
+<div>
+       <entity-list list-title='All properties' list-entities='vm.mentity.all_mproperties'
+               list-object-filter='{}' />
+</div>
diff --git a/share/nitweb/views/doc/code.html b/share/nitweb/views/doc/code.html
new file mode 100644 (file)
index 0000000..48f4282
--- /dev/null
@@ -0,0 +1,6 @@
+<div class='card'>
+       <div class='card-body'>
+               <pre ng-bind-html='vm.code' />
+               <entity-location mentity='vm.mentity' />
+       </div>
+</div>
diff --git a/share/nitweb/views/doc/defs.html b/share/nitweb/views/doc/defs.html
new file mode 100644 (file)
index 0000000..66cd995
--- /dev/null
@@ -0,0 +1,4 @@
+<div>
+       <entity-list list-title='Class definitions'
+               list-entities='vm.defs' list-object-filter='{}' />
+</div>
diff --git a/share/nitweb/views/doc/doc.html b/share/nitweb/views/doc/doc.html
new file mode 100644 (file)
index 0000000..1cf3c8b
--- /dev/null
@@ -0,0 +1,46 @@
+<div>
+       <div class='col-xs-3'>
+               <ui-summary target='#summary-content' />
+       </div>
+       <div class='col-xs-9' id='summary-content'>
+               <entity-card mentity='vm.mentity' default-tab='doc' no-synopsis='true' />
+
+               <entity-list list-title='Groups' list-entities='vm.mentity.mgroups'
+                       list-object-filter='{}' />
+
+               <entity-list list-title='Parent group' list-entities='[vm.mentity.parent]'
+                       list-object-filter='{}' ng-if='vm.mentity.parent' />
+
+               <entity-list list-title='Subgroups' list-entities='vm.mentity.mgroups'
+                       list-object-filter='{}' />
+
+               <entity-list list-title='Modules' list-entities='vm.mentity.mmodules'
+                       list-object-filter='{}' />
+
+               <entity-list list-title='Imported modules' list-entities='vm.mentity.imports'
+                       list-object-filter='{}' />
+
+               <entity-list list-title='Introduced classes' list-entities='vm.mentity.intro_mclasses'
+                       list-object-filter='{}' />
+
+               <entity-list list-title='Class redefinitions' list-entities='vm.mentity.redef_mclassdefs'
+                       list-object-filter='{}' />
+
+               <entity-list list-title='Parents'
+                       list-entities='vm.mentity.parents'
+                       list-object-filter='{}' />
+
+               <entity-list list-title='Constructors'
+                       list-entities='vm.mentity.all_mproperties'
+                       list-object-filter='{is_init: true}' />
+
+               <entity-list list-title='Introduced properties'
+                       list-entities='vm.mentity.intro_mproperties'
+                       list-object-filter='{is_init: "!true"}' />
+
+               <entity-list list-title='Redefined properties'
+                       list-entities='vm.mentity.redef_mproperties'
+                       list-object-filter='{is_init: "!true"}' />
+
+       </div>
+</div>
diff --git a/share/nitweb/views/doc/entity.html b/share/nitweb/views/doc/entity.html
new file mode 100644 (file)
index 0000000..1a0dcf7
--- /dev/null
@@ -0,0 +1,81 @@
+<div>
+       <ul class='nav nav-tabs' role='tablist'>
+
+               <!-- doc -->
+               <li role='presentation' ui-sref-active='active' ng-if='
+                               vm.mentity.class_name == "MPackage" ||
+                               vm.mentity.class_name == "MGroup" ||
+                               vm.mentity.class_name == "MModule" ||
+                               vm.mentity.class_name == "MClass" ||
+                               vm.mentity.class_name == "MMethod" ||
+                               vm.mentity.class_name == "MAttribute" ||
+                               vm.mentity.class_name == "MVirtualTypeProp"'>
+                       <a ui-sref='.doc'>
+                               <span class='glyphicon glyphicon-book'/> Doc
+                       </a>
+               </li>
+
+               <!-- graph -->
+               <li role='presentation' ui-sref-active='active' ng-if='
+                               vm.mentity.class_name == "MPackage" ||
+                               vm.mentity.class_name == "MGroup" ||
+                               vm.mentity.class_name == "MModule" ||
+                               vm.mentity.class_name == "MClass"'>
+                       <a ui-sref='.graph'>
+                               <span class='glyphicon glyphicon-object-align-vertical'/>
+                               <span ng-if='vm.mentity.class_name == "MPackage"'>Dependencies</span>
+                               <span ng-if='vm.mentity.class_name == "MGroup"'>Imports</span>
+                               <span ng-if='vm.mentity.class_name == "MModule"'>Imports</span>
+                               <span ng-if='vm.mentity.class_name == "MClass"'>Inheritance</span>
+                       </a>
+               </li>
+
+               <!-- code -->
+               <li role='presentation' ui-sref-active='active' ng-if='
+                               vm.mentity.class_name == "MModule"'>
+                       <a ui-sref='.code'>
+                               <span class='glyphicon glyphicon-console'/> Code
+                       </a>
+               </li>
+
+               <!-- definitions -->
+               <li role='presentation' ui-sref-active='active' ng-if='
+                               vm.mentity.class_name == "MModule"'>
+                       <a ui-sref='.defs'>
+                               <span class='glyphicon glyphicon-asterisk'/> Class definitions
+                       </a>
+               </li>
+
+               <!-- all -->
+               <li role='presentation' ui-sref-active='active' ng-if='
+                               vm.mentity.class_name == "MClass"'>
+                       <a ui-sref='.all'>
+                               <span class='glyphicon glyphicon-tags'/> All properties
+                       </a>
+               </li>
+
+               <!-- linearization -->
+               <li role='presentation' ui-sref-active='active' ng-if='
+                               vm.mentity.class_name == "MClass" ||
+                               vm.mentity.class_name == "MMethod" ||
+                               vm.mentity.class_name == "MAttribute" ||
+                               vm.mentity.class_name == "MVirtualTypeProp"'>
+                       <a ui-sref='.lin'>
+                               <span class='glyphicon glyphicon-arrow-down'/> Linearization
+                       </a>
+               </li>
+
+               <!-- metrics -->
+               <li role='presentation' ui-sref-active='active' ng-if='
+                               vm.mentity.class_name == "MPackage" ||
+                               vm.mentity.class_name == "MGroup" ||
+                               vm.mentity.class_name == "MModule" ||
+                               vm.mentity.class_name == "MClass"'>
+                       <a ui-sref='.metrics'>
+                               <span class='glyphicon glyphicon-stats'/> Metrics
+                       </a>
+               </li>
+       </ul>
+       <br>
+       <ui-view />
+</div>
diff --git a/share/nitweb/views/doc/graph.html b/share/nitweb/views/doc/graph.html
new file mode 100644 (file)
index 0000000..a346d99
--- /dev/null
@@ -0,0 +1,5 @@
+<div class='card'>
+       <div class='card-body text-center'>
+               <entity-graph mentity='mentity' graph='vm.graph' />
+       </div>
+</div>
diff --git a/share/nitweb/views/doc/index.html b/share/nitweb/views/doc/index.html
new file mode 100644 (file)
index 0000000..aab7423
--- /dev/null
@@ -0,0 +1,18 @@
+<div class='container-fluid'>
+
+       <div ng-if='vm.error' class='alert alert-danger' role='alert'>
+               <span class='glyphicon glyphicon-exclamation-sign' aria-hidden='true'></span>
+               <span class='sr-only'>Error:</span>
+               <span ng-switch='vm.error.status'>
+                       <span ng-switch-when='404'>Entity <code>{{vm.entityId}}</code> not found!</span>
+                       <span ng-switch-default>An error occured<br/>{{vm.error.status}}: {{vm.error.message}}</span>
+               </span>
+       </div>
+
+       <div class='page-header'>
+               <h2><entity-signature mentity='vm.mentity' /></h2>
+               <entity-namespace namespace='vm.mentity.namespace' />
+       </div>
+
+       <ui-view />
+</div>
diff --git a/share/nitweb/views/doc/lin.html b/share/nitweb/views/doc/lin.html
new file mode 100644 (file)
index 0000000..96d9b82
--- /dev/null
@@ -0,0 +1,6 @@
+<div>
+       <entity-linearization
+               list-title='Class definitions'
+               list-entities='vm.linearization'
+               list-focus='vm.focus' />
+</div>
diff --git a/share/nitweb/views/doc/metrics.html b/share/nitweb/views/doc/metrics.html
new file mode 100644 (file)
index 0000000..fd52704
--- /dev/null
@@ -0,0 +1,157 @@
+<div id='metrics'>
+
+       <!-- packages, groups -->
+
+       <metrics-list
+               ng-if='vm.mentity.class_name == "MPackage" || vm.mentity.class_name == "MGroup"'
+               list-id='modules_importation'
+               list-title='Modules importation'
+               list-metrics='vm.metrics.mmodules'
+               list-metrics-names='["mdit", "mnoa", "mnop", "mnoc", "mnod"]'
+               list-metrics-default='"mdit"' />
+       <metrics-list
+               ng-if='vm.mentity.class_name == "MPackage" || vm.mentity.class_name == "MGroup"'
+               list-id='modules_definitions'
+               list-title='Modules content'
+               list-metrics='vm.metrics.mmodules'
+               list-metrics-names='["mnbi", "mnbr", "mnbic", "mnbac", "mnbcc"]'
+               list-metrics-default='"mnbi"' />
+       <metrics-list
+               ng-if='vm.mentity.class_name == "MPackage" || vm.mentity.class_name == "MGroup"'
+               list-id='classes_inheritance'
+               list-title='Classes inheritance'
+               list-metrics='vm.metrics.mclasses'
+               list-metrics-names='["cdit", "cnoa", "cnop", "cnoc", "cnod"]'
+               list-metrics-default='"cdit"' />
+       <metrics-list
+               ng-if='vm.mentity.class_name == "MPackage" || vm.mentity.class_name == "MGroup"'
+               list-id='classes_properties'
+               list-title='Classes properties'
+               list-metrics='vm.metrics.mclasses'
+               list-metrics-names='["cnbp", "cnba", "cnbip", "cnbrp", "cnbhp", "cnblp"]'
+               list-metrics-default='"cnbp"' />
+
+       <!-- module -->
+
+       <div class='card' ng-if='vm.mentity.class_name == "MModule"'>
+               <div class='card-body container-fluid'>
+                       <h3 class='card-heading'>Module importation</h3>
+                       <div class='col-sm-6'>
+                               <h4>
+                                       Importation metrics
+                               </h4>
+                               <dl class='dl-horizontal'>
+                                       <dt>{{vm.metrics.mmodule.mnoa.values[vm.mentity.full_name].value}}</dt>
+                                       <dd>ancestors</dd>
+                                       <dt>{{vm.metrics.mmodule.mnop.values[vm.mentity.full_name].value}}</dt>
+                                       <dd>direct parents</dd>
+                                       <dt>{{vm.metrics.mmodule.mnoc.values[vm.mentity.full_name].value}}</dt>
+                                       <dd>direct children</dd>
+                                       <dt>{{vm.metrics.mmodule.mnod.values[vm.mentity.full_name].value}}</dt>
+                                       <dd>descendants</dd>
+                               </dl>
+                               <dl class='dl-horizontal'>
+                                       <dt>{{vm.metrics.mmodule.mdit.values[vm.mentity.full_name].value}}</dt>
+                                       <dd>Depth in Inheritance Tree</dd>
+                               </dl>
+                       </div>
+               </div>
+       </div>
+       <div class='card' ng-if='vm.mentity.class_name == "MModule"'>
+               <div class='card-body container-fluid'>
+                       <h3 class='card-heading'>Module definitions</h3>
+                       <div class='col-sm-6'>
+                               <h4>
+                                       Class definition kinds
+                                       <small>({{vm.metrics.mmodule['mnbi'].values[vm.mentity.full_name].value +
+                                               vm.metrics.mclass['mnbc'].values[vm.mentity.full_name].value}}
+                                       class definitions)</small>
+                               </h4>
+                               <chart-module-definitions-kind chart-id='chartDefinitionsKind'
+                                       chart-metrics='vm.metrics.mmodule' />
+                       </div>
+                       <div class='col-sm-6'>
+                               <h4>
+                                       Class definition inheritance
+                                       <small>({{vm.metrics.mmodule['mnbd'].values[vm.mentity.full_name].value}}
+                                       accessible definitions)</small>
+                               </h4>
+                               <chart-module-definitions-inh chart-id='chartDefinitionsInh'
+                                       chart-metrics='vm.metrics.mmodule' />
+                       </div>
+               </div>
+       </div>
+       <metrics-list
+               ng-if='vm.mentity.class_name == "MModule"'
+               list-id='classes_inheritance'
+               list-title='Classes inheritance'
+               list-metrics='vm.metrics.mclasses'
+               list-metrics-names='["cdit", "cnoa", "cnop", "cnoc", "cnod"]'
+               list-metrics-default='"cdit"' />
+       <metrics-list
+               ng-if='vm.mentity.class_name == "MModule"'
+               list-id='classes_properties'
+               list-title='Classes properties'
+               list-metrics='vm.metrics.mclasses'
+               list-metrics-names='["cnbp", "cnba", "cnbip", "cnbrp", "cnbhp", "cnblp"]'
+               list-metrics-default='"cnbp"' />
+
+       <!-- mclass -->
+
+       <div class='card' ng-if='vm.mentity.class_name == "MClass"'>
+               <div class='card-body container-fluid'>
+                       <h3 class='card-heading'>Class inheritance</h3>
+                       <div class='col-sm-6'>
+                               <h4>
+                                       Inheritance kind
+                                       <small>({{vm.metrics.mclass['cnoa'].values[vm.mentity.full_name].value}}
+                                       ancestors)</small>
+                               </h4>
+                               <chart-class-inheritance-kind chart-id='chartInheritanceKind'
+                                       chart-metrics='vm.metrics.mclass' />
+                       </div>
+                       <div class='col-sm-6'>
+                               <h4>
+                                       Inheritance metrics
+                               </h4>
+                               <dl class='dl-horizontal'>
+                                       <dt>{{vm.metrics.mclass.cnoa.values[vm.mentity.full_name].value}}</dt>
+                                       <dd>ancestors</dd>
+                                       <dt>{{vm.metrics.mclass.cnop.values[vm.mentity.full_name].value}}</dt>
+                                       <dd>direct parents</dd>
+                                       <dt>{{vm.metrics.mclass.cnoc.values[vm.mentity.full_name].value}}</dt>
+                                       <dd>direct children</dd>
+                                       <dt>{{vm.metrics.mclass.cnod.values[vm.mentity.full_name].value}}</dt>
+                                       <dd>descendants</dd>
+                               </dl>
+                               <dl class='dl-horizontal'>
+                                       <dt>{{vm.metrics.mclass.cdit.values[vm.mentity.full_name].value}}</dt>
+                                       <dd>Depth in Inheritance Tree</dd>
+                               </dl>
+                       </div>
+               </div>
+       </div>
+       <div class='card' ng-if='vm.mentity.class_name == "MClass"'>
+               <div class='card-body container-fluid'>
+                       <h3 class='card-heading'>Class properties</h3>
+                       <div class='col-sm-6'>
+                               <h4>
+                                       Properties kind
+                                       <small>({{vm.metrics.mclass['cnbp'].values[vm.mentity.full_name].value}}
+                                       accessible properties)</small>
+                               </h4>
+                               <chart-class-properties-kind chart-id='chartPropertiesKind'
+                                       chart-metrics='vm.metrics.mclass' />
+                       </div>
+                       <div class='col-sm-6'>
+                               <h4>
+                                       Properties inheritance
+                                       <small>({{vm.metrics.mclass['cnbp'].values[vm.mentity.full_name].value}}
+                                       accessible properties)</small>
+                               </h4>
+                               <chart-class-properties-inh chart-id='chartPropertiesInh'
+                                       chart-metrics='vm.metrics.mclass' />
+                       </div>
+               </div>
+       </div>
+</div>
diff --git a/share/nitweb/views/group.html b/share/nitweb/views/group.html
deleted file mode 100644 (file)
index 0f7347b..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-<ul class='nav nav-tabs'>
-       <li class='active'>
-               <a data-toggle='tab' data-target='#doc'>
-                       <span class='glyphicon glyphicon-book'/> Doc
-               </a>
-       </li>
-       <li role='presentation'>
-               <a data-toggle='tab' data-target='#graph' ng-click="entityCtrl.loadEntityGraph()">
-                       <span class='glyphicon glyphicon-object-align-vertical'/> Imports
-               </a>
-       </li>
-       <li role='presentation'>
-               <a data-toggle='tab' role='tab' data-target='#metrics' aria-controls='metrics' ng-click='entityCtrl.loadStructuralMetrics()'>
-                       <span class='glyphicon glyphicon-stats'/> Metrics
-               </a>
-       </li>
-</ul>
-
-<div class='tab-content'>
-       <div role='tabpanel' class='tab-pane fade in active' id='doc'>
-               <div class='col-xs-3'>
-                       <ui-summary target='#summary-content' />
-               </div>
-               <div class='col-xs-9' id='summary-content'>
-                       <entity-card mentity='mentity' default-tab='doc' no-synopsis='true' />
-
-                       <entity-list list-title='Parent group' list-entities='[mentity.parent]'
-                               list-object-filter='{}' ng-if='mentity.parent' />
-
-                       <entity-list list-title='Subgroups' list-entities='mentity.mgroups'
-                               list-object-filter='{}' />
-
-                       <entity-list list-title='Modules' list-entities='mentity.mmodules'
-                               list-object-filter='{}' />
-               </div>
-       </div>
-       <div role='tabpanel' class='tab-pane fade' id='graph'>
-               <div class='card'>
-                       <div class='card-body text-center'>
-                               <entity-graph mentity='mentity' graph='graph' />
-                       </div>
-               </div>
-       </div>
-       <div role='tabpanel' class='tab-pane fade' id='metrics'>
-               <metrics-list
-                       list-id='modules_importation'
-                       list-title='Modules importation'
-                       list-metrics='metrics.mmodules'
-                       list-metrics-names='["mdit", "mnoa", "mnop", "mnoc", "mnod"]'
-                       list-metrics-default='"mdit"' />
-               <metrics-list
-                       list-id='modules_definitions'
-                       list-title='Modules content'
-                       list-metrics='metrics.mmodules'
-                       list-metrics-names='["mnbi", "mnbr", "mnbic", "mnbac", "mnbcc"]'
-                       list-metrics-default='"mnbi"' />
-               <metrics-list
-                       list-id='classes_inheritance'
-                       list-title='Classes inheritance'
-                       list-metrics='metrics.mclasses'
-                       list-metrics-names='["cdit", "cnoa", "cnop", "cnoc", "cnod"]'
-                       list-metrics-default='"cdit"' />
-               <metrics-list
-                       list-id='classes_properties'
-                       list-title='Classes properties'
-                       list-metrics='metrics.mclasses'
-                       list-metrics-names='["cnbp", "cnba", "cnbip", "cnbrp", "cnbhp", "cnblp"]'
-                       list-metrics-default='"cnbp"' />
-       </div>
-</div>
diff --git a/share/nitweb/views/index.html b/share/nitweb/views/index.html
deleted file mode 100644 (file)
index b7bee8e..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-<div class='container-fluid'>
-       <div class='page-header'>
-               <h2>Welcome to NitWeb!</h2>
-               <p class='text-muted'>The Nit knowledge base.</p>
-       </div>
-
-       <ul class='nav nav-tabs' role='tablist'>
-               <li role='presentation' class='active'>
-                       <a data-toggle='tab' role='tab' data-target='#highlighted' aria-controls='highlighted'>
-                               <span class='glyphicon glyphicon-book'/> Highlighed
-                       </a>
-               </li>
-               <li role='presentation'>
-                       <a data-toggle='tab' role='tab' data-target='#required' aria-controls='required'
-                               ng-click='indexCtrl.loadMostRequired()'>
-                               <span class='glyphicon glyphicon-book'/> Most required
-                       </a>
-               </li>
-               <li role='presentation'>
-                       <a data-toggle='tab' role='tab' data-target='#bytags' aria-controls='bytags'
-                               ng-click='indexCtrl.loadByTags()'>
-                               <span class='glyphicon glyphicon-book'/> By tags
-                       </a>
-               </li>
-       </ul>
-       <table class='table'>
-               <tr>
-                       <td ng-repeat='(key, value) in stats'>
-                               <h5><strong>{{value}}</strong>&nbsp;<span>{{key}}</span></h5>
-                       </td>
-               </tr>
-       </table>
-
-       <div class='container-fluid'>
-               <div class='col-xs-9'>
-                       <div class='tab-content'>
-                               <div role='tabpanel' class='tab-pane fade in active' id='highlighted'>
-                                       <entity-list list-title='Highlighted packages'
-                                               list-entities='highlighted'
-                                               list-object-filter='{}' />
-                               </div>
-                               <div role='tabpanel' class='tab-pane fade' id='required'>
-                                       <entity-list list-title='Most required'
-                                               list-entities='required'
-                                               list-object-filter='{}' />
-                               </div>
-                               <div role='tabpanel' class='tab-pane fade' id='bytags'>
-                                       <h3>Tags</h3>
-                                       <div class='container-fluid'>
-                                               <div class='col-xs-3' ng-repeat='(tag, packages) in bytags'>
-                                                       <span class='badge'>{{packages.length}}</span>
-                                                       <a ng-click='indexCtrl.scrollTo(tag)'>{{tag}}</a>
-                                               </div>
-                                       </div>
-                                       <div ng-repeat='(tag, packages) in bytags'>
-                                               <entity-list list-id='{{tag}}' list-title='{{tag}}'
-                                                       list-entities='packages'
-                                                       list-object-filter='{}' />
-                                       </div>
-                               </div>
-                       </div>
-               </div>
-               <div class='col-xs-3'>
-                       <contributor-list list-title='Maintainers'
-                                       list-contributors='contributors.maintainers' />
-                       <contributor-list list-title='Contributors'
-                                       list-contributors='contributors.contributors' />
-               </div>
-       </div>
-</div>
diff --git a/share/nitweb/views/module.html b/share/nitweb/views/module.html
deleted file mode 100644 (file)
index 3891267..0000000
+++ /dev/null
@@ -1,128 +0,0 @@
-<ul class='nav nav-tabs'>
-       <li role='presentation' class='active'>
-               <a data-toggle='tab' data-target='#doc'>
-                       <span class='glyphicon glyphicon-book'/> Doc
-               </a>
-       </li>
-       <li role='presentation'>
-               <a data-toggle='tab' data-target='#graph' ng-click="entityCtrl.loadEntityGraph()">
-                       <span class='glyphicon glyphicon-object-align-vertical'/> Imports
-               </a>
-       </li>
-       <li role='presentation'>
-               <a data-toggle='tab' data-target='#code' ng-click="entityCtrl.loadEntityCode()">
-                       <span class='glyphicon glyphicon-console'/> Code
-               </a>
-       </li>
-       <li role='presentation'>
-               <a data-toggle='tab' data-target='#defs' ng-click="entityCtrl.loadEntityDefs()">
-                       <span class='glyphicon glyphicon-asterisk'/> Class definitions
-               </a>
-       </li>
-       <li role='presentation'>
-               <a data-toggle='tab' role='tab' data-target='#metrics' aria-controls='metrics' ng-click='entityCtrl.loadStructuralMetrics()'>
-                       <span class='glyphicon glyphicon-stats'/> Metrics
-               </a>
-       </li>
-</ul>
-
-<div class='tab-content'>
-       <div role='tabpanel' class='tab-pane fade in active' id='doc'>
-               <div class='col-xs-3'>
-                       <ui-summary target='#summary-content' />
-               </div>
-               <div class='col-xs-9' id='summary-content'>
-                       <entity-card mentity='mentity' default-tab='doc' no-synopsis='true' />
-
-                       <entity-list list-title='Imported modules' list-entities='mentity.imports'
-                               list-object-filter='{}' />
-
-                       <entity-list list-title='Introduced classes' list-entities='mentity.intro_mclasses'
-                               list-object-filter='{}' />
-
-                       <entity-list list-title='Class redefinitions' list-entities='mentity.redef_mclassdefs'
-                               list-object-filter='{}' />
-               </div>
-       </div>
-       <div role='tabpanel' class='tab-pane fade' id='code'>
-               <div class='card'>
-                       <div class='card-body'>
-                               <pre ng-bind-html='code' />
-                               <entity-location mentity='mentity' />
-                       </div>
-               </div>
-       </div>
-       <div role='tabpanel' class='tab-pane fade' id='defs'>
-               <entity-list list-title='Class definitions' list-entities='defs'
-                       list-object-filter='{}' />
-       </div>
-       <div class='tab-pane fade' id='graph'>
-               <div class='card'>
-                       <div class='card-body text-center'>
-                               <entity-graph mentity='mentity' graph='graph' />
-                       </div>
-               </div>
-       </div>
-       <div role='tabpanel' class='tab-pane fade' id='metrics'>
-               <div class='card'>
-                       <div class='card-body container-fluid'>
-                               <h3 class='card-heading'>Module importation</h3>
-                               <div class='col-sm-6'>
-                                       <h4>
-                                               Importation metrics
-                                       </h4>
-                                       <dl class='dl-horizontal'>
-                                               <dt>{{metrics.mmodule.mnoa.values[mentity.full_name].value}}</dt>
-                                               <dd>ancestors</dd>
-                                               <dt>{{metrics.mmodule.mnop.values[mentity.full_name].value}}</dt>
-                                               <dd>direct parents</dd>
-                                               <dt>{{metrics.mmodule.mnoc.values[mentity.full_name].value}}</dt>
-                                               <dd>direct children</dd>
-                                               <dt>{{metrics.mmodule.mnod.values[mentity.full_name].value}}</dt>
-                                               <dd>descendants</dd>
-                                       </dl>
-                                       <dl class='dl-horizontal'>
-                                               <dt>{{metrics.mmodule.mdit.values[mentity.full_name].value}}</dt>
-                                               <dd>Depth in Inheritance Tree</dd>
-                                       </dl>
-                               </div>
-                       </div>
-               </div>
-               <div class='card'>
-                       <div class='card-body container-fluid'>
-                               <h3 class='card-heading'>Module definitions</h3>
-                               <div class='col-sm-6'>
-                                       <h4>
-                                               Class definition kinds
-                                               <small>({{metrics.mmodule['mnbi'].values[mentity.full_name].value +
-                                                       metrics.mclass['mnbc'].values[mentity.full_name].value}}
-                                               class definitions)</small>
-                                       </h4>
-                                       <chart-module-definitions-kind chart-id='chartDefinitionsKind'
-                                               chart-metrics='metrics.mmodule' />
-                               </div>
-                               <div class='col-sm-6'>
-                                       <h4>
-                                               Class definition inheritance
-                                               <small>({{metrics.mmodule['mnbd'].values[mentity.full_name].value}}
-                                               accessible definitions)</small>
-                                       </h4>
-                                       <chart-module-definitions-inh chart-id='chartDefinitionsInh'
-                                               chart-metrics='metrics.mmodule' />
-                               </div>
-                       </div>
-               </div>
-               <metrics-list
-                       list-id='classes_inheritance'
-                       list-title='Classes inheritance'
-                       list-metrics='metrics.mclasses'
-                       list-metrics-names='["cdit", "cnoa", "cnop", "cnoc", "cnod"]'
-                       list-metrics-default='"cdit"' />
-               <metrics-list
-                       list-id='classes_properties'
-                       list-title='Classes properties'
-                       list-metrics='metrics.mclasses'
-                       list-metrics-names='["cnbp", "cnba", "cnbip", "cnbrp", "cnbhp", "cnblp"]'
-                       list-metrics-default='"cnbp"' />
-       </div>
-</div>
diff --git a/share/nitweb/views/package.html b/share/nitweb/views/package.html
deleted file mode 100644 (file)
index 38b1bf6..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-<ul class='nav nav-tabs' role='tablist'>
-       <li role='presentation' class='active'>
-               <a data-toggle='tab' role='tab' data-target='#doc' aria-controls="doc">
-                       <span class='glyphicon glyphicon-book'/> Doc
-               </a>
-       </li>
-       <li role='presentation'>
-               <a data-toggle='tab' data-target='#graph' ng-click="entityCtrl.loadEntityGraph()">
-                       <span class='glyphicon glyphicon-object-align-vertical'/> Dependencies
-               </a>
-       </li>
-       <li role='presentation'>
-               <a data-toggle='tab' role='tab' data-target='#metrics' aria-controls='metrics' ng-click='entityCtrl.loadStructuralMetrics()'>
-                       <span class='glyphicon glyphicon-stats'/> Metrics
-               </a>
-       </li>
-</ul>
-
-<div class='tab-content'>
-       <div role='tabpanel' class='tab-pane fade in active' id='doc'>
-               <div class='col-xs-3'>
-                       <ui-summary target='#summary-content' />
-               </div>
-               <div class='col-xs-9' id='summary-content'>
-                       <entity-card mentity='mentity' default-tab='doc' no-synopsis='true' />
-
-                       <entity-list list-title='Groups' list-entities='mentity.mgroups'
-                               list-object-filter='{}' />
-               </div>
-       </div>
-       <div role='tabpanel' class='tab-pane fade' id='graph'>
-               <div class='card'>
-                       <div class='card-body text-center'>
-                               <entity-graph mentity='mentity' graph='graph' />
-                       </div>
-               </div>
-       </div>
-       <div role='tabpanel' class='tab-pane fade' id='metrics'>
-               <metrics-list
-                       list-id='modules_importation'
-                       list-title='Modules importation'
-                       list-metrics='metrics.mmodules'
-                       list-metrics-names='["mdit", "mnoa", "mnop", "mnoc", "mnod"]'
-                       list-metrics-default='"mdit"' />
-               <metrics-list
-                       list-id='modules_definitions'
-                       list-title='Modules content'
-                       list-metrics='metrics.mmodules'
-                       list-metrics-names='["mnbi", "mnbr", "mnbic", "mnbac", "mnbcc"]'
-                       list-metrics-default='"mnbi"' />
-               <metrics-list
-                       list-id='classes_inheritance'
-                       list-title='Classes inheritance'
-                       list-metrics='metrics.mclasses'
-                       list-metrics-names='["cdit", "cnoa", "cnop", "cnoc", "cnod"]'
-                       list-metrics-default='"cdit"' />
-               <metrics-list
-                       list-id='classes_properties'
-                       list-title='Classes properties'
-                       list-metrics='metrics.mclasses'
-                       list-metrics-names='["cnbp", "cnba", "cnbip", "cnbrp", "cnbhp", "cnblp"]'
-                       list-metrics-default='"cnbp"' />
-       </div>
-</div>
diff --git a/share/nitweb/views/propdef.html b/share/nitweb/views/propdef.html
deleted file mode 100644 (file)
index 975cfb5..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-<ul class='nav nav-tabs' ng-init='entityCtrl.loadEntityLinearization()'>
-       <li role='presentation' class='warning'>
-               <a href='{{mentity.mproperty.web_url}}'>
-                       <span class='glyphicon glyphicon-chevron-left'/> Go to property
-               </a>
-       </li>
-       <li role='presentation' class='active'>
-               <a data-toggle='tab' role='tab' data-target='#linearization' aria-controls='linearization'>
-                       <span class='glyphicon glyphicon-arrow-down'/> Linearization
-               </a>
-       </li>
-</ul>
-
-<div class='tab-content'>
-       <div role='tabpanel' class='tab-pane fade in active' id='linearization'>
-               <entity-linearization
-                       list-title='Class definitions'
-                       list-entities='linearization'
-                       list-focus='mentity' />
-       </div>
-</div>
diff --git a/share/nitweb/views/property.html b/share/nitweb/views/property.html
deleted file mode 100644 (file)
index 345a3bf..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-<ul class='nav nav-tabs'>
-       <li role='presentation' class='active'>
-               <a data-toggle='tab' data-target='#doc'>
-                       <span class='glyphicon glyphicon-book'/> Doc
-               </a>
-       </li>
-       <li role='presentation'>
-               <a data-toggle='tab' role='tab' data-target='#linearization' aria-controls='linearization' ng-click='entityCtrl.loadEntityLinearization()'>
-                       <span class='glyphicon glyphicon-arrow-down'/> Linearization
-               </a>
-       </li>
-</ul>
-
-<div class='tab-content'>
-       <div role='tabpanel' class='tab-pane fade in active' id='doc'>
-               <div class='col-xs-3'>
-                       <ui-summary target='#summary-content' />
-               </div>
-               <div class='col-xs-9' id='summary-content'>
-                       <entity-card mentity='mentity' default-tab='doc' no-synopsis='true' />
-               </div>
-       </div>
-       <div role='tabpanel' class='tab-pane fade' id='linearization'>
-               <entity-linearization
-                       list-title='Class definitions'
-                       list-entities='linearization'
-                       list-focus='mentity.intro' />
-       </div>
-</div>
index 31795fc..a3a0bc8 100644 (file)
@@ -360,8 +360,12 @@ class MakefileToolchain
                var debug = toolcontext.opt_debug.value
 
                makefile.write """
-CC ?= ccache cc
-CXX ?= ccache c++
+ifeq ($(origin CC), default)
+        CC = ccache cc
+endif
+ifeq ($(origin CXX), default)
+        CXX = ccache c++
+endif
 CFLAGS ?= -g {{{if not debug then "-O2" else ""}}} -Wno-unused-value -Wno-switch -Wno-attributes -Wno-trigraphs
 CINCL =
 LDFLAGS ?=
diff --git a/src/metrics/codesmells_metrics.nit b/src/metrics/codesmells_metrics.nit
new file mode 100644 (file)
index 0000000..1bd502d
--- /dev/null
@@ -0,0 +1,317 @@
+# 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.
+# Detect the code smells and antipatterns in the code.
+
+module codesmells_metrics
+
+import frontend
+import metrics_base
+import mclasses_metrics
+import semantize
+import method_analyze_metrics
+import mclassdef_collect
+
+redef class ToolContext
+       var codesmells_metrics_phase = new CodeSmellsMetricsPhase(self, null)
+end
+
+class CodeSmellsMetricsPhase
+       super Phase
+       var average_number_of_lines = 0.0
+       var average_number_of_parameter = 0.0
+       var average_number_of_method = 0.0
+       var average_number_of_attribute = 0.0
+
+       redef fun process_mainmodule(mainmodule, given_mmodules) do
+               print toolcontext.format_h1("--- Code Smells Metrics ---")
+               self.set_all_average_metrics
+               var mclass_codesmell = new BadConceptonController
+               var collect = new Counter[MClassDef]
+               var mclassdefs = new Array[MClassDef]
+
+               for mclass in mainmodule.flatten_mclass_hierarchy do
+                       mclass_codesmell.collect(mclass.mclassdefs,self)
+               end
+               mclass_codesmell.print_top(10)
+       end
+
+       fun set_all_average_metrics do
+               var model_builder = toolcontext.modelbuilder
+               var model_view = model_builder.model.private_view
+               self.average_number_of_lines = model_view.get_avg_linenumber(model_builder)
+               self.average_number_of_parameter = model_view.get_avg_parameter
+               self.average_number_of_method = model_view.get_avg_method
+               self.average_number_of_attribute = model_view.get_avg_attribut
+       end
+end
+
+class BadConceptonController
+       # Code smell list
+       var bad_conception_elements = new Array[BadConceptionFinder]
+
+       # Print all element conception
+       fun print_all do
+               for bad_conception in bad_conception_elements do
+                       bad_conception.print_all
+               end
+       end
+
+       # Print number of top element conception
+       fun print_top(number: Int) do
+               var test = self.get_numbers_of_elements(number)
+               for bad_conception in test do
+                       bad_conception.print_all
+               end
+       end
+
+       # Collection
+       fun collect(mclassdefs: Array[MClassDef],phase: CodeSmellsMetricsPhase) do
+               for mclassdef in mclassdefs do
+                       var bad_conception_class = new BadConceptionFinder(mclassdef,phase)
+                       bad_conception_class.collect
+                       bad_conception_elements.add(bad_conception_class)
+               end
+       end
+
+       fun sort: Array[BadConceptionFinder]
+       do
+               var res = bad_conception_elements
+               var sorter = new BadConceptionComparator
+               sorter.sort(res)
+               return res
+       end
+
+       fun get_numbers_of_elements(number : Int) : Array[BadConceptionFinder]do
+               var return_values = new Array[BadConceptionFinder]
+               var list = self.sort
+               var min = number
+               if list.length <= number*2 then min = list.length
+               for i in [0..min[ do
+                       var t = list[list.length-i-1]
+                       return_values.add(t)
+               end
+               return return_values
+       end
+end
+
+class BadConceptionFinder
+       var mclassdef: MClassDef
+       var array_badconception = new Array[BadConception]
+       var phase: CodeSmellsMetricsPhase
+
+       fun collect do
+               var bad_conception_elements = new Array[BadConception]
+               bad_conception_elements.add(new LargeClass(phase))
+               bad_conception_elements.add(new LongParameterList(phase))
+               bad_conception_elements.add(new FeatureEnvy(phase))
+               bad_conception_elements.add(new LongMethod(phase))
+               for bad_conception_element in bad_conception_elements do
+                       if bad_conception_element.collect(self.mclassdef,phase.toolcontext.modelbuilder) == true then array_badconception.add(bad_conception_element)
+               end
+       end
+
+       fun print_all do
+               if array_badconception.length != 0 then
+                       print "-----------"
+                       print "{mclassdef.full_name}"
+                       for bad_conception in array_badconception do
+                               bad_conception.print_result
+                       end
+               end
+       end
+end
+
+class BadConception
+       var phase: CodeSmellsMetricsPhase
+
+       # Name
+       fun name: String is abstract
+
+       # Description
+       fun desc: String is abstract
+
+       # Collection method
+       fun collect(mclassdef: MClassDef, model_builder: ModelBuilder): Bool is abstract
+
+       # Show results in console
+       fun print_result is abstract
+end
+
+class LargeClass
+       super BadConception
+       var number_attribut = 0
+
+       var number_method = 0
+
+       redef fun name do return "LARGC"
+
+       redef fun desc do return "Large class"
+
+       redef fun collect(mclassdef, model_builder): Bool do
+               number_attribut = mclassdef.collect_intro_and_redef_mattributes(model_builder.model.private_view).length
+               # get the number of methods (subtract the get and set of attibutes with (numberAtribut*2))
+               number_method = mclassdef.collect_intro_and_redef_methods(model_builder.model.private_view).length
+               return number_method.to_f > phase.average_number_of_method and number_attribut.to_f > phase.average_number_of_attribute
+       end
+
+       redef fun print_result do
+               print phase.toolcontext.format_h2("{desc}: {number_attribut} attributes and {number_method} methods ({phase.average_number_of_attribute}A {phase.average_number_of_method}M Average)")
+       end
+end
+
+class LongParameterList
+       super BadConception
+       var bad_methods = new Array[MMethodDef]
+
+       redef fun name do return "LONGPL"
+
+       redef fun desc do return "Long parameter list"
+
+       redef fun collect(mclassdef, model_builder): Bool do
+               for meth in mclassdef.collect_intro_and_redef_mpropdefs(model_builder.model.private_view) do
+                       # check if the property is a method definition
+                       if not meth isa MMethodDef then continue
+                       # Check if method has a signature
+                       if meth.msignature == null then continue
+                       if meth.msignature.mparameters.length <= 4 then continue
+                       bad_methods.add(meth)
+               end
+               return bad_methods.not_empty
+       end
+
+
+       redef fun print_result do
+               print "{desc}:"
+               if bad_methods.length >= 1 then
+                       print " Affected method(s):"
+                       for method in bad_methods do
+                               print "         -{method.name} has {method.msignature.mparameters.length} parameters"
+                       end
+               end
+       end
+end
+
+class FeatureEnvy
+       super BadConception
+       var bad_methods = new Array[MMethodDef]
+
+       redef fun name do return "FEM"
+
+       redef fun desc do return "Feature envy"
+
+       redef fun collect(mclassdef, model_builder): Bool do
+               var mmethoddefs = call_analyze_methods(mclassdef,model_builder)
+               for mmethoddef in mmethoddefs do
+                       if mmethoddef.total_extern_call <= mmethoddef.total_self_call then continue
+                       bad_methods.add(mmethoddef)
+               end
+               return bad_methods.not_empty
+       end
+
+       redef fun print_result do
+               print "{desc}:"
+               if bad_methods.length >= 1 then
+                       print " Affected method(s):"
+                       for method in bad_methods do
+                               print "         -{method.name} {method.total_self_call}/{method.total_call}"
+                       end
+               end
+       end
+end
+
+class LongMethod
+       super BadConception
+       var bad_methods = new Array[MMethodDef]
+
+       redef fun name do return "LONGMETH"
+
+       redef fun desc do return "Long method"
+
+       redef fun collect(mclassdef, model_builder): Bool do
+               var mmethoddefs = call_analyze_methods(mclassdef,model_builder)
+               for mmethoddef in mmethoddefs do
+                       if mmethoddef.line_number <= phase.average_number_of_lines.to_i then continue
+                       bad_methods.add(mmethoddef)
+               end
+               return bad_methods.not_empty
+       end
+
+       redef fun print_result do
+               print "{desc}:  Average {phase.average_number_of_lines.to_i} lines"
+               if bad_methods.length >= 1 then
+                       print " Affected method(s):"
+                       for method in bad_methods do
+                               print "         -{method.name} has {method.line_number} lines"
+                       end
+               end
+       end
+end
+
+redef class ModelView
+       fun get_avg_parameter: Float do
+               var counter = new Counter[MMethodDef]
+               for mclassdef in mclassdefs do
+                       for method in mclassdef.collect_intro_and_redef_mpropdefs(self) do
+                       # check if the property is a method definition
+                               if not method isa MMethodDef then continue
+                               #Check if method has a signature
+                               if method.msignature == null then continue
+                               if method.msignature.mparameters.length == 0 then continue
+                               counter[method] = method.msignature.mparameters.length
+                       end
+               end
+               return counter.avg + counter.std_dev
+       end
+
+       fun get_avg_attribut: Float do
+               var counter = new Counter[MClassDef]
+               for mclassdef in mclassdefs do
+                       var number_attributs = mclassdef.collect_intro_and_redef_mattributes(self).length
+                       if number_attributs != 0 then counter[mclassdef] = number_attributs
+               end
+               return counter.avg + counter.std_dev
+       end
+
+       fun get_avg_method: Float do
+               var counter = new Counter[MClassDef]
+               for mclassdef in mclassdefs do
+                       var number_methodes = mclassdef.collect_intro_and_redef_methods(self).length
+                       if number_methodes != 0 then counter[mclassdef] = number_methodes
+               end
+               return counter.avg + counter.std_dev
+       end
+
+       fun get_avg_linenumber(model_builder: ModelBuilder): Float do
+               var methods_analyse_metrics = new Counter[MClassDef]
+               for mclassdef in mclassdefs do
+                       var result = 0
+                       var count = 0
+                       for mmethoddef in call_analyze_methods(mclassdef,model_builder) do
+                               result += mmethoddef.line_number
+                               if mmethoddef.line_number == 0 then continue
+                               count += 1
+                       end
+                       if not mclassdef.collect_local_mproperties(self).length != 0 then continue
+                       if count == 0 then continue
+                       methods_analyse_metrics[mclassdef] = (result/count).to_i
+               end
+               return methods_analyse_metrics.avg + methods_analyse_metrics.std_dev
+       end
+end
+
+class BadConceptionComparator
+       super Comparator
+       redef type COMPARED: BadConceptionFinder
+       redef fun compare(a,b) do return a.array_badconception.length <=> b.array_badconception.length
+end
diff --git a/src/metrics/mclassdef_collect.nit b/src/metrics/mclassdef_collect.nit
new file mode 100644 (file)
index 0000000..5b6c937
--- /dev/null
@@ -0,0 +1,167 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# This module redef Mclassdef to add new collect methods.
+
+module mclassdef_collect
+
+# We usualy need specific phases
+# NOTE: `frontend` is sufficent in most case (it is often too much)
+import frontend
+import model_views
+import model_collect
+
+redef class MClassDef
+       # Collect all mproperties introduced in 'self' with `visibility >= min_visibility`.
+       fun collect_intro_mproperties(view: ModelView): Set[MProperty] do
+               var set = new HashSet[MProperty]
+                       for mprop in self.intro_mproperties do
+                               if not view.accept_mentity(mprop) then continue
+                               set.add(mprop)
+                       end
+               return set
+       end
+
+       # Collect mmethods introduced in 'self' with `visibility >= min_visibility`.
+       fun collect_intro_mmethods(view: ModelView): Set[MMethod] do
+               var res = new HashSet[MMethod]
+               for mproperty in collect_intro_mproperties(view) do
+                       if not view.accept_mentity(mproperty) then continue
+                       if mproperty isa MMethod then res.add(mproperty)
+               end
+               return res
+       end
+
+       # Collect mmethods redefined in 'self' with `visibility >= min_visibility`.
+       fun collect_redef_mmethods(view: ModelView): Set[MMethod] do
+               var res = new HashSet[MMethod]
+               for mproperty in collect_redef_mproperties(view) do
+                       if not view.accept_mentity(mproperty) then continue
+                       if mproperty isa MMethod then res.add(mproperty)
+               end
+               return res
+       end
+
+       # Collect mattributes redefined in 'self' with `visibility >= min_visibility`.
+       fun collect_redef_mattributes(view: ModelView): Set[MAttribute] do
+               var res = new HashSet[MAttribute]
+               for mproperty in collect_redef_mproperties(view) do
+                       if not view.accept_mentity(mproperty) then continue
+                       if mproperty isa MAttribute then res.add(mproperty)
+               end
+               return res
+       end
+
+       # Collect mattributes introduced in 'self' with `visibility >= min_visibility`.
+       fun collect_intro_mattributes(view: ModelView): Set[MAttribute] do
+               var res = new HashSet[MAttribute]
+               for mproperty in collect_intro_mproperties(view) do
+                       if not view.accept_mentity(mproperty) then continue
+                       if mproperty isa MAttribute then res.add(mproperty)
+               end
+               return res
+       end
+
+       # Collect all mproperties redefined in 'self' with `visibility >= min_visibility`.
+       fun collect_redef_mproperties(view: ModelView): Set[MProperty] do
+               var set = new HashSet[MProperty]
+               for mpropdef in self.mpropdefs do
+                       if not view.accept_mentity(mpropdef) then continue
+                       if mpropdef.mproperty.intro_mclassdef.mclass == self then continue
+                               set.add(mpropdef.mproperty)
+                       end
+               return set
+       end
+
+       # Collect mmethods inherited by 'self' if accepted by `view`.
+       fun collect_inherited_mmethods(view: ModelView): Set[MMethod] do
+               var res = new HashSet[MMethod]
+               for mproperty in collect_inherited_mproperties(view) do
+                       if not view.accept_mentity(mproperty) then continue
+                       if mproperty isa MMethod then res.add(mproperty)
+               end
+               return res
+       end
+
+       # Collect mproperties introduced and redefined in 'self' with `visibility >= min_visibility`.
+       fun collect_local_mproperties(view: ModelView): Set[MProperty] do
+               var set = new HashSet[MProperty]
+               set.add_all collect_intro_mproperties(view)
+               set.add_all collect_redef_mproperties(view)
+               return set
+       end
+
+       # Collect all mproperties inehrited by 'self' with `visibility >= min_visibility`.
+       fun collect_inherited_mproperties(view: ModelView): Set[MProperty] do
+               var set = new HashSet[MProperty]
+               for parent in collect_parents(view) do
+                       set.add_all(parent.collect_intro_mproperties(view))
+                       set.add_all(parent.collect_inherited_mproperties(view))
+               end
+               return set
+       end
+
+       # Collect mattributes inherited by 'self' with `visibility >= min_visibility`.
+       fun collect_inherited_mattributes(view: ModelView): Set[MAttribute] do
+               var res = new HashSet[MAttribute]
+               for mproperty in collect_inherited_mproperties(view) do
+                       if not view.accept_mentity(mproperty) then continue
+                       if mproperty isa MAttribute then res.add(mproperty)
+               end
+               return res
+       end
+
+       fun collect_all_methods(view: ModelView): Set[MMethod] do
+               var set = new HashSet[MMethod]
+               set.add_all collect_intro_mmethods(view)
+               set.add_all collect_redef_mmethods(view)
+               set.add_all collect_inherited_mmethods(view)
+               return set
+       end
+
+       fun collect_all_mattributes(view: ModelView): Set[MAttribute] do
+               var set = new HashSet[MAttribute]
+               set.add_all collect_redef_mattributes(view)
+               set.add_all collect_intro_mattributes(view)
+               set.add_all collect_inherited_mattributes(view)
+               return set
+       end
+
+       fun collect_intro_and_redef_methods(view: ModelView): Set[MMethod] do
+               var set = new HashSet[MMethod]
+               set.add_all collect_intro_mmethods(view)
+               set.add_all collect_redef_mmethods(view)
+               return set
+       end
+
+       fun collect_intro_and_redef_mattributes(view: ModelView): Set[MAttribute] do
+               var set = new HashSet[MAttribute]
+               set.add_all collect_redef_mattributes(view)
+               set.add_all collect_intro_mattributes(view)
+               return set
+       end
+
+       fun collect_intro_and_redef_mproperties(view: ModelView): Set[MProperty] do
+               var set = new HashSet[MProperty]
+               set.add_all collect_redef_mproperties(view)
+               set.add_all collect_intro_mproperties(view)
+               return set
+       end
+
+       fun collect_intro_and_redef_mpropdefs(view: ModelView): Set[MPropDef] do
+               var set = new HashSet[MPropDef]
+               set.add_all collect_intro_mpropdefs(view)
+               set.add_all collect_redef_mpropdefs(view)
+               return set
+       end
+end
diff --git a/src/metrics/method_analyze_metrics.nit b/src/metrics/method_analyze_metrics.nit
new file mode 100644 (file)
index 0000000..6aee2a9
--- /dev/null
@@ -0,0 +1,83 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# This module call visitor for get number of line, total attributs call and total of self attributes call
+
+module method_analyze_metrics
+
+# We usualy need specific phases
+# NOTE: `frontend` is sufficent in most case (it is often too much)
+import metrics_base
+import mclasses_metrics
+import semantize
+import mclassdef_collect
+
+
+fun call_analyze_methods(mclassdef: MClassDef, model_builder: ModelBuilder): Array[MMethodDef] do
+       var mmethoddefs = new Array[MMethodDef]
+       for m_prop in mclassdef.collect_intro_and_redef_mpropdefs(model_builder.model.private_view) do
+               var n_prop = model_builder.mpropdef2node(m_prop)
+               #Check if the property is a method definition
+               if n_prop isa AMethPropdef and m_prop isa MMethodDef then
+                       if n_prop.n_methid isa AIdMethid then
+                               #Call visitor to analyse the method
+                               var visitor = new MethodAnalyzeMetrics(n_prop)
+                               visitor.enter_visit(n_prop)
+                               mmethoddefs.add(set_analyse_result_methoddef(m_prop,visitor))
+                       end
+               end
+       end
+       return mmethoddefs
+end
+
+fun set_analyse_result_methoddef(mmethoddef: MMethodDef, visitor: MethodAnalyzeMetrics): MMethodDef do
+       mmethoddef.total_call = visitor.total_call
+       mmethoddef.line_number = visitor.line_number.length
+       mmethoddef.total_self_call = visitor.total_self_call
+       mmethoddef.total_extern_call = visitor.total_call - visitor.total_self_call
+       return mmethoddef
+end
+
+public class MethodAnalyzeMetrics
+       super Visitor
+       var ameth_prop_def: AMethPropdef
+       var total_call = 0
+       var line_number = new Counter[nullable Int]
+       var total_self_call = 0
+
+       redef fun visit(n) do
+               n.visit_all(self)
+               if n isa AExpr then
+                       if not n isa ABlockExpr then
+                               if n.first_location != null then
+                                       line_number.inc(n.first_location.line_start)
+                               end
+                       end
+               end
+
+               if n isa ASendExpr then
+                       var callsite = n.callsite
+                       if callsite != null then
+                               self.total_call += 1
+                               if callsite.recv_is_self == true then self.total_self_call += 1
+                       end
+               end
+       end
+end
+
+redef class MMethodDef
+       var total_call = 0
+       var line_number = 0
+       var total_self_call = 0
+       var total_extern_call = 0
+end
index af53e3d..a920aef 100644 (file)
@@ -50,6 +50,13 @@ redef class Model
        #
        # Each classdef is associated with its super-classdefs in regard to
        # its module of definition.
+       #
+       # ~~~
+       # var m = new ModelDiamond
+       # assert     m.mclassdef_hierarchy.has_edge(m.mclassdef_b, m.mclassdef_a)
+       # assert not m.mclassdef_hierarchy.has_edge(m.mclassdef_a, m.mclassdef_b)
+       # assert not m.mclassdef_hierarchy.has_edge(m.mclassdef_b, m.mclassdef_c)
+       # ~~~
        var mclassdef_hierarchy = new POSet[MClassDef]
 
        # Class-type hierarchy restricted to the introduction.
@@ -81,6 +88,12 @@ redef class Model
        # (instead of an empty array)
        #
        # Visibility or modules are not considered
+       #
+       # ~~~
+       # var m = new ModelStandalone
+       # assert m.get_mclasses_by_name("Object") == [m.mclass_o]
+       # assert m.get_mclasses_by_name("Fail") == null
+       # ~~~
        fun get_mclasses_by_name(name: String): nullable Array[MClass]
        do
                return mclasses_by_name.get_or_null(name)
@@ -2602,3 +2615,70 @@ fun interface_kind: MClassKind do return once new MClassKind("interface", false)
 fun enum_kind: MClassKind do return once new MClassKind("enum", false)
 # The class kind `extern`
 fun extern_kind: MClassKind do return once new MClassKind("extern class", false)
+
+# A standalone pre-constructed model used to test various model-related methods.
+#
+# When instantiated, a standalone model is already filled with entities that are exposed as attributes.
+class ModelStandalone
+       super Model
+
+       redef var location = new Location.opaque_file("ModelStandalone")
+
+       # The first module
+       var mmodule0 = new MModule(self, null, "module0", location)
+
+       # The root Object class
+       var mclass_o = new MClass(mmodule0, "Object", location, null, interface_kind, public_visibility)
+
+       # The introduction of `mclass_o`
+       var mclassdef_o = new MClassDef(mmodule0, mclass_o.mclass_type, location)
+end
+
+# A standalone model with the common class diamond-hierarchy ABCD
+class ModelDiamond
+       super ModelStandalone
+
+       # A, a simple subclass of Object
+       var mclass_a = new MClass(mmodule0, "A", location, null, concrete_kind, public_visibility)
+
+       # The introduction of `mclass_a`
+       var mclassdef_a: MClassDef do
+               var res = new MClassDef(mmodule0, mclass_a.mclass_type, location)
+               res.set_supertypes([mclass_o.mclass_type])
+               res.add_in_hierarchy
+               return res
+       end
+
+       # B, a subclass of A (`mclass_a`)
+       var mclass_b = new MClass(mmodule0, "B", location, null, concrete_kind, public_visibility)
+
+       # The introduction of `mclass_b`
+       var mclassdef_b: MClassDef do
+               var res = new MClassDef(mmodule0, mclass_b.mclass_type, location)
+               res.set_supertypes([mclass_a.mclass_type])
+               res.add_in_hierarchy
+               return res
+       end
+
+       # C, another subclass of A (`mclass_a`)
+       var mclass_c = new MClass(mmodule0, "C", location, null, concrete_kind, public_visibility)
+
+       # The introduction of `mclass_c`
+       var mclassdef_c: MClassDef do
+               var res = new MClassDef(mmodule0, mclass_c.mclass_type, location)
+               res.set_supertypes([mclass_a.mclass_type])
+               res.add_in_hierarchy
+               return res
+       end
+
+       # D, a multiple subclass of B (`mclass_b`) and C (`mclass_c`)
+       var mclass_d = new MClass(mmodule0, "D", location, null, concrete_kind, public_visibility)
+
+       # The introduction of `mclass_d`
+       var mclassdef_d: MClassDef do
+               var res = new MClassDef(mmodule0, mclass_d.mclass_type, location)
+               res.set_supertypes([mclass_b.mclass_type, mclass_c.mclass_type])
+               res.add_in_hierarchy
+               return res
+       end
+end
index 7580dde..69aea68 100644 (file)
@@ -149,7 +149,6 @@ redef class ModelBuilder
        private fun build_a_mclassdef(nmodule: AModule, nclassdef: AClassdef)
        do
                var mmodule = nmodule.mmodule.as(not null)
-               var objectclass = try_get_mclass_by_name(nmodule, mmodule, "Object")
                var mclass = nclassdef.mclass
                if mclass == null then return # Skip error
 
@@ -161,8 +160,45 @@ redef class ModelBuilder
                        return
                end
 
+               var bound_mtype = build_a_bound_mtype(nmodule, nclassdef)
+               if bound_mtype == null then return
+               var mclassdef = new MClassDef(mmodule, bound_mtype, nclassdef.location)
+               nclassdef.mclassdef = mclassdef
+               self.mclassdef2nclassdef[mclassdef] = nclassdef
+
+               if nclassdef isa AStdClassdef then
+                       var ndoc = nclassdef.n_doc
+                       if ndoc != null then
+                               var mdoc = ndoc.to_mdoc
+                               mclassdef.mdoc = mdoc
+                               mdoc.original_mentity = mclassdef
+                       else if mclassdef.is_intro and mclass.visibility >= public_visibility then
+                               advice(nclassdef, "missing-doc", "Documentation warning: Undocumented public class `{mclass}`")
+                       end
+               end
+
+               if mclassdef.is_intro then
+                       self.toolcontext.info("{mclassdef} introduces new {mclass.kind} {mclass.full_name}", 3)
+               else
+                       self.toolcontext.info("{mclassdef} refines {mclass.kind} {mclass.full_name}", 3)
+               end
+       end
+
+       # Determine the type parameter bounds for `nclassdef`.
+       #
+       # In case of error, return `null`.
+       #
+       # REQUIRE: `nmodule.mmodule != null`
+       # REQUIRE: `nclassdef.mclass != null`
+       private fun build_a_bound_mtype(nmodule: AModule, nclassdef: AClassdef): nullable MClassType
+       do
+               var mmodule = nmodule.mmodule.as(not null)
+               var mclass = nclassdef.mclass.as(not null)
+
                var bounds = new Array[MType]
                if nclassdef isa AStdClassdef and mclass.arity > 0 then
+                       var objectclass = try_get_mclass_by_name(nmodule, mmodule, "Object")
+
                        # Revolve bound for formal parameters
                        for i in [0..mclass.arity[ do
                                if nclassdef.n_formaldefs.is_empty then
@@ -180,7 +216,7 @@ redef class ModelBuilder
                                var nfdt = nfd.n_type
                                if nfdt != null then
                                        var bound = resolve_mtype3_unchecked(mmodule, null, null, nfdt, false)
-                                       if bound == null then return # Forward error
+                                       if bound == null then return null # Forward error
                                        if bound.need_anchor then
                                                # No F-bounds!
                                                error(nfd, "Error: formal parameter type `{pname}` bounded with a formal parameter type.")
@@ -191,7 +227,7 @@ redef class ModelBuilder
                                else if mclass.mclassdefs.is_empty then
                                        if objectclass == null then
                                                error(nfd, "Error: formal parameter type `{pname}` unbounded but no `Object` class exists.")
-                                               return
+                                               return null
                                        end
                                        # No bound, then implicitely bound by nullable Object
                                        var bound = objectclass.mclass_type.as_nullable
@@ -206,41 +242,39 @@ redef class ModelBuilder
                        end
                end
 
-               var bound_mtype = mclass.get_mtype(bounds)
-               var mclassdef = new MClassDef(mmodule, bound_mtype, nclassdef.location)
-               nclassdef.mclassdef = mclassdef
-               self.mclassdef2nclassdef[mclassdef] = nclassdef
-
-               if nclassdef isa AStdClassdef then
-                       var ndoc = nclassdef.n_doc
-                       if ndoc != null then
-                               var mdoc = ndoc.to_mdoc
-                               mclassdef.mdoc = mdoc
-                               mdoc.original_mentity = mclassdef
-                       else if mclassdef.is_intro and mclass.visibility >= public_visibility then
-                               advice(nclassdef, "missing-doc", "Documentation warning: Undocumented public class `{mclass}`")
-                       end
-               end
-
-               if mclassdef.is_intro then
-                       self.toolcontext.info("{mclassdef} introduces new {mclass.kind} {mclass.full_name}", 3)
-               else
-                       self.toolcontext.info("{mclassdef} refines {mclass.kind} {mclass.full_name}", 3)
-               end
+               return mclass.get_mtype(bounds)
        end
 
        # Visit the AST and set the super-types of the `MClassDef` objects
-       private fun collect_a_mclassdef_inheritance(nmodule: AModule, nclassdef: AClassdef)
+       private fun build_a_mclassdef_inheritance(nmodule: AModule, nclassdef: AClassdef)
        do
                var mmodule = nmodule.mmodule
                if mmodule == null then return
-               var objectclass = try_get_mclass_by_name(nmodule, mmodule, "Object")
-               var pointerclass = try_get_mclass_by_name(nmodule, mmodule, "Pointer")
                var mclass = nclassdef.mclass
                if mclass == null then return
                var mclassdef = nclassdef.mclassdef
                if mclassdef == null then return
 
+               var supertypes = collect_supertypes(nmodule, nclassdef, mclassdef.is_intro)
+               mclassdef.set_supertypes(supertypes)
+               if not supertypes.is_empty then self.toolcontext.info("{mclassdef} new super-types: {supertypes.join(", ")}", 3)
+       end
+
+       # List the supertypes specified or implied by `nclassdef`.
+       #
+       # REQUIRE: `nmodule.mmodule != null`
+       # REQUIRE: `nclassdef.mclass != null`
+       private fun collect_supertypes(nmodule: AModule, nclassdef: AClassdef,
+                       is_intro: Bool): Array[MClassType]
+       do
+               var mmodule = nmodule.mmodule.as(not null)
+               var mclass = nclassdef.mclass.as(not null)
+               var name = mclass.name
+               var kind = mclass.kind
+
+               var objectclass = try_get_mclass_by_name(nmodule, mmodule, "Object")
+               var pointerclass = try_get_mclass_by_name(nmodule, mmodule, "Pointer")
+
                # Do we need to specify Object as a super class?
                var specobject = true
 
@@ -253,42 +287,43 @@ redef class ModelBuilder
                        for nsc in nclassdef.n_superclasses do
                                specobject = false
                                var ntype = nsc.n_type
-                               var mtype = resolve_mtype_unchecked(mclassdef, ntype, false)
+                               var mtype = resolve_mtype3_unchecked(mmodule, mclass, null,
+                                               ntype, false)
                                if mtype == null then continue # Skip because of error
                                if not mtype isa MClassType then
-                                       error(ntype, "Error: supertypes cannot be a formal type.")
-                                       return
+                                       error(ntype, "Error: a supertype cannot be a formal type.")
+                                       continue
                                end
-                               if not mclass.kind.can_specialize(mtype.mclass.kind) then
-                                       error(ntype, "Error: {mclass.kind} `{mclass}` cannot specialize {mtype.mclass.kind} `{mtype.mclass}`.")
+                               var superclass = mtype.mclass
+                               var super_kind = superclass.kind
+                               if not kind.can_specialize(super_kind) then
+                                       error(ntype, "Error: {kind} `{mclass}` cannot specialize {super_kind} `{superclass}`.")
                                end
                                supertypes.add mtype
                                #print "new super : {mclass} < {mtype}"
-                               if mtype.mclass.kind == extern_kind then specpointer = false
+                               if super_kind == extern_kind then specpointer = false
                        end
                end
 
-               if mclassdef.is_intro and objectclass != null then
-                       if mclass.kind == extern_kind and mclass.name != "Pointer" then
+               if is_intro and objectclass != null then
+                       if kind == extern_kind and name != "Pointer" then
                                # it is an extern class, but not a Pointer
                                if pointerclass == null then
                                        error(nclassdef, "Error: `Pointer` must be defined first.")
-                                       return
+                                       return supertypes
                                end
                                if specpointer then supertypes.add pointerclass.mclass_type
                        else if specobject then
-                               if mclass.name != "Object" then
+                               if name != "Object" then
                                        # it is a standard class without super class (but is not Object)
                                        supertypes.add objectclass.mclass_type
-                               else if mclass.kind != interface_kind then
+                               else if kind != interface_kind then
                                        error(nclassdef, "Error: `Object` must be an {interface_kind}.")
-                                       return
                                end
                        end
                end
 
-               mclassdef.set_supertypes(supertypes)
-               if not supertypes.is_empty then self.toolcontext.info("{mclassdef} new super-types: {supertypes.join(", ")}", 3)
+               return supertypes
        end
 
        # Check the validity of the specialization heirarchy
@@ -344,7 +379,7 @@ redef class ModelBuilder
 
                # Create inheritance on all classdefs
                for nclassdef in nmodule.n_classdefs do
-                       self.collect_a_mclassdef_inheritance(nmodule, nclassdef)
+                       self.build_a_mclassdef_inheritance(nmodule, nclassdef)
                end
 
                # Create the mclassdef hierarchy
@@ -435,8 +470,11 @@ redef class ModelBuilder
                        for nsc in nclassdef.n_superclasses do
                                var ntype = nsc.n_type
                                var mtype = ntype.mtype
-                               if mtype == null then continue
-                               assert mtype isa MClassType
+
+                               # If the supertype is `null` or don’t refer to a class, we
+                               # already raised an error.
+                               if not mtype isa MClassType then continue
+
                                var sc = mtype.mclass
                                if not parents.has(sc) or sc == objectclass then
                                        # Skip the warning on generated code
index a7f75ff..8880ef7 100644 (file)
@@ -769,22 +769,21 @@ redef class AMethPropdef
        do
                var n_kwinit = n_kwinit
                var n_kwnew = n_kwnew
-               var is_init = n_kwinit != null or n_kwnew != null
+               var is_new = n_kwnew != null
+               var is_init = n_kwinit != null or is_new
                var name: String
                var amethodid = self.n_methid
                var name_node: ANode
                if amethodid == null then
-                       if not is_init then
-                               name = "main"
-                               name_node = self
-                       else if n_kwinit != null then
+                       if n_kwinit != null then
                                name = "init"
                                name_node = n_kwinit
                        else if n_kwnew != null then
                                name = "new"
                                name_node = n_kwnew
                        else
-                               abort
+                               name = "main"
+                               name_node = self
                        end
                else if amethodid isa AIdMethid then
                        name = amethodid.n_id.text
@@ -828,8 +827,8 @@ redef class AMethPropdef
                                mprop.is_root_init = true
                        end
                        mprop.is_init = is_init
-                       mprop.is_new = n_kwnew != null
-                       if mprop.is_new then mclassdef.mclass.has_new_factory = true
+                       mprop.is_new = is_new
+                       if is_new then mclassdef.mclass.has_new_factory = true
                        if name == "sys" then mprop.is_toplevel = true # special case for sys allowed in `new` factories
                        if not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, false, mprop) then
                                mprop.is_broken = true
@@ -873,11 +872,12 @@ redef class AMethPropdef
        do
                var mpropdef = self.mpropdef
                if mpropdef == null then return # Error thus skiped
+               var mproperty = mpropdef.mproperty
                var mclassdef = mpropdef.mclassdef
                var mmodule = mclassdef.mmodule
                var nsig = self.n_signature
 
-               if mpropdef.mproperty.is_root_init and not mclassdef.is_intro then
+               if mproperty.is_root_init and not mclassdef.is_intro then
                        var root_init = mclassdef.mclass.root_init
                        if root_init != null then
                                # Inherit the initializers by refinement
@@ -907,7 +907,7 @@ redef class AMethPropdef
                # FIXME: do not inherit from the intro, but from the most specific
                var msignature: nullable MSignature = null
                if not mpropdef.is_intro then
-                       msignature = mpropdef.mproperty.intro.msignature
+                       msignature = mproperty.intro.msignature
                        if msignature == null then return # Skip error
 
                        # The local signature is adapted to use the local formal types, if any.
@@ -917,14 +917,14 @@ redef class AMethPropdef
                        if param_names.length != msignature.arity then
                                var node: ANode
                                if nsig != null then node = nsig else node = self
-                               modelbuilder.error(node, "Redef Error: expected {msignature.arity} parameter(s) for `{mpropdef.mproperty.name}{msignature}`; got {param_names.length}. See introduction at `{mpropdef.mproperty.full_name}`.")
+                               modelbuilder.error(node, "Redef Error: expected {msignature.arity} parameter(s) for `{mproperty.name}{msignature}`; got {param_names.length}. See introduction at `{mproperty.full_name}`.")
                                return
                        end
-               else if mpropdef.mproperty.is_init and not mpropdef.mproperty.is_new then
+               else if mproperty.is_init and not mproperty.is_new then
                        # FIXME UGLY: inherit signature from a super-constructor
                        for msupertype in mclassdef.supertypes do
                                msupertype = msupertype.anchor_to(mmodule, mclassdef.bound_mtype)
-                               var candidate = modelbuilder.try_get_mproperty_by_name2(self, mmodule, msupertype, mpropdef.mproperty.name)
+                               var candidate = modelbuilder.try_get_mproperty_by_name2(self, mmodule, msupertype, mproperty.name)
                                if candidate != null then
                                        if msignature == null then
                                                msignature = candidate.intro.as(MMethodDef).msignature
@@ -961,14 +961,14 @@ redef class AMethPropdef
                end
 
                # In `new`-factories, the return type is by default the classtype.
-               if ret_type == null and mpropdef.mproperty.is_new then ret_type = mclassdef.mclass.mclass_type
+               if ret_type == null and mproperty.is_new then ret_type = mclassdef.mclass.mclass_type
 
                # Special checks for operator methods
                if not accept_special_last_parameter and mparameters.not_empty and mparameters.last.is_vararg then
-                       modelbuilder.error(self.n_signature.n_params.last, "Error: illegal variadic parameter `{mparameters.last}` for `{mpropdef.mproperty.name}`.")
+                       modelbuilder.error(self.n_signature.n_params.last, "Error: illegal variadic parameter `{mparameters.last}` for `{mproperty.name}`.")
                end
                if ret_type == null and return_is_mandatory then
-                       modelbuilder.error(self.n_methid, "Error: mandatory return type for `{mpropdef.mproperty.name}`.")
+                       modelbuilder.error(self.n_methid, "Error: mandatory return type for `{mproperty.name}`.")
                end
 
                msignature = new MSignature(mparameters, ret_type)
diff --git a/src/nitsmells.nit b/src/nitsmells.nit
new file mode 100644 (file)
index 0000000..da8454a
--- /dev/null
@@ -0,0 +1,36 @@
+# 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 metrics_base
+import mclasses_metrics
+import semantize
+
+import codesmells_metrics
+
+# Create a tool context to handle options and paths
+var toolcontext = new ToolContext
+toolcontext.tooldescription = "Usage: nitsmells [OPTION]... <file.nit>...\n Computes code smells on Nit programs."
+# We do not add other options, so process them now!
+toolcontext.process_options(args)
+# Get arguments
+var arguments = toolcontext.option_context.rest
+# We need a model to collect stufs
+var model = new Model
+# An a model builder to parse files
+var modelbuilder = new ModelBuilder(model, toolcontext)
+# Here we load an process all modules passed on the command line
+var mmodules = modelbuilder.parse_full(arguments)
+modelbuilder.run_phases
+print "*** CODE SMELLS METRICS ***"
+toolcontext.run_global_phases(mmodules)
index 265cadf..1daebdd 100644 (file)
@@ -278,6 +278,8 @@ redef class MClassDef
                end
                return new Namespace.from([mmodule.full_name, "$::", mclass.intro_mmodule.to_ns_ref: nullable NSEntity])
        end
+
+       redef fun web_url do return "{mclass.web_url}/lin#{full_name}"
 end
 
 redef class MProperty
@@ -317,6 +319,8 @@ redef class MPropDef
                end
                return res
        end
+
+       redef fun web_url do return "{mproperty.web_url}/lin#{full_name}"
 end
 
 redef class MClassType
diff --git a/tests/TestNitsmells/FeatureEnvy/featureenvy.nit b/tests/TestNitsmells/FeatureEnvy/featureenvy.nit
new file mode 100644 (file)
index 0000000..6a11274
--- /dev/null
@@ -0,0 +1,42 @@
+# 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.
+
+# A test program with a fake model to check model tools.
+module featureenvy
+
+import platform
+
+class Starter
+       var attribute = 0
+       var attribute1 = 0
+       var attribute2 = 0
+       fun start do
+               self.attribute1 = 10
+               self.attribute = 2
+       end
+
+       fun ended do end
+end
+
+class FeatureEnvy
+       var testVariable = 0
+
+       fun feature_envy_method do
+               var starter = new Starter
+               testVariable = starter.attribute
+               if starter.attribute == self.testVariable then
+                       starter.attribute = 10
+               end
+       end
+end
diff --git a/tests/TestNitsmells/LargeClass/largeclass.nit b/tests/TestNitsmells/LargeClass/largeclass.nit
new file mode 100644 (file)
index 0000000..1c18141
--- /dev/null
@@ -0,0 +1,61 @@
+# 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.
+
+# A test program with a fake model to check model tools.
+module largeclass
+
+import platform
+
+class LargeClass
+       var attribute = 0
+       var attribute1 = 0
+       var attribute2 = 0
+       var attribute3 = 0
+       var attribute4 = 0
+       var attribute5 = 0
+       var attribut6 = 0
+       var attribute7 = 0
+       var attribute8 = 0
+       var attribute9 = 0
+       var attribute10 = 0
+       var attribute11 = 0
+       var attribute12 = 0
+       var attribute13 = 0
+       var attribute14 = 0
+       var attribute15 = 0
+       var attribute16 = 0
+       var attribute17 = 0
+
+       fun start do
+               self.attribute16 = 10
+       end
+
+       fun ended do end
+       fun replay do end
+       fun restart do end
+       fun start1 do end
+       fun ended1 do end
+       fun replay1 do end
+       fun restart1 do end
+       fun start2 do end
+       fun ended2 do end
+       fun replay2 do end
+       fun restart2 do end
+end
+
+class NoLargeclass
+       var testVariable = 0
+
+       fun test do end
+end
\ No newline at end of file
diff --git a/tests/TestNitsmells/LongMethod/longmethod.nit b/tests/TestNitsmells/LongMethod/longmethod.nit
new file mode 100644 (file)
index 0000000..584ef44
--- /dev/null
@@ -0,0 +1,38 @@
+# 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.
+
+# A test program with a fake model to check model tools.
+module longmethod
+
+import platform
+
+class Starter
+       var attribute = 0
+       var attribute1 = 0
+       var attribute2 = 0
+       fun start do
+               self.attribute1 = 10
+       end
+
+       fun ended do end
+end
+
+class LongMethodClass
+       var test_variable = 0
+
+       fun long_method do
+               var starter = new Starter
+               test_variable = 3
+       end
+end
\ No newline at end of file
diff --git a/tests/TestNitsmells/LongParameterList/longparameterlist.nit b/tests/TestNitsmells/LongParameterList/longparameterlist.nit
new file mode 100644 (file)
index 0000000..6988536
--- /dev/null
@@ -0,0 +1,34 @@
+# 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.
+
+# A test program with a fake model to check model tools.
+module longparameterlist
+
+import platform
+
+class Starter
+       fun no_para do end
+
+       fun no_para2() do end
+end
+
+class Test
+       fun short_list_parameter(numbers : Int, para1 : Bool, para2 : Float, para3 : Int) do
+               var starter = new Starter
+       end
+
+       fun long_list_parameter(numbers : Int, para1 : Bool, para2 : Float, para3 : Int, para4 : Starter) do
+               var starter = new Starter
+       end
+end
diff --git a/tests/TestNitsmells/platform/platform.nit b/tests/TestNitsmells/platform/platform.nit
new file mode 100644 (file)
index 0000000..175e6fc
--- /dev/null
@@ -0,0 +1,59 @@
+# 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.
+
+# Declares base types allowed on the platform.
+module platform
+
+import end
+
+# Root of everything.
+interface Object
+       # Used for comparisons.
+       type OTHER: nullable Object
+
+       # Is `other` equqls to `self`?
+       fun ==(other: OTHER): Bool is intern
+
+       # Is `other` different from `self`?
+       fun !=(other: OTHER): Bool do return not self == other
+end
+
+# Some services about Integers.
+class Int
+       fun -: Int is intern
+       fun +(i: Int): Int is intern
+       fun -(i: Int): Int is intern
+       fun *(i: Int): Int is intern
+       fun /(i: Int): Int is intern
+       fun >(i: Int): Bool is intern
+       fun to_f: Float is intern
+end
+
+# Some services about Floats.
+class Float
+       fun +(f: Float): Float is intern
+       fun -(f: Float): Float is intern
+       fun *(f: Float): Float is intern
+       fun /(f: Float): Float is intern
+       fun >(f: Float): Bool is intern
+end
+
+# Booleans, `true` or `false`.
+class Bool end
+
+# Strings (there is no chars...).
+class String end
+
+# List of things.
+class List[E] end
diff --git a/tests/error_formal_super.nit b/tests/error_formal_super.nit
new file mode 100644 (file)
index 0000000..216dac8
--- /dev/null
@@ -0,0 +1,21 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Test of formal types as supertypes.
+
+import core::kernel
+
+class Foo[E]
+       super E
+end
diff --git a/tests/nitsmells.args b/tests/nitsmells.args
new file mode 100644 (file)
index 0000000..0032cd9
--- /dev/null
@@ -0,0 +1,5 @@
+--no-colors test_prog/
+--no-colors TestNitsmells/FeatureEnvy/
+--no-colors TestNitsmells/LargeClass/
+--no-colors TestNitsmells/LongMethod/
+--no-colors TestNitsmells/LongParameterList/
\ No newline at end of file
diff --git a/tests/sav/clusters.res b/tests/sav/clusters.res
new file mode 100644 (file)
index 0000000..1a47c6a
--- /dev/null
@@ -0,0 +1,36 @@
+digraph "G" {
+subgraph "cluster_0" {
+label="process #1";
+style="filled";
+color="lightgrey";
+node[style="filled",color="white",];
+"a0" ;
+"a1" ;
+"a2" ;
+"a3" ;
+"a0" -> "a1" ;
+"a1" -> "a2" ;
+"a2" -> "a3" ;
+};
+subgraph "cluster_1" {
+label="process #2";
+color="blue";
+node[style="filled",];
+"b0" ;
+"b1" ;
+"b2" ;
+"b3" ;
+"b0" -> "b1" ;
+"b1" -> "b2" ;
+"b2" -> "b3" ;
+};
+"start" [shape="Mdiamond",];
+"end" [shape="Msquare",];
+"start" -> "a0" ;
+"start" -> "b0" ;
+"a1" -> "b3" ;
+"b2" -> "a3" ;
+"a3" -> "a0" ;
+"a3" -> "end" ;
+"b3" -> "end" ;
+}
diff --git a/tests/sav/error_formal_super.res b/tests/sav/error_formal_super.res
new file mode 100644 (file)
index 0000000..b63cceb
--- /dev/null
@@ -0,0 +1 @@
+error_formal_super.nit:20,8: Error: a supertype cannot be a formal type.
diff --git a/tests/sav/hello.res b/tests/sav/hello.res
new file mode 100644 (file)
index 0000000..8441112
--- /dev/null
@@ -0,0 +1,5 @@
+digraph "G" {
+"hello" ;
+"world" ;
+"hello" -> "world" ;
+}
diff --git a/tests/sav/nitsmells.res b/tests/sav/nitsmells.res
new file mode 100644 (file)
index 0000000..9b27b1c
--- /dev/null
@@ -0,0 +1,3 @@
+Usage: nitsmells [OPTION]... <file.nit>...
+ Computes code smells on Nit programs.
+Use --help for help
diff --git a/tests/sav/nitsmells_args1.res b/tests/sav/nitsmells_args1.res
new file mode 100644 (file)
index 0000000..68b3680
--- /dev/null
@@ -0,0 +1,20 @@
+*** CODE SMELLS METRICS ***
+--- Code Smells Metrics ---
+-----------
+test_prog$Character
+Large class: 6 attributes and 18 methods (5.414A 7.161M Average)
+Feature envy:
+       Affected method(s):
+               -total_strengh 4/9
+               -total_endurance 4/9
+               -total_intelligence 4/9
+Long method:  Average 1 lines
+       Affected method(s):
+               -total_strengh has 2 lines
+               -total_endurance has 2 lines
+               -total_intelligence has 2 lines
+-----------
+test_prog::combat$Dwarf
+Feature envy:
+       Affected method(s):
+               -dps 1/3
diff --git a/tests/sav/nitsmells_args2.res b/tests/sav/nitsmells_args2.res
new file mode 100644 (file)
index 0000000..26092c7
--- /dev/null
@@ -0,0 +1,7 @@
+*** CODE SMELLS METRICS ***
+--- Code Smells Metrics ---
+-----------
+TestNitsmells$FeatureEnvy
+Feature envy:
+       Affected method(s):
+               -feature_envy_method 2/6
diff --git a/tests/sav/nitsmells_args3.res b/tests/sav/nitsmells_args3.res
new file mode 100644 (file)
index 0000000..fd60962
--- /dev/null
@@ -0,0 +1,5 @@
+*** CODE SMELLS METRICS ***
+--- Code Smells Metrics ---
+-----------
+TestNitsmells$LargeClass
+Large class: 18 attributes and 48 methods (17.515A 30.464M Average)
diff --git a/tests/sav/nitsmells_args4.res b/tests/sav/nitsmells_args4.res
new file mode 100644 (file)
index 0000000..1fe9b09
--- /dev/null
@@ -0,0 +1,7 @@
+*** CODE SMELLS METRICS ***
+--- Code Smells Metrics ---
+-----------
+TestNitsmells$LongMethodClass
+Long method:  Average 1 lines
+       Affected method(s):
+               -long_method has 2 lines
diff --git a/tests/sav/undirected_clusters.res b/tests/sav/undirected_clusters.res
new file mode 100644 (file)
index 0000000..a986173
--- /dev/null
@@ -0,0 +1,13 @@
+graph "G" {
+subgraph "clusterA" {
+"a" -- "b" ;
+subgraph "clusterB" {
+"c" -- "d" ;
+};
+};
+subgraph "clusterC" {
+"e" -- "g" ;
+};
+"e" -- "d" ;
+"f" -- "g" ;
+}
index e556349..5da93bb 100644 (file)
@@ -14,6 +14,7 @@
 
 import neo4j
 
+# key used to loosely assume unicity and prevent conflicting db accesses
 var key = "NIT_TESTING_ID".environ.to_i
 
 var srv = new Neo4jServer
@@ -24,6 +25,9 @@ print "# Test local\n"
 var client = new Neo4jClient("http://localhost:7474")
 assert client.is_ok
 
+# Clear the previous objects, if any
+client.cypher(new CypherQuery.from_string("MATCH (n) WHERE n.key = {key} OPTIONAL MATCH n-[r]-() DELETE r, n"))
+
 var andres = new NeoNode
 andres.labels.add_all(["PERSON", "MALE"])
 andres["name"] = "Andres"
@@ -122,5 +126,5 @@ var query = (new CypherQuery).
        nand("n.key = {key}").
        nreturn("n, r, m")
 var res7 = client.cypher(query)
-assert not res7.as(JsonObject)["data"].as(JsonArray).is_empty
+assert res7.as(JsonObject)["data"].as(JsonArray).length == 1
 
index 35f550c..e098357 100644 (file)
@@ -17,6 +17,7 @@ import neo4j
 var srv = new Neo4jServer
 srv.start_quiet
 
+# key used to loosely assume unicity and prevent conflicting db accesses
 var key = "NIT_TESTING_ID".environ.to_i
 
 var andres = new NeoNode
@@ -39,6 +40,9 @@ loves["since"] = 1999
 var client = new Neo4jClient("http://localhost:7474")
 assert client.is_ok
 
+# Clear the previous objects, if any
+client.cypher(new CypherQuery.from_string("MATCH (n) WHERE n.key = {key} OPTIONAL MATCH n-[r]-() DELETE r, n"))
+
 print "# Save batch\n"
 
 var batch = new NeoBatch(client)
index e413069..a402eb2 100755 (executable)
@@ -130,12 +130,12 @@ else
 fi
 
 # Detect a working time command
-if env time --quiet -f%U true 2>/dev/null; then
-       TIME="env time --quiet -f%U"
-elif env time -f%U true 2>/dev/null; then
-       TIME="env time -f%U"
-elif env gtime -f%U true 2>/dev/null; then
-       TIME="env gtime -f%U"
+if command time --quiet -f%e true 2>/dev/null; then
+       TIME="command time --quiet -f%e"
+elif command time -f%e true 2>/dev/null; then
+       TIME="command time -f%e"
+elif command gtime -f%e true 2>/dev/null; then
+       TIME="command gtime -f%e"
 else
        TIME=
 fi