From: Jean Privat Date: Fri, 3 Jun 2016 17:17:39 +0000 (-0400) Subject: Merge: nitunit: improve progress display X-Git-Url: http://nitlanguage.org?hp=003d9eaef9c3e2d2fb9172cd383953f4e79b25c1 Merge: nitunit: improve progress display The results are printed progressively instead of at the end of each test-suite. The progress bar in therefore displayed below the results. See it in action: https://asciinema.org/a/17u9jwts0nz5dfpxp8p9aab0f Pull-Request: #2156 Reviewed-by: Alexandre Terrasa --- diff --git a/contrib/benitlux/src/client/base.nit b/contrib/benitlux/src/client/base.nit index bcc1efd..438a125 100644 --- a/contrib/benitlux/src/client/base.nit +++ b/contrib/benitlux/src/client/base.nit @@ -117,14 +117,17 @@ class BenitluxHttpRequest app.user = null return true else if res isa BenitluxError then - app.feedback((res.user_message or else res.message).t) + feedback((res.user_message or else res.message).t) return true else if res isa Error then - app.feedback res.message.t + feedback res.message.t return true end return false end + + # Show feedback pertinent to the user, defaults to a platform specific popup + fun feedback(text: String) do app.feedback text end # Async request with services to act on the windows of the app diff --git a/contrib/benitlux/src/client/features/translations.nit b/contrib/benitlux/src/client/features/translations.nit index 02deb61..be42f57 100644 --- a/contrib/benitlux/src/client/features/translations.nit +++ b/contrib/benitlux/src/client/features/translations.nit @@ -63,13 +63,23 @@ do map["Welcome %0!"] = "Bienvenue %0!" map["Logged in as %0"] = "Connecté en tant que %0" map["Username"] = "Nom d'utilisateur" - map["Invalid name"] = "Nom d'utilisateur invalide" map["Password"] = "Mot de passe" - map["Passwords must be composed of at least 6 characters."] = "Le mot de passe doit avoir au moins 6 charactères." + map["Repeat password"] = "Répéter le mot de passe" map["Email"] = "Courriel" map["Login"] = "Se connecter" + map["Loging in..."] = "Authentification..." map["Logout"] = "Se déconnecter" map["Signup"] = "Créer un compte" + map["Signing up..."] = "Création du compte..." + + map["Passwords must be composed of at least 6 characters."] = "Le mot de passe doit avoir au moins 6 charactères." + map["Fill the following fields to sign up."] = "Remplissez les champs suivants pour créer un compte." + + map["Passwords do not match."] = "Les mots de passe ne correspondent pas." + map["Invalid username."] = "Nom d'utilisateur invalide." + map["Invalid password."] = "Mot de passe invalide." + map["Username already in use."] = "Le nom d'utilisateur est déjà réservé." + map["Invalid username and password combination."] = "La combinaison de nom et mot de passe n'est pas reconnue." # Social views map["Follow"] = "Suivre" diff --git a/contrib/benitlux/src/client/views/user_views.nit b/contrib/benitlux/src/client/views/user_views.nit index e45fcb5..b69b1db 100644 --- a/contrib/benitlux/src/client/views/user_views.nit +++ b/contrib/benitlux/src/client/views/user_views.nit @@ -86,38 +86,50 @@ end class SignupWindow super Window - # Main window layout - var layout = new ListLayout(parent=self) + private var list = new ListLayout(parent=self) + private var lbl_feedback = new Label(parent=list, text="Welcome") - private var lbl_welcome = new Label(parent=layout, text="Welcome") + private var layout_login = new VerticalLayout(parent=list) + + # --- + # First the login options # Name - private var name_line = new HorizontalLayout(parent=layout) + private var name_line = new HorizontalLayout(parent=layout_login) private var lbl_name = new Label(parent=name_line, text="Username".t) private var txt_name = new TextInput(parent=name_line, text=app.user) - # Pass - private var pass_line = new HorizontalLayout(parent=layout) + # Password + private var pass_line = new HorizontalLayout(parent=layout_login) private var lbl_pass = new Label(parent=pass_line, text="Password".t) private var txt_pass = new TextInput(parent=pass_line, is_password=true) - private var lbl_pass_desc = new Label(parent=layout, + private var lbl_pass_desc = new Label(parent=layout_login, size = 0.5, text="Passwords must be composed of at least 6 characters.".t) - private var but_login = new Button(parent=layout, text="Login".t) + private var but_login = new Button(parent=layout_login, text="Login".t) + + # --- + # Then, the signup options + + private var layout_register = new VerticalLayout(parent=list) + + private var lbl_signup_desc = new Label(parent=layout_register, size = 0.5, + text="Fill the following fields to sign up.".t) + + # Repeat password + private var pass_line2 = new HorizontalLayout(parent=layout_register) + private var lbl_pass2 = new Label(parent=pass_line2, text="Repeat password".t) + private var txt_pass2 = new TextInput(parent=pass_line2, is_password=true) # Email - private var email_line = new HorizontalLayout(parent=layout) + private var email_line = new HorizontalLayout(parent=layout_register) private var lbl_email = new Label(parent=email_line, text="Email".t) private var txt_email = new TextInput(parent=email_line) - private var but_signup = new Button(parent=layout, text="Signup".t) - - private var lbl_feedback = new Label(parent=layout, text="") + private var but_signup = new Button(parent=layout_register, text="Signup".t) init do - lbl_pass_desc.size = 0.5 - for c in [but_login, but_signup] do c.observers.add self end @@ -133,25 +145,32 @@ class SignupWindow var name = txt_name.text if name == null or not name.name_is_ok then - feedback "Invalid name".t + feedback "Invalid username.".t return end var pass = txt_pass.text if pass == null or not pass.pass_is_ok then - feedback "Invalid password".t + feedback "Invalid password.".t return end if sender == but_login then + feedback "Logging in...".t (new LoginOrSignupAction(self, "rest/login?name={name}&pass={pass.pass_hash}")).start else if sender == but_signup then + if pass != txt_pass2.text then + feedback "Passwords do not match.".t + return + end + var email = txt_email.text if email == null or email.is_empty then feedback "Invalid email".t return end + feedback "Signing up...".t (new LoginOrSignupAction(self, "rest/signup?name={name}&pass={pass.pass_hash}&email={email}")).start end end @@ -189,27 +208,6 @@ class LoginOrSignupAction app.on_log_in end -end - -# Async request for signing up -class SignupAction - super WindowHttpRequest - redef type W: SignupWindow - - init do affected_views.add_all([window.but_signup]) - - redef fun on_load(res) - do - if intercept_error(res) then return - - if not res isa LoginResult then - on_fail new Error("Server sent unexpected data {res or else "null"}") - return - end - - app.token = res.token - app.user = res.user.name - app.on_log_in - end + redef fun feedback(text) do window.feedback text end diff --git a/contrib/benitlux/src/server/benitlux_social.nit b/contrib/benitlux/src/server/benitlux_social.nit index bdd83b4..7764c75 100644 --- a/contrib/benitlux/src/server/benitlux_social.nit +++ b/contrib/benitlux/src/server/benitlux_social.nit @@ -182,7 +182,7 @@ GROUP BY beer0, beer1""") else # Check if already in user var stmt = select("ROWID FROM users WHERE lower({user.to_sql_string}) = lower(name)") assert stmt != null else print_error "Select 'sign_up' failed with: {error or else "?"}" - if not stmt.iterator.to_a.is_empty then return "Username already in use" + if not stmt.iterator.to_a.is_empty then return "Username already in use." # Check email use stmt = select("ROWID FROM users WHERE lower({email.to_sql_string}) = lower(email)") diff --git a/contrib/nitiwiki/src/wiki_base.nit b/contrib/nitiwiki/src/wiki_base.nit index 6fb5e05..d3adc26 100644 --- a/contrib/nitiwiki/src/wiki_base.nit +++ b/contrib/nitiwiki/src/wiki_base.nit @@ -244,7 +244,7 @@ class Nitiwiki # Used to translate ids in beautiful page names. fun pretty_name(name: String): String do name = name.replace("_", " ") - name = name.capitalized + name = name.capitalized(keep_upper=true) return name end end diff --git a/lib/app/README.md b/lib/app/README.md index c5caa4b..0d2d24c 100644 --- a/lib/app/README.md +++ b/lib/app/README.md @@ -5,13 +5,14 @@ The framework provides services to manage common needs of modern mobile applicat * Life-cycle * User interface * Persistence +* Async HTTP requests * Package metadata * Compilation and packaging The features offered by _app.nit_ are common to all platforms, but may not be available on all devices. -## Application Life-Cycle +# Application Life-Cycle The _app.nit_ application life-cycle is compatible with all target platforms. It relies on the following sequence of events, represented here by their callback method name: @@ -44,7 +45,7 @@ The `App` instance is the first to be notified of these events. Other UI elements, from the `ui` submodule, are notified of the same events using a simple depth first visit. So all UI elements can react separately to live-cycle events. -## User Interface +# User Interface The `app::ui` module defines an abstract API to build a portable graphical application. The API is composed of interactive `Control`s, visible `View`s and an active `Window`. @@ -65,21 +66,20 @@ So there is two ways to customize the behavior on a given event: * Add an observer to a `Button` instance, and implement `on_event` in the observer. -### Usage Example +## Usage Example The calculator example (at `../../examples/calculator/src/calculator.nit`) is a concrete, simple and complete use of the _app.nit_ portable UI. -### Platform-specific UI +## Platform-specific UI You can go beyond the portable UI API of _app.nit_ by using the natives services of a platform. The suggested approach is to use platform specific modules to customize the application on a precise platform. -This module redefine `Window::on_start` to call the native language of the platform and setup a native UI. +See the calculator example for an adaptation of the UI on Android, +the interesting module is in this repository at ../../examples/calculator/src/android_calculator.nit -_TODO complete description and add concrete examples_ - -## Persistent State with data\_store +# Persistent State with data\_store _app.nit_ offers the submodule `app::data_store` to easily save the application state and user preferences. The service is accessible by the method `App::data_store`. The `DataStore` itself defines 2 methods: @@ -90,7 +90,7 @@ Pass `null` to clear the value associated to a key. * `DataStore::[]` returns the object associated to a `String` key. It returns `null` if nothing is associated to the key. -### Usage Example +## Usage Example ~~~ import app::data_store @@ -123,7 +123,14 @@ redef class App end ~~~ -## Metadata annotations +# Async HTTP request + +The module `app::http_request` provides services to execute asynchronous HTTP request. +The class `AsyncHttpRequest` hides the complex parallel logic and +lets the user implement methods acting only on the UI thread. +See the documentation of `AsyncHttpRequest` for more information. + +# Metadata annotations The _app.nit_ framework defines three annotations to customize the application package. @@ -142,7 +149,7 @@ The _app.nit_ framework defines three annotations to customize the application p The special function `git_revision` will use the prefix of the hash of the latest git commit. By default, the version is 0.1. -### Usage Example +## Usage Example ~~~ module my_module is @@ -152,33 +159,38 @@ module my_module is end ~~~ -## Compiling and Packaging an Application +# Compiling and Packaging an Application The Nit compiler detects the target platform from the importations and generates the appropriate application format and package. Applications using only the portable services of _app.nit_ require some special care at compilation. Such an application, let's say `calculator.nit`, does not depend on a specific platform and use the portable UI. -The target platform must be specifed to the compiler for it to produce the correct application package. +The target platform must be specified to the compiler for it to produce the correct application package. There is two main ways to achieve this goal: -* The the mixin option (`-m path`) loads an additionnal module before compiling. +* The mixin option (`-m module`) imports an additional module before compiling. It can be used to load platform specific implementations of the _app.nit_ portable UI. ~~~ # GNU/Linux version, using GTK - nitc calculator.nit -m NIT_DIR/lib/linux/ui.nit + nitc calculator.nit -m linux # Android version - nitc calculator.nit -m NIT_DIR/lib/android/ui/ + nitc calculator.nit -m android + + # iOS version + nitc calculator.nit -m ios ~~~ * A common alternative for larger projects is to use platform specific modules. - Continuing with the `calculator.nit` example, it can be accompagnied by the module `calculator_linux.nit`. - This module imports both `calculator` and `linux::ui`, and can also use other GNU/Linux specific code. + Continuing with the calculator example, it is adapted for Android by the module `android_calculator.nit`. + This module imports both `calculator` and `android`, it can then use Android specific code. ~~~ - module calculator_linux + module android_calculator import calculator - import linux::ui + import android + + # ... ~~~ diff --git a/lib/app/http_request.nit b/lib/app/http_request.nit index 9a5feca..694ba33 100644 --- a/lib/app/http_request.nit +++ b/lib/app/http_request.nit @@ -28,14 +28,21 @@ redef class App fun run_on_ui_thread(task: Task) is abstract end -# Thread executing an HTTP request and deserializing JSON asynchronously +# Thread executing an HTTP request asynchronously # -# This class defines four methods acting on the main/UI thread, -# they should be implemented as needed: -# * before -# * on_load -# * on_fail -# * after +# The request is sent to `rest_server_uri / rest_action`. +# +# If `deserialize_json`, the default behavior, the response is deserialized from JSON +# +# If `delay > 0.0`, sending the reqest is delayed by the given `delay` in seconds. +# It can be used to delay resending a request on error. +# +# Four callback methods act on the main/UI thread, +# they should be implemented as needed in subclasses: +# * `before` +# * `on_load` +# * `on_fail` +# * `after` class AsyncHttpRequest super Thread diff --git a/lib/app/package.ini b/lib/app/package.ini index d5c48c1..88a75fb 100644 --- a/lib/app/package.ini +++ b/lib/app/package.ini @@ -1,6 +1,6 @@ [package] name=app -tags=lib +tags=lib,mobile maintainer=Alexis Laferrière license=Apache-2.0 [upstream] diff --git a/lib/core/text/abstract_text.nit b/lib/core/text/abstract_text.nit index ed04967..8fefb24 100644 --- a/lib/core/text/abstract_text.nit +++ b/lib/core/text/abstract_text.nit @@ -1360,30 +1360,19 @@ abstract class String # Letters that follow a letter are lowercased # Letters that follow a non-letter are upcased. # + # If `keep_upper = true`, already uppercase letters are not lowercased. + # # SEE : `Char::is_letter` for the definition of letter. # # assert "jAVASCRIPT".capitalized == "Javascript" # assert "i am root".capitalized == "I Am Root" # assert "ab_c -ab0c ab\nc".capitalized == "Ab_C -Ab0C Ab\nC" - fun capitalized: SELFTYPE do + # assert "preserve my ACRONYMS".capitalized(keep_upper=true) == "Preserve My ACRONYMS" + fun capitalized(keep_upper: nullable Bool): SELFTYPE do if length == 0 then return self var buf = new Buffer.with_cap(length) - - var curr = chars[0].to_upper - var prev = curr - buf[0] = curr - - for i in [1 .. length[ do - prev = curr - curr = self[i] - if prev.is_letter then - buf[i] = curr.to_lower - else - buf[i] = curr.to_upper - end - end - + buf.capitalize(keep_upper=keep_upper, src=self) return buf.to_s end end @@ -1478,6 +1467,13 @@ abstract class Buffer # Letters that follow a letter are lowercased # Letters that follow a non-letter are upcased. # + # If `keep_upper = true`, uppercase letters are not lowercased. + # + # When `src` is specified, this method reads from `src` instead of `self` + # but it still writes the result to the beginning of `self`. + # This requires `self` to have the capacity to receive all of the + # capitalized content of `src`. + # # SEE: `Char::is_letter` for the definition of a letter. # # var b = new FlatBuffer.from("jAVAsCriPt") @@ -1489,16 +1485,32 @@ abstract class Buffer # b = new FlatBuffer.from("ab_c -ab0c ab\nc") # b.capitalize # assert b == "Ab_C -Ab0C Ab\nC" - fun capitalize do + # + # b = new FlatBuffer.from("12345") + # b.capitalize(src="foo") + # assert b == "Foo45" + # + # b = new FlatBuffer.from("preserve my ACRONYMS") + # b.capitalize(keep_upper=true) + # assert b == "Preserve My ACRONYMS" + fun capitalize(keep_upper: nullable Bool, src: nullable Text) do + src = src or else self + var length = src.length if length == 0 then return - var c = self[0].to_upper + keep_upper = keep_upper or else false + + var c = src[0].to_upper self[0] = c var prev = c for i in [1 .. length[ do prev = c - c = self[i] + c = src[i] if prev.is_letter then - self[i] = c.to_lower + if keep_upper then + self[i] = c + else + self[i] = c.to_lower + end else self[i] = c.to_upper end diff --git a/share/man/nit.md b/share/man/nit.md index 9503883..fcdec6c 100644 --- a/share/man/nit.md +++ b/share/man/nit.md @@ -6,6 +6,8 @@ nit - interprets and debugs Nit programs. nit [*options*] FILE [ARG]... +nit [*options*] - [ARG]... + nit [*options*] -e COMMAND [ARG]... # DESCRIPTION @@ -16,6 +18,9 @@ It takes the main module of a program as the first argument then the options and $ nit examples/hello_world.nit hello world +If `-` is used instead of a module, then the program is read from the standard input. +The whole program is read before its interpretation starts. + The Nit interpreter is usable and valid as a *shebang* interpreted directive. It is however recommended to use with `/usr/bin/env` because the location of the executable is not standardized. diff --git a/share/nitweb/directives/entity/card.html b/share/nitweb/directives/entity/card.html new file mode 100644 index 0000000..aa9ccb3 --- /dev/null +++ b/share/nitweb/directives/entity/card.html @@ -0,0 +1,11 @@ +
+
+ +
+
+
+ +
+ +
+
diff --git a/share/nitweb/directives/entity/list.html b/share/nitweb/directives/entity/list.html new file mode 100644 index 0000000..cc026a4 --- /dev/null +++ b/share/nitweb/directives/entity/list.html @@ -0,0 +1,19 @@ +
+

+ {{listTitle}} + +

+
+ +
+
+ +
+
+ diff --git a/share/nitweb/directives/entity/location.html b/share/nitweb/directives/entity/location.html new file mode 100644 index 0000000..7e817c6 --- /dev/null +++ b/share/nitweb/directives/entity/location.html @@ -0,0 +1,5 @@ + + {{mentity.location.file}} + :{{mentity.location.line_start}} + + diff --git a/share/nitweb/directives/entity/tag.html b/share/nitweb/directives/entity/tag.html new file mode 100644 index 0000000..7c69f1e --- /dev/null +++ b/share/nitweb/directives/entity/tag.html @@ -0,0 +1,5 @@ + diff --git a/share/nitweb/directives/ui-filter-button-vis.html b/share/nitweb/directives/ui-filter-button-vis.html new file mode 100644 index 0000000..8d4f8d4 --- /dev/null +++ b/share/nitweb/directives/ui-filter-button-vis.html @@ -0,0 +1,6 @@ + diff --git a/share/nitweb/directives/ui-filter-field.html b/share/nitweb/directives/ui-filter-field.html new file mode 100644 index 0000000..05c51e4 --- /dev/null +++ b/share/nitweb/directives/ui-filter-field.html @@ -0,0 +1,4 @@ +
+ + +
diff --git a/share/nitweb/directives/ui-filter-form.html b/share/nitweb/directives/ui-filter-form.html new file mode 100644 index 0000000..9782af8 --- /dev/null +++ b/share/nitweb/directives/ui-filter-form.html @@ -0,0 +1,6 @@ +
+ +
+ +
+ diff --git a/share/nitweb/directives/ui-filter-group-vis.html b/share/nitweb/directives/ui-filter-group-vis.html new file mode 100644 index 0000000..7fcd475 --- /dev/null +++ b/share/nitweb/directives/ui-filter-group-vis.html @@ -0,0 +1,14 @@ +
+ + + +
diff --git a/share/nitweb/index.html b/share/nitweb/index.html index 2a4bdde..17bcd23 100644 --- a/share/nitweb/index.html +++ b/share/nitweb/index.html @@ -20,6 +20,21 @@ +
+
+
+ + +
+
+
+ +
+
+
+
@@ -40,5 +55,6 @@ + diff --git a/share/nitweb/javascripts/entities.js b/share/nitweb/javascripts/entities.js index 4fe181b..255602c 100644 --- a/share/nitweb/javascripts/entities.js +++ b/share/nitweb/javascripts/entities.js @@ -16,7 +16,7 @@ (function() { angular - .module('entities', ['model']) + .module('entities', ['ui', 'model']) .controller('EntityCtrl', ['Model', '$routeParams', '$scope', function(Model, $routeParams, $scope) { Model.loadEntity($routeParams.id, @@ -56,4 +56,64 @@ templateUrl: '/directives/entity/signature.html' }; }) + + .directive('entityTag', function() { + return { + restrict: 'E', + scope: { + mentity: '=' + }, + replace: true, + templateUrl: '/directives/entity/tag.html' + }; + }) + + .directive('entityLocation', function() { + return { + restrict: 'E', + scope: { + mentity: '=' + }, + templateUrl: '/directives/entity/location.html' + }; + }) + + .directive('entityCard', function() { + return { + restrict: 'E', + scope: { + mentity: '=' + }, + replace: true, + templateUrl: '/directives/entity/card.html' + }; + }) + + .directive('entityList', function() { + return { + restrict: 'E', + scope: { + listEntities: '=', + listTitle: '@', + listObjectFilter: '=', + }, + templateUrl: '/directives/entity/list.html', + link: function ($scope, element, attrs) { + $scope.showFilters = false; + if(!$scope.listObjectFilter) { + $scope.listObjectFilter = {}; + } + if(!$scope.visibilityFilter) { + $scope.visibilityFilter = { + public: true, + protected: true, + private: false + }; + } + $scope.toggleFilters = function() { + $scope.showFilters = !$scope.showFilters; + }; + } + }; + }) })(); diff --git a/share/nitweb/javascripts/model.js b/share/nitweb/javascripts/model.js index b5e8bbf..7d5051b 100644 --- a/share/nitweb/javascripts/model.js +++ b/share/nitweb/javascripts/model.js @@ -26,8 +26,13 @@ $http.get(apiUrl + '/entity/' + id) .success(cb) .error(cbErr); - } + }, + search: function(q, n, cb, cbErr) { + $http.get(apiUrl + '/search?q=' + q + '&n=' + n) + .success(cb) + .error(cbErr); + } }; }]) })(); diff --git a/share/nitweb/javascripts/ui.js b/share/nitweb/javascripts/ui.js new file mode 100644 index 0000000..cd30d06 --- /dev/null +++ b/share/nitweb/javascripts/ui.js @@ -0,0 +1,168 @@ +/* + * Copyright 2016 Alexandre Terrasa . + * + * 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() { + angular + .module('ui', [ 'model' ]) + + .controller('SearchCtrl', ['Model', '$routeParams', '$scope', '$window', function(Model, $routeParams, $scope, $window) { + $scope.query = ''; + + $scope.reset = function() { + $scope.activeItem = 0; + $scope.results = []; + } + + $scope.update = function(e) { + if(e.keyCode == 38) { + $scope.selectUp(); + } else if(e.keyCode == 40) { + $scope.selectDown(); + } else if(e.keyCode == 27) { + $scope.selectEscape(); + } else if(e.keyCode == 13) { + $scope.selectEnter(); + } + } + + $scope.selectUp = function() { + if($scope.activeItem > 0) { + $scope.activeItem -= 1; + } + } + + $scope.selectDown = function() { + if($scope.activeItem < $scope.results.length - 1) { + $scope.activeItem += 1; + } + } + + $scope.selectEnter = function() { + $window.location.href = $scope.results[$scope.activeItem].web_url; + $scope.reset(); + } + + $scope.selectEscape = function() { + $scope.reset(); + } + + $scope.search = function() { + if(!$scope.query) { + $scope.reset(); + return; + } + Model.search($scope.query, 10, + function(data) { + $scope.reset(); + $scope.results = data; + }, function(err) { + $scope.reset(); + $scope.error = err; + }); + } + + $scope.reset(); + }]) + + .directive('uiFilters', function() { + return { + restrict: 'E', + scope: { + property: '=', + classesOn: '=', + classesOff: '=' + }, + replace: true, + templateUrl: '/directives/ui-filter-button-vis.html', + link: function ($scope, element, attrs) { + $scope.toggle = function() { + $scope.property = !$scope.property; + } + } + }; + }) + + .filter('visibility', function() { + return function(input, visibilityFilter) { + var res = []; + input.forEach(function(entry) { + if(visibilityFilter.public == false && entry.visibility == "public") { + return; + } + if(visibilityFilter.protected == false && entry.visibility == "protected") { + return; + } + if(visibilityFilter.private == false && entry.visibility == "private") { + return; + } + res.push(entry); + }); + return res; + }; + }) + + .directive('uiFilterForm', function() { + return { + restrict: 'E', + scope: { + searchFilter: '=', + visibilityFilter: '=' + }, + replace: true, + templateUrl: '/directives/ui-filter-form.html' + }; + }) + + .directive('uiFilterField', function() { + return { + restrict: 'E', + scope: { + property: '=' + }, + replace: true, + templateUrl: '/directives/ui-filter-field.html' + }; + }) + + .directive('uiFilterGroupVis', function() { + return { + restrict: 'E', + scope: { + property: '=' + }, + replace: true, + templateUrl: '/directives/ui-filter-group-vis.html' + }; + }) + + .directive('uiFilterButtonVis', function() { + return { + restrict: 'E', + scope: { + property: '=', + classesOn: '=', + classesOff: '=' + }, + replace: true, + templateUrl: '/directives/ui-filter-button-vis.html', + link: function ($scope, element, attrs) { + $scope.toggle = function() { + $scope.property = !$scope.property; + } + } + }; + }) +})(); diff --git a/share/nitweb/stylesheets/nitweb.css b/share/nitweb/stylesheets/nitweb.css index 2baad72..2d38457 100644 --- a/share/nitweb/stylesheets/nitweb.css +++ b/share/nitweb/stylesheets/nitweb.css @@ -48,11 +48,43 @@ a { width: 10000px; } -.card-body { +.card-body, .card-right, .card-left { display: table-cell; vertical-align: top; } +.card-left, .card>.pull-left { + padding: 15px; + padding-right: 0px; +} +.card-right, .card>.pull-right { + padding: 15px; + padding-left: 0px; +} + +.card-list { + margin-top: 10px; +} + +.card-list > .card:first-child { + border-top: 1px solid #ccc; +} + +.card-list > .card { + margin-top: 0; + border-top: none; +} + +/* ui */ + +entity-list .btn-filter { + visibility: hidden; +} + +entity-list:hover .btn-filter { + visibility: visible; +} + /* doc */ .nitdoc .synopsys { @@ -90,6 +122,89 @@ a { background-color: #ff9c0f; } +/* forms */ + +.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; +} + +/* search */ + +.search-input { + width: 100%; +} + +.search-results { + position: absolute; + right: 0; +} + +.search-results .card.active { + background: #eee; + border-color: #eee; +} + +/* 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; +} /* * Code Highlighting */ diff --git a/share/nitweb/views/class.html b/share/nitweb/views/class.html index ac7c432..52bd1e9 100644 --- a/share/nitweb/views/class.html +++ b/share/nitweb/views/class.html @@ -15,6 +15,22 @@
+ + + + + + + +
diff --git a/share/nitweb/views/group.html b/share/nitweb/views/group.html index ef07e48..ca19974 100644 --- a/share/nitweb/views/group.html +++ b/share/nitweb/views/group.html @@ -15,6 +15,15 @@
+ + + + + +
diff --git a/share/nitweb/views/module.html b/share/nitweb/views/module.html index 23f6d52..79d905d 100644 --- a/share/nitweb/views/module.html +++ b/share/nitweb/views/module.html @@ -15,6 +15,16 @@
+ + + + + + +
diff --git a/share/nitweb/views/package.html b/share/nitweb/views/package.html index 4abcbc7..ee19aa5 100644 --- a/share/nitweb/views/package.html +++ b/share/nitweb/views/package.html @@ -14,6 +14,9 @@
+ +
diff --git a/src/nit.nit b/src/nit.nit index 9410b4d..a28bb6f 100644 --- a/src/nit.nit +++ b/src/nit.nit @@ -64,6 +64,12 @@ if opt_eval.value then modelbuilder.load_rt_module(parent, amodule, "-") mmodules = [amodule.mmodule.as(not null)] +else if progname == "-" then + var content = stdin.read_all + var amodule = toolcontext.parse_module(content) + toolcontext.check_errors + modelbuilder.load_rt_module(null, amodule, "-") + mmodules = [amodule.mmodule.as(not null)] else mmodules = modelbuilder.parse([progname]) end diff --git a/src/web/model_api.nit b/src/web/model_api.nit index 85a2360..9bbe5ad 100644 --- a/src/web/model_api.nit +++ b/src/web/model_api.nit @@ -77,26 +77,6 @@ class APIRouter end end -# Search mentities from a query string. -# -# Example: `GET /search?q=Arr` -class APISearch - super APIHandler - - redef fun get(req, res) do - var q = req.string_arg("q") - if q == null then - res.error 400 - return - end - var arr = new JsonArray - for mentity in view.mentities do - if mentity.name.has_prefix(q) then arr.add mentity - end - res.json arr - end -end - # List all mentities. # # MEntities can be filtered on their kind using the `k` parameter. @@ -144,9 +124,24 @@ class APIList redef fun get(req, res) do var mentities = list_mentities(req) mentities = limit_mentities(req, mentities) - var arr = new JsonArray - for mentity in mentities do arr.add mentity - res.json arr + res.json new JsonArray.from(mentities) + end +end + +# Search mentities from a query string. +# +# Example: `GET /search?q=Arr` +class APISearch + super APIList + + redef fun list_mentities(req) do + var q = req.string_arg("q") + var mentities = new Array[MEntity] + if q == null then return mentities + for mentity in view.mentities do + if mentity.name.has_prefix(q) then mentities.add mentity + end + return mentities end end @@ -167,9 +162,7 @@ class APIRandom var mentities = list_mentities(req) mentities = limit_mentities(req, mentities) mentities = randomize_mentities(req, mentities) - var arr = new JsonArray - for mentity in mentities do arr.add mentity - res.json arr + res.json new JsonArray.from(mentities) end end diff --git a/tests/nit.args b/tests/nit.args index cbaafd9..ba1a276 100644 --- a/tests/nit.args +++ b/tests/nit.args @@ -5,3 +5,4 @@ base_simple3.nit -e 'print "hello world"' -n -e 'print line' test_prog/README.md test_prog/test_prog.nit test_ffi_c_interpreter.nit +- diff --git a/tests/nit_args8.inputs b/tests/nit_args8.inputs new file mode 100644 index 0000000..c878eeb --- /dev/null +++ b/tests/nit_args8.inputs @@ -0,0 +1 @@ +print "Hello!" diff --git a/tests/niti.skip b/tests/niti.skip index 4634f40..d93ce27 100644 --- a/tests/niti.skip +++ b/tests/niti.skip @@ -4,6 +4,7 @@ nit_args3 nit_args4 nit_args5 nit_args6 +nit_args8 nitvm_args1 nitvm_args3 nitc_args1 diff --git a/tests/nitvm.skip b/tests/nitvm.skip index a68b4cc..b8ae2ec 100644 --- a/tests/nitvm.skip +++ b/tests/nitvm.skip @@ -4,6 +4,7 @@ nit_args3 nit_args4 nit_args5 nit_args6 +nit_args8 nitvm_args1 nitvm_args3 nitc_args1 diff --git a/tests/sav/nit_args8.res b/tests/sav/nit_args8.res new file mode 100644 index 0000000..10ddd6d --- /dev/null +++ b/tests/sav/nit_args8.res @@ -0,0 +1 @@ +Hello!