Intro a new serialization engine to pretty print the attributes of the receiver on calls to `inspect`. The format is designed to be helpful, reproducible and compact.
Note that, asides from direct calls to `inspect`, this will also affect the default behavior of `Serializable::to_s`. And this will not affect all Nit programs, but it will affect all those importing the serialization services, so most larger projects using either `json`, `nitcorn`, `gamnit`, `more_collections`, etc.
Simple immutable data are inspected as they would be written in Nit code:
~~~
assert 123.inspect == "123"
assert 1.5.inspect == "1.5"
assert 0xa1u8.inspect == "0xa1u8"
assert 'c'.inspect == "'c'"
assert "asdf\n".inspect == "\"asdf\\n\""
~~~
Inspections of mutable object show their dynamic type and an id unique to each call to `inspect`. A change from using their `object_id`.
Items of collections are flattened:
~~~
assert [1, 2, 3].inspect == "<Array[Int]#0 [1, 2, 3]>"
var set = new HashSet[Object].from([1, 1.5, "two": Object])
assert set.inspect == """<HashSet[Object]#0 [1, 1.5, "two"]>"""
var map = new Map[Int, String]
map[1] = "one"
map[2] = "two"
assert map.inspect == """<HashMap[Int, String]#0 {1:"one", 2:"two"}>"""
~~~
Inspecting other `Serializable` classes shows the first level attributes only:
~~~
class MyClass
serialize
var i: Int
var o: nullable Object
end
var class_with_null = new MyClass(123)
assert class_with_null.to_s == class_with_null.inspect
assert class_with_null.to_s == "<MyClass#0 i:123, o:null>"
var class_with_other = new MyClass(456, class_with_null)
assert class_with_other.to_s == "<MyClass#0 i:456, o:<MyClass#1>>"
var class_with_cycle = new MyClass(789)
class_with_cycle.o = class_with_cycle
assert class_with_cycle.to_s == "<MyClass#0 i:789, o:<MyClass#0>>"
~~~
Inspections producing over 80 characters are cut short:
~~~
var long_class = new MyClass(
123456789, "Some " + "very "*8 + "long string")
assert long_class.to_s == "<MyClass#0 i:
123456789, o:\"Some very very very very very very very very long s…>"
~~~
Pull-Request: #2548
Reviewed-by: Jean Privat <jean@pryen.org>
import social_views
import user_views
-redef class App
- redef fun on_create
- do
- if debug then print "App::on_create"
-
- # Create the main window
- show_home
- super
- end
-
- # Show the home/main windows
- fun show_home
- do
- var window = new HomeWindow
- window.refresh
- push_window window
- end
+redef fun root_window do return new HomeWindow
+redef class App
redef fun on_log_in
do
super
# Delay in seconds before the next request after an error
fun request_delay_on_error: Float do return 60.0
-redef class App
- redef fun on_create
- do
- # Create the main window
- push_window new TnitterWindow
- super
- end
-end
-
-# Main window
-class TnitterWindow
- super Window
+redef class Window
private var layout = new VerticalLayout(parent=self)
private var list_posts = new ListLayout(parent=layout)
abstract class AsyncTnitterRequest
super AsyncHttpRequest
- private var window: TnitterWindow
+ private var window: Window
redef fun uri_root do return tnitter_server_uri
height="512"
id="svg2"
version="1.1"
- inkscape:version="0.48.5 r10040"
- sodipodi:docname="icon_sci.svg">
+ inkscape:version="0.92.1 r15371"
+ sodipodi:docname="icon-sci.svg">
<defs
id="defs4" />
<sodipodi:namedview
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.4"
- inkscape:cx="164.13268"
+ inkscape:cx="165.56125"
inkscape:cy="278.8294"
inkscape:document-units="px"
inkscape:current-layer="layer1"
id="path2999"
style="" />
</g>
- <text
- xml:space="preserve"
- style="font-size:190.84666443px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Droid Sans;-inkscape-font-specification:Droid Sans"
- x="318.1875"
- y="744.69647"
- id="text2994"
- sodipodi:linespacing="125%"><tspan
- sodipodi:role="line"
- id="tspan2996"
- x="318.1875"
- y="744.69647">√</tspan></text>
- <text
- xml:space="preserve"
- style="font-size:190.84666443px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Droid Sans;-inkscape-font-specification:Droid Sans"
- x="72.978729"
- y="725.84735"
- id="text2998"
- sodipodi:linespacing="125%"><tspan
- sodipodi:role="line"
- id="tspan3000"
- x="72.978729"
- y="725.84735">π</tspan></text>
+ <path
+ inkscape:connector-curvature="0"
+ id="path4504"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:125%;font-family:'Noto Sans';-inkscape-font-specification:'Noto Sans';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
+ d="M 376.61565,748.14397 H 364.2218 L 338.40905,675.6446 h -16.77364 v -13.32572 h 27.30375 l 21.52616,62.80794 47.71167,-136.14598 h 13.60528 z" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4507"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:125%;font-family:'Noto Sans';-inkscape-font-specification:'Noto Sans';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
+ d="m 181.35503,716.1555 q 4.93891,0 8.38682,-1.86373 v 13.13934 q -4.56616,2.42286 -12.58022,2.42286 -21.15342,0 -21.15342,-24.41495 V 639.6491 h -43.33188 v 88.34113 H 96.089068 V 639.6491 H 75.3084 v -7.26857 l 13.698467,-6.70945 H 192.63064 v 13.97802 h -20.03517 v 64.67168 q 0,11.83472 8.75956,11.83472 z" />
<flowRoot
xml:space="preserve"
id="flowRoot3002"
height="282.85715"
x="-225"
y="-62.285713" /></flowRegion><flowPara
- id="flowPara3008"></flowPara></flowRoot> <text
- xml:space="preserve"
- style="font-size:190.84666443px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Droid Sans;-inkscape-font-specification:Droid Sans"
- x="303.01755"
- y="973.79059"
- id="text3014"
- sodipodi:linespacing="125%"><tspan
- sodipodi:role="line"
- id="tspan3016"
- x="303.01755"
- y="973.79059">x²</tspan></text>
+ id="flowPara3008" /></flowRoot> <path
+ inkscape:connector-curvature="0"
+ id="path4499"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:125%;font-family:'Noto Sans';-inkscape-font-specification:'Noto Sans';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
+ d="m 335.60349,924.36988 -35.31781,-50.04133 h 19.1033 l 26.37188,38.57935 26.09232,-38.57935 h 18.91693 l -35.31782,50.04133 37.27474,52.27783 h -19.1033 l -27.86287,-40.81584 -28.23562,40.81584 h -18.91693 z" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4501"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:125%;font-family:'Noto Sans';-inkscape-font-specification:'Noto Sans';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
+ d="m 454.6031,922.04021 h -53.862 v -10.25055 l 21.99209,-21.43298 q 11.83473,-11.46198 15.09627,-16.68044 3.35473,-5.31165 3.35473,-11.74154 0,-6.05715 -3.5411,-9.13232 -3.44791,-3.07516 -9.2255,-3.07516 -5.77758,0 -10.43693,2.23648 -4.65934,2.23649 -9.59824,5.96396 l -6.70945,-8.75956 q 12.39385,-10.53012 26.931,-10.53012 12.30066,0 19.19649,6.15034 6.98901,6.05714 6.98901,16.40088 0,4.2866 -1.21143,8.01407 -1.11824,3.63429 -3.5411,7.45495 -2.42286,3.72747 -6.33671,8.01407 -3.82066,4.28659 -26.37187,25.53319 h 37.27474 z" />
</g>
</svg>
# Show debug output?
fun debug: Bool do return false
-redef class App
- redef fun on_create
- do
- if debug then print "App::on_create"
-
- # Create the main window
- push_window new CalculatorWindow
- super
- end
-end
+redef fun root_window do return new CalculatorWindow
# The main (and only) window of this calculator
class CalculatorWindow
module data_store
import app::data_store
-private import shared_preferences
+import shared_preferences
-redef class App
- redef var data_store = new SharedPreferenceView
-end
-
-private class SharedPreferenceView
- super DataStore
+redef class DataStore
# The `SharedPreferences` used to implement the `DataStore`
var shared_preferences = new SharedPreferences.privately(app, "data_store") is lazy
if serialized_string == "" then return null
var deserializer = new JsonDeserializer(serialized_string)
- return deserializer.deserialize
+ var deserialized = deserializer.deserialize
+
+ var errors = deserializer.errors
+ if errors.not_empty then
+ # An update may have broken the versioning compatibility
+ print_error "{class_name} error at deserialization: {errors.join(", ")}"
+ return null # Let's be safe
+ end
+
+ return deserialized
end
end
# See the License for the specific language governing permissions and
# limitations under the License.
-# Simple data storage services
+# Key/value storage services
#
-# The implementation varies per platform.
+# The main services is `App::data_store`, a `DataStore` holding any
+# serializable Nit object.
module data_store
import app_base
import ios::data_store is conditional(ios)
redef class App
+
# Services to store and load data
- fun data_store: DataStore is abstract
+ #
+ # ~~~
+ # import app::ui
+ # import app::data_store
+ #
+ # class MyWindow
+ # super Window
+ #
+ # var state = "Simple string or any serializable class"
+ #
+ # redef fun on_save_state do app.data_store["state"] = state
+ #
+ # redef fun on_restore_state
+ # do
+ # var state = app.data_store["state"]
+ # if state isa String then self.state = state
+ # end
+ # end
+ # ~~~
+ var data_store = new DataStore is lazy
end
# Simple data storage facility
-interface DataStore
+#
+# Write values with `[]=` and read with `[]`.
+# ~~~
+# import linux::data_store # Needed for testing only
+#
+# class A
+# serialize
+#
+# var b = true
+# var f = 1.234
+# end
+#
+# var data_store = new DataStore
+# data_store["one"] = 1
+# data_store["str"] = "Some string"
+# data_store["a"] = new A
+#
+# assert data_store["one"] == 1
+# assert data_store["str"] == "Some string"
+# assert data_store["a"].as(A).b
+# assert data_store["a"].as(A).f == 1.234
+# assert data_store["other"] == null
+# ~~~
+#
+# Set to `null` to clear a value.
+# ~~~
+# data_store["one"] = null
+# assert data_store["one"] == null
+# ~~~
+class DataStore
# Get the object stored at `key`, or null if nothing is available
fun [](key: String): nullable Object is abstract
module ui_example is
app_name "app.nit UI"
app_namespace "org.nitlanguage.ui_example"
- android_api_target 15
+ android_api_min 21
+ android_api_target 21
+ android_manifest_activity "android:theme=\"@android:style/Theme.Material\""
end
import app::ui
var layout = new ListLayout(parent=self)
# Some label
- var some_label = new Label(parent=layout, text="This Window uses a ListLayout.")
+ var some_label = new Label(parent=layout, text="Sample Window using a ListLayout.")
# A checkbox
var checkbox = new CheckBox(parent=layout, text="A CheckBox")
var another_label = new Label(parent=layout, text="Close it by tapping the back button.")
end
-redef class App
- redef fun on_create
- do
- # Create the main window
- push_window new UiExampleWindow
- super
- end
-end
+redef fun root_window do return new UiExampleWindow
# limitations under the License.
# HTTP request services: `AsyncHttpRequest` and `Text::http_get`
+#
+# ~~~nitish
+# import app::http_request
+#
+# class MyHttpRequest
+# super AsyncHttpRequest
+#
+# redef fun uri do return "http://example.com/"
+#
+# redef fun on_load(data, status) do print "Received: {data or else "null"}"
+#
+# redef fun on_fail(error) do print "Connection error: {error}"
+# end
+#
+# var req = new MyHttpRequest
+# req.start
+# ~~~
module http_request
import app_base
fun after do end
end
-# Minimal implementation of `AsyncHttpRequest` where `uri` is an attribute
+# Simple `AsyncHttpRequest` where `uri` is an attribute
+#
+# Prints on communication errors and when the remote server returns an
+# HTTP status code not in the 200s.
#
-# Prints on communication errors and when the server returns an HTTP status code not in the 200s.
+# This class can be instantiated to execute a request where the response is
+# ignored by the application. Alternatively, it can be subclassed to implement
+# `on_load`.
#
# ~~~nitish
# var request = new SimpleAsyncHttpRequest("http://example.com")
# See the License for the specific language governing permissions and
# limitations under the License.
-# Portable UI API for the _app.nit_ framework
+# Portable UI controls for mobiles apps
+#
+# ~~~
+# import app::ui
+#
+# class MyWindow
+# super Window
+#
+# var layout = new ListLayout(parent=self)
+# var lbl = new Label(parent=layout, text="Hello world", align=0.5)
+# var but = new Button(parent=layout, text="Press here")
+#
+# redef fun on_event(event) do lbl.text = "Pressed!"
+# end
+#
+# redef fun root_window do return new MyWindow
+# ~~~
module ui
import app_base
# This attribute is set by `push_window`.
var window: Window is noinit
- # Make visible and push `window` on the top of `pop_window`
+ # Make `window` visible and push it on the top of the `window_stack`
#
- # This method must be called at least once within `App::on_create`.
- # It can be called at any times while the app is active.
+ # This method can be called at any times while the app is active.
fun push_window(window: Window)
do
window_stack.add window
self.window = window
+ window.on_create
end
# Pop the current `window` from the stack and show the previous one
# Stack of active windows
var window_stack = new Array[Window]
- redef fun on_create do window.on_create
+ redef fun on_create
+ do
+ var window = root_window
+ push_window window
+ end
redef fun on_resume do window.on_resume
redef fun on_save_state do window.on_save_state
end
+# Hook to create the first window shown to the user
+#
+# By default, a `Window` is created, which can be refined to customize it.
+# However, most apps should refine this method to return a different window,
+# this way the app can have more than one window.
+fun root_window: Window do return new Window
+
# An event created by an `AppComponent` and sent to `AppObserver`s
interface AppEvent
end
end
# A window, root of the `Control` tree
+#
+# Each window should hold a single control, usually a `CompositeControl`,
+# which in turn holds all the displayed controls.
class Window
super CompositeControl
fun on_back_button do app.pop_window
end
-# A viewable `Control`
+# A visible `Control`
abstract class View
super Control
var enabled: nullable Bool is writable, abstract, autoinit
end
-# A control with some `text`
+# A control displaying some `text`
+#
+# For a text-only control, see `Label`.
abstract class TextView
super View
var is_password: nullable Bool is writable
end
-# A pushable button, raises `ButtonPressEvent`
+# A pressable button, raises `ButtonPressEvent`
class Button
super TextView
end
-# A text label
+# A simple text label
class Label
super TextView
end
-# Toggle control with two states and a label
+# Toggle control between two states, also displays a label
class CheckBox
super TextView
return slice(st, ed - st + 1)
end
- # Returns a subset of the content of `self` starting at `from` and of length `count`
+ # Copy a subset of `self` starting at `from` and of `count` bytes
#
# var b = "abcd".to_bytes
# assert b.slice(1, 2).hexdigest == "6263"
return ret
end
- # Returns a copy of `self` starting at `from`
+ # Copy of `self` starting at `from`
#
# var b = "abcd".to_bytes
# assert b.slice_from(1).hexdigest == "626364"
length += ln
end
- # Appends the bytes of `s` to `selftextextt`
- fun append_text(s: Text) do
- for i in s.substrings do
- append_ns(i.fast_cstring, i.byte_length)
- end
- end
+ # Appends the bytes of `str` to `self`
+ fun append_text(str: Text) do str.append_to_bytes self
redef fun append_to(b) do b.append self
redef fun enlarge(sz) do
if capacity >= sz then return
persisted = false
+ if capacity < 16 then capacity = 16
while capacity < sz do capacity = capacity * 2 + 2
var ns = new CString(capacity)
items.copy_to(ns, length, 0, 0)
redef class FlatText
redef fun append_to_bytes(b) do
var from = if self isa FlatString then first_byte else 0
- b.append_ns_from(items, byte_length, from)
+ if isset _items then b.append_ns_from(items, byte_length, from)
end
end
super Writer
end
-# `Stream` that can be used to write to a `String`
+# Write to `bytes` in memory
#
-# Mainly used for compatibility with Writer type and tests.
-class StringWriter
+# ~~~
+# var writer = new BytesWriter
+#
+# writer.write "Strings "
+# writer.write_char '&'
+# writer.write_byte 0x20u8
+# writer.write_bytes "bytes".to_bytes
+#
+# assert writer.to_s == "\\x53\\x74\\x72\\x69\\x6E\\x67\\x73\\x20\\x26\\x20\\x62\\x79\\x74\\x65\\x73"
+# assert writer.bytes.to_s == "Strings & bytes"
+# ~~~
+#
+# As with any binary data, UTF-8 code points encoded on two bytes or more
+# can be constructed byte by byte.
+#
+# ~~~
+# writer = new BytesWriter
+#
+# # Write just the character first half
+# writer.write_byte 0xC2u8
+# assert writer.to_s == "\\xC2"
+# assert writer.bytes.to_s == "�"
+#
+# # Complete the character
+# writer.write_byte 0xA2u8
+# assert writer.to_s == "\\xC2\\xA2"
+# assert writer.bytes.to_s == "¢"
+# ~~~
+class BytesWriter
super Writer
- private var content = new Buffer
- redef fun to_s do return content.to_s
- redef fun is_writable do return not closed
+ # Written memory
+ var bytes = new Bytes.empty
- redef fun write_bytes(b) do
- content.append(b.to_s)
- end
+ redef fun to_s do return bytes.chexdigest
redef fun write(str)
do
- assert not closed
- content.append(str)
+ if closed then return
+ str.append_to_bytes bytes
end
redef fun write_char(c)
do
- assert not closed
- content.add(c)
+ if closed then return
+ bytes.add_char c
+ end
+
+ redef fun write_byte(value)
+ do
+ if closed then return
+ bytes.add value
+ end
+
+ redef fun write_bytes(b)
+ do
+ if closed then return
+ bytes.append b
end
# Is the stream closed?
protected var closed = false
redef fun close do closed = true
+ redef fun is_writable do return not closed
end
-# `Stream` used to read from a `String`
+# `Stream` writing to a `String`
#
-# Mainly used for compatibility with Reader type and tests.
-class StringReader
+# This class has the same behavior as `BytesWriter`
+# except for `to_s` which decodes `bytes` to a string.
+#
+# ~~~
+# var writer = new StringWriter
+#
+# writer.write "Strings "
+# writer.write_char '&'
+# writer.write_byte 0x20u8
+# writer.write_bytes "bytes".to_bytes
+#
+# assert writer.to_s == "Strings & bytes"
+# ~~~
+class StringWriter
+ super BytesWriter
+
+ redef fun to_s do return bytes.to_s
+end
+
+# Read from `bytes` in memory
+#
+# ~~~
+# var reader = new BytesReader(b"a…b")
+# assert reader.read_char == 'a'
+# assert reader.read_byte == 0xE2u8 # 1st byte of '…'
+# assert reader.read_byte == 0x80u8 # 2nd byte of '…'
+# assert reader.read_char == '�' # Reads the last byte as an invalid char
+# assert reader.read_all_bytes == b"b"
+# ~~~
+class BytesReader
super Reader
- # The string to read from.
- var source: String
+ # Source data to read
+ var bytes: Bytes
- # The current position in the string (bytewise).
- private var cursor: Int = 0
+ # The current position in `bytes`
+ private var cursor = 0
- redef fun read_char do
- if cursor < source.length then
- # Fix when supporting UTF-8
- var c = source[cursor]
- cursor += 1
- return c
- else
- return null
- end
- end
+ redef fun read_char
+ do
+ if cursor >= bytes.length then return null
- redef fun read_byte do
- if cursor < source.length then
- var c = source.bytes[cursor]
- cursor += 1
- return c
- else
- return null
- end
+ var len = bytes.items.length_of_char_at(cursor)
+ var char = bytes.items.char_at(cursor)
+ cursor += len
+ return char
end
- redef fun close do
- source = ""
+ redef fun read_byte
+ do
+ if cursor >= bytes.length then return null
+
+ var c = bytes[cursor]
+ cursor += 1
+ return c
end
- redef fun read_all_bytes do
- var nslen = source.length - cursor
- var nns = new CString(nslen)
- source.copy_to_native(nns, nslen, cursor, 0)
- return new Bytes(nns, nslen, nslen)
+ redef fun close do bytes = new Bytes.empty
+
+ redef fun read_all_bytes
+ do
+ var res = bytes.slice_from(cursor)
+ cursor = bytes.length
+ return res
end
- redef fun eof do return cursor >= source.byte_length
+ redef fun eof do return cursor >= bytes.length
+end
+
+# `Stream` reading from a `String` source
+#
+# This class has the same behavior as `BytesReader`
+# except for its constructor accepting a `String`.
+#
+# ~~~
+# var reader = new StringReader("a…b")
+# assert reader.read_char == 'a'
+# assert reader.read_byte == 0xE2u8 # 1st byte of '…'
+# assert reader.read_byte == 0x80u8 # 2nd byte of '…'
+# assert reader.read_char == '�' # Reads the last byte as an invalid char
+# assert reader.read_all == "b"
+# ~~~
+class StringReader
+ super BytesReader
+
+ autoinit source
+
+ # Source data to read
+ var source: String
+
+ init do bytes = source.to_bytes
+
+ redef fun close
+ do
+ source = ""
+ super
+ end
end
var curr_pos: Int
- init do target_items = target._items
+ init do if isset target._items then target_items = target._items
redef fun index do return curr_pos
import cocoa::foundation
private import json
-redef class App
- redef var data_store = new UserDefaultView
-end
-
-private class UserDefaultView
- super DataStore
+redef class DataStore
# The `NSUserDefaults` used to implement `DataStore`
var user_defaults = new NSUserDefaults.standard_user_defaults is lazy
# TODO report errors
var deserializer = new JsonDeserializer(nsstr.to_s)
- return deserializer.deserialize
+ var deserialized = deserializer.deserialize
+
+ var errors = deserializer.errors
+ if errors.not_empty then
+ # An update may have broken the versioning compatibility
+ print_error "{class_name} error at deserialization: {errors.join(", ")}"
+ return null # Let's be safe
+ end
+
+ return deserialized
end
redef fun []=(key, value)
import app::data_store
private import xdg_basedir
-private import sqlite3
+import sqlite3
private import json
-redef class App
- redef var data_store = new LinuxStore
-end
-
-private class LinuxStore
- super DataStore
+redef class DataStore
# File path of the Sqlite3 DB file
fun db_file: String do return "data_store.db"
# Sqlite3 table used
fun db_table: String do return "data_store"
- var db_cache: nullable Sqlite3DB = null
+ private var db_cache: nullable Sqlite3DB = null
# Database to use to implement the `DataStore`
fun db: nullable Sqlite3DB
# Prepare SELECT statement
var stmt = db.select("* FROM {db_table} WHERE key == {key.to_sql_string}")
+ if stmt == null then return null
# Execute statment
for row in stmt do
var deserializer = new JsonDeserializer(serialized)
var deserialized = deserializer.deserialize
+ var errors = deserializer.errors
+ if errors.not_empty then
+ # An update may have broken the versioning compatibility
+ print_error "{class_name} error at deserialization: {errors.join(", ")}"
+ return null # Let's be safe
+ end
+
return deserialized
end
import astutil
import serialization
-# Fully process a content as a nit source file.
-fun hightlightcode(hl: HighlightVisitor, content: String): HLCode
+# Fully process `content` as a Nit source file.
+#
+# Set `print_errors = true` to print errors in the code to the console.
+fun hightlightcode(hl: HighlightVisitor, content: String, print_errors: nullable Bool): HLCode
do
# Prepare a stand-alone tool context
var tc = new ToolContext
tc.nit_dir = tc.locate_nit_dir # still use the common lib to have core
tc.keep_going = true # no exit, obviously
- tc.opt_warn.value = -1 # no output, obviously
+ if print_errors != true then tc.opt_warn.value = -1 # no output
# Prepare an stand-alone model and model builder.
# Unfortunately, models are enclosing and append-only.