-all:
- mkdir -p bin/
+NITC=../../bin/nitc
+NITLS=../../bin/nitls
- ../../bin/nitc --dir bin/ src/calculator_gtk.nit src/calculator_test.nit
+all: bin/calculator bin/calculator.apk bin/test
-android:
- mkdir -p bin/ res/
+bin/calculator: $(shell ${NITLS} -M src/calculator.nit ../../lib/linux/ui.nit) ${NITC}
+ mkdir -p bin
+ ${NITC} -o $@ src/calculator.nit -m ../../lib/linux/ui.nit
+
+bin/calculator.apk: $(shell ${NITLS} -M src/calculator.nit ../../lib/android/ui/) ${NITC} ../../contrib/inkscape_tools/bin/svg_to_icons
+ mkdir -p bin res
../../contrib/inkscape_tools/bin/svg_to_icons art/icon.svg --android --out res/
- ../../bin/nitc -o bin/calculator.apk src/calculator_android.nit
+ ${NITC} -o $@ src/calculator.nit -m ../../lib/android/ui/
+
+../../contrib/inkscape_tools/bin/svg_to_icons:
+ make -C ../../contrib/inkscape_tools/
-android-install: android
+android-install: bin/calculator.apk
adb install -r bin/calculator.apk
+
+bin/test: $(shell ${NITLS} -M src/calculator_test.nit) ${NITC}
+ mkdir -p bin
+ ${NITC} -o $@ src/calculator_test.nit
+
+check: bin/test
+ bin/test
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Portable calculator UI
+module calculator is
+app_name "app.nit Calc."
+ app_version(0, 1, git_revision)
+ app_namespace "org.nitlanguage.calculator"
+
+ # Lock in portrait mode
+ android_manifest_activity """android:screenOrientation="portrait""""
+ android_api_target 15
+end
+
+import app::ui
+import app::data_store
+import android::aware
+
+import calculator_logic
+
+redef class App
+ redef fun on_create
+ do
+ # Create the main window
+ window = new CalculatorWindow
+ super
+ end
+end
+
+# The main (and only) window of this calculator
+class CalculatorWindow
+ super Window
+
+ # Calculator context, our business logic
+ private var context = new CalculatorContext
+
+ # Main window layout
+ private var layout = new VerticalLayout(parent=self)
+
+ # Main display, at the top of the screen
+ private var display = new TextInput(parent=layout)
+
+ # Maps operators as `String` to their `Button`
+ private var buttons = new HashMap[String, Button]
+
+ init
+ do
+ # All the button labels, row by row
+ var rows = [["7", "8", "9", "+"],
+ ["4", "5", "6", "-"],
+ ["1", "2", "3", "*"],
+ ["0", ".", "C", "/"],
+ ["="]]
+
+ for row in rows do
+ var row_layout = new HorizontalLayout(parent=layout)
+
+ for op in row do
+ var but = new Button(parent=row_layout, text=op)
+ but.observers.add self
+ buttons[op] = but
+ end
+ end
+ end
+
+ redef fun on_event(event)
+ do
+ if event isa ButtonPressEvent then
+
+ var op = event.sender.text
+ if op == "." then
+ event.sender.enabled = false
+ context.switch_to_decimals
+ else if op.is_numeric then
+ var n = op.to_i
+ context.push_digit n
+ else
+ buttons["."].enabled = true
+ context.push_op op.chars.first
+ end
+
+ display.text = context.display_text
+ end
+ end
+
+ redef fun on_save_state
+ do
+ app.data_store["context"] = context.to_json
+ super
+ end
+
+ redef fun on_restore_state
+ do
+ super
+
+ var save = app.data_store["context"]
+ if save == null then return
+ assert save isa String
+
+ self.context = new CalculatorContext.from_json(save)
+ display.text = context.display_text
+ end
+end
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
-#
-# 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.
-
-# Android calculator application
-module calculator_android is
- app_name "app.nit Calc."
- app_version(0, 1, git_revision)
- app_namespace "org.nitlanguage.calculator"
-
- # Lock in portrait mode
- android_manifest_activity """android:screenOrientation="portrait""""
-end
-
-# FIXME the next line should import `android` only when it uses nit_activity
-import android::log
-import android::ui
-
-import calculator_logic
-
-redef class Activity
- super EventCatcher
-
- private var context = new CalculatorContext
-
- # The main display, at the top of the screen
- private var display: EditText
-
- # Maps operators as `String` to their `Button`
- private var op2but = new HashMap[String, Button]
-
- # Has this window been initialized?
- private var inited = false
-
- redef fun on_start
- do
- super
-
- if inited then return
- inited = true
-
- # Setup UI
- var layout = new NativeLinearLayout(native)
- layout.set_vertical
-
- # Display screen
- var display = new EditText
- layout.add_view_with_weight(display.native, 1.0)
- display.text_size = 36.0
- self.display = display
-
- # Buttons; numbers and operators
- var ops = [["7", "8", "9", "+"],
- ["4", "5", "6", "-"],
- ["1", "2", "3", "*"],
- ["0", ".", "C", "/"],
- ["="]]
-
- for line in ops do
- var buts_layout = new NativeLinearLayout(native)
- buts_layout.set_horizontal
- layout.add_view_with_weight(buts_layout, 1.0)
-
- for op in line do
- var but = new Button
- but.event_catcher = self
- but.text = op
- but.text_size = 40
- buts_layout.add_view_with_weight(but.native, 1.0)
- op2but[op] = but
- end
- end
-
- native.content_view = layout
- end
-
- redef fun on_save_instance_state(state)
- do
- super
-
- var nity = new Bundle.from(state)
- nity["context"] = context.to_json
- end
-
- redef fun on_restore_instance_state(state)
- do
- super
-
- var nity = new Bundle.from(state)
- if not nity.has("context") then return
-
- var json = nity.string("context")
- if json == null then return
-
- context = new CalculatorContext.from_json(json)
- end
-
- redef fun on_resume
- do
- display.text = context.display_text
- end
-
- redef fun catch_event(event)
- do
- if event isa ClickEvent then
- var sender = event.sender
- var op = sender.text
-
- if op == "." then
- sender.enabled = false
- context.switch_to_decimals
- else if op.is_numeric then
- var n = op.to_i
- context.push_digit n
- else
- op2but["."].enabled = true
- context.push_op op.chars.first
- end
-
- display.text = context.display_text
- end
- end
-end
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2013-2014 Alexis Laferrière <alexis.laf@xymus.net>
-#
-# 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.
-
-# GTK calculator
-module calculator_gtk
-
-import calculator_logic
-
-import gtk
-
-# GTK calculator UI
-class CalculatorGui
- super GtkCallable
-
- private var win: GtkWindow is noinit
- private var container: GtkGrid is noinit
-
- private var lbl_disp: GtkLabel is noinit
- private var but_eq: GtkButton is noinit
- private var but_dot: GtkButton is noinit
-
- private var context = new CalculatorContext
-
- redef fun signal(sender, op)
- do
- if op isa Char then # is an operation
- if op == '.' then
- but_dot.sensitive = false
- context.switch_to_decimals
- else
- but_dot.sensitive = true
- context.push_op op
- end
- else if op isa Int then # is a number
- context.push_digit op
- end
-
- lbl_disp.text = context.display_text
- end
-
- init
- do
- init_gtk
-
- win = new GtkWindow(new GtkWindowType.toplevel)
- win.connect_destroy_signal_to_quit
-
- container = new GtkGrid
- win.add container
-
- lbl_disp = new GtkLabel("_")
- container.attach(lbl_disp, 0, 0, 5, 1)
-
- # Digits
- for n in [0..9] do
- var but = new GtkButton.with_label(n.to_s)
- but.request_size(64, 64)
- but.signal_connect("clicked", self, n)
- if n == 0 then
- container.attach(but, 0, 4, 1, 1)
- else container.attach(but, (n-1)%3, 3-(n-1)/3, 1, 1)
- end
-
- # Operators
- var r = 1
- for op in ['+', '-', '*', '/'] do
- var but = new GtkButton.with_label(op.to_s)
- but.request_size(64, 64)
- but.signal_connect("clicked", self, op)
- container.attach(but, 3, r, 1, 1)
- r+=1
- end
-
- # =
- but_eq = new GtkButton.with_label("=")
- but_eq.request_size(64, 64)
- but_eq.signal_connect("clicked", self, '=')
- container.attach(but_eq, 4, 3, 1, 2)
-
- # .
- but_dot = new GtkButton.with_label(".")
- but_dot.request_size(64, 64)
- but_dot.signal_connect("clicked", self, '.')
- container.attach(but_dot, 1, 4, 1, 1)
-
- # C
- var but_c = new GtkButton.with_label("C")
- but_c.request_size(64, 64)
- but_c.signal_connect("clicked", self, 'C')
- container.attach(but_c, 2, 4, 1, 1)
-
- win.show_all
- end
-end
-
-# Do not show GUI in when testing
-if "NIT_TESTING".environ == "true" then exit 0
-
-var app = new CalculatorGui
-run_gtk
context.push_digit( 2 )
context.push_op( '=' )
var r = context.result
-assert r == "30.00" else print r or else "-"
+assert r == 30 else print r or else "-"
context = new CalculatorContext
context.push_digit( 1 )
context.push_digit( 3 )
context.push_op( '=' )
r = context.result
-assert r == "42.30" else print r or else "-"
+assert r == 42.3 else print r or else "-"
context.push_op( '+' )
context.push_digit( 1 )
context.push_digit( 1 )
context.push_op( '=' )
r = context.result
-assert r == "53.30" else print r or else "-"
+assert r == 53.3 else print r or else "-"
context = new CalculatorContext
context.push_digit( 4 )
context.push_digit( 3 )
context.push_op( '=' )
r = context.result
-assert r == "14.10" else print r or else "-"
+assert r == 14.1 else print r or else "-"
#test multiple decimals
context = new CalculatorContext
context.push_digit( 1 )
context.push_op( '=' )
r = context.result
-assert r == "51.123" else print r or else "-"
+assert r == 51.123 else print r or else "-"
#test 'C' button
context = new CalculatorContext
context.push_op( '=' )
context.push_op( 'C' )
r = context.result
-assert r == "0.0" else print r or else "-"
+assert r == null else print r
# Configure your Android application
-The `app.nit` framework and this project offers some services to
-customized the generated Android application.
+The _app.nit_ framework and this project offers some services to
+customize the generated Android application.
-## Module annotations
+## Annotations
-* `app_version` specifies the version of the generated APK file.
-It takes 3 arguments: the major, minor and revision version numbers.
-The special function `git_revision` will use the prefix of the hash of the
-latest git commit. The default version is 1.0.
+* All _app.nit_ annotations are applied to Android projects:
+ `app_name`, `app_namespace` and `app_version`.
- Example: `app_version(1, 0, git_revision)`
-
-* `app_name` takes a single argument, the visible name of the Android
-application. By default, the compiler would use the name of the target
-module. This name will be used as the name of the main activity and
-as the launcher name.
-
- Example: `app_name "My App"`
-
-* `app_namespace` specifies the package used by the generated Java
-classes and the APK file. Once the application is published, this
-value should not be changed. By default, the compiler will use
-the package `org.nitlanguage.{module_name}`.
+ See: `../app/README.md`
* Custom information can be added to the Android manifest file
using the annotations `android_manifest`, `android_manifest_application`
android_manifest """<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>"""
~~~
-* The API version target can be specified with `min_api_version`,
-`max_api_version` and `target_api_version`. These take a single
+* The API version target can be specified with `android_api_min`,
+`android_api_max` and `android_api_target`. These take a single
integer as argument. They are applied in the Android manifest as
`minSdkVesion`, `targetSdkVersion` and `maxSdkVersion`.
mkdir -p bin/ res/
../../../contrib/inkscape_tools/bin/svg_to_icons art/icon.svg --android --out res/
../../../bin/nitc --dir bin/ src/ui_test.nit
- adb install -r bin/ui_test.apk
install: android
- adb install -r bin/ui.apk
+ adb install -r bin/ui_test.apk
clean:
rm -rf bin
app_version(0, 1, git_revision)
app_namespace "org.nitlanguage.ui_test"
android_manifest_activity """android:theme="@android:style/Theme.Light""""
+ android_api_target 15
end
-import android
import android::ui
import android::toast
import android::notification
redef class App
-
- var but_notif: Button
- var but_toast: Button
-
- var notif: nullable Notification = null
-
- var inited = false
- redef fun init_window
+ redef fun on_create
do
+ self.window = new Window
super
+ end
+end
- if inited then return
- inited = true
+redef class Window
- # Setup UI
- var context = native_activity
- var layout = new NativeLinearLayout(context)
- layout.set_vertical
+ private var layout = new VerticalLayout(parent=self)
- but_notif = new Button
- but_notif.text = "Show Notification"
- layout.add_view but_notif.native
+ private var but_notif = new Button(parent=layout, text="Show Notification")
+ private var but_toast = new Button(parent=layout, text="Show Toast")
- but_toast = new Button
- but_toast.text = "Show Toast"
- layout.add_view but_toast.native
+ private var notif: nullable Notification = null
- context.content_view = layout
+ init
+ do
+ but_notif.observers.add self
+ but_toast.observers.add self
end
+ # Action when pressing `but_notif`
fun act_notif
do
var notif = self.notif
end
end
+ # Action when pressing `but_toast`
fun act_toast
do
- toast("Sample toast from app.nit at {get_time}", false)
+ app.toast("Sample toast from app.nit at {get_time}", false)
end
- redef fun catch_event(event)
+ redef fun on_event(event)
do
- if event isa ClickEvent then
+ print "on_event {event}"
+ if event isa ButtonPressEvent then
var sender = event.sender
if sender == but_notif then
act_notif
# This file is part of NIT (http://www.nitlanguage.org).
#
-# Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
-#
# 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
# Views and services to use the Android native user interface
module ui
+# Implementation note:
+#
+# We cannot rely on `Activity::on_restore_instance_state` to implement
+# `on_restore_state` is it only invoked if there is a bundled state,
+# and we don't use the Android bundled state.
+
import native_ui
+import log
+import nit_activity
+
+import app::ui
+private import data_store
+
+redef class Control
+ # The Android element used to implement `self`
+ fun native: NATIVE is abstract
-# An event from the `app.nit` framework
-interface AppEvent
- # Reaction to this event
- fun react do end
+ # Type of `native`
+ type NATIVE: JavaObject
end
-# A control click event
-class ClickEvent
- super AppEvent
+redef class Window
+ redef var native = app.native_activity
- # Sender of this event
- var sender: Button
+ redef type NATIVE: NativeActivity
- redef fun react do sender.click self
-end
+ redef fun add(item)
+ do
+ super
-# Receiver of events not handled directly by the sender
-interface EventCatcher
- fun catch_event(event: AppEvent) do end
+ # FIXME abstract the Android restriction where `content_view` must be a layout
+ assert item isa Layout
+ native.content_view = item.native
+ end
end
-redef class App
- super EventCatcher
-end
+redef class View
+ redef type NATIVE: NativeView
-# An `Object` that raises events
-abstract class Eventful
- var event_catcher: EventCatcher = app is lazy, writable
+ redef fun enabled=(enabled) do native.enabled = enabled or else true
+ redef fun enabled do return native.enabled
end
-#
-## Nity classes and services
-#
+redef class Layout
+ redef type NATIVE: NativeViewGroup
-# An Android control with text
-abstract class TextView
- super Finalizable
- super Eventful
-
- # Native Java variant to this Nity class
- type NATIVE: NativeTextView
+ redef fun add(item)
+ do
+ super
- # The native Java object encapsulated by `self`
- var native: NATIVE is noinit
+ assert item isa View
- # Get the text of this view
- fun text: String
- do
- var jstr = native.text
- var str = jstr.to_s
- jstr.delete_local_ref
- return str
+ # FIXME abstract the use either homogeneous or weight to balance views size in a layout
+ native.add_view_with_weight(item.native, 1.0)
end
+end
- # Set the text of this view
- fun text=(value: Text)
- do
- var jstr = value.to_s.to_java_string
- native.text = jstr
- jstr.delete_local_ref
+redef class HorizontalLayout
+ redef var native do
+ var layout = new NativeLinearLayout(app.native_activity)
+ layout.set_horizontal
+ return layout
end
+end
- # Get whether this view is enabled or not
- fun enabled: Bool do return native.enabled
-
- # Set if this view is enabled
- fun enabled=(val: Bool) do native.enabled = val
-
- # Set the size of the text in this view at `dpi`
- fun text_size=(dpi: Numeric) do native.text_size = dpi.to_f
-
- private var finalized = false
- redef fun finalize
- do
- if not finalized then
- native.delete_global_ref
- finalized = true
- end
+redef class VerticalLayout
+ redef var native do
+ var layout = new NativeLinearLayout(app.native_activity)
+ layout.set_vertical
+ return layout
end
end
-# An Android button
-class Button
- super TextView
+redef class TextView
+ redef type NATIVE: NativeTextView
- redef type NATIVE: NativeButton
-
- init
- do
- var native = new NativeButton(app.native_activity, self)
- self.native = native.new_global_ref
+ redef fun text do return native.text.to_s
+ redef fun text=(value) do
+ if value == null then value = ""
+ native.text = value.to_java_string
end
- # Click event
- #
- # By default, this method calls `app.catch_event`. It can be specialized
- # with custom behavior or the receiver of `catch_event` can be changed
- # with `event_catcher=`.
- fun click(event: AppEvent) do event_catcher.catch_event(event)
+ # Size of the text
+ fun text_size: Float do return native.text_size
- private fun click_from_native do click(new ClickEvent(self))
+ # Size of the text
+ fun text_size=(text_size: nullable Float) do
+ if text_size != null then native.text_size = text_size
+ end
end
-# An Android editable text field
-class EditText
- super TextView
-
+redef class TextInput
redef type NATIVE: NativeEditText
+ redef var native = (new NativeEditText(app.native_activity)).new_global_ref
+end
- init
- do
- var native = new NativeEditText(app.activities.first.native)
- self.native = native.new_global_ref
- end
+redef class Button
+ super Finalizable
+
+ redef type NATIVE: NativeButton
+ redef var native = (new NativeButton(app.native_activity, self)).new_global_ref
+
+ private fun on_click do notify_observers new ButtonPressEvent(self)
+
+ redef fun finalize do native.delete_global_ref
end
redef class NativeButton
- new (context: NativeActivity, sender_object: Object)
- import Button.click_from_native in "Java" `{
+ private new (context: NativeActivity, sender_object: Button)
+ import Button.on_click in "Java" `{
final int final_sender_object = sender_object;
return new android.widget.Button(context){
@Override
public boolean onTouchEvent(android.view.MotionEvent event) {
if(event.getAction() == android.view.MotionEvent.ACTION_DOWN) {
- Button_click_from_native(final_sender_object);
+ Button_on_click(final_sender_object);
return true;
}
return false;
--- /dev/null
+_app.nit_ is a framework to create cross-platform applications
+
+The framework provides services to manage common needs of modern mobile applications:
+
+* Life-cycle
+* User interface
+* Persistence
+* 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
+
+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:
+
+1. `on_create`: The application is being created.
+ You should build the UI at this time.
+
+2. `on_start`: The app is starting or restarting, background activities may
+
+3. `on_resume`: The app enters the active state, it is in the foreground.
+
+4. `on_pause`: The app leaves the active state and the foreground.
+ It may still be visible in the background.
+ It may then go back to `on_resume` or `on_stop`.
+
+5. `on_stop`: The app is completely hidden.
+ It may then be destroyed (`on_destroy`) or go back to `on_start`.
+
+6. `on_destroy`: The app is being destroyed.
+
+Life-cycle events related to saving and restoring the application state are provided by two special callback methods:
+
+* `on_save_state`: The app may be destroyed soon, save its state for a future `on_restore_state`.
+ More on how it can be done in the `app::data_store` section.
+
+* `on_restore_state`: The app is launching, restore its state from a previous `on_save_state`.
+
+These events are synchronized to the native platforms applications
+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
+
+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`.
+
+Here is a subset of the most useful controls and views:
+
+* The classic pushable `Button` with text (usually rectangular).
+
+* `TextInput` is a field for the user to enter text.
+
+* `HorizontalLayout` and `VerticalLayout` organize other controls in order.
+
+Each control is notified of input events by callbacks to `on_event`.
+All controls have observers that are also notified of the events.
+So there is two ways to customize the behavior on a given event:
+
+* Create a subclass of the wanted `Control`, let's say `Button`, and specialize `on_event`.
+
+* Add an observer to a `Button` instance, and implement `on_event` in the observer.
+
+### 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
+
+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.
+
+_TODO complete description and add concrete examples_
+
+## 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:
+
+* `DataStore::[]=` saves and associates any serializable instances to a `String` key.
+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
+
+~~~
+import app::data_store
+
+redef class App
+ var user_name: String
+
+ redef fun on_save_state
+ do
+ app.data_store["first run"] = false
+ app.data_store["user name"] = user_name
+
+ super # call `on_save_state` on all attached instances of `AppComponent`
+ end
+
+ redef fun on_restore_state
+ do
+ var first_run = app.data_store["first run"]
+ if first_run != true then
+ print "It's the first run of this application"
+ end
+
+ var user_name = app.data_store["user name"]
+ if user_name isa String then
+ self.user_name = user_name
+ else self.user_name = "Undefined"
+
+ super
+ end
+end
+~~~
+
+## Metadata annotations
+
+The _app.nit_ framework defines three annotations to customize the application package.
+
+* `app_name` takes a single argument, the visible name of the application.
+ This name is used for launchers and window title.
+ By default, the name of the target module.
+
+* `app_namespace` specifies the full namespace (or package name) of the application package.
+ This value usually identify the application uniquely on application stores.
+ It should not change once the application has benn published.
+ By default, the namespace is `org.nitlanguage.{module_name}`.
+
+* `app_version` specifies the version of the application package.
+ This annotation expects at least one argument, usually we use three version numbers:
+ the major, minor and revision.
+ 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
+
+~~~
+module my_module is
+ app_name "My App"
+ app_namespace "org.example.my_app"
+ app_version(1, 0, git_revision)
+end
+~~~
+
+## 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.
+There is two main ways to achieve this goal:
+
+* The the mixin option (`-m path`) loads an additionnal 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
+
+ # Android version
+ nitc calculator.nit -m NIT_DIR/lib/android/ui/
+ ~~~
+
+* 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.
+
+ ~~~
+ module calculator_linux
+
+ import calculator
+ import linux::ui
+ ~~~
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Portable UI API for the _app.nit_ framework
+module ui
+
+import app_base
+
+redef class App
+ super AppComponent
+
+ # The current `Window` of this activity
+ #
+ # This attribute must be set by refinements of `App`.
+ var window: Window is writable
+
+ redef fun on_create do window.on_create
+
+ redef fun on_start do window.on_start
+
+ redef fun on_resume do window.on_resume
+
+ redef fun on_pause do window.on_pause
+
+ redef fun on_stop do window.on_stop
+
+ redef fun on_destroy do window.on_destroy
+
+ redef fun on_restore_state do window.on_restore_state
+
+ redef fun on_save_state do window.on_save_state
+end
+
+# An event created by an `AppComponent` and sent to `AppObserver`s
+interface AppEvent
+end
+
+# Observer of `AppEvent`s raised by `AppComponent`s
+interface AppObserver
+ # Notification of `event` raised by `sender`
+ #
+ # To be implemented in subclasses as needed.
+ fun on_event(event: AppEvent) do end
+end
+
+redef class AppComponent
+ super AppObserver
+
+ # All `AppObserver` notified of events raised by `self`
+ #
+ # By default, only `self` is an observer.
+ # Any other `AppObserver` can be added to this collection.
+ var observers = new HashSet[AppObserver].from([self: AppObserver])
+
+ # Propagate `event` to all `observers` by calling `AppObserver::on_event`
+ fun notify_observers(event: AppEvent)
+ do
+ for observer in observers do
+ observer.on_event(event)
+ end
+ end
+end
+
+# A control implementing the UI
+class Control
+ super AppComponent
+
+ # Direct parent `Control` in the control tree
+ #
+ # If `null` then `self` is at the root of the tree, or not yet attached.
+ var parent: nullable CompositeControl = null is private writable(set_parent)
+
+ # Direct parent `Control` in the control tree
+ #
+ # Setting `parent` calls `remove` on the old parent and `add` on the new one.
+ fun parent=(parent: nullable CompositeControl)
+ is autoinit do
+ var old_parent = self.parent
+ if old_parent != null and old_parent != parent then
+ old_parent.remove self
+ end
+
+ if parent != null then parent.add self
+
+ set_parent parent
+ end
+end
+
+# A `Control` grouping other controls
+class CompositeControl
+ super Control
+
+ private var items = new HashSet[Control]
+
+ # Add `item` as a child of `self`
+ protected fun add(item: Control) do items.add item
+
+ # Remove `item` from `self`
+ protected fun remove(item: Control) do if has(item) then items.remove item
+
+ # Is `item` in `self`?
+ protected fun has(item: Control): Bool do return items.has(item)
+
+ redef fun on_create do for i in items do i.on_create
+
+ redef fun on_start do for i in items do i.on_start
+
+ redef fun on_resume do for i in items do i.on_resume
+
+ redef fun on_pause do for i in items do i.on_pause
+
+ redef fun on_stop do for i in items do i.on_stop
+
+ redef fun on_destroy do for i in items do i.on_destroy
+
+ redef fun on_restore_state do for i in items do i.on_restore_state
+
+ redef fun on_save_state do for i in items do i.on_save_state
+end
+
+# A window, root of the `Control` tree
+class Window
+ super CompositeControl
+end
+
+# A viewable `Control`
+abstract class View
+ super Control
+
+ # Is this control enabled so the user can interact with it?
+ #
+ # By default, or if set to `null`, the control is enabled.
+ var enabled: nullable Bool is writable #, abstract FIXME with #1311
+end
+
+# A control with some `text`
+abstract class TextView
+ super View
+
+ # Main `Text` of this control
+ #
+ # By default, or if set to `null`, no text is shown.
+ var text: nullable Text is writable #, abstract FIXME with #1311
+end
+
+# A control for the user to enter custom `text`
+class TextInput
+ super TextView
+end
+
+# A pushable button, raises `ButtonPressEvent`
+class Button
+ super TextView
+end
+
+# A `Button` press event
+class ButtonPressEvent
+ super AppEvent
+
+ # The `Button` that raised this event
+ var sender: Button
+end
+
+# A layout to visually organize `Control`s
+abstract class Layout
+ super View
+ super CompositeControl
+end
+
+# An horizontal linear organization
+class HorizontalLayout
+ super Layout
+end
+
+# A vertical linear organization
+class VerticalLayout
+ super Layout
+end
--- /dev/null
+# This file is part of NIT (http://www.nitlanguage.org).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Add reading and writing binary services
+#
+# ~~~
+# var w = new FileWriter.open("/tmp/data.bin")
+# w.write "hello"
+# w.write_int64 123456789
+# w.write_byte 3
+# w.write_float 1.25
+# w.write_double 1.234567
+# w.write_bits(true, false, true)
+# assert w.last_error == null
+# w.close
+#
+# var r = new FileReader.open("/tmp/data.bin")
+# assert r.read(5) == "hello"
+# assert r.read_int64 == 123456789
+# assert r.read_byte == 3
+# assert r.read_float == 1.25
+# assert r.read_double == 1.234567
+#
+# var bits = r.read_bits
+# assert bits[0] and not bits[1] and bits[2]
+#
+# assert r.last_error == null
+# r.close
+# ~~~
+module binary
+
+in "C" `{
+ #include <inttypes.h>
+ #include <endian.h>
+
+ // Android compatibility
+ #ifndef be64toh
+ #define be64toh(val) betoh64(val)
+ #endif
+ #ifndef le64toh
+ #define le64toh(val) letoh64(val)
+ #endif
+`}
+
+# A stream of binary data
+abstract class BinaryStream
+ super Stream
+
+ # Use the big-endian convention? otherwise use little-endian.
+ #
+ # By default, `true` to use big-endian.
+ var big_endian = true is writable
+end
+
+redef abstract class Writer
+ super BinaryStream
+
+ # Write a boolean `value` on a byte, using 0 for `false` and 1 for `true`
+ fun write_bool(value: Bool) do write_byte if value then 1 else 0
+
+ # Write up to 8 `Bool` in a byte
+ #
+ # To be used with `BinaryReader::read_bits`.
+ #
+ # Ensure: `bits.length <= 8`
+ fun write_bits(bits: Bool...)
+ do
+ assert bits.length <= 8
+
+ var int = 0
+ for b in bits.length.times do
+ if bits[b] then int += 2**b
+ end
+
+ write_byte int
+ end
+
+ # Write a floating point `value` on 32 bits
+ #
+ # Using this format may result in a loss of precision as it uses less bits
+ # than Nit `Float`.
+ fun write_float(value: Float)
+ do
+ for i in [0..4[ do write_byte value.float_byte_at(i, big_endian)
+ end
+
+ # Write a floating point `value` on 64 bits
+ fun write_double(value: Float)
+ do
+ for i in [0..8[ do write_byte value.double_byte_at(i, big_endian)
+ end
+
+ # Write `value` as a signed integer on 64 bits
+ #
+ # Using this format may result in a loss of precision as the length of a
+ # Nit `Int` may be more than 64 bits on some platforms.
+ fun write_int64(value: Int)
+ do
+ for i in [0..8[ do write_byte value.int64_byte_at(i, big_endian)
+ end
+
+ # TODO:
+ #
+ # fun write_int8
+ # fun write_uint8 # No need for this one, it is the current `read_char`
+ # fun write_int16
+ # fun write_uint16
+ # fun write_int32
+ # fun write_uint32
+ # fun write_uint64
+ # fun write_long_double?
+end
+
+redef abstract class Reader
+ super BinaryStream
+
+ # Read a single byte and return `true` if its value is different than 0
+ fun read_bool: Bool do return read_byte != 0
+
+ # Get an `Array` of 8 `Bool` by reading a single byte
+ #
+ # To be used with `BinaryWriter::write_bits`.
+ fun read_bits: Array[Bool]
+ do
+ var int = read_byte
+ if int == null then return new Array[Bool]
+ return [for b in 8.times do int.bin_and(2**b) > 0]
+ end
+
+ # Read a floating point on 32 bits and return it as a `Float`
+ #
+ # Using this format may result in a loss of precision as it uses less bits
+ # than Nit `Float`.
+ fun read_float: Float
+ do
+ if last_error != null then return 0.0
+
+ var b0 = read_byte
+ var b1 = read_byte
+ var b2 = read_byte
+ var b3 = read_byte
+
+ # Check for error, `last_error` is set by `read_byte`
+ if b0 == null or b1 == null or b2 == null or b3 == null then return 0.0
+
+ return native_read_float(b0, b1, b2, b3, big_endian)
+ end
+
+ # Utility for `read_float`
+ private fun native_read_float(b0, b1, b2, b3: Int, big_endian: Bool): Float `{
+ union {
+ unsigned char b[4];
+ float val;
+ uint32_t conv;
+ } u;
+
+ u.b[0] = b0;
+ u.b[1] = b1;
+ u.b[2] = b2;
+ u.b[3] = b3;
+
+ if (big_endian)
+ u.conv = be32toh(u.conv);
+ else u.conv = le32toh(u.conv);
+
+ return u.val;
+ `}
+
+ # Read a floating point on 64 bits and return it as a `Float`
+ fun read_double: Float
+ do
+ if last_error != null then return 0.0
+
+ var b0 = read_byte
+ var b1 = read_byte
+ var b2 = read_byte
+ var b3 = read_byte
+ var b4 = read_byte
+ var b5 = read_byte
+ var b6 = read_byte
+ var b7 = read_byte
+
+ # Check for error, `last_error` is set by `read_byte`
+ if b0 == null or b1 == null or b2 == null or b3 == null or
+ b4 == null or b5 == null or b6 == null or b7 == null then return 0.0
+
+ return native_read_double(b0, b1, b2, b3, b4, b5, b6, b7, big_endian)
+ end
+
+ # Utility for `read_double`
+ private fun native_read_double(b0, b1, b2, b3, b4, b5, b6, b7: Int, big_endian: Bool): Float `{
+ union {
+ unsigned char b[8];
+ double val;
+ uint64_t conv;
+ } u;
+
+ u.b[0] = b0;
+ u.b[1] = b1;
+ u.b[2] = b2;
+ u.b[3] = b3;
+ u.b[4] = b4;
+ u.b[5] = b5;
+ u.b[6] = b6;
+ u.b[7] = b7;
+
+ if (big_endian)
+ u.conv = be64toh(u.conv);
+ else u.conv = le64toh(u.conv);
+
+ return u.val;
+ `}
+
+ # Read a signed integer on 64 bits and return is an `Int`
+ #
+ # Using this format may result in a loss of precision as the length of a
+ # Nit `Int` may be less than 64 bits on some platforms.
+ fun read_int64: Int
+ do
+ if last_error != null then return 0
+
+ var b0 = read_byte
+ var b1 = read_byte
+ var b2 = read_byte
+ var b3 = read_byte
+ var b4 = read_byte
+ var b5 = read_byte
+ var b6 = read_byte
+ var b7 = read_byte
+
+ # Check for error, `last_error` is set by `read_byte`
+ if b0 == null or b1 == null or b2 == null or b3 == null or
+ b4 == null or b5 == null or b6 == null or b7 == null then return 0
+
+ return native_read_int64(b0, b1, b2, b3, b4, b5, b6, b7, big_endian)
+ end
+
+ # Utility for `read_int64`
+ private fun native_read_int64(b0, b1, b2, b3, b4, b5, b6, b7: Int, big_endian: Bool): Int `{
+ union {
+ unsigned char b[8];
+ int64_t val;
+ uint64_t conv;
+ } u;
+
+ u.b[0] = b0;
+ u.b[1] = b1;
+ u.b[2] = b2;
+ u.b[3] = b3;
+ u.b[4] = b4;
+ u.b[5] = b5;
+ u.b[6] = b6;
+ u.b[7] = b7;
+
+ if (big_endian)
+ u.conv = be64toh(u.conv);
+ else u.conv = le64toh(u.conv);
+
+ return u.val;
+ `}
+end
+
+redef class Int
+ # Utility for `BinaryWriter`
+ private fun int64_byte_at(index: Int, big_endian: Bool): Int `{
+ union {
+ unsigned char bytes[8];
+ int64_t val;
+ uint64_t conv;
+ } u;
+
+ u.val = recv;
+
+ if (big_endian)
+ u.conv = htobe64(u.conv);
+ else u.conv = htole64(u.conv);
+
+ return u.bytes[index];
+ `}
+end
+
+redef class Float
+ # Utility for `BinaryWriter`
+ private fun float_byte_at(index: Int, big_endian: Bool): Int `{
+ union {
+ unsigned char bytes[4];
+ float val;
+ uint32_t conv;
+ } u;
+
+ u.val = recv;
+
+ if (big_endian)
+ u.conv = htobe32(u.conv);
+ else u.conv = htole32(u.conv);
+
+ return u.bytes[index];
+ `}
+
+ # Utility for `BinaryWriter`
+ private fun double_byte_at(index: Int, big_endian: Bool): Int `{
+ union {
+ unsigned char bytes[8];
+ double val;
+ uint64_t conv;
+ } u;
+
+ u.val = recv;
+
+ if (big_endian)
+ u.conv = htobe64(u.conv);
+ else u.conv = htole64(u.conv);
+
+ return u.bytes[index];
+ `}
+end
# such as a forest with many individual trees.
module bucketed_game
+import serialization
+
# Something acting on the game
class Turnable[G: Game]
+ auto_serializable
# Execute `turn` for this instance.
fun do_turn(turn: GameTurn[G]) is abstract
# Something acting on the game from time to time
class Bucketable[G: Game]
super Turnable[G]
+ auto_serializable
+
private var act_at: nullable Int = null
# Cancel the previously registered acting turn
# Optimized organization of `Bucketable` instances
class Buckets[G: Game]
super Turnable[G]
+ auto_serializable
# Bucket type used in this implementation.
type BUCKET: HashSet[Bucketable[G]]
# Event raised at the first turn
class FirstTurnEvent
super GameEvent
+ auto_serializable
end
# Game logic on the client
class ThinGame
+ auto_serializable
# Game tick when `self` should act.
#
# Game turn on the client
class ThinGameTurn[G: ThinGame]
+ auto_serializable
# Game tick when `self` should act.
var tick: Int is protected writable
- # List of game events occured for `self`.
- var events = new List[GameEvent] is protected writable
+ # Game events occurred for `self`.
+ var events = new Array[GameEvent] is protected writable
end
# Game turn on the full logic
class GameTurn[G: Game]
super ThinGameTurn[G]
+ auto_serializable
# Game that `self` belongs to.
var game: G
# Full game logic
class Game
super ThinGame
+ auto_serializable
# Game type used in this implementation.
type G: Game
redef fun iterator do return map.iterator
# The number of counted occurrences of `e`
- redef fun [](e: E): Int
+ redef fun [](e)
do
var map = self.map
if map.has_key(e) then return map[e]
return 0
end
- redef fun []=(e: E, value: Int)
+ redef fun []=(e, value)
do
sum -= self[e]
self.map[e] = value
_length = l + 1
end
- redef fun remove(value: Int)
+ redef fun remove(value)
do
assert not is_empty
+ if not value isa Int then return
var l = _length
if l > 1 then
var last = _values[l - 1]
_length = l - 1
end
- redef fun has(value: Int): Bool
+ redef fun has(value)
do
+ if not value isa Int then return false
assert value < _capacity
var pos = _keys[value]
if pos < _length then
`}
# Give the children of `self` equal space in the box?
- fun omogeneous: Bool `{ return gtk_box_get_homogeneous(recv); `}
+ fun homogeneous: Bool `{ return gtk_box_get_homogeneous(recv); `}
# Give the children of `self` equal space in the box?
- fun omogeneous=(omogeneous: Bool) `{
- gtk_box_set_homogeneous(recv, omogeneous);
+ fun homogeneous=(homogeneous: Bool) `{
+ gtk_box_set_homogeneous(recv, homogeneous);
`}
# Add `child` and pack it at the start of the box
return (GtkSearchEntry *)gtk_search_entry_new();
`}
end
+
+redef extern class GtkEntry
+ # Purpose of this text field
+ #
+ # Can be used by on-screen keyboards and other input methods to adjust their behaviour.
+ fun input_purpose: GtkInputPurpose `{
+ return gtk_entry_get_input_purpose(recv);
+ `}
+
+ # Input purpose, tweaks the behavior of this widget
+ #
+ # Can be used by on-screen keyboards and other input methods to adjust their behaviour.
+ fun input_purpose=(purpose: GtkInputPurpose) `{
+ gtk_entry_set_input_purpose(recv, purpose);
+ `}
+end
+
+# Describe the purpose of an input widget
+extern class GtkInputPurpose `{ GtkInputPurpose `}
+ # Allow any character
+ new free_form `{ return GTK_INPUT_PURPOSE_FREE_FORM; `}
+
+ # Allow only alphabetic characters
+ new alpha `{ return GTK_INPUT_PURPOSE_ALPHA; `}
+
+ # Allow only digits
+ new digits `{ return GTK_INPUT_PURPOSE_DIGITS; `}
+
+ # Edited field expects numbers
+ new number `{ return GTK_INPUT_PURPOSE_NUMBER; `}
+
+ # Edited field expects phone number
+ new phone `{ return GTK_INPUT_PURPOSE_PHONE; `}
+
+ # Edited field expects URL
+ new url `{ return GTK_INPUT_PURPOSE_URL; `}
+
+ # Edited field expects email address
+ new email `{ return GTK_INPUT_PURPOSE_EMAIL; `}
+
+ # Edited field expects the name of a person
+ new name `{ return GTK_INPUT_PURPOSE_NAME; `}
+
+ # Like `free_form`, but characters are hidden
+ new password `{ return GTK_INPUT_PURPOSE_PASSWORD; `}
+
+ # Like `digits`, but characters are hidden
+ new pin `{ return GTK_INPUT_PURPOSE_PIN; `}
+end
redef fun serialize_reference(object)
do
- if refs_map.keys.has(object) then
+ if refs_map.has_key(object) then
# if already serialized, add local reference
var id = ref_id_for(object)
stream.write "\{\"__kind\": \"ref\", \"__id\": {id}\}"
end
# Map of references to already serialized objects.
- var refs_map = new HashMap[Serializable,Int]
+ private var refs_map = new StrictHashMap[Serializable,Int]
# Get the internal serialized reference for this `object`.
private fun ref_id_for(object: Serializable): Int
do
- if refs_map.keys.has(object) then
+ if refs_map.has_key(object) then
return refs_map[object]
else
var id = refs_map.length
# Depth-first path in the serialized object tree.
var path = new Array[JsonObject]
- # Map of refenrences to already deserialized objects.
- var id_to_object = new HashMap[Int, Object]
+ # Map of references to already deserialized objects.
+ private var id_to_object = new StrictHashMap[Int, Object]
# Last encountered object reference id.
#
redef fun notify_of_creation(new_object)
do
var id = just_opened_id
- assert id != null
+ if id == null then return # Register `new_object` only once
id_to_object[id] = new_object
end
var id = object["__id"]
assert id isa Int
- assert id_to_object.keys.has(id)
+ assert id_to_object.has_key(id)
return id_to_object[id]
end
var class_name = object["__class"]
assert class_name isa String
- assert not id_to_object.keys.has(id) else print "Error: Object with id '{id}' is deserialized twice."
+ assert not id_to_object.has_key(id) else print "Error: Object with id '{id}' of {class_name} is deserialized twice."
# advance on path
path.push object
redef fun serialize_to_json(v) do to_s.serialize_to_json(v)
end
-redef class Array[E]
- redef fun serialize_to_json(v)
+redef class Collection[E]
+ # Utility to serialize a normal Json array
+ private fun serialize_to_pure_json(v: JsonSerializer)
do
- if class_name == "Array[nullable Serializable]" then
- # Using class_name to the the exact type
- # We do not want Array[Int] or anything else here
v.stream.write "["
var is_first = true
for e in self do
if is_first then
is_first = false
- else v.stream.write(", ")
+ else v.stream.write ", "
if not v.try_to_serialize(e) then
v.warn("element of type {e.class_name} is not serializable.")
end
end
v.stream.write "]"
- else
- # Register as pseudo object
- var id = v.ref_id_for(self)
- v.stream.write "\{\"__kind\": \"obj\", \"__id\": {id}, \"__class\": \"{class_name}\""
- v.stream.write """, "__length": {{{length}}}, "__items": ["""
- var is_first = true
- for e in self do
- if is_first then
- is_first = false
- else v.stream.write(", ")
+ end
+end
- if not v.try_to_serialize(e) then
- v.warn("element of type {e.class_name} is not serializable.")
- end
- end
- v.stream.write "]"
- v.stream.write "\}"
- end
+redef class SimpleCollection[E]
+ redef fun serialize_to_json(v)
+ do
+ # Register as pseudo object
+ var id = v.ref_id_for(self)
+ v.stream.write """{"__kind": "obj", "__id": """
+ v.stream.write id.to_s
+ v.stream.write """, "__class": """"
+ v.stream.write class_name
+ v.stream.write """", "__length": """
+ v.stream.write length.to_s
+ v.stream.write """, "__items": """
+ serialize_to_pure_json v
+ v.stream.write "\}"
end
- # Instanciate a new `Array` from its serialized representation.
- init from_deserializer(v: Deserializer)
+ redef init from_deserializer(v: Deserializer)
do
if v isa JsonDeserializer then
v.notify_of_creation self
+ init
var length = v.deserialize_attribute("__length").as(Int)
var arr = v.path.last["__items"].as(SequenceRead[nullable Object])
end
end
end
+
+redef class Array[E]
+ redef fun serialize_to_json(v)
+ do
+ if class_name == "Array[nullable Serializable]" then
+ # Using class_name to get the exact type,
+ # we do not want Array[Int] or anything else here.
+
+ serialize_to_pure_json v
+ else super
+ end
+end
+
+redef class Map[K, V]
+ redef fun serialize_to_json(v)
+ do
+ # Register as pseudo object
+ var id = v.ref_id_for(self)
+
+ v.stream.write """{"__kind": "obj", "__id": """
+ v.stream.write id.to_s
+ v.stream.write """, "__class": """"
+ v.stream.write class_name
+ v.stream.write """", "__length": """
+ v.stream.write length.to_s
+ v.stream.write """, "__keys": """
+
+ keys.serialize_to_pure_json v
+
+ v.stream.write """, "__values": """
+ values.serialize_to_pure_json v
+ v.stream.write "\}"
+ end
+
+ # Instantiate a new `Array` from its serialized representation.
+ redef init from_deserializer(v: Deserializer)
+ do
+ init
+
+ if v isa JsonDeserializer then
+ v.notify_of_creation self
+
+ var length = v.deserialize_attribute("__length").as(Int)
+ var keys = v.path.last["__keys"].as(SequenceRead[nullable Object])
+ var values = v.path.last["__values"].as(SequenceRead[nullable Object])
+ for i in length.times do
+ var key = v.convert_object(keys[i])
+ var value = v.convert_object(values[i])
+ self[key] = value
+ end
+ end
+ end
+end
+
+# Maps instances to a value, uses `is_same_instance`
+#
+# Warning: This class does not implement all the services from `Map`.
+private class StrictHashMap[K, V]
+ super Map[K, V]
+
+ # private
+ var map = new HashMap[K, Array[Couple[K, V]]]
+
+ redef var length = 0
+
+ redef fun is_empty do return length == 0
+
+ # private
+ fun node_at(key: K): nullable Couple[K, V]
+ do
+ if not map.keys.has(key) then return null
+
+ var arr = map[key]
+ for couple in arr do
+ if couple.first.is_same_serialized(key) then
+ return couple
+ end
+ end
+
+ return null
+ end
+
+ redef fun [](key)
+ do
+ var node = node_at(key)
+ assert node != null
+ return node.second
+ end
+
+ redef fun []=(key, value)
+ do
+ var node = node_at(key)
+ if node != null then
+ node.second = value
+ return
+ end
+
+ var arr
+ if not map.keys.has(key) then
+ arr = new Array[Couple[K, V]]
+ map[key] = arr
+ else arr = map[key]
+
+ arr.add new Couple[K, V](key, value)
+ self.length += 1
+ end
+
+ redef fun has_key(key) do return node_at(key) != null
+end
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Implementation of the app.nit UI module for GNU/Linux
+module ui
+
+import app::ui
+import gtk
+
+import data_store
+
+redef class App
+ redef fun setup do init_gtk
+
+ # On GNU/Linux, we go through all the callbacks once,
+ # there is no complex life-cycle.
+ redef fun run
+ do
+ app.on_create
+ app.on_restore_state
+ app.on_start
+ app.on_resume
+
+ var window = window
+ window.native.show_all
+ run_gtk
+
+ app.on_pause
+ app.on_stop
+ app.on_save_state
+ app.on_destroy
+ end
+
+ # Spacing between GTK controls, default at 2
+ var control_spacing = 2 is writable
+end
+
+redef class Control
+ super GtkCallable
+ super Finalizable
+
+ # The GTK element used to implement `self`
+ fun native: NATIVE is abstract
+
+ # Type of `native`
+ type NATIVE: GtkWidget
+
+ redef fun finalize
+ do
+ var native = native
+ if not native.address_is_null then native.destroy
+ end
+end
+
+redef class CompositeControl
+ redef type NATIVE: GtkContainer
+
+ redef fun add(item)
+ do
+ super
+ native.add item.native
+ end
+end
+
+redef class Window
+ redef type NATIVE: GtkWindow
+ redef var native do
+ var win = new GtkWindow(new GtkWindowType.toplevel)
+ win.connect_destroy_signal_to_quit
+ return win
+ end
+end
+
+redef class View
+ redef fun enabled do return native.sensitive
+ redef fun enabled=(enabled) do native.sensitive = enabled or else true
+end
+
+redef class Layout
+ redef type NATIVE: GtkBox
+end
+
+redef class HorizontalLayout
+ redef var native = new GtkBox(new GtkOrientation.horizontal, app.control_spacing)
+
+ redef fun add(item)
+ do
+ super
+ native.homogeneous = true
+ native.set_child_packing(item.native, true, true, 0, new GtkPackType.start)
+ end
+end
+
+redef class VerticalLayout
+ redef var native = new GtkBox(new GtkOrientation.vertical, app.control_spacing)
+
+ redef fun add(item)
+ do
+ super
+
+ # FIXME abstract the use either homogeneous or weight to balance views size in a layout
+ native.homogeneous = true
+ native.set_child_packing(item.native, true, true, 0, new GtkPackType.start)
+ end
+end
+
+redef class Button
+ redef type NATIVE: GtkButton
+ redef var native = new GtkButton
+
+ redef fun text do return native.text
+ redef fun text=(value) do native.text = (value or else "").to_s
+
+ redef fun signal(sender, data) do notify_observers new ButtonPressEvent(self)
+
+ init do native.signal_connect("clicked", self, null)
+end
+
+redef class TextInput
+ redef type NATIVE: GtkEntry
+ redef var native = new GtkEntry
+
+ redef fun text do return native.text
+ redef fun text=(value) do
+ if value == null then value = ""
+ native.text = value.to_s
+ end
+end
for node in self do remove_node(node)
end
- redef fun remove(node: NeoNode) do
+ redef fun remove(node) do
for n in self do
if node == n then
remove_node(n)
end
end
- redef fun remove_all(node: NeoNode) do
+ redef fun remove_all(node) do
for n in self do
if node == n then remove_node(n)
end
fun serialize_attribute(name: String, value: nullable Object)
do
if not try_to_serialize(value) then
- warn("argument {value.class_name}::{name} is not serializable.")
+ warn("argument {name} of type {value.class_name} is not serializable.")
end
end
# The subclass change the default behavior, which will accept references,
# to force to always serialize copies of `self`.
private fun serialize_to_or_delay(v: Serializer) do v.serialize_reference(self)
+
+ # Create an instance of this class from the `deserializer`
+ #
+ # This constructor is refined by subclasses to correctly build their instances.
+ init from_deserializer(deserializer: Deserializer) do end
+end
+
+redef interface Object
+ # Is `self` the same as `other` in a serialization context?
+ #
+ # Used to determine if an object has already been serialized.
+ fun is_same_serialized(other: nullable Object): Bool do return is_same_instance(other)
end
# Instances of this class are not delayed and instead serialized immediately
redef class Float super DirectSerializable end
redef class NativeString super DirectSerializable end
redef class String super DirectSerializable end
-redef class Array[E] super Serializable end
+redef class SimpleCollection[E] super Serializable end
+redef class Map[K, V] super Serializable end
socket.write(msg.to_s)
end
+ redef fun write_byte(value)
+ do
+ if closed then return
+ socket.write_byte value
+ end
+
fun write_ln(msg: Text)
do
if end_reached then return
return write(*recv, (char*)String_to_cstring(buffer), String_length(buffer));
`}
+ # Write `value` as a single byte
+ fun write_byte(value: Int): Int `{
+ return write(*recv, &value, 1);
+ `}
+
fun read: String import NativeString.to_s_with_length `{
static char c[1024];
int n = read(*recv, c, 1024);
# assert [1,2,3].has(9) == false
# assert [1..5[.has(2) == true
# assert [1..5[.has(9) == false
- fun has(item: E): Bool
+ fun has(item: nullable Object): Bool
do
for i in self do if i == item then return true
return false
# assert [3..3[.has_only(1) == true # empty collection
#
# ENSURE `is_empty implies result == true`
- fun has_only(item: E): Bool
+ fun has_only(item: nullable Object): Bool
do
for i in self do if i != item then return false
return true
# Comparisons are done with ==
#
# assert [10,20,10].count(10) == 2
- fun count(item: E): Int
+ fun count(item: nullable Object): Int
do
var nb = 0
for i in self do if i == item then nb += 1
#
# Note that the default implementation is general and correct for any lawful Collections.
# It is memory-efficient but relies on `has` so may be CPU-inefficient for some kind of collections.
- fun has_all(other: Collection[E]): Bool
+ fun has_all(other: Collection[nullable Object]): Bool
do
for x in other do if not has(x) then return false
return true
#
# Note that the default implementation is general and correct for any lawful Collections.
# It is memory-efficient but relies on `count` so may be CPU-inefficient for some kind of collections.
- fun has_exactly(other: Collection[E]): Bool
+ fun has_exactly(other: Collection[nullable Object]): Bool
do
if length != other.length then return false
for e in self do if self.count(e) != other.count(e) then return false
# ENSURE `is_empty`
fun clear is abstract
- # Remove an occucence of `item`
+ # Remove an occurrence of `item`
#
# var a = [1,2,3,1,2,3]
# a.remove 2
# assert a == [1,3,1,2,3]
- fun remove(item: E) is abstract
+ fun remove(item: nullable Object) is abstract
- # Remove all occurences of `item`
+ # Remove all occurrences of `item`
#
# var a = [1,2,3,1,2,3]
# a.remove_all 2
# assert a == [1,3,1,3]
- fun remove_all(item: E) do while has(item) do remove(item)
+ fun remove_all(item: nullable Object) do while has(item) do remove(item)
end
# Items can be added to these collections.
# Equality is defined on set and means that each set contains the same elements
redef fun ==(other)
do
- if not other isa Set[Object] then return false
+ if not other isa Set[nullable Object] then return false
if other.length != length then return false
return has_all(other)
end
#
# If the key is not in the map, `provide_default_value` is called (that aborts by default)
# See `get_or_null` and `get_or_default` for safe variations.
- fun [](key: K): V is abstract
+ fun [](key: nullable Object): V is abstract
# Get the item at `key` or null if `key` is not in the map.
#
# assert x.get_or_null("five") == null
#
# Note: use `has_key` and `[]` if you need the distinction between a key associated with null, and no key.
- fun get_or_null(key: K): nullable V
+ fun get_or_null(key: nullable Object): nullable V
do
if has_key(key) then return self[key]
return null
# assert x.get_or_default("four", 40) == 4
# assert x.get_or_default("five", 50) == 50
#
- fun get_or_default(key: K, default: V): V
+ fun get_or_default(key: nullable Object, default: V): V
do
if has_key(key) then return self[key]
return default
# assert x.has_key("five") == false
#
# By default it is a synonymous to `keys.has` but could be redefined with a direct implementation.
- fun has_key(key: K): Bool do return self.keys.has(key)
+ fun has_key(key: nullable Object): Bool do return self.keys.has(key)
# Get a new iterator on the map.
fun iterator: MapIterator[K, V] is abstract
#
# Note: the value is returned *as is*, implementations may want to store the value in the map before returning it
# @toimplement
- protected fun provide_default_value(key: K): V do abort
+ protected fun provide_default_value(key: nullable Object): V do abort
# Does `self` and `other` have the same keys associated with the same values?
#
# var a = [10,20,30,10,20,30]
# assert a.index_of(20) == 1
# assert a.index_of(40) == -1
- fun index_of(item: E): Int do return index_of_from(item, 0)
+ fun index_of(item: nullable Object): Int do return index_of_from(item, 0)
# The index of the last occurrence of `item`.
# Return -1 if `item` is not found.
# var a = [10,20,30,10,20,30]
# assert a.last_index_of(20) == 4
# assert a.last_index_of(40) == -1
- fun last_index_of(item: E): Int do return last_index_of_from(item, length-1)
+ fun last_index_of(item: nullable Object): Int do return last_index_of_from(item, length-1)
# The index of the first occurrence of `item`, starting from pos.
# Return -1 if `item` is not found.
# assert a.index_of_from(20, 3) == 4
# assert a.index_of_from(20, 4) == 4
# assert a.index_of_from(20, 5) == -1
- fun index_of_from(item: E, pos: Int): Int
+ fun index_of_from(item: nullable Object, pos: Int): Int
do
var p = 0
var i = iterator
# assert a.last_index_of_from(20, 2) == 1
# assert a.last_index_of_from(20, 1) == 1
# assert a.last_index_of_from(20, 0) == -1
- fun last_index_of_from(item: E, pos: Int): Int
+ fun last_index_of_from(item: nullable Object, pos: Int): Int
do
var res = -1
var p = 0
# Return the couple of the corresponding key
# Return null if the key is no associated element
- protected fun couple_at(key: K): nullable Couple[K, V] is abstract
+ protected fun couple_at(key: nullable Object): nullable Couple[K, V] is abstract
# Return a new iteralot on all couples
# Used to provide `iterator` and others
redef fun index_of(item) do return index_of_from(item, 0)
- redef fun last_index_of(item: E): Int do return last_index_of_from(item, length-1)
+ redef fun last_index_of(item) do return last_index_of_from(item, length-1)
- redef fun index_of_from(item: E, pos: Int): Int
+ redef fun index_of_from(item, pos)
do
var i = pos
var len = length
return -1
end
- redef fun last_index_of_from(item: E, pos: Int): Int
+ redef fun last_index_of_from(item, pos)
do
var i = pos
while i >= 0 do
var last_item: nullable N = null # Last added item (same)
# The last key accessed (used for cache)
- var last_accessed_key: nullable K = null
+ var last_accessed_key: nullable Object = null
# The last node accessed (used for cache)
var last_accessed_node: nullable N = null
# Return the index of the key k
- fun index_at(k: K): Int
+ fun index_at(k: nullable Object): Int
do
if k == null then return 0
end
# Return the node associated with the key
- fun node_at(k: K): nullable N
+ fun node_at(k: nullable Object): nullable N
do
# cache: `is` is used instead of `==` because it is a faster filter (even if not exact)
if k.is_same_instance(_last_accessed_key) then return _last_accessed_node
end
# Return the node associated with the key (but with the index already known)
- fun node_at_idx(i: Int, k: K): nullable N
+ fun node_at_idx(i: Int, k: nullable Object): nullable N
do
var c = _array[i]
while c != null do
end
# Remove the node assosiated with the key
- fun remove_node(k: K)
+ fun remove_node(k: nullable Object)
do
var i = index_at(k)
var node = node_at_idx(i, k)
# assert [1..10].has(5)
# assert [1..10].has(10)
# assert not [1..10[.has(10)
- redef fun has(item) do return item >= first and item <= last
+ redef fun has(item) do return item isa Comparable and item >= first and item <= last
# assert [1..1].has_only(1)
# assert not [1..10].has_only(1)
# s.add(1)
# assert s.has(1)
# assert not s.has(2)
- redef fun has(e: E): Bool
+ redef fun has(e)
do
return nodes.has_key(e)
end
end
end
+ redef fun write_byte(value)
+ do
+ if last_error != null then return
+ if not _is_writable then
+ last_error = new IOError("Cannot write to non-writable stream")
+ return
+ end
+ if _file.address_is_null then
+ last_error = new IOError("Writing on a null stream")
+ _is_writable = false
+ return
+ end
+
+ var err = _file.write_byte(value)
+ if err != 1 then
+ # Big problem
+ last_error = new IOError("Problem writing a byte: {err}")
+ end
+ end
+
redef fun close
do
super
private extern class NativeFile `{ FILE* `}
fun io_read(buf: NativeString, len: Int): Int is extern "file_NativeFile_NativeFile_io_read_2"
fun io_write(buf: NativeString, len: Int): Int is extern "file_NativeFile_NativeFile_io_write_2"
+ fun write_byte(value: Int): Int `{
+ unsigned char b = (unsigned char)value;
+ return fwrite(&b, 1, 1, recv);
+ `}
fun io_close: Int is extern "file_NativeFile_NativeFile_io_close_0"
fun file_stat: NativeFileStat is extern "file_NativeFile_NativeFile_file_stat_0"
fun fileno: Int `{ return fileno(recv); `}
# Have `self` and `other` the same value?
#
- # The exact meaning of "same value" is let to the subclasses.
+ # The exact meaning of "same value" is left to the subclasses.
# Implicitly, the default implementation, is `is_same_instance`
fun ==(other: nullable Object): Bool do return self.is_same_instance(other)
# Display class name on stdout (debug only).
# This method MUST not be used by programs, it is here for debugging
# only and can be removed without any notice
- fun output_class_name is intern
+ fun output_class_name is intern
# The hash code of the object.
# Assuming that a == b -> a.hash == b.hash
# Something that can be cloned
#
# This interface introduces the `clone` method used to duplicate an instance
-# Its specific semantic is let to the subclasses.
+# Its specific semantic is left to the subclasses.
interface Cloneable
# Duplicate `self`
#
- # The specific semantic of this method is let to the subclasses;
+ # The specific semantic of this method is left to the subclasses;
# Especially, if (and how) attributes are cloned (depth vs. shallow).
#
# As a rule of thumb, the principle of least astonishment should
# write a string
fun write(s: Text) is abstract
+ # Write a single byte
+ fun write_byte(value: Int) is abstract
+
# Can the stream be used to write
fun is_writable: Bool is abstract
end
# assert not tree.has_key(0)
# assert tree.has_key(2)
# assert not tree.has_key(6)
- redef fun has_key(key: K): Bool do
+ redef fun has_key(key) do
if is_empty then return false
var res = search_down(root.as(not null), key)
if res != null then
# assert tree[1] == "n1"
# assert tree.has_key(1)
# assert tree[2] == "n2"
- redef fun [](key: K): E do
+ redef fun [](key) do
assert not_empty: not is_empty
if cache_node != null and cache_node.key == key then return cache_node.value
var res = search_down(root.as(not null), key)
end
# Search `key` in `from` and its children nodes.
- protected fun search_down(from: N, key: K): nullable N do
+ protected fun search_down(from: N, key: nullable Object): nullable N do
+ if not key isa Comparable then return null
var cmp = key <=> from.key
if cmp == 0 then return from
if from.left != null and cmp < 0 then
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Console templates for Nit model MEntities.
+#
+# This module introduces console rendering services in model entities.
+module console_model
+
+import console
+import doc_base
+import ordered_tree
+
+redef class MDoc
+ # Returns the full comment formatted for the console.
+ fun cs_comment: String do
+ var res = new FlatBuffer
+ for line in content do
+ res.append " {line}\n"
+ end
+ return res.write_to_string
+ end
+
+ # Returns the synopsys formatted for the console.
+ fun cs_short_comment: String do return content.first
+end
+
+redef class MEntity
+
+ # Returns the mentity name with short signature.
+ #
+ # * MProject: `foo`
+ # * MGroup: `foo`
+ # * MModule: `foo`
+ # * MClass: `Foo[E]`
+ # * MClassDef: `Foo[E]`
+ # * MProperty: `foo(e)`
+ # * MPropdef: `foo(e)`
+ var cs_name: String is lazy do return name
+
+ # Returns the list of keyword used in `self` declaration.
+ fun cs_modifiers: Array[String] is abstract
+
+ # Returns the complete MEntity declaration (modifiers + name + signature).
+ #
+ # * MProject: `project foo`
+ # * MGroup: `group foo`
+ # * MModule: `module foo`
+ # * MClass: `private abstract class Foo[E: Object]`
+ # * MClassDef: `redef class Foo[E]`
+ # * MProperty: `private fun foo(e: Object): Int`
+ # * MPropdef: `redef fun foo(e)`
+ var cs_declaration: String is lazy do
+ var tpl = new FlatBuffer
+ tpl.append cs_modifiers.join(" ")
+ tpl.append " "
+ tpl.append cs_name
+ return tpl.write_to_string
+ end
+
+ # Returns `self` namespace formatted for console.
+ #
+ # * MProject: `mproject`
+ # * MGroup: `mproject(::group)`
+ # * MModule: `mgroup::mmodule`
+ # * MClass: `mproject::mclass`
+ # * MClassDef: `mmodule::mclassdef`
+ # * MProperty: `mclass::mprop`
+ # * MPropdef: `mclassdef:mpropdef`
+ fun cs_namespace: String is abstract
+
+ # Returns the comment of this MEntity formatted for console.
+ var cs_comment: nullable String is lazy do
+ var mdoc = mdoc_or_fallback
+ if mdoc == null then return null
+ # FIXME add markdown for console
+ return mdoc.cs_comment
+ end
+
+ # Returns the comment of this MEntity formatted for console.
+ var cs_short_comment: nullable String is lazy do
+ var mdoc = mdoc_or_fallback
+ if mdoc == null then return null
+ return mdoc.cs_short_comment
+ end
+
+ # Returns 1self` as a list element that can be displayed in console.
+ fun cs_list_item: String do
+ var tpl = new FlatBuffer
+ tpl.append " {cs_visibility_color(cs_icon).bold} {cs_name.blue.bold}"
+ var comment = cs_short_comment
+ if comment != null then
+ tpl.append " # {comment}".green
+ end
+ tpl.append "\n "
+ tpl.append cs_namespace.gray.bold
+ tpl.append "\n "
+ tpl.append cs_declaration
+ tpl.append "\n "
+ tpl.append cs_location.gray
+ return tpl.write_to_string
+ end
+
+ # ASCII icon to be displayed in front of the list item.
+ fun cs_icon: String do return "*"
+
+ # Source code location of this MEntity formatted for console.
+ fun cs_location: String is abstract
+
+ # Sets text color depending on visibility.
+ #
+ # See module `console`.
+ fun cs_visibility_color(string: String): String do return string.green
+end
+
+redef class MProject
+ redef var cs_modifiers = ["project"]
+ redef fun cs_namespace do return cs_name
+ redef fun cs_icon do return "P"
+ redef fun cs_location do return root.mmodules.first.location.to_s
+end
+
+redef class MGroup
+ redef var cs_modifiers = ["group"]
+ redef fun cs_icon do return "G"
+
+ # Depends if `self` is root or not.
+ #
+ # * If root `mproject`.
+ # * Else `mproject::self`.
+ redef fun cs_namespace do
+ var tpl = new FlatBuffer
+ tpl.append mproject.cs_namespace
+ if mproject.root != self then
+ tpl.append "::"
+ tpl.append cs_name
+ end
+ return tpl.write_to_string
+ end
+
+ redef fun cs_location do return mmodules.first.location.to_s
+end
+
+redef class MModule
+ redef var cs_modifiers = ["module"]
+ redef fun cs_icon do return "M"
+
+ # Depends if `self` belongs to a MGroup.
+ #
+ # * If mgroup `mgroup::self`.
+ # * Else `self`.
+ redef fun cs_namespace do
+ var tpl = new FlatBuffer
+ if mgroup != null then
+ tpl.append mgroup.cs_namespace
+ tpl.append "::"
+ end
+ tpl.append cs_name
+ return tpl.write_to_string
+ end
+
+ redef fun cs_location do return location.to_s
+end
+
+redef class MClass
+ redef fun mdoc_or_fallback do return intro.mdoc
+ redef fun cs_icon do return intro.cs_icon
+
+ # Format: `Foo[E]`
+ redef var cs_name is lazy do
+ var tpl = new FlatBuffer
+ tpl.append name
+ if arity > 0 then
+ tpl.append "["
+ var parameter_names = new Array[String]
+ for p in mparameters do
+ parameter_names.add(p.cs_name)
+ end
+ tpl.append parameter_names.join(", ")
+ tpl.append "]"
+ end
+ return tpl.write_to_string
+ end
+
+ redef fun cs_modifiers do return intro.cs_modifiers
+ redef fun cs_declaration do return intro.cs_declaration
+
+ # Returns `mproject::self`.
+ redef fun cs_namespace do
+ var tpl = new FlatBuffer
+ tpl.append intro_mmodule.mgroup.mproject.cs_namespace
+ tpl.append "::"
+ tpl.append cs_name
+ return tpl.write_to_string
+ end
+
+ # Returns `intro.cs_short_signature`.
+ fun cs_short_signature: String do return intro.cs_short_signature
+
+ # Returns `intro.cs_signature`.
+ fun cs_signature: String do return intro.cs_signature
+
+ redef fun cs_visibility_color(string) do
+ if visibility == private_visibility then
+ return string.red
+ else if visibility == protected_visibility then
+ return string.yellow
+ end
+ return string.green
+ end
+
+ redef fun cs_location do return intro.location.to_s
+end
+
+redef class MClassDef
+ redef fun mdoc_or_fallback do return mdoc or else mclass.mdoc_or_fallback
+ redef fun cs_icon do return "C"
+
+ # Depends if `self` is an intro or not.
+ #
+ # * If intro contains the visibility and kind.
+ # * If redef contains the `redef` keyword and kind.
+ redef fun cs_modifiers do
+ var res = new Array[String]
+ if not is_intro then
+ res.add "redef"
+ else
+ if mclass.visibility != public_visibility then
+ res.add mclass.visibility.to_s
+ end
+ end
+ res.add mclass.kind.to_s
+ return res
+ end
+
+ # Depends if `self` is an intro or not.
+ #
+ # For intro: `private abstract class Foo[E: Object]`
+ # For redef: `redef class Foo[E]`
+ redef fun cs_declaration do
+ var tpl = new FlatBuffer
+ tpl.append cs_modifiers.join(" ")
+ tpl.append " "
+ tpl.append cs_name
+ if is_intro then
+ tpl.append cs_signature
+ else
+ tpl.append cs_short_signature
+ end
+ return tpl.write_to_string.write_to_string.write_to_string
+ end
+
+ # Returns `mmodule::self`
+ redef fun cs_namespace do
+ var tpl = new FlatBuffer
+ tpl.append mmodule.cs_namespace
+ tpl.append "::"
+ tpl.append mclass.cs_name
+ return tpl.write_to_string.write_to_string
+ end
+
+ # Returns the MClassDef generic signature without static bounds.
+ fun cs_short_signature: String do
+ var tpl = new FlatBuffer
+ var mparameters = mclass.mparameters
+ if not mparameters.is_empty then
+ tpl.append "["
+ for i in [0..mparameters.length[ do
+ tpl.append mparameters[i].cs_name
+ if i < mparameters.length - 1 then tpl.append ", "
+ end
+ tpl.append "]"
+ end
+ return tpl.write_to_string
+ end
+
+ # Returns the MClassDef generic signature with static bounds.
+ fun cs_signature: String do
+ var tpl = new FlatBuffer
+ var mparameters = mclass.mparameters
+ if not mparameters.is_empty then
+ tpl.append "["
+ for i in [0..mparameters.length[ do
+ tpl.append "{mparameters[i].cs_name}: "
+ tpl.append bound_mtype.arguments[i].cs_signature
+ if i < mparameters.length - 1 then tpl.append ", "
+ end
+ tpl.append "]"
+ end
+ return tpl.write_to_string
+ end
+
+ redef fun cs_location do return location.to_s
+end
+
+redef class MProperty
+ redef fun mdoc_or_fallback do return intro.mdoc
+ redef fun cs_modifiers do return intro.cs_modifiers
+ redef fun cs_declaration do return intro.cs_declaration
+ redef fun cs_icon do return intro.cs_icon
+
+ # Returns `mclass::self`.
+ redef fun cs_namespace do
+ var tpl = new FlatBuffer
+ tpl.append intro_mclassdef.mclass.cs_namespace
+ tpl.append "::"
+ tpl.append intro.cs_name
+ return tpl.write_to_string
+ end
+
+ # Returns `intro.cs_short_signature`.
+ fun cs_short_signature: String do return intro.cs_short_signature
+
+ # Returns `intro.cs_signature`.
+ fun cs_signature: String do return intro.cs_signature
+
+ redef fun cs_visibility_color(string) do
+ if visibility == private_visibility then
+ return string.red
+ else if visibility == protected_visibility then
+ return string.yellow
+ end
+ return string.green
+ end
+
+ # Returns `intro.cs_location`.
+ redef fun cs_location do return intro.location.to_s
+end
+
+redef class MPropDef
+ redef fun mdoc_or_fallback do return mdoc or else mproperty.mdoc_or_fallback
+
+ # Depends if `self` is an intro or not.
+ #
+ # * If intro contains the visibility and kind.
+ # * If redef contains the `redef` keyword and kind.
+ redef fun cs_modifiers do
+ var res = new Array[String]
+ if not is_intro then
+ res.add "redef"
+ else
+ if mproperty.visibility != public_visibility then
+ res.add mproperty.visibility.to_s
+ end
+ end
+ return res
+ end
+
+ # Depends if `self` is an intro or not.
+ #
+ # For intro: `private fun foo(e: Object): Bar is abstract`
+ # For redef: `redef fun foo(e) is cached`
+ redef fun cs_declaration do
+ var tpl = new FlatBuffer
+ tpl.append cs_modifiers.join(" ")
+ tpl.append " "
+ if is_intro then
+ tpl.append cs_name
+ tpl.append cs_signature
+ else
+ tpl.append mproperty.intro.cs_name
+ tpl.append cs_short_signature
+ end
+ return tpl.write_to_string
+ end
+
+ # Returns `mclassdef::self`
+ redef fun cs_namespace do
+ var tpl = new FlatBuffer
+ tpl.append mclassdef.cs_namespace
+ tpl.append "::"
+ tpl.append cs_name
+ return tpl.write_to_string
+ end
+
+ redef fun cs_location do return location.to_s
+
+ # Returns the MPropdDef signature without static types.
+ fun cs_short_signature: String is abstract
+
+ # Returns the MPropDef signature with static types.
+ fun cs_signature: String is abstract
+end
+
+redef class MAttributeDef
+
+ redef fun cs_modifiers do
+ var res = super
+ res.add "var"
+ return res
+ end
+
+ redef fun cs_short_signature do return ""
+
+ redef fun cs_signature do
+ var tpl = new FlatBuffer
+ if static_mtype != null then
+ tpl.append ": "
+ tpl.append static_mtype.cs_signature
+ end
+ return tpl.write_to_string
+ end
+
+ redef fun cs_icon do return "A"
+end
+
+redef class MMethodDef
+
+ redef fun cs_modifiers do
+ if mproperty.is_init then
+ var res = new Array[String]
+ if mproperty.visibility != public_visibility then
+ res.add mproperty.visibility.to_s
+ end
+ return res
+ end
+ var res = super
+ if is_abstract then
+ res.add "abstract"
+ else if is_intern then
+ res.add "intern"
+ end
+ res.add "fun"
+ return res
+ end
+
+ redef fun cs_declaration do
+ if mproperty.is_init then
+ var tpl = new FlatBuffer
+ if not cs_modifiers.is_empty then
+ tpl.append cs_modifiers.join(" ")
+ tpl.append " "
+ end
+ tpl.append cs_name
+ tpl.append cs_signature
+ return tpl.write_to_string
+ end
+ return super
+ end
+
+ redef fun cs_short_signature do
+ if mproperty.is_root_init then
+ return new_msignature.cs_short_signature
+ end
+ return msignature.cs_short_signature
+ end
+
+ redef fun cs_signature do
+ if mproperty.is_root_init then
+ return new_msignature.cs_signature
+ end
+ return msignature.cs_signature
+ end
+
+ redef fun cs_icon do
+ if mproperty.is_init then
+ return "I"
+ end
+ return "F"
+ end
+end
+
+redef class MVirtualTypeDef
+
+ redef fun cs_modifiers do
+ var res = super
+ res.add "type"
+ return res
+ end
+
+ # Short signature for `MVirtualType` is always empty.
+ redef fun cs_short_signature do return ""
+
+ redef fun cs_signature do
+ var tpl = new FlatBuffer
+ if bound == null then return tpl.write_to_string
+ tpl.append ": "
+ tpl.append bound.cs_signature
+ return tpl.write_to_string
+ end
+ redef fun cs_icon do return "V"
+end
+
+redef class MType
+ # Returns the signature of this type whithout bounds.
+ fun cs_short_signature: String is abstract
+
+ # Returns the signature of this type.
+ fun cs_signature: String is abstract
+
+ redef fun cs_icon do return "T"
+end
+
+redef class MClassType
+ redef fun cs_short_signature do return cs_name
+ redef fun cs_signature do return cs_name
+end
+
+redef class MNullableType
+
+ redef fun cs_short_signature do
+ var tpl = new FlatBuffer
+ tpl.append "nullable "
+ tpl.append mtype.cs_short_signature
+ return tpl.write_to_string
+ end
+
+ redef fun cs_signature do
+ var tpl = new FlatBuffer
+ tpl.append "nullable "
+ tpl.append mtype.cs_signature
+ return tpl.write_to_string
+ end
+end
+
+redef class MGenericType
+ redef fun cs_short_signature do
+ var tpl = new FlatBuffer
+ tpl.append name
+ tpl.append "["
+ for i in [0..arguments.length[ do
+ tpl.append arguments[i].cs_short_signature
+ if i < arguments.length - 1 then tpl.append ", "
+ end
+ tpl.append "]"
+ return tpl.write_to_string
+ end
+
+ redef fun cs_signature do
+ var tpl = new FlatBuffer
+ tpl.append mclass.name
+ tpl.append "["
+ for i in [0..arguments.length[ do
+ tpl.append arguments[i].cs_signature
+ if i < arguments.length - 1 then tpl.append ", "
+ end
+ tpl.append "]"
+ return tpl.write_to_string
+ end
+end
+
+redef class MParameterType
+ redef fun cs_short_signature do return cs_name
+ redef fun cs_signature do return cs_name
+end
+
+redef class MVirtualType
+ redef fun cs_signature do return cs_name
+end
+
+redef class MSignature
+
+ redef fun cs_short_signature do
+ var tpl = new FlatBuffer
+ if not mparameters.is_empty then
+ tpl.append "("
+ for i in [0..mparameters.length[ do
+ tpl.append mparameters[i].cs_short_signature
+ if i < mparameters.length - 1 then tpl.append ", "
+ end
+ tpl.append ")"
+ end
+ return tpl.write_to_string
+ end
+
+ redef fun cs_signature do
+ var tpl = new FlatBuffer
+ if not mparameters.is_empty then
+ tpl.append "("
+ for i in [0..mparameters.length[ do
+ tpl.append mparameters[i].cs_signature
+ if i < mparameters.length - 1 then tpl.append ", "
+ end
+ tpl.append ")"
+ end
+ if return_mtype != null then
+ tpl.append ": "
+ tpl.append return_mtype.cs_signature
+ end
+ return tpl.write_to_string
+ end
+end
+
+redef class MParameter
+
+ # Returns `self` name and ellipsys if any.
+ fun cs_short_signature: String do
+ var tpl = new FlatBuffer
+ tpl.append name
+ if is_vararg then tpl.append "..."
+ return tpl.write_to_string
+ end
+
+ # Returns `self` name with it's static type and ellipsys if any.
+ fun cs_signature: String do
+ var tpl = new FlatBuffer
+ tpl.append "{name}: "
+ tpl.append mtype.cs_signature
+ if is_vararg then tpl.append "..."
+ return tpl.write_to_string
+ end
+end
+
+################################################################################
+# Additions to `model_ext`.
+
+redef class MRawType
+ redef fun cs_signature do
+ var tpl = new FlatBuffer
+
+ for part in parts do
+ if part.target != null then
+ tpl.append part.target.as(not null).cs_name
+ else
+ tpl.append part.text
+ end
+ end
+ return tpl.write_to_string
+ end
+end
+
+redef class MInnerClass
+ redef fun cs_signature do return inner.cs_signature
+end
+
+redef class MInnerClassDef
+ redef fun cs_signature do return inner.cs_signature
+end
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Introduces templates that compose the documentation in console rendering.
+module console_templates
+
+import console_model
+import doc_phases::doc_structure
+
+# Renders the page displayable as ASCII.
+redef class DocPage
+ super Template
+
+ # Renders the footer and content.
+ private fun render_content do add root
+
+ # Renders the whole page
+ redef fun rendering do render_content
+end
+
+redef class DocComposite
+ super Template
+
+ # Title that can be decorated for console display.
+ #
+ # Set as `null` if you don't want to display a title.
+ var cs_title: nullable String is noinit, writable
+
+ # Subtitle that can be decorated for console display.
+ #
+ # Set as `null` if you don't want to display a subtitle.
+ var cs_subtitle: nullable String is noinit, writable
+
+ # Renders the element `cs_title` and `cs_subtitle` as any.
+ fun render_title do
+ if cs_title != null then
+ add "{"#" * depth} ".blue.bold
+ addn cs_title.blue.bold
+ end
+ if cs_subtitle != null then
+ addn cs_subtitle.gray.bold
+ end
+ if cs_title != null or cs_subtitle != null then addn ""
+ end
+
+ # Renders the element body.
+ fun render_body do for child in children do add child.write_to_string
+
+ redef fun rendering do
+ render_title
+ render_body
+ end
+end
+
+redef class DocRoot
+ redef fun rendering do render_body
+end
+
+redef class ConcernSection
+ redef var cs_title is lazy do return "in {mentity.cs_namespace}"
+ redef var cs_subtitle is lazy do return mentity.cs_declaration
+
+ redef fun rendering do
+ var mentity = self.mentity
+ if mentity isa MGroup and mentity.mproject.root == mentity then
+ render_body
+ else
+ super
+ end
+ end
+end
+
+redef class ConstructorsSection
+ redef var cs_title = "Constructors"
+ redef var cs_subtitle = null
+end
+
+redef class MEntityComposite
+ redef var cs_title is lazy do return mentity.cs_name
+ redef var cs_subtitle is lazy do return mentity.cs_namespace
+end
+
+redef class IntroArticle
+ redef var cs_title = null
+ redef var cs_subtitle = null
+
+ redef fun render_body do
+ addn " {mentity.cs_declaration.bold}"
+ addn " {mentity.cs_location.gray.bold}"
+ addn ""
+ var comment = mentity.cs_comment
+ if comment != null then
+ add comment
+ end
+ addn ""
+ super
+ end
+end
+
+redef class ConcernsArticle
+ redef var cs_title = "Concerns"
+ redef var cs_subtitle = null
+
+ redef fun render_body do
+ var w = new StringWriter
+ concerns.write_to(w)
+ addn w.to_s
+ end
+end
+
+redef class DefinitionArticle
+ # If short, displays only synopsyses.
+ var is_short = false is writable
+
+ redef fun render_body do
+ addn " {mentity.cs_visibility_color(mentity.cs_declaration).bold}"
+ addn " {mentity.cs_location.gray.bold}"
+ addn ""
+ var comment
+ if is_short then
+ comment = mentity.cs_short_comment
+ else
+ comment = mentity.cs_comment
+ end
+ if comment != null then
+ add comment
+ end
+ addn ""
+ super
+ end
+end
child.parent = self
children.add child
end
+
+ # Depth of `self` in the composite tree.
+ fun depth: Int do
+ if parent == null then return 0
+ return parent.depth + 1
+ end
end
# The `DocComposite` element that contains all the other.
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Nitx related components
+#
+# This module is a place holder for `nitx` related services.
+# No `doc_phase` can be found here, only components used by Nitx tool.
+module doc_console
+
+import semantize
+import doc::console_templates
+
+# Nitx handles console I/O.
+#
+# Using `prompt`, the command line can be turned on an interactive tool.
+class Nitx
+
+ # ToolContext used to access options.
+ var ctx: ToolContext
+
+ # DocModel that contains the informations to display.
+ var doc: DocModel
+
+ # Comparator used to sort MEntities.
+ var sorter = new MEntityNameSorter
+
+ # Displays the welcome message and start prompt.
+ fun start do
+ welcome
+ prompt
+ end
+
+ # Displays the welcome message and the list of loaded modules.
+ fun welcome do
+ print "Welcome in the Nit Index."
+ print ""
+ print "Loaded modules:"
+ var mmodules = doc.mmodules.to_a
+ sorter.sort(mmodules)
+ for m in mmodules do
+ print "\t{m.name}"
+ end
+ print ""
+ help
+ end
+
+ # Displays the list of available commands.
+ fun help do
+ print "\nCommands:"
+ print "\tname\t\tlookup module, class and property with the corresponding 'name'"
+ print "\t:h\t\tdisplay this help message"
+ print "\t:q\t\tquit interactive mode"
+ print ""
+ end
+
+ # Prompts the user for a command.
+ fun prompt do
+ printn ">> "
+ do_query(sys.stdin.read_line)
+ prompt
+ end
+
+ # Processes the query string and performs it.
+ fun do_query(str: String) do
+ var query = parse_query(str)
+ var res = query.perform(self, doc)
+ var page = query.make_results(self, res)
+ print page.write_to_string
+ end
+
+ # Returns an `NitxQuery` from a raw query string.
+ fun parse_query(str: String): NitxQuery do
+ var query = new NitxQuery(str)
+ if query isa NitxCommand then
+ query.execute(self)
+ end
+ return query
+ end
+end
+
+# A query performed on Nitx.
+#
+# Queries are responsible to collect matching results and render them as a
+# DocPage.
+#
+# Used as a factory to concrete instances.
+interface NitxQuery
+
+ # Original query string.
+ fun query_string: String is abstract
+
+ # Query factory.
+ #
+ # Will return a concrete instance of NitxQuery.
+ new(query_string: String) do
+ if query_string == ":q" then
+ return new NitxQuit
+ else if query_string == ":h" then
+ return new NitxHelp
+ else if query_string.has_prefix("comment:") then
+ return new CommentQuery(query_string)
+ end
+ return new CommentQuery("comment: {query_string}")
+ end
+
+ # Looks up the `doc` model and returns possible matches.
+ fun perform(nitx: Nitx, doc: DocModel): Array[NitxMatch] is abstract
+
+ # Pretty prints the results for the console.
+ fun make_results(nitx: Nitx, results: Array[NitxMatch]): DocPage do
+ var page = new DocPage("Results")
+ page.root.add_child(new QueryResultArticle(self, results))
+ return page
+ end
+
+ redef fun to_s do return query_string
+end
+
+# Something that matches a `NitxQuery`.
+abstract class NitxMatch
+
+ # Query matched by `self`.
+ var query: NitxQuery
+
+ # Pretty prints `self` for console.
+ fun make_list_item: String is abstract
+end
+
+# A query that contains a meta command.
+#
+# In Nitx, commands are written such as `command: args...`.
+abstract class MetaQuery
+ super NitxQuery
+
+ redef var query_string
+
+ # Meta command used.
+ var command: String is noinit
+
+ # Arguments passed to the `command`.
+ var args = new Array[String]
+
+ init do
+ # parse command
+ var str = new FlatBuffer
+ var i = 0
+ while i < query_string.length do
+ var c = query_string[i]
+ i += 1
+ if c == ':' then break
+ str.add c
+ end
+ command = str.write_to_string
+ # parse args
+ args.add query_string.substring_from(i).trim
+ end
+end
+
+# A match between a `NitxQuery` and a `MEntity`.
+class MEntityMatch
+ super NitxMatch
+
+ # MEntity matched.
+ var mentity: MEntity
+
+ redef fun make_list_item do return mentity.cs_list_item
+end
+
+# A query to search a `MEntity` comment by its name or namespace.
+class CommentQuery
+ super MetaQuery
+
+ redef fun perform(nitx, doc) do
+ var name = args.first
+ var res = new Array[NitxMatch]
+ for mentity in doc.search_mentities(name) do
+ res.add new MEntityMatch(self, mentity)
+ end
+ return res
+ end
+
+ redef fun make_results(nitx, results) do
+ var len = results.length
+ if len == 1 then
+ var res = results.first.as(MEntityMatch)
+ var mentity = res.mentity
+ var page = new DocPage("Results")
+ var article = new DefinitionArticle(mentity)
+ article.cs_title = mentity.name
+ article.cs_subtitle = mentity.cs_declaration
+ page.root.add_child article
+ return page
+ else
+ return super
+ end
+ end
+end
+
+# A query that contains a nitx command.
+#
+# These commands are prefixed with `:` and are used to control the execution of
+# `nitx` like displaying the help or quiting.
+interface NitxCommand
+ super NitxQuery
+
+ # Executes the command.
+ fun execute(nitx: Nitx) is abstract
+end
+
+# Exits nitx.
+class NitxQuit
+ super NitxCommand
+
+ redef fun execute(nitx) do exit 0
+end
+
+# Displays the help message.
+class NitxHelp
+ super NitxCommand
+
+ redef fun execute(nitx) do nitx.help
+end
+
+## exploration
+
+redef class DocModel
+
+ # Lists all MEntities in the model.
+ private var mentities: Collection[MEntity] is lazy do
+ var res = new HashSet[MEntity]
+ res.add_all mprojects
+ res.add_all mgroups
+ res.add_all mmodules
+ res.add_all mclasses
+ res.add_all mclassdefs
+ res.add_all mproperties
+ res.add_all mpropdefs
+ return res
+ end
+
+ # Search MEntities that match `name` by their name or namespace.
+ private fun search_mentities(name: String): Array[MEntity] do
+ var res = new Array[MEntity]
+ for mentity in mentities do
+ if mentity.name != name and mentity.cs_namespace != name then continue
+ res.add mentity
+ end
+ return res
+ end
+end
+
+# display
+
+# A `DocArticle` that displays query results.
+private class QueryResultArticle
+ super DocArticle
+
+ # Query linked to the results to display.
+ var query: NitxQuery
+
+ # Results to display.
+ var results: Array[NitxMatch]
+
+ redef fun render_title do
+ var len = results.length
+ if len == 0 then
+ add "No result found for '{query.query_string}'..."
+ else
+ add "# {len} result(s) for '{query.query_string}'".green.bold
+ end
+ end
+
+ redef fun render_body do
+ addn ""
+ for result in results do
+ addn ""
+ addn result.make_list_item
+ end
+ end
+end
+
+# A Pager is used to display data into a unix `less` container.
+private class Pager
+
+ # Content to display.
+ var content = new FlatBuffer
+
+ # Adds text to the pager.
+ fun add(text: String) do
+ content.append(escape(text))
+ end
+
+ fun render do sys.system("echo \"{content}\" | less -r")
+
+ fun escape(str: String): String
+ do
+ var b = new FlatBuffer
+ for c in str.chars do
+ if c == '\n' then
+ b.append("\\n")
+ else if c == '\0' then
+ b.append("\\0")
+ else if c == '"' then
+ b.append("\\\"")
+ else if c == '\\' then
+ b.append("\\\\")
+ else if c == '`' then
+ b.append("'")
+ else if c.ascii < 32 then
+ b.append("\\{c.ascii.to_base(8, false)}")
+ else
+ b.add(c)
+ end
+ end
+ return b.to_s
+ end
+end
redef fun provide_default_value(key) do
var v = new QuickSearchResultList
+ assert key isa String
self[key] = v
return v
end
module doc_structure
import doc_concerns
+import modelize
# StructurePhase populates the DocPage content with section and article.
#
mentity.intro_mmodule.mgroup.mproject.booster_rank = 0
mentity.intro_mmodule.mgroup.booster_rank = 0
mentity.intro_mmodule.booster_rank = 0
+ var constructors = new ConstructorsSection(mentity)
+ var minit = mentity.root_init
+ if minit != null then
+ constructors.add_child new DefinitionArticle(minit)
+ end
+ section.add_child constructors
section.add_child new ConcernsArticle(mentity, concerns)
for mentity in concerns do
var ssection = new ConcernSection(mentity)
v.name_sorter.sort(group)
for mprop in group do
for mpropdef in mpropdefs_for(mprop, mentity) do
- ssection.add_child new DefinitionArticle(mpropdef)
+ if mpropdef isa MMethodDef and mpropdef.mproperty.is_init then
+ if mpropdef == minit then continue
+ constructors.add_child new DefinitionArticle(mpropdef)
+ else
+ ssection.add_child new DefinitionArticle(mpropdef)
+ end
end
end
end
var mentity: MEntity
end
+# A list of constructors.
+class ConstructorsSection
+ super MEntitySection
+end
+
# A Section about a Concern.
#
# Those sections are used to build the page summary.
# FIXME annotation should be handled in their own way
redef fun html_modifiers do
+ if mproperty.is_init then
+ var res = new Array[String]
+ if mproperty.visibility != public_visibility then
+ res.add mproperty.visibility.to_s
+ end
+ return res
+ end
var res = super
if is_abstract then
res.add "abstract"
else if is_intern then
res.add "intern"
end
+ res.add "fun"
+ return res
+ end
+
+ redef fun html_declaration do
if mproperty.is_init then
- res.add "init"
- else
- res.add "fun"
+ var tpl = new Template
+ tpl.add "<span>"
+ tpl.add html_modifiers.join(" ")
+ tpl.add " "
+ tpl.add html_link
+ tpl.add html_signature
+ tpl.add "</span>"
+ return tpl
end
- return res
+ return super
+ end
+
+ redef fun html_short_signature do
+ if mproperty.is_root_init and new_msignature != null then
+ return new_msignature.html_short_signature
+ end
+ return msignature.html_short_signature
end
- redef fun html_short_signature do return msignature.html_short_signature
- redef fun html_signature do return msignature.html_signature
+ redef fun html_signature do
+ if mproperty.is_root_init and new_msignature != null then
+ return new_msignature.html_signature
+ end
+ return msignature.html_signature
+ end
end
redef class MVirtualTypeProp
end
# Level <hX> for HTML heading.
- private fun hlvl: Int do
- if parent == null then return 0
- return parent.hlvl + 1
- end
+ private fun hlvl: Int do return depth
# Is `self` not displayed in the page.
#
redef var html_subtitle is lazy do return mentity.html_declaration
end
+redef class ConstructorsSection
+ redef var html_id is lazy do return "article:{mentity.nitdoc_id}.constructors"
+ redef var html_title = "Constructors"
+ redef var html_subtitle = null
+ redef fun is_toc_hidden do return is_empty
+end
+
redef class ConcernSection
redef var html_id is lazy do return "concern:{mentity.nitdoc_id}"
redef var html_title is lazy do return "in {mentity.nitdoc_name}"
# Add a constructor to the automated nclassdef
fun generate_deserialization_init(nclassdef: AStdClassdef)
do
- # Do not generate constructors for abstract classes
- if nclassdef.n_classkind isa AAbstractClasskind then return
-
var npropdefs = nclassdef.n_propdefs
var code = new Array[String]
- code.add "init from_deserializer(v: Deserializer)"
+ code.add "redef init from_deserializer(v: Deserializer)"
code.add "do"
+ code.add " super"
code.add " v.notify_of_creation self"
for attribute in npropdefs do if attribute isa AAttrPropdef then
code.add ""
code.add "\tvar {name} = v.deserialize_attribute(\"{name}\")"
- code.add "\tassert {name} isa {type_name} else print \"Unsupported type for attribute '{name}', got '\{{name}.class_name\}' (ex {type_name})\""
+ code.add "\tassert {name} isa {type_name} else print \"Unsupported type for `\{class_name\}::{name}`, got '\{{name}.class_name\}'; expected {type_name}\""
code.add "\tself.{name} = {name}"
end
# End of this location on `line_end`
var column_end: Int
+ # Builds a location instance from its string representation.
+ #
+ # Examples:
+ #
+ # ~~~
+ # var loc = new Location.from_string("location.nit:82,2--105,8")
+ # assert loc.to_s == "location.nit:82,2--105,8"
+ #
+ # loc = new Location.from_string("location.nit")
+ # assert loc.to_s == "location.nit"
+ #
+ # loc = new Location.from_string("location.nit:82,2")
+ # assert loc.to_s == "location.nit:82,2--0,0"
+ #
+ # loc = new Location.from_string("location.nit:82--105")
+ # assert loc.to_s == "location.nit:82,0--105,0"
+ #
+ # loc = new Location.from_string("location.nit:82,2--105")
+ # assert loc.to_s == "location.nit:82,2--105,0"
+ #
+ # loc = new Location.from_string("location.nit:82--105,8")
+ # assert loc.to_s == "location.nit:82,0--105,8"
+ # ~~~
+ init from_string(string: String) do
+ self.line_start = 0
+ self.line_end = 0
+ self.column_start = 0
+ self.column_end = 0
+ # parses the location string and init position vars
+ var parts = string.split_with(":")
+ var filename = parts.shift
+ self.file = new SourceFile(filename, new FileReader.open(filename))
+ # split position
+ if parts.is_empty then return
+ var pos = parts.first.split_with("--")
+ # split start position
+ if pos.first.has(",") then
+ var pos1 = pos.first.split_with(",")
+ self.line_start = pos1[0].to_i
+ if pos1.length > 1 then
+ self.column_start = pos1[1].to_i
+ end
+ else
+ self.line_start = pos.first.to_i
+ end
+ # split end position
+ if pos.length <= 1 then return
+ if pos[1].has(",") then
+ var pos2 = pos[1].split_with(",")
+ if pos2.length > 1 then
+ self.line_end = pos2[0].to_i
+ self.column_end = pos2[1].to_i
+ else
+ self.line_end = self.line_start
+ self.column_end = pos2[0].to_i
+ end
+ else
+ self.line_end = pos[1].to_i
+ end
+ end
+
# The index in the start character in the source
fun pstart: Int do return file.line_starts[line_start-1] + column_start-1
# Get a `Location` from its string representation.
private fun to_location(loc: String): Location do
- #TODO filepath
- var parts = loc.split_with(":")
- var file = new SourceFile.from_string(parts[0], "")
- if parts.length == 1 then
- return new Location(file, 0, 0, 0, 0)
- end
- var pos = parts[1].split_with("--")
- var pos1 = pos[0].split_with(",")
- var pos2 = pos[1].split_with(",")
- var line_s = pos1[0].to_i
- var line_e = pos2[0].to_i
- var column_s = pos1[1].to_i
- var column_e = 0
- if pos2.length == 2 then pos2[1].to_i
- return new Location(file, line_s, line_e, column_s, column_e)
+ return new Location.from_string(loc)
end
# Get a `MVisibility` from its string representation.
<C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl false> 1111 qwer>>
# Json:
-{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array", "__length": 3, "__items": [88, "hello", null]}}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "array": {"__kind": "ref", "__id": 2}, "ii": 1111, "ss": "qwer"}, "aa": {"__kind": "ref", "__id": 1}}
+{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array", "__length": 3, "__items": [88, "hello", null]}}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "array": {"__kind": "obj", "__id": 4, "__class": "Array", "__length": 3, "__items": [88, "hello", null]}, "ii": 1111, "ss": "qwer"}, "aa": {"__kind": "ref", "__id": 1}}
# Nit:
<D: <B: <A: false b 123.123 2345 new line ->
<C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl false> 1111 qwer>>
# Json:
-{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array", "__length": 3, "__items": [88, "hello", null]}}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "array": {"__kind": "ref", "__id": 2}, "ii": 1111, "ss": "qwer"}, "aa": {"__kind": "ref", "__id": 1}}
+{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array", "__length": 3, "__items": [88, "hello", null]}}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "array": {"__kind": "obj", "__id": 4, "__class": "Array", "__length": 3, "__items": [88, "hello", null]}, "ii": 1111, "ss": "qwer"}, "aa": {"__kind": "ref", "__id": 1}}
# Nit:
<D: <B: <A: false b 123.123 2345 new line ->
<C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl false> 1111 qwer>>
# Json:
-{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array", "__length": 3, "__items": [88, "hello", null]}, "iii": 6789, "sss": "redef"}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "array": {"__kind": "ref", "__id": 2}, "iii": 6789, "sss": "redef", "ii": 1111, "ss": "qwer"}, "aa": {"__kind": "ref", "__id": 1}}
+{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array", "__length": 3, "__items": [88, "hello", null]}, "iii": 6789, "sss": "redef"}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "array": {"__kind": "obj", "__id": 4, "__class": "Array", "__length": 3, "__items": [88, "hello", null]}, "iii": 6789, "sss": "redef", "ii": 1111, "ss": "qwer"}, "aa": {"__kind": "ref", "__id": 1}}
# Nit:
<D: <B: <A: false b 123.123 2345 new line ->
<C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl false> 1111 qwer>>
# Json:
-{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array", "__length": 3, "__items": [88, "hello", null]}}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "array": {"__kind": "ref", "__id": 2}, "ii": 1111, "ss": "qwer", "ffff": 6.789, "bbbb": false}, "aa": {"__kind": "ref", "__id": 1}}
+{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array", "__length": 3, "__items": [88, "hello", null]}}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "array": {"__kind": "obj", "__id": 4, "__class": "Array", "__length": 3, "__items": [88, "hello", null]}, "ii": 1111, "ss": "qwer", "ffff": 6.789, "bbbb": false}, "aa": {"__kind": "ref", "__id": 1}}
# Nit:
<D: <B: <A: false b 123.123 2345 new line ->
<C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl false> 1111 qwer>>
# Json:
-{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array", "__length": 3, "__items": [88, "hello", null]}, "iii": 6789, "sss": "redef"}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "array": {"__kind": "ref", "__id": 2}, "iii": 6789, "sss": "redef", "ii": 1111, "ss": "qwer", "ffff": 6.789, "bbbb": false}, "aa": {"__kind": "ref", "__id": 1}}
+{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array", "__length": 3, "__items": [88, "hello", null]}, "iii": 6789, "sss": "redef"}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "array": {"__kind": "obj", "__id": 4, "__class": "Array", "__length": 3, "__items": [88, "hello", null]}, "iii": 6789, "sss": "redef", "ii": 1111, "ss": "qwer", "ffff": 6.789, "bbbb": false}, "aa": {"__kind": "ref", "__id": 1}}
# Nit:
<D: <B: <A: false b 123.123 2345 new line ->
if name == "Array[nullable Object]" then return new Array[nullable Object].from_deserializer(self)
if name == "Array[Serializable]" then return new Array[Serializable].from_deserializer(self)
if name == "Array[String]" then return new Array[String].from_deserializer(self)
+ if name == "StrictHashMap[Serializable, Int]" then return new StrictHashMap[Serializable, Int].from_deserializer(self)
+ if name == "HashMap[Serializable, Array[Couple[Serializable, Int]]]" then return new HashMap[Serializable, Array[Couple[Serializable, Int]]].from_deserializer(self)
+ if name == "Array[Couple[Serializable, Int]]" then return new Array[Couple[Serializable, Int]].from_deserializer(self)
return super
end
end
--- /dev/null
+no error
+hello
+77
+1.235
+1.235
+123456789
+no error
--- /dev/null
+no error
+hello
+77
+144545136640.0
+0.0
+1571011930645069824
+no error
--- /dev/null
+no error
+hello
+77
+144545136640.0
+0.0
+1571011930645069824
+no error
--- /dev/null
+no error
+hello
+77
+1.235
+1.235
+123456789
+no error
--- /dev/null
+true
+true
+true
+true
+true
+true
+
+true
+true
+true
+
+true
+hello
# Back in Nit:
<E: 33.33>
+# Nit:
+<G: hs: -1, 0; s: one, two; hm: one. 1, two. 2; am: three. 3, four. 4>
+
+# Json:
+{"__kind": "obj", "__id": 0, "__class": "G", "hs": {"__kind": "obj", "__id": 1, "__class": "HashSet[Int]", "__length": 2, "__items": [-1, 0]}, "s": {"__kind": "obj", "__id": 2, "__class": "ArraySet[String]", "__length": 2, "__items": ["one", "two"]}, "hm": {"__kind": "obj", "__id": 3, "__class": "HashMap[String, Int]", "__length": 2, "__keys": ["one", "two"], "__values": [1, 2]}, "am": {"__kind": "obj", "__id": 4, "__class": "ArrayMap[String, String]", "__length": 2, "__keys": ["three", "four"], "__values": ["3", "4"]}}
+
+# Back in Nit:
+<G: hs: -1, 0; s: one, two; hm: one. 1, two. 2; am: three. 3, four. 4>
+
<C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl false> 1111 qwer>>
# Json:
-{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array[nullable Object]", "__length": 3, "__items": [88, "hello", null]}}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "array": {"__kind": "ref", "__id": 2}, "ii": 1111, "ss": "qwer"}, "aa": {"__kind": "ref", "__id": 1}}
+{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array[nullable Object]", "__length": 3, "__items": [88, "hello", null]}}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "array": {"__kind": "obj", "__id": 4, "__class": "Array[nullable Object]", "__length": 3, "__items": [88, "hello", null]}, "ii": 1111, "ss": "qwer"}, "aa": {"__kind": "ref", "__id": 1}}
# Nit:
<D: <B: <A: false b 123.123 2345 new line ->
<C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl false> 1111 qwer>>
# Json:
-{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array[nullable Object]", "__length": 3, "__items": [88, "hello", null]}}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "array": {"__kind": "ref", "__id": 2}, "ii": 1111, "ss": "qwer"}, "aa": {"__kind": "ref", "__id": 1}}
+{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array[nullable Object]", "__length": 3, "__items": [88, "hello", null]}}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "array": {"__kind": "obj", "__id": 4, "__class": "Array[nullable Object]", "__length": 3, "__items": [88, "hello", null]}, "ii": 1111, "ss": "qwer"}, "aa": {"__kind": "ref", "__id": 1}}
# Nit:
<D: <B: <A: false b 123.123 2345 new line ->
<C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl false> 1111 qwer>>
# Json:
-{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array[nullable Object]", "__length": 3, "__items": [88, "hello", null]}, "iii": 6789, "sss": "redef"}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "array": {"__kind": "ref", "__id": 2}, "iii": 6789, "sss": "redef", "ii": 1111, "ss": "qwer"}, "aa": {"__kind": "ref", "__id": 1}}
+{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array[nullable Object]", "__length": 3, "__items": [88, "hello", null]}, "iii": 6789, "sss": "redef"}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "array": {"__kind": "obj", "__id": 4, "__class": "Array[nullable Object]", "__length": 3, "__items": [88, "hello", null]}, "iii": 6789, "sss": "redef", "ii": 1111, "ss": "qwer"}, "aa": {"__kind": "ref", "__id": 1}}
# Nit:
<D: <B: <A: false b 123.123 2345 new line ->
<C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl false> 1111 qwer>>
# Json:
-{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array[nullable Object]", "__length": 3, "__items": [88, "hello", null]}}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "array": {"__kind": "ref", "__id": 2}, "ii": 1111, "ss": "qwer", "ffff": 6.789, "bbbb": false}, "aa": {"__kind": "ref", "__id": 1}}
+{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array[nullable Object]", "__length": 3, "__items": [88, "hello", null]}}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "array": {"__kind": "obj", "__id": 4, "__class": "Array[nullable Object]", "__length": 3, "__items": [88, "hello", null]}, "ii": 1111, "ss": "qwer", "ffff": 6.789, "bbbb": false}, "aa": {"__kind": "ref", "__id": 1}}
# Nit:
<D: <B: <A: false b 123.123 2345 new line ->
<C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl false> 1111 qwer>>
# Json:
-{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array[nullable Object]", "__length": 3, "__items": [88, "hello", null]}, "iii": 6789, "sss": "redef"}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "array": {"__kind": "ref", "__id": 2}, "iii": 6789, "sss": "redef", "ii": 1111, "ss": "qwer", "ffff": 6.789, "bbbb": false}, "aa": {"__kind": "ref", "__id": 1}}
+{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array[nullable Object]", "__length": 3, "__items": [88, "hello", null]}, "iii": 6789, "sss": "redef"}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "array": {"__kind": "obj", "__id": 4, "__class": "Array[nullable Object]", "__length": 3, "__items": [88, "hello", null]}, "iii": 6789, "sss": "redef", "ii": 1111, "ss": "qwer", "ffff": 6.789, "bbbb": false}, "aa": {"__kind": "ref", "__id": 1}}
# Nit:
<D: <B: <A: false b 123.123 2345 new line ->
--- /dev/null
+# This file is part of NIT (http://www.nitlanguage.org).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import binary
+
+var path = "/tmp/bin"
+var writer = new FileWriter.open(path)
+#alt1# writer.big_endian = false
+#alt3# writer.big_endian = false
+writer.write "hello"
+writer.write_byte 77
+writer.write_float 1.23456789
+writer.write_double 1.23456789
+writer.write_int64 123456789
+print writer.last_error or else "no error"
+writer.close
+
+var reader = new FileReader.open(path)
+#alt2# reader.big_endian = false
+#alt3# reader.big_endian = false
+print reader.read(5)
+print reader.read_byte or else "null"
+print reader.read_float
+print reader.read_double
+print reader.read_int64
+print reader.last_error or else "no error"
+reader.close
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+var a = [1,2,3]
+var l = new List[Int].from(a)
+var l2 = new List[Object].from(a)
+
+print a == l
+print a == l2
+print l == a
+print l == l2
+print l2 == a
+print l2 == l
+print ""
+
+var aa = [a]
+print aa.has(a)
+print aa.has(l)
+print aa.has(l2)
+print ""
+
+var map = new Map[List[Int], String]
+map[l] = "hello"
+
+var mapr: MapRead[Object, String] = map
+print mapr.has_key(a)
+print mapr[l2]
redef fun to_s do return "<E: a: {a.join(", ")}; b: {b.join(", ")}>"
end
-# Class with generics
+# Parameterized class
class F[N: Numeric]
auto_serializable
redef fun to_s do return "<E: {n}>"
end
+# Other collections
+class G
+ auto_serializable
+
+ var hs = new HashSet[Int]
+ var s: Set[String] = new ArraySet[String]
+ var hm = new HashMap[String, Int]
+ var am = new ArrayMap[String, String]
+
+ init
+ do
+ hs.add -1
+ hs.add 0
+ s.add "one"
+ s.add "two"
+ hm["one"] = 1
+ hm["two"] = 2
+ am["three"] = 3.to_s
+ am["four"] = 4.to_s
+ end
+
+ redef fun to_s do return "<G: hs: {hs.join(", ")}; s: {s.join(", ")}; "+
+ "hm: {hm.join(", ", ". ")}; am: {am.join(", ", ". ")}>"
+end
+
var a = new A(true, 'a', 0.1234, 1234, "asdf", null)
var b = new B(false, 'b', 123.123, 2345, "hjkl", 12, 1111, "qwer")
var c = new C(a, b)
var e = new E
var fi = new F[Int](2222)
var ff = new F[Float](33.33)
+var g = new G
# Default works only with Nit serial
-var tests = new Array[Serializable].with_items(a, b, c, d, e, fi, ff)
+var tests = [a, b, c, d, e, fi, ff, g: Serializable]
# Alt1 should work without nitserial
#alt1# tests = new Array[Serializable].with_items(a, b, c, d)
redef class Deserializer
redef fun deserialize_class(name)
do
- if name == "Array[Object]" then return new Array[Object].from_deserializer(self)
- if name == "Array[nullable Serializable]" then return new Array[nullable Serializable].from_deserializer(self)
- if name == "F[Int]" then return new F[Int].from_deserializer(self)
- if name == "F[Float]" then return new F[Float].from_deserializer(self)
- if name == "Array[Serializable]" then return new Array[Serializable].from_deserializer(self)
- if name == "Array[String]" then return new Array[String].from_deserializer(self)
- if name == "Array[HashMap[String, nullable Object]]" then return new Array[HashMap[String, nullable Object]].from_deserializer(self)
- if name == "Array[Match]" then return new Array[Match].from_deserializer(self)
- if name == "Array[nullable Object]" then return new Array[nullable Object].from_deserializer(self)
- if name == "Array[FlatBuffer]" then return new Array[FlatBuffer].from_deserializer(self)
+ # Module: test_deserialization
+ if name == "Array[Object]" then return new Array[Object].from_deserializer(self)
+ if name == "Array[nullable Serializable]" then return new Array[nullable Serializable].from_deserializer(self)
+ if name == "F[Int]" then return new F[Int].from_deserializer(self)
+ if name == "F[Float]" then return new F[Float].from_deserializer(self)
+ if name == "HashSet[Int]" then return new HashSet[Int].from_deserializer(self)
+ if name == "ArraySet[String]" then return new ArraySet[String].from_deserializer(self)
+ if name == "HashMap[String, Int]" then return new HashMap[String, Int].from_deserializer(self)
+ if name == "ArrayMap[String, String]" then return new ArrayMap[String, String].from_deserializer(self)
+ if name == "Array[Serializable]" then return new Array[Serializable].from_deserializer(self)
+ if name == "Array[String]" then return new Array[String].from_deserializer(self)
+ if name == "HashMap[Serializable, Int]" then return new HashMap[Serializable, Int].from_deserializer(self)
+ if name == "Array[JsonObject]" then return new Array[JsonObject].from_deserializer(self)
+ if name == "HashMap[Int, Object]" then return new HashMap[Int, Object].from_deserializer(self)
+ if name == "Array[Node]" then return new Array[Node].from_deserializer(self)
+ if name == "Array[LRState]" then return new Array[LRState].from_deserializer(self)
+ if name == "Array[Couple[String, String]]" then return new Array[Couple[String, String]].from_deserializer(self)
+ if name == "Array[nullable Jsonable]" then return new Array[nullable Jsonable].from_deserializer(self)
return super
end
end