# Move the camera to show all the world world in the screen range
world_camera.reset_height(world.half_height * 2.0)
+
+ ui_camera.reset_height 720.0
end
# Main spritesheet with ships, asteroids and beams
application in the specified orientation. This can be useful for games and
other multimedia applications.
+## Resources and application icon
+
+Resources specific to the Android platform should be placed in an `android/` folder at the root of the project.
+The folder should adopt the structure of a normal Android project, e.g., a custom XML resource file can be placed
+at `android/res/values/color.xml` to be compiled with the Android application.
+
+The application icon should also be placed in the `android/` folder.
+Place the classic bitmap version at `android/res/mipmap-hdpi/ic_launcher.png` (and others),
+and the adaptive version at `android/res/mipmap-anydpi-v26/ic_launcher.xml`.
+The Nit compiler detects these files and uses them as the application icon.
+
+Additional `android/` folders may be placed next to more specific Nit modules to change the Android resources
+for application variants. The more specific resources will have priority over the project level `android/` files.
+
# Compilation modes
There are two compilation modes for the Android platform, debug and release.
return self.hasAlpha();
`}
+ fun recycle in "Java" `{
+ self.recycle();
+ `}
+
# HACK for bug #845
redef fun new_global_ref import sys, Sys.jni_env `{
Sys sys = NativeBitmap_sys(self);
# Notification from the Android framework, the system is running low on memory
#
# Try to reduce your memory use.
- fun low_memory do end
+ fun low_memory do force_garbage_collection
# Notification from the Android framework, the current device configuration has changed
fun config_changed do end
# Notification from Android, the system is running low on memory
#
# Try to reduce your memory use.
- fun on_low_memory do end
+ fun on_low_memory do force_garbage_collection
# Notification from Android, the current window of the activity has lost or gained focus
fun on_window_focus_changed(has_focus: Bool) do end
fun red_size: Int do return display.config_attrib(config, 0x3024)
fun depth_size: Int do return display.config_attrib(config, 0x3025)
fun stencil_size: Int do return display.config_attrib(config, 0x3026)
+ fun samples: Int do return display.config_attrib(config, 0x3031)
+ fun sample_buffers: Int do return display.config_attrib(config, 0x3032)
fun native_visual_id: Int do return display.config_attrib(config, 0x302E)
fun native_visual_type: Int do return display.config_attrib(config, 0x302F)
fun alpha_size=(size: Int) do insert_attrib_with_val(0x3021, size)
fun depth_size=(size: Int) do insert_attrib_with_val(0x3025, size)
fun stencil_size=(size: Int) do insert_attrib_with_val(0x3026, size)
- fun sample_buffers=(size: Int) do insert_attrib_with_val(0x3031, size)
+ fun samples=(count: Int) do insert_attrib_with_val(0x3031, count)
+ fun sample_buffers=(size: Int) do insert_attrib_with_val(0x3032, size)
fun caveat=(caveat: EGLConfigCaveat) do insert_attrib_with_val(0x3050, caveat.to_i)
var bottom: IPoint3d[Float] = new CameraAnchor(self, 0.5, -1.0)
# Center of the left border of the screen, at z = 0
- var left: IPoint3d[Float] = new CameraAnchor(self, 0.0, -1.0)
+ var left: IPoint3d[Float] = new CameraAnchor(self, 0.0, -0.5)
# Center of the right border of the screen, at z = 0
- var right: IPoint3d[Float] = new CameraAnchor(self, 1.0, -1.0)
+ var right: IPoint3d[Float] = new CameraAnchor(self, 1.0, -0.5)
# Top left corner of the screen, at z = 0
var top_left: IPoint3d[Float] = new CameraAnchor(self, 0.0, 0.0)
glCullFace gl_BACK
# Prepare programs
- var programs = [versatile_program, normals_program, explosion_program, smoke_program, static_program, selection_program: GamnitProgram]
+ var programs = [blinn_phong_program, normals_program, explosion_program, smoke_program, static_program, selection_program: GamnitProgram]
for program in programs do
program.compile_and_link
var gamnit_error = program.error
glDepthMask true
perfs["gamnit depth particles"].add frame_core_depth_clock.lapse
+ # Stop using the dynamic resolution before drawing UI sprites
+ frame_core_dynamic_resolution_after display
+
frame_core_ui_sprites display
perfs["gamnit depth ui_sprites"].add frame_core_depth_clock.lapse
- frame_core_dynamic_resolution_after display
-
# Debug, show the light point of view
#frame_core_shadow_debug display
end
# ~~~
class Mesh
- # Vertices coordinates
+ # Number for vertices
+ fun n_vertices: Int do return vertices.length / 3
+
+ # Vertices relative coordinates, 3 floats per vertex
var vertices = new Array[Float] is lazy, writable
# Indices to draw triangles with `glDrawElements`
private var indices_c = new CUInt16Array.from(indices) is lazy, writable
- # Normals on each vertex
+ # Normals, 3 floats per vertex
var normals = new Array[Float] is lazy, writable
- # Coordinates on the texture per vertex
+ # Coordinates on the texture, 2 floats per vertex
var texture_coords = new Array[Float] is lazy, writable
# `GLDrawMode` used to display this mesh, defaults to `gl_TRIANGLES`
redef fun draw(actor, model, camera)
do
- var program = app.versatile_program
+ var program = app.blinn_phong_program
program.use
program.mvp.uniform camera.mvp_matrix
var mesh = model.mesh
# Actor specs
+ glDisableVertexAttribArray program.translation.location
+ glDisableVertexAttribArray program.scale.location
+
program.translation.uniform(actor.center.x, actor.center.y, actor.center.z, 0.0)
program.scale.uniform actor.scale
- program.rotation.uniform new Matrix.gamnit_euler_rotation(actor.pitch, actor.yaw, actor.roll)
+ program.alpha.uniform actor.alpha
+ program.rotation = new Matrix.gamnit_euler_rotation(actor.pitch, actor.yaw, actor.roll)
# From mesh
program.coord.array_enabled = true
program.camera.uniform(camera.position.x, camera.position.y, camera.position.z)
# Colors from the material
- var a = actor.alpha
- program.ambient_color.uniform(ambient_color[0]*a, ambient_color[1]*a,
- ambient_color[2]*a, ambient_color[3]*a)
- program.diffuse_color.uniform(diffuse_color[0]*a, diffuse_color[1]*a,
- diffuse_color[2]*a, diffuse_color[3]*a)
- program.specular_color.uniform(specular_color[0]*a, specular_color[1]*a,
- specular_color[2]*a, specular_color[3]*a)
+ program.ambient_color.uniform(ambient_color[0], ambient_color[1],
+ ambient_color[2], ambient_color[3])
+ program.diffuse_color.uniform(diffuse_color[0], diffuse_color[1],
+ diffuse_color[2], diffuse_color[3])
+ program.specular_color.uniform(specular_color[0], specular_color[1],
+ specular_color[2], specular_color[3])
- setup_lights(actor, model, camera, program)
+ setup_lights(camera, program)
# Execute draw
if mesh.indices.is_empty then
assert glGetError == gl_NO_ERROR
end
- private fun setup_lights(actor: Actor, model: LeafModel, camera: Camera, program: BlinnPhongProgram)
+ private fun setup_lights(camera: Camera, program: BlinnPhongProgram)
do
# TODO use a list of lights
do
var mesh = model.mesh
- var program = app.versatile_program
+ var program = app.blinn_phong_program
program.use
# One of the textures used, if any
program.use_map_bump.uniform false
end
+ glDisableVertexAttribArray program.translation.location
+ glDisableVertexAttribArray program.scale.location
+
program.mvp.uniform camera.mvp_matrix
program.translation.uniform(actor.center.x, actor.center.y, actor.center.z, 0.0)
program.scale.uniform actor.scale
+ program.alpha.uniform actor.alpha
# If using a texture, set `texture_coords`
program.tex_coord.array_enabled = sample_used_texture != null
program.coord.array_enabled = true
program.coord.array(mesh.vertices, 3)
- program.rotation.uniform new Matrix.gamnit_euler_rotation(actor.pitch, actor.yaw, actor.roll)
+ program.rotation = new Matrix.gamnit_euler_rotation(actor.pitch, actor.yaw, actor.roll)
- var a = actor.alpha
- program.ambient_color.uniform(ambient_color[0]*a, ambient_color[1]*a,
- ambient_color[2]*a, ambient_color[3]*a)
- program.diffuse_color.uniform(diffuse_color[0]*a, diffuse_color[1]*a,
- diffuse_color[2]*a, diffuse_color[3]*a)
- program.specular_color.uniform(specular_color[0]*a, specular_color[1]*a,
- specular_color[2]*a, specular_color[3]*a)
+ program.ambient_color.uniform(ambient_color[0], ambient_color[1],
+ ambient_color[2], ambient_color[3])
+ program.diffuse_color.uniform(diffuse_color[0], diffuse_color[1],
+ diffuse_color[2], diffuse_color[3])
+ program.specular_color.uniform(specular_color[0], specular_color[1],
+ specular_color[2], specular_color[3])
program.normal.array_enabled = true
program.normal.array(mesh.normals, 3)
# Light
- setup_lights(actor, model, camera, program)
+ setup_lights(camera, program)
# Camera
program.camera.uniform(camera.position.x, camera.position.y, camera.position.z)
program.coord.array_enabled = true
program.coord.array(mesh.vertices, 3)
- program.rotation.uniform new Matrix.gamnit_euler_rotation(actor.pitch, actor.yaw, actor.roll)
+ program.rotation = new Matrix.gamnit_euler_rotation(actor.pitch, actor.yaw, actor.roll)
program.normal.array_enabled = true
program.normal.array(mesh.normals, 3)
attribute vec4 coord;
// Vertex translation
- uniform vec4 translation;
+ attribute vec4 translation;
// Vertex scaling
- uniform float scale;
+ attribute float scale;
+
+ attribute float alpha;
// Vertex coordinates on textures
attribute vec2 tex_coord;
// Camera model view projection matrix
uniform mat4 mvp;
- uniform mat4 rotation;
+ // Actor rotation
+ attribute vec4 rotation_row0;
+ attribute vec4 rotation_row1;
+ attribute vec4 rotation_row2;
+ attribute vec4 rotation_row3;
+
+ mat4 rotation()
+ {
+ return mat4(rotation_row0, rotation_row1, rotation_row2, rotation_row3);
+ }
// Lights config
uniform lowp int light_kind;
varying vec4 v_to_light;
varying vec4 v_to_camera;
varying vec4 v_depth_pos;
+ varying float v_alpha;
void main()
{
+ mat4 rotation = rotation();
vec4 pos = (vec4(coord.xyz * scale, 1.0) * rotation + translation);
gl_Position = pos * mvp;
v_depth_pos = (pos * light_mvp) * 0.5 + 0.5;
// Point light (and others?)
v_to_light = normalize(vec4(light_center, 1.0) - pos);
}
+
+ v_alpha = alpha;
}
""" @ glsl_vertex_shader
varying vec4 v_to_light;
varying vec4 v_to_camera;
varying vec4 v_depth_pos;
+ varying float v_alpha;
// Colors
uniform vec4 ambient_color;
uniform lowp int light_kind;
uniform bool use_shadows;
uniform sampler2D depth_texture;
- uniform float depth_texture_size;
- uniform int depth_texture_taps;
+ uniform float depth_size;
+ uniform int depth_taps;
// Shadow effect on the diffuse colors of the fragment at offset `x, y`
float shadow_lookup(vec2 depth_coord, float x, float y) {
float tap_width = 1.0;
- float pixel_size = tap_width/depth_texture_size;
+ float pixel_size = tap_width/depth_size;
vec2 offset = vec2(x * pixel_size * v_depth_pos.w,
y * pixel_size * v_depth_pos.w);
vec2 depth_coord = v_depth_pos.xy/v_depth_pos.w;
- float taps = float(depth_texture_taps);
+ float taps = float(depth_taps);
float tap_step = 2.00/taps;
float sum = 0.0;
for (float x = -1.0; x <= 0.99; x += tap_step)
}
// Ambient light
- vec4 ambient = ambient_color;
+ vec4 ambient = ambient_color * v_alpha;
if (use_map_ambient) ambient *= texture2D(map_ambient, v_tex_coord);
if (light_kind == 0) {
// No light, show diffuse and ambient
- vec4 diffuse = diffuse_color;
+ vec4 diffuse = diffuse_color * v_alpha;
if (use_map_diffuse) diffuse *= texture2D(map_diffuse, v_tex_coord);
gl_FragColor = ambient + diffuse;
diffuse *= shadow();
}
- vec4 specular = s * specular_color;
+ vec4 specular = s * specular_color * v_alpha;
if (use_map_specular) specular *= texture2D(map_specular, v_tex_coord).x;
gl_FragColor = ambient + diffuse + specular;
var depth_texture = uniforms["depth_texture"].as(UniformSampler2D) is lazy
# Size, in pixels, of `depth_texture`
- var depth_texture_size = uniforms["depth_texture_size"].as(UniformFloat) is lazy
+ var depth_texture_size = uniforms["depth_size"].as(UniformFloat) is lazy
# Times to tap the `depth_texture`, square root (set to 3 for a total of 9 taps)
- var depth_texture_taps = uniforms["depth_texture_taps"].as(UniformInt) is lazy
+ var depth_texture_taps = uniforms["depth_taps"].as(UniformInt) is lazy
# Camera position
var camera = uniforms["camera"].as(UniformVec3) is lazy
# Translation applied to each vertex
- var translation = uniforms["translation"].as(UniformVec4) is lazy
+ var translation = attributes["translation"].as(AttributeVec4) is lazy # TODO attribute
- # Rotation matrix
- var rotation = uniforms["rotation"].as(UniformMat4) is lazy
+ # Set `mat` at the uniform rotation matrix
+ fun rotation=(mat: Matrix)
+ do
+ var i = 0
+ for r in [rotation_row0, rotation_row1, rotation_row2, rotation_row3] do
+ if r.is_active then
+ glDisableVertexAttribArray r.location
+ r.uniform(mat[0, i], mat[1, i], mat[2, i], mat[3, i])
+ end
+ i += 1
+ end
+ var gl_error = glGetError
+ assert gl_error == gl_NO_ERROR else print_error gl_error
+ end
+
+ # Rotation matrix, row0
+ var rotation_row0 = attributes["rotation_row0"].as(AttributeVec4) is lazy
+
+ # Rotation matrix, row 1
+ var rotation_row1 = attributes["rotation_row1"].as(AttributeVec4) is lazy
+
+ # Rotation matrix, row 2
+ var rotation_row2 = attributes["rotation_row2"].as(AttributeVec4) is lazy
+
+ # Rotation matrix, row 3
+ var rotation_row3 = attributes["rotation_row3"].as(AttributeVec4) is lazy
+
+ # Scaling per vertex
+ var scale = attributes["scale"].as(AttributeFloat) is lazy
# Scaling per vertex
- var scale = uniforms["scale"].as(UniformFloat) is lazy
+ var alpha = attributes["alpha"].as(AttributeFloat) is lazy
# Camera model view projection matrix
var mvp = uniforms["mvp"].as(UniformMat4) is lazy
end
redef class App
- private var versatile_program = new BlinnPhongProgram is lazy
+ private var blinn_phong_program = new BlinnPhongProgram is lazy
private var normals_program = new NormalProgram is lazy
end
# Time-to-live of each particle
private var ttls = new Array[Float]
+ # Clear all particles
+ fun clear
+ do
+ centers.clear
+ ots.clear
+ scales.clear
+ ttls.clear
+ total_particles = 0
+ end
+
# Add a particle at `center` with `scale`, living for `ttl` from `time_offset`
#
# `time_offset` is added to the creation time, it can be used to delay the
var light = app.light
if not light isa LightCastingShadows then return
- perf_clock_shadow.lapse
-
# Make sure there's no errors pending
assert glGetError == gl_NO_ERROR
# Take down, bring back default values
glBindFramebuffer(gl_FRAMEBUFFER, shadow_context.screen_framebuffer)
glColorMask(true, true, true, true)
-
- perfs["gamnit shadows prep"].add perf_clock_shadow.lapse
end
# ---
var pixels = buf.native_array
load_from_pixels(pixels, bmp.width, bmp.height, gl_RGBA)
+ buf.destroy
+ bmp.recycle
jni_env.pop_local_frame
end
var pixels = surface.pixels
load_from_pixels(pixels, surface.w, surface.h, format)
+
+ surface.free
end
end
#
# This value is applied to both X and Y, so it has an exponential effect on
# the number of pixels.
- var dynamic_resolution_ratio = 1.0
+ var dynamic_resolution_ratio = 1.0 is writable
# Minimum dynamic screen resolution
var min_dynamic_resolution_ratio = 0.0125 is writable
var texture: Texture is writable(texture_direct=)
# Texture drawn to screen
- fun texture=(value: Texture)
+ fun texture=(texture: Texture)
do
- if isset _texture and value != texture then
+ if isset _texture and texture != self.texture then
needs_update
- if value.root != texture.root then needs_remap
+ if texture.root != self.texture.root then needs_remap
end
- texture_direct = value
+ texture_direct = texture
end
# Center position of this sprite in world coordinates
var center: Point3d[Float] is writable(center_direct=), noautoinit
# Center position of this sprite in world coordinates
- fun center=(value: Point3d[Float]) is autoinit do
- if isset _center and value != center then
+ fun center=(center: Point3d[Float]) is autoinit do
+ if isset _center and center != self.center then
needs_update
- center.sprites_remove self
+ self.center.sprites_remove self
end
- value.sprites_add self
- center_direct = value
+ center.sprites_add self
+ center_direct = center
end
# Last animation set with `animate`
redef class GLfloatArray
private fun fill_from_matrix(matrix: Matrix, dst_offset: nullable Int)
do
- dst_offset = dst_offset or else 0
+ dst_offset = dst_offset or else add_index
var mat_len = matrix.width*matrix.height
assert length >= mat_len + dst_offset
native_array.fill_from_matrix_native(matrix.items, dst_offset, mat_len)
+ add_index += mat_len
end
end
# Zero (or a negative value) means no limit.
#
# Applications can modify this value even during the main-loop.
- var maximum_fps = 60.0 is writable
+ var maximum_fps = 0.0 is writable
# Current frame-rate
#
var native = native_float_array
if native == null or array.length > native.length then
- if native != null then native.destroy
+ if native != null then native.finalize
native = new GLfloatArray.from(array)
self.native_float_array = native
else
# Overwrite this matrix with the identity matrix
fun set_identity
do
- for i in 4.times do
- for j in 4.times do
+ for i in [0..4[ do
+ for j in [0..4[ do
matrix_set(i, j, if i == j then 1.0 else 0.0)
end
end
# Copy content of this matrix to a `NativeGLfloatArray`
fun fill_native(native: NativeGLfloatArray)
do
- for i in width.times do
- for j in height.times do
+ for i in [0..width[ do
+ for j in [0..height[ do
native.matrix_set(i, j, self[i, j])
end
end
# The argument `color` should be an array of up to 4 floats (RGBA).
# If `color` has less than 4 items, the missing items are replaced by 1.0.
#
- # Require: `not loaded and x < width.to_i and y < height.to_i`
+ # Require: `x < width.to_i and y < height.to_i`
fun []=(x, y: Int, color: Array[Float])
do
- assert not loaded else print_error "{class_name}::[]= already loaded"
assert x < width.to_i and y < height.to_i else print_error "{class_name}::[] out of bounds"
# Simple conversion from [0.0..1.0] to [0..255]
var offset = 4*(x + y*width.to_i)
for i in [0..4[ do cpixels[offset+i] = bytes[i]
+
+ loaded = false
end
# Overwrite all pixels with `color`, return `self`
#
# The argument `color` should be an array of up to 4 floats (RGBA).
# If `color` has less than 4 items, the missing items are replaced by 1.0.
- #
- # Require: `not loaded`
fun fill(color: Array[Float]): SELF
do
- assert not loaded else print_error "{class_name}::fill already loaded"
-
# Simple conversion from [0.0..1.0] to [0..255]
var bytes = [for c in color do (c*255.0).round.to_i.clamp(0, 255).to_bytes.last]
while bytes.length < 4 do bytes.add 255u8
end
end
+ loaded = false
return self
end
redef fun load(force)
do
- if loaded then return
+ force = force or else false
+ if loaded and not force then return
+
+ if force and glIsTexture(gl_texture) then
+ # Was already loaded, free the previous GL name
+ glDeleteTextures([gl_texture])
+ end
+ gl_texture = -1
# Round down the desired dimension
var width = width.to_i
load_from_pixels(cpixels.native_array, width, height, gl_RGBA)
- cpixels.destroy
loaded = true
end
end
# Parent texture, from which this texture was created
var parent: Texture
- redef var root = parent.root is lateinit
+ redef fun root do return parent.root
redef fun load(force) do root.load(force)
end
end
redef class Pointer
- # Multiply RBG values by their alpha value
+ # Multiply RGB values by their alpha value
private fun premultiply_alpha(width, height: Int) `{
uint8_t *bytes = (uint8_t *)self;
int x, y, i = 0;
# Low level array of `Float`
class GLfloatArray
- super CArray[Float]
- redef type NATIVE: NativeGLfloatArray
+ super FinalizableOnce
- redef init(length)
+ var length: Int
+
+ var native_array = new NativeGLfloatArray(length) is lateinit
+
+ fun [](index: Int): Float do return native_array[index]
+
+ fun []=(index: Int, val: Float) do native_array[index] = val
+
+ var add_index = 0
+
+ fun reset_add do add_index = 0
+
+ # Require: `add_index < length`
+ fun add(value: Float)
do
- native_array = new NativeGLfloatArray(length)
+ var index = add_index
+ assert index < length
+ native_array[index] = value
+ self.add_index = index + 1
end
# Create with the content of `array`
# Require: `length >= array.length + dst_offset or else 0`
fun fill_from(array: Array[Float], dst_offset: nullable Int)
do
- dst_offset = dst_offset or else 0
+ dst_offset = dst_offset or else add_index
assert length >= array.length + dst_offset
for k in [0..array.length[ do
self[dst_offset+k] = array[k]
end
end
+
+ redef fun finalize_once do native_array.free
end
# An array of `GLfloat` in C (`GLfloat*`)
extern class NativeGLfloatArray `{ GLfloat* `}
- super NativeCArray
- redef type E: Float
new(size: Int) `{ return calloc(size, sizeof(GLfloat)); `}
- redef fun [](index) `{ return self[index]; `}
- redef fun []=(index, val) `{ self[index] = val; `}
+ fun [](index: Int): Float `{ return self[index]; `}
+
+ fun []=(index: Int, val: Float) `{ self[index] = val; `}
- redef fun +(offset) `{ return self + offset; `}
+ fun +(offset: Int): NativeGLfloatArray `{ return self + offset; `}
end
# General type for OpenGL enumerations
for j in height.times do assert items[j].length == width
- for j in height.times do
- for i in width.times do
+ for j in [0..height[ do
+ for i in [0..width[ do
self[j, i] = items[j][i]
end
end
assert size >= 0
var matrix = new Matrix(size, size)
- for i in size.times do
- for j in size.times do
+ for i in [0..size[ do
+ for j in [0..size[ do
matrix[j, i] = if i == j then 1.0 else 0.0
end
end
assert self.width == other.height
var out = new Matrix(other.width, self.height)
- for j in self.height.times do
- for i in other.width.times do
- var sum = items[0].zero
- for k in self.width.times do sum += self[j, k] * other[k, i]
- out[j, i] = sum
- end
- end
+ out.items.mul(items, other.items, self.width, self.height, other.width)
return out
end
r = r * 3 / 2 + (long)(i*1024.0);
return r;
`}
+
+ fun mul(a, b: NativeDoubleArray, a_width, a_height, b_width: Int) `{
+ int i, j, k;
+ for (j = 0; j < a_height; j ++)
+ for (i = 0; i < b_width; i ++) {
+ float sum = 0.0;
+ for (k = 0; k < a_width; k ++) sum += a[j*a_width + k] * b[k*b_width + i];
+ self[j*b_width + i] = sum;
+ }
+ `}
end
#
# This service aims to respect the world axes and logic of `gamnit`,
# it may not correspond to all needs.
+ #
+ # The returned `Matrix` may be cached, it must not be modified.
new gamnit_euler_rotation(pitch, yaw, roll: Float)
do
+ if pitch == 0.0 and yaw == 0.0 and roll == 0.0 then
+ return once new Matrix.identity(4)
+ end
+
+ if rotation_pitch == pitch and rotation_yaw == yaw and rotation_roll == roll then
+ var rot = rotation_matrix_cache
+ if rot != null then return rot
+ end
+
var c1 = pitch.cos
var s1 = pitch.sin
var c2 = yaw.cos
var s2 = yaw.sin
var c3 = roll.cos
var s3 = roll.sin
- return new Matrix.from(
- [[ c2*c3, -c2*s3, -s2, 0.0],
- [ c1*s3+c3*s1*s2, c1*c3-s1*s2*s3, c2*s1, 0.0],
- [-s1*s3+c1*c3*s2, -c3*s1-c1*s2*s3, c1*c2, 0.0],
- [ 0.0, 0.0, 0.0, 1.0]])
+
+ var rot = new Matrix(4, 4)
+ rot.items.mat4_set(
+ c2*c3, -c2*s3, -s2, 0.0,
+ c1*s3+c3*s1*s2, c1*c3-s1*s2*s3, c2*s1, 0.0,
+ -s1*s3+c1*c3*s2, -c3*s1-c1*s2*s3, c1*c2, 0.0,
+ 0.0, 0.0, 0.0, 1.0)
+
+ rotation_matrix_cache = rot
+ rotation_pitch = pitch
+ rotation_yaw = yaw
+ rotation_roll = roll
+ return rot
end
end
+
+redef class NativeDoubleArray
+ fun mat4_set(f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15: Float) `{
+ self[ 0] = f0;
+ self[ 1] = f1;
+ self[ 2] = f2;
+ self[ 3] = f3;
+
+ self[ 4] = f4;
+ self[ 5] = f5;
+ self[ 6] = f6;
+ self[ 7] = f7;
+
+ self[ 8] = f8;
+ self[ 9] = f9;
+ self[10] = f10;
+ self[11] = f11;
+
+ self[12] = f12;
+ self[13] = f13;
+ self[14] = f14;
+ self[15] = f15;
+ `}
+end
+
+redef class Sys
+
+ private var rotation_matrix_cache: nullable Matrix = null
+ private var rotation_pitch = 0.0
+ private var rotation_yaw = 0.0
+ private var rotation_roll = 0.0
+end
return ts
end
+ # Number of digits to the right of the decimal points in reports created by `to_s`
+ #
+ # Defaults to 4.
+ var precision = 4 is writable
+
redef fun to_s
do
- var prec = 3
+ var prec = precision
var table = new Map[String, Array[String]]
for event, stats in self do
`}
# Elapsed time representation.
-extern class Timespec `{struct timespec*`}
+private extern class Timespec `{struct timespec*`}
# Init a new Timespec from `s` seconds and `ns` nanoseconds.
new ( s, ns : Int ) `{
return tv;
`}
+ # Set `self` to `a` - `b`
+ fun minus(a, b: Timespec) `{
+ time_t s = a->tv_sec - b->tv_sec;
+ long ns = a->tv_nsec - b->tv_nsec;
+ if (ns < 0) {
+ s -= 1;
+ ns += 1000000000l;
+ }
+ self->tv_sec = s;
+ self->tv_nsec = ns;
+ `}
+
# Number of whole seconds of elapsed time.
fun sec : Int `{
return self->tv_sec;
# TODO use less mallocs
# Time at creation
- protected var time_at_beginning = new Timespec.monotonic_now
+ private var time_at_beginning = new Timespec.monotonic_now
# Time at last time a lapse method was called
- protected var time_at_last_lapse = new Timespec.monotonic_now
+ private var time_at_last_lapse = new Timespec.monotonic_now
+
+ private var temp = new Timespec.monotonic_now
# Smallest time frame reported by clock
- fun resolution: Timespec `{
+ private fun resolution: Timespec `{
struct timespec* tv = malloc( sizeof(struct timespec) );
#if defined(__MACH__) && !defined(CLOCK_REALTIME)
clock_serv_t cclock;
# Seconds since the creation of this instance
fun total: Float
do
- var now = new Timespec.monotonic_now
- var diff = now - time_at_beginning
- var r = diff.to_f
- diff.free
- now.free
- return r
+ var now = temp
+ now.update
+ now.minus(now, time_at_beginning)
+ return now.to_f
end
# Seconds since the last call to `lapse`
fun lapse: Float
do
- var nt = new Timespec.monotonic_now
- var dt = nt - time_at_last_lapse
- var r = dt.to_f
- dt.free
- time_at_last_lapse.free
- time_at_last_lapse = nt
+ var time_at_last_lapse = time_at_last_lapse
+ var now = temp
+ now.update
+ time_at_last_lapse.minus(now, time_at_last_lapse)
+ var r = time_at_last_lapse.to_f
+
+ self.temp = time_at_last_lapse
+ self.time_at_last_lapse = now
+
return r
end
# Seconds since the last call to `lapse`, without resetting the lapse counter
fun peek_lapse: Float
do
- var nt = new Timespec.monotonic_now
- var dt = nt - time_at_last_lapse
- var r = dt.to_f
- nt.free
- dt.free
- return r
+ var now = temp
+ now.update
+ now.minus(now, time_at_last_lapse)
+ return now.to_f
end
redef fun finalize_once
do
time_at_beginning.free
time_at_last_lapse.free
+ temp.free
end
end
Common options of the Nit tools are understood.
Here, only the specific one are indicated.
+### `--txt`
+Generate text with ANSI coloring escape sequences.
+
+Instead of HTML, this generate text files with ANSI escape sequence for colors.
+The options `-f`, `--line-id-prefix` and `--ast` are ignored in this mode.
+
### `-f`, `--fragment`
Omit document header and footer.
With the `--full` option, all imported modules (even those in standard) are also precessed.
+### `--ast`
+Generate specific HTML elements for each Node of the AST.
+
+Additional `<span>` elements are generated for each node of the AST.
+The HTML generated can become quite large.
+
# SEE ALSO
The Nit language documentation and the source code of its tools and libraries may be downloaded from <http://nitlanguage.org>
import model::model_collect
import modelize
import modelbuilder
-import highlight
+import htmlight
import doc_down
# Retrieve the MDoc related to a MEntity
var node = self.node
if node == null then return null
if format == "html" then
- var hl = new HighlightVisitor
- hl.enter_visit node
+ var hl = new HtmlightVisitor
+ hl.highlight_node node
return hl.html
end
# TODO make a raw visitor
module doc_down
import markdown
-import highlight
+import htmlight
private import parser_util
redef class MDoc
return
end
v.add "<pre class=\"nitcode\"><code>"
- var hl = new HighlightVisitor
+ var hl = new HtmlightVisitor
hl.line_id_prefix = ""
- hl.enter_visit(ast)
+ hl.highlight_node(ast)
v.add(hl.html)
v.add "</code></pre>\n"
end
append_code(v, text, from, to)
else
v.add "<code class=\"nitcode\">"
- var hl = new HighlightVisitor
+ var hl = new HtmlightVisitor
hl.line_id_prefix = ""
- hl.enter_visit(ast)
+ hl.highlight_node(ast)
v.add(hl.html)
end
v.add "</code>"
return
end
v.add "<code class=\"nitcode\">"
- var hl = new HighlightVisitor
- hl.enter_visit(ast)
+ var hl = new HtmlightVisitor
+ hl.highlight_node(ast)
v.add(hl.html)
v.add "</code>"
end
module nitlight_as_a_service is example
import frontend
-import highlight
+import htmlight
import nitcorn
import nitcorn::log
import template
import json::serialization_write
-# Nitcorn service to hightlight code
+# Nitcorn service to highlight code
#
# It's a single stand-alone page that has to form to itself.
class HighlightAction
redef fun answer(http_request, turi)
do
- var hl = new HighlightVisitor
+ var hl = new HtmlightVisitor
var page = new Template
# There is code? Process it
var code = http_request.post_args.get_or_null("code")
var hlcode = null
- if code != null then hlcode = hightlightcode(hl, code)
+ if code != null then hlcode = hl.highlightcode(code)
if http_request.post_args.get_or_null("json") == "true" and hlcode != null then
var response = new HttpResponse(200)
module highlight
import frontend
-import html
-import pipeline
import astutil
-import serialization
-
-# 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
- 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.
- # There is no way (yet?) to have a shared module `core` with
- # isolated and throwable user modules.
- var model = new Model
- var mb = new ModelBuilder(model, tc)
-
- # Parse the code
- var source = new SourceFile.from_string("", content + "\n")
- var lexer = new Lexer(source)
- var parser = new Parser(lexer)
- var tree = parser.parse
-
- var hlcode = new HLCode(hl, content, source)
-
- # Check syntax error
- var eof = tree.n_eof
- if eof isa AError then
- mb.error(eof, eof.message)
- hl.hightlight_source(source)
- return hlcode
- end
- var amodule = tree.n_base.as(not null)
-
- # Load the AST as a module in the model
- # Then process it
- mb.load_rt_module(null, amodule, "")
- mb.run_phases
-
- # Highlight the processed module
- hl.enter_visit(amodule)
- return hlcode
-end
-
-# A standalone highlighted piece of code
-class HLCode
- super Serializable
-
- # The highlighter used
- var hl: HighlightVisitor
-
- # The raw code source
- var content: String
-
- # The pseudo source-file
- var source: SourceFile
-
- # JavaScript code to update an existing codemirror editor.
- fun code_mirror_update: Template
- do
-
- var res = new Template
- res.add """
- function nitmessage() {
- editor.operation(function(){
- for (var i = 0; i < widgets.length; ++i)
- editor.removeLineWidget(widgets[i]);
- widgets.length = 0;
-"""
-
- for m in source.messages do
- res.add """
- var l = document.createElement("div");
- l.className = "lint-error"
- l.innerHTML = "<span class='glyphicon glyphicon-warning-sign lint-error-icon'></span> {{{m.text.html_escape}}}";
- var w = editor.addLineWidget({{{m.location.line_start-1}}}, l);
- widgets.push(w);
-"""
- end
- res.add """});}"""
- return res
- end
-
- redef fun core_serialize_to(v)
- do
- v.serialize_attribute("code", hl.html.write_to_string)
- var msgs = new Array[Map[String, Serializable]]
- for m in source.messages do
- var o = new Map[String, Serializable]
- msgs.add o
- o["line"] = m.location.line_start-1
- o["message"] = m.text
- end
- v.serialize_attribute("messages", msgs)
- end
-end
+import console
# Visitor used to produce a HTML tree based on a AST on a `Source`
-class HighlightVisitor
- # The root of the HTML hierarchy
- var html = new HTMLTag("span")
-
- # Should the HTML include a nested `<span class"{type_of_node}">` element for each `ANode` of the AST?
- #
- # Used to have a really huge and verbose HTML (mainly for debug)
- var with_ast = false is writable
-
- # Prefixes used in generated IDs for line `<span>` elements.
- # Useful if more than one highlighted code is present in the same HTML document.
- #
- # If set to the empty string, id for lines are disabled.
- #
- # Is `"L"` by default.
- var line_id_prefix = "L" is writable
-
+class AbstractHighlightVisitor
# The first line to generate, null if start at the first line
var first_line: nullable Int = null is writable
# default: true
var show_messages = true is writable
- # When highlighting a node, attach a full popupable infobox, if any.
- #
- # If `false`, only a simple `title` tooltip is used.
- #
- # default: true
- var show_infobox = true is writable
-
- # A reference to an entity used in generated `<a>` elements.
- #
- # It is used to refer to some specific entities when generating links.
- # If `null` is returned, then no link are generated and `<a>` elements become `<span>`.
- #
- # By default, `null` is returned.
- # Clients are therefore encouraged to redefine the method in a subclass to control where entities should link to.
- fun hrefto(entity: MEntity): nullable String do return null
-
- init
- do
- html.add_class("nitcode")
- end
-
# When highlighting a node, also consider the loose tokens around it.
#
# Loose tokens are tokens discarded from the AST but attached before
# Default: false
var include_whole_lines = false is writable
- # The entry-point of the highlighting.
- # Will fill `html` with the generated HTML content.
- fun enter_visit(n: ANode)
+ # Highlight a AST element.
+ fun highlight_node(n: ANode)
do
n.parentize_tokens
l = l.last_real_token_in_line
end
- htmlize(f, l)
- end
-
- private fun full_tag(anode: ANode, hv: HighlightVisitor): nullable HTMLTag
- do
- var tag = anode.make_tag(hv)
- if tag == null then return null
- var infobox = anode.infobox(hv)
- if infobox == null and anode isa Token then
- var pa = anode.parent
- if pa != null then
- infobox = pa.decorate_tag(hv, tag, anode)
- end
- end
- if infobox != null and not show_infobox then
- tag.attr("title", infobox.title)
- tag.classes.add "titled"
- infobox = null
- end
- var messages = anode.location.messages
- if messages != null and show_messages then
- tag.css("border-bottom", "solid 2px red")
- if infobox == null then
- infobox = new HInfoBox(hv, "Messages")
- end
- var c = infobox.new_dropdown("{messages.length} message(s)", "")
- for m in messages do
- c.open("li").append(m.text)
- end
- end
- if infobox != null then
- tag.attach_infobox(infobox)
- end
- return tag
+ do_highlight(f, l)
end
# Highlight a full lexed source file.
#
# REQUIRE `source.first_token != null`
- fun hightlight_source(source: SourceFile)
- do
- htmlize(source.first_token.as(not null), null)
- end
-
- # Produce HTML between two tokens
- protected fun htmlize(first_token: Token, last_token: nullable Token)
- do
- var stack2 = new Array[HTMLTag]
- var stack = new Array[Prod]
- var line = 0
- var c: nullable Token = first_token
- var hv = self
- while c != null do
- var starting
-
- # Handle start of line
- var cline = c.location.line_start
- if cline != line then
- # Handle starting block productions,
- # Because c could be a detached token, get prods in
- # the first AST token
- var c0 = c.first_token_in_line
- starting = null
- if c0 != null then starting = c0.starting_prods
- if starting != null then for p in starting do
- if not p.is_block then continue
- var tag = full_tag(p, hv)
- if tag == null then continue
- tag.add_class("foldable")
- stack2.add(html)
- html.add tag
- html = tag
- stack.add(p)
- end
-
- # Add a div for the whole line
- var tag = new HTMLTag("span")
- var p = line_id_prefix
- if p != "" then tag.attrs["id"] = "{p}{cline}"
- tag.classes.add "line"
- stack2.add(html)
- html.add tag
- html = tag
- line = cline
- end
-
- # Add the blank, verbatim
- html.add_raw_html c.blank_before
-
- # Handle starting span production
- starting = c.starting_prods
- if starting != null then for p in starting do
- if not p.is_span then continue
- var tag = full_tag(p, hv)
- if tag == null then continue
- stack2.add(html)
- html.add tag
- html = tag
- stack.add(p)
- end
-
- # Add the token
- if c isa TEol then
- html.append "\n"
- else
- var tag = full_tag(c, hv)
- if tag != null then html.add tag
- end
-
- # Handle ending span productions
- var ending = c.ending_prods
- if ending != null then for p in ending do
- if not p.is_span then continue
- if stack.is_empty or p != stack.last then continue
- stack.pop
- html = stack2.pop
- end
-
- # Handle end of line and end of file
- var n = c.next_token
- if c == last_token then n = null
- if n == null or n.location.line_start != line then
- # closes the line div
- html = stack2.pop
-
- # close the block production divs
- var c0 = c.last_token_in_line
- ending = null
- if c0 != null then ending = c0.ending_prods
- if ending != null then for p in ending do
- if not p.is_block then continue
- if stack.is_empty or p != stack.last then continue
- stack.pop
- html = stack2.pop
- end
- end
-
- c = n
- end
- if not stack2.is_empty then html = stack2.first
- end
-
- # Return a default CSS content related to CSS classes used in the `html` tree.
- # Could be inlined in the `.html` file of saved as a specific `.css` file.
- fun css_content: String
+ fun highlight_source(source: SourceFile)
do
- return """
-.nitcode a { color: inherit; cursor:pointer; }
-.nitcode .titled:hover { text-decoration: underline; } /* underline titles */
-.nitcode .popupable:hover { text-decoration: underline; cursor:help; } /* underline titles */
-.nitcode .foldable { display: block } /* for block productions*/
-.nitcode .line{ display: block } /* for lines */
-.nitcode .line:hover{ background-color: #FFFFE0; } /* current line */
-.nitcode :target { background-color: #FFF3C2 } /* target highlight*/
-/* lexical raw tokens. independent of usage or semantic: */
-.nitcode .nc_c { color: gray; font-style: italic; } /* comment */
-.nitcode .nc_d { color: #3D8127; font-style: italic; } /* documentation comments */
-.nitcode .nc_k { font-weight: bold; } /* keyword */
-.nitcode .nc_o {} /* operator */
-.nitcode .nc_i {} /* standard identifier */
-.nitcode .nc_t { color: #445588; font-weight: bold; } /* type/class identifier */
-.nitcode .nc_a { color: #445588; font-style: italic; } /* old style attribute identifier */
-.nitcode .nc_l { color: #009999; } /* char and number literal */
-.nitcode .nc_s { color: #8F1546; } /* string literal */
-/* syntactic token usage. added because of their position in the AST */
-.nitcode .nc_ast { color: blue; } /* assert label */
-.nitcode .nc_la { color: blue; } /* break/continue label */
-.nitcode .nc_m { color: #445588; } /* module name */
-/* syntactic groups */
-.nitcode .nc_def { font-weight: bold; color: blue; } /* name used in a definition */
- .nitcode .nc_def.nc_a { color: blue; } /* name used in a attribute definition */
- .nitcode .nc_def.nc_t { color: blue; } /* name used in a class or vt definition */
-.nitcode .nc_ss { color: #9E6BEB; } /* superstrings */
-.nitcode .nc_cdef {} /* A whole class definition */
-.nitcode .nc_pdef {} /* A whole property definition */
-/* semantic token usage */
-.nitcode .nc_v { font-style: italic; } /* local variable or parameter */
-.nitcode .nc_vt { font-style: italic; } /* virtual type or formal type */
-
-.nitcode .nc_error { border: 1px red solid;} /* not used */
-.popover { max-width: 800px !important; }
-"""
+ do_highlight(source.first_token.as(not null), null)
end
- # Additional content to inject in the <head> tag
- # Note: does not include `css_content`; handle it yourself.
- fun head_content: String
- do
- return """<link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">\n"""
- end
-
- # Additional content to inject just before the closing </body> tag
- fun foot_content: String
- do
- return """
-<script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
-<script src="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
-<script>$(".popupable").popover({html:true, placement:'top'})/*initialize bootstrap popover*/</script>"""
- end
-end
-
-redef class HTMLTag
- # Attach the infobox to the node by using BootStrap popover
- fun attach_infobox(infobox: HInfoBox)
- do
- classes.add("popupable")
- attrs["title"] = infobox.title
- var href = infobox.href
- if href != null then
- attrs["data-title"] = """<a href="{{{href}}}">{{{infobox.title}}}</a>"""
- end
- attrs["data-content"] = infobox.content.write_to_string
- attrs["data-toggle"] = "popover"
- end
+ # Low-level highlighting between 2 tokens
+ protected fun do_highlight(first_token: Token, last_token: nullable Token) is abstract
end
+# Text-based highlighter that use ANSI escape sequence for colors
+class AnsiHighlightVisitor
+ super AbstractHighlightVisitor
-# A generic information container that can be used to decorate AST entities
-class HInfoBox
- # The visitor used for contextualisation, if needed
- var visitor: HighlightVisitor
-
- # A short title for the AST element
- var title: String
-
- # The primary link where the entity points
- # null if no link
- var href: nullable String = null
-
- # The content of the popuped infobox
- var content = new HTMLTag("div")
-
- # Append a new field in the popuped infobox
- fun new_field(title: String): HTMLTag
- do
- content.open("b").text(title)
- content.append(" ")
- var res = content.open("span")
- content.open("br")
- return res
- end
-
- # Append a new dropdown in the popuped content
- fun new_dropdown(title, text: String, text_is_html: nullable Bool): HTMLTag
- do
- content.add_raw_html """<div class="dropdown"> <a data-toggle="dropdown" href="#"><b>"""
- content.append(title)
- content.add_raw_html "</b> "
- if text_is_html == true then
- content.add_raw_html(text)
- else content.append(text)
- content.add_raw_html """<span class="caret"></span></a>"""
- var res = content.open("ul").add_class("dropdown-menu").attr("role", "menu").attr("aria-labelledby", "dLabel")
- content.add_raw_html "</div>"
- return res
- end
-end
-
-##
-
-# Model entity or whatever that can produce an infobox
-interface HInfoBoxable
- # An new infobox documenting the entity
- fun infobox(v: HighlightVisitor): HInfoBox is abstract
-end
-
-redef class MDoc
- # Append an entry for the doc in the given infobox
- fun fill_infobox(res: HInfoBox)
- do
- if content.length < 2 then
- res.new_field("doc").text(content.first)
- return
- end
- var c = res.new_dropdown("doc", content.first)
- for x in content.iterator.skip_head(1) do
- c.append x
- c.add_raw_html "<br>"
- end
- end
-end
-
-redef class MEntity
- super HInfoBoxable
-
- # A HTML version of `to_s` with hyper-links.
- #
- # By default, `linkto_text(v, to_s)` is used, c.f. see `linkto_text`.
- #
- # For some complex entities, like generic types, multiple `<a>` and `<span>` elements can be generated.
- # E.g. `Array[Int]` might become `<a>Array</a>[<a>Int</a>]` with the correct `href` attributes
- # provided by `v.hrefto`.
- fun linkto(v: HighlightVisitor): HTMLTag do return linkto_text(v, to_s)
-
- # Link to the `self` with a specific text.
- #
- # The whole text is linked with a single `<a>` element.
- #
- # The `href` used is provided by `v.hrefto`.
- # If `href` is null then a `<span>` element is used instead of `<a>`.
- fun linkto_text(v: HighlightVisitor, text: String): HTMLTag
- do
- var href = v.hrefto(self)
- if href == null then
- return (new HTMLTag("span")).text(text)
- end
- return (new HTMLTag("a")).attr("href", href).text(text)
- end
-
- # Append an entry for the doc in the given infobox
- private fun add_doc_to_infobox(res: HInfoBox)
- do
- var mdoc = mdoc_or_fallback
- if mdoc != null then mdoc.fill_infobox(res)
- end
-end
-
-redef class MModule
- redef fun infobox(v)
- do
- var res = new HInfoBox(v, "module {name}")
- res.href = v.hrefto(self)
- res.new_field("module").add(linkto(v))
- add_doc_to_infobox(res)
- if in_importation.greaters.length > 1 then
- var c = res.new_dropdown("imports", "{in_importation.greaters.length-1} modules")
- for x in in_importation.greaters do
- if x == self then continue
- c.open("li").add x.linkto(v)
- end
- end
- return res
- end
-
- redef fun linkto(v) do return linkto_text(v, name)
-end
-
-redef class MClassDef
- redef fun infobox(v)
- do
- var res = new HInfoBox(v, "class {mclass.name}")
- res.href = v.hrefto(self)
- if is_intro then
- res.new_field("class").text(mclass.name)
- else
- res.new_field("redef class").text(mclass.name)
- res.new_field("intro").add mclass.intro.linkto_text(v, "in {mclass.intro_mmodule.to_s}")
- end
- add_doc_to_infobox(res)
-
- var in_hierarchy = self.in_hierarchy
- if in_hierarchy == null then return res
-
- if in_hierarchy.greaters.length > 1 then
- var c = res.new_dropdown("hier", "super-classes")
- for x in in_hierarchy.greaters do
- if x == self then continue
- if not x.is_intro then continue
- c.open("li").add x.linkto(v)
- end
- end
- if in_hierarchy.smallers.length > 1 then
- var c = res.new_dropdown("hier", "sub-classes")
- for x in in_hierarchy.smallers do
- if x == self then continue
- if not x.is_intro then continue
- c.open("li").add x.linkto(v)
- end
- end
- if mclass.mclassdefs.length > 1 then
- var c = res.new_dropdown("redefs", "refinements")
- for x in mclass.mclassdefs do
- if x == self then continue
- c.open("li").add x.linkto_text(v, "in {x.mmodule}")
- end
- end
- return res
- end
-end
-
-redef class MPropDef
- redef fun infobox(v)
- do
- var res = new HInfoBox(v, to_s)
- res.href = v.hrefto(self)
- if self isa MMethodDef then
- var msignature = self.msignature
- if msignature != null then res.new_field("fun").append(mproperty.name).add msignature.linkto(v)
- else if self isa MAttributeDef then
- var static_mtype = self.static_mtype
- if static_mtype != null then res.new_field("fun").append(mproperty.name).add static_mtype.linkto(v)
- else if self isa MVirtualTypeDef then
- var bound = self.bound
- if bound != null then res.new_field("add").append(mproperty.name).add bound.linkto(v)
- else
- res.new_field("wat?").append(mproperty.name)
- end
-
- if is_intro then
- else
- res.new_field("intro").add mproperty.intro.linkto_text(v, "in {mproperty.intro.mclassdef}")
- end
- add_doc_to_infobox(res)
- if mproperty.mpropdefs.length > 1 then
- var c = res.new_dropdown("redef", "redefinitions")
- for x in mproperty.mpropdefs do
- c.open("li").add x.linkto_text(v, "in {x.mclassdef}")
- end
- end
-
- return res
- end
-end
-
-redef class MClassType
- redef fun infobox(v)
- do
- var res = new HInfoBox(v, to_s)
- res.href = v.hrefto(self)
- res.new_field("class").add mclass.intro.linkto(v)
- add_doc_to_infobox(res)
- return res
- end
- redef fun linkto(v)
- do
- return mclass.intro.linkto(v)
- end
-end
-redef class MVirtualType
- redef fun infobox(v)
- do
- var res = new HInfoBox(v, to_s)
- res.href = v.hrefto(mproperty)
- var p = mproperty
- res.new_field("virtual type").add p.intro.linkto(v)
- add_doc_to_infobox(res)
- return res
- end
- redef fun linkto(v)
- do
- return mproperty.intro.linkto(v)
- end
-end
-redef class MParameterType
- redef fun infobox(v)
- do
- var res = new HInfoBox(v, to_s)
- res.new_field("parameter type").append("{name} from class ").add mclass.intro.linkto(v)
- return res
- end
-end
-
-redef class MNullableType
- redef fun infobox(v)
- do
- return mtype.infobox(v)
- end
- redef fun linkto(v)
- do
- var res = new HTMLTag("span")
- res.append("nullable ").add(mtype.linkto(v))
- return res
- end
-end
-
-redef class MNotNullType
- redef fun infobox(v)
- do
- return mtype.infobox(v)
- end
- redef fun linkto(v)
- do
- var res = new HTMLTag("span")
- res.append("not null ").add(mtype.linkto(v))
- return res
- end
-end
+ # The produced highlighting
+ var result = new Template
-redef class MNullType
- redef fun infobox(v)
+ redef fun do_highlight(f, l)
do
- var res = new HInfoBox(v, to_s)
- return res
- end
- redef fun linkto(v)
- do
- var res = new HTMLTag("span")
- res.append("null")
- return res
- end
-end
+ var c
+ c = f
+ while c != null do
+ if c != f then result.add(c.blank_before)
+ result.add c.ansi_colored
-redef class MSignature
- redef fun linkto(v)
- do
- var res = new HTMLTag("span")
- var first = true
- if not mparameters.is_empty then
- res.append "("
- for p in mparameters do
- if first then
- first = false
- else
- res.append ", "
- end
- res.append p.name
- res.append ": "
- res.add p.mtype.linkto(v)
+ if c == l then
+ c = null
+ else
+ c = c.next_token
end
- res.append ")"
- end
- var ret = return_mtype
- if ret != null then
- res.append ": "
- res.add ret.linkto(v)
end
- return res
- end
-end
-
-redef class CallSite
- redef fun infobox(v)
- do
- var res = new HInfoBox(v, "call {mpropdef}")
- res.href = v.hrefto(mpropdef)
- res.new_field("call").add(mpropdef.linkto(v)).add(msignature.linkto(v))
- if mpropdef.is_intro then
- else
- res.new_field("intro").add mproperty.intro.linkto_text(v, "in {mproperty.intro.mclassdef}")
- end
- add_doc_to_infobox(res)
-
- return res
- end
- redef fun linkto(v)
- do
- return mpropdef.linkto(v)
- end
-end
-
-redef class Variable
- super HInfoBoxable
- redef fun infobox(v)
- do
- var declared_type = self.declared_type
- if declared_type == null then
- var res = new HInfoBox(v, "{name}")
- res.new_field("local var").append("{name}")
- return res
- end
- var res = new HInfoBox(v, "{name}: {declared_type}")
- res.new_field("local var").append("{name}:").add(declared_type.linkto(v))
- return res
- end
-end
-
-
-##
-
-redef class ANode
- # Optionally creates a tag that encapsulate the AST element on HTML rendering
- protected fun make_tag(v: HighlightVisitor): nullable HTMLTag do return null
-
- # Add aditionnal information on a child-token and return an additionnal HInfoBox on it
- protected fun decorate_tag(v: HighlightVisitor, res: HTMLTag, token: Token): nullable HInfoBox
- do
- #debug("no decoration for {token.inspect}")
- #res.add_class("nc_error")
- return null
- end
-
- # Return a optional infobox
- fun infobox(v: HighlightVisitor): nullable HInfoBox do return null
-end
-
-redef class AQclassid
- redef fun decorate_tag(v, res, token)
- do
- if token != n_id then return null
- var parent = self.parent
- if parent == null then return null
- return parent.decorate_tag(v, res, token)
- end
-end
-
-redef class AQid
- redef fun decorate_tag(v, res, token)
- do
- if token != n_id then return null
- var parent = self.parent
- if parent == null then return null
- return parent.decorate_tag(v, res, token)
- end
-end
-
-redef class AStdClassdef
- redef fun make_tag(v)
- do
- var res = new HTMLTag("span")
- res.add_class("nc_cdef")
- var md = mclassdef
- if md != null then res.attr("id", md.to_s)
- return res
- end
- redef fun decorate_tag(v, res, token)
- do
- if not token isa TClassid then return null
- res.add_class("nc_def")
-
- var md = mclassdef
- if md == null then return null
- return md.infobox(v)
- end
-end
-redef class APropdef
- redef fun make_tag(v)
- do
- var res = new HTMLTag("span")
- res.add_class("nc_pdef")
- var mpd
- mpd = mpropdef
- if mpd != null then
- #res.add(tag(mpd))
- res.attr("id", mpd.to_s)
- end
- if self isa AAttrPropdef then
- mpd = mreadpropdef
- if mpd != null then res.add(tag(mpd))
- mpd = mwritepropdef
- if mpd != null then res.add(tag(mpd))
- end
- return res
- end
-
- private fun tag(mpd: MPropDef): HTMLTag
- do
- var a = new HTMLTag("a")
- a.attr("id", mpd.to_s)
- return a
end
end
redef class Token
- # Produce an HTMLTag with the correct contents and CSS classes
- # Subclasses can redefine it to decorate the tag
- redef fun make_tag(v): HTMLTag
- do
- var res = new HTMLTag("span")
- res.text(text)
- return res
- end
-end
-
-redef class TokenKeyword
- redef fun make_tag(v)
- do
- var res = super
- res.add_class("nc_k")
- return res
- end
-end
-redef class TokenOperator
- redef fun make_tag(v)
- do
- var res = super
- res.add_class("nc_o")
- return res
- end
-end
-
-redef class AVarFormExpr
- redef fun decorate_tag(v, res, token)
- do
- if token != n_id then return null
- var variable = self.variable
- if variable == null then return null
- res.add_class("nc_v")
- return variable.infobox(v)
- end
+ # Return the ANSI colored text
+ fun ansi_colored: String do return text
end
-redef class AVardeclExpr
- redef fun decorate_tag(v, res, token)
- do
- if token != n_id then return null
- var variable = self.variable
- if variable == null then return null
- res.add_class("nc_v")
- return variable.infobox(v)
- end
-end
-
-redef class AForGroup
- redef fun decorate_tag(v, res, token)
- do
- if not token isa TId then return null
- var vs = variables
- if vs == null then return null
- res.add_class("nc_v")
- var idx = n_ids.index_of(token)
- var variable = vs[idx]
- return variable.infobox(v)
- end
-end
-
-redef class AParam
- redef fun decorate_tag(v, res, token)
- do
- if token != n_id then return null
- var mp = mparameter
- if mp == null then return null
- var variable = self.variable
- if variable == null then return null
- res.add_class("nc_v")
- return variable.infobox(v)
- end
-end
-
-redef class AAssertExpr
- redef fun decorate_tag(v, res, token)
- do
- if not token isa TId then return null
- res.add_class("nc_ast")
- return null
- end
-end
-
-redef class ALabel
- redef fun decorate_tag(v, res, token)
- do
- if not token isa TId then return null
- res.add_class("nc_la")
- return null
- end
-end
-
-redef class ASendExpr
- redef fun decorate_tag(v, res, token)
- do
- var callsite = self.callsite
- if callsite == null then return null
- return callsite.infobox(v)
- end
-end
-
-redef class ANewExpr
- redef fun decorate_tag(v, res, token)
- do
- var callsite = self.callsite
- if callsite == null then return null
- return callsite.infobox(v)
- end
-end
-
-redef class AAssignOp
- redef fun decorate_tag(v, res, token)
- do
- var p = parent
- assert p isa AReassignFormExpr
-
- var callsite = p.reassign_callsite
- if callsite == null then return null
- return callsite.infobox(v)
- end
-end
-
-redef class AModuleName
- redef fun decorate_tag(v, res, token)
- do
- var p = parent
- if p == null then return null
- return p.decorate_tag(v, res, token)
- end
-end
-
-redef class AModuledecl
- redef fun decorate_tag(v, res, token)
- do
- if not token isa TId then return null
- res.add_class("nc_def")
- res.add_class("nc_m")
- var p = parent
- assert p isa AModule
- var mm = p.mmodule
- if mm == null then return null
- return mm.infobox(v)
- end
+redef class TComment
+ redef fun ansi_colored do return super.blue
end
-redef class AStdImport
- redef fun decorate_tag(v, res, token)
- do
- if not token isa TId then return null
- res.add_class("nc_m")
- var mm = mmodule
- if mm == null then return null
- return mm.infobox(v)
- end
-end
-redef class AAttrPropdef
- redef fun decorate_tag(v, res, token)
- do
- if not token isa TId then return null
- res.add_class("nc_def")
- var mpd: nullable MPropDef
- mpd = mreadpropdef
- if mpd == null then mpd = mpropdef
- if mpd == null then return null
- return mpd.infobox(v)
- end
+redef class TokenKeyword
+ redef fun ansi_colored do return super.yellow
end
-redef class TId
- redef fun make_tag(v)
- do
- var res = super
- res.add_class("nc_i")
- return res
- end
-end
-redef class AMethid
- redef fun make_tag(v)
- do
- var res = new HTMLTag("span")
- res.add_class("nc_def")
- return res
- end
- redef fun decorate_tag(v, res, token)
- do
- return null
- # nothing to decorate
- end
- redef fun infobox(v)
- do
- var p = parent
- if not p isa AMethPropdef then return null
- var mpd = p.mpropdef
- if mpd == null then return null
- return mpd.infobox(v)
- end
-end
-redef class TAttrid
- redef fun make_tag(v)
- do
- var res = super
- res.add_class("nc_a")
- return res
- end
-end
-redef class AAttrFormExpr
- redef fun decorate_tag(v, res, token)
- do
- if not token isa TAttrid then return null
- var p = mproperty
- if p == null then return null
- return p.intro.infobox(v)
- end
-end
redef class TClassid
- redef fun make_tag(v)
- do
- var res = super
- res.add_class("nc_t")
- return res
- end
-end
-redef class AType
- redef fun decorate_tag(v, res, token)
- do
- if not token isa TClassid then return null
- var mt = mtype
- if mt == null then return null
- mt = mt.undecorate
- if mt isa MFormalType then
- res.add_class("nc_vt")
- end
- return mt.infobox(v)
- end
-end
-redef class AFormaldef
- redef fun decorate_tag(v, res, token)
- do
- if not token isa TClassid then return null
- res.add_class("nc_vt")
- var mtype = self.mtype
- if mtype == null then return null
- return mtype.infobox(v)
- end
-end
-redef class ATypePropdef
- redef fun decorate_tag(v, res, token)
- do
- if not token isa TClassid then return null
- res.add_class("nc_def")
- var md = mpropdef
- if md == null then return null
- return md.infobox(v)
- end
-end
-redef class TComment
- redef fun make_tag(v)
- do
- var res = super
- if is_loose then
- res.add_class("nc_c")
- end
- return res
- end
-end
-redef class ADoc
- redef fun make_tag(v)
- do
- var res = new HTMLTag("span")
- res.add_class("nc_d")
- return res
- end
+ redef fun ansi_colored do return super.green
end
+
redef class TokenLiteral
- redef fun make_tag(v)
- do
- var res = super
- res.add_class("nc_l")
- return res
- end
-end
-redef class ASuperstringExpr
- redef fun make_tag(v)
- do
- var res = new HTMLTag("span")
- res.add_class("nc_ss")
- return res
- end
-end
-redef class AStringFormExpr
- redef fun decorate_tag(v, res, token)
- do
- # Workaround to tag strings
- res.classes.remove("nc_l")
- res.add_class("nc_s")
- return super
- end
-end
-redef class AExpr
- redef fun decorate_tag(v, res, token)
- do
- var t = mtype
- if t == null then return null
- return t.infobox(v)
- end
+ redef fun ansi_colored do return super.red
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.
+
+# Highlighting of Nit AST with HTML
+module htmlight
+
+import highlight
+import html
+import pipeline
+import serialization
+
+# A standalone highlighted piece of code
+class HLCode
+ super Serializable
+
+ # The highlighter used
+ var hl: HtmlightVisitor
+
+ # The raw code source
+ var content: String
+
+ # The pseudo source-file
+ var source: SourceFile
+
+ # JavaScript code to update an existing codemirror editor.
+ fun code_mirror_update: Template
+ do
+
+ var res = new Template
+ res.add """
+ function nitmessage() {
+ editor.operation(function(){
+ for (var i = 0; i < widgets.length; ++i)
+ editor.removeLineWidget(widgets[i]);
+ widgets.length = 0;
+"""
+
+ for m in source.messages do
+ res.add """
+ var l = document.createElement("div");
+ l.className = "lint-error"
+ l.innerHTML = "<span class='glyphicon glyphicon-warning-sign lint-error-icon'></span> {{{m.text.html_escape}}}";
+ var w = editor.addLineWidget({{{m.location.line_start-1}}}, l);
+ widgets.push(w);
+"""
+ end
+ res.add """});}"""
+ return res
+ end
+
+ redef fun core_serialize_to(v)
+ do
+ v.serialize_attribute("code", hl.html.write_to_string)
+ var msgs = new Array[Map[String, Serializable]]
+ for m in source.messages do
+ var o = new Map[String, Serializable]
+ msgs.add o
+ o["line"] = m.location.line_start-1
+ o["message"] = m.text
+ end
+ v.serialize_attribute("messages", msgs)
+ end
+end
+
+# Visitor used to produce a HTML tree based on a AST on a `Source`
+class HtmlightVisitor
+ super AbstractHighlightVisitor
+
+ # The root of the HTML hierarchy
+ var html = new HTMLTag("span")
+
+ # Should the HTML include a nested `<span class"{type_of_node}">` element for each `ANode` of the AST?
+ #
+ # Used to have a really huge and verbose HTML (mainly for debug)
+ var with_ast = false is writable
+
+ # Prefixes used in generated IDs for line `<span>` elements.
+ # Useful if more than one highlighted code is present in the same HTML document.
+ #
+ # If set to the empty string, id for lines are disabled.
+ #
+ # Is `"L"` by default.
+ var line_id_prefix = "L" is writable
+
+ # When highlighting a node, attach a full popupable infobox, if any.
+ #
+ # If `false`, only a simple `title` tooltip is used.
+ #
+ # default: true
+ var show_infobox = true is writable
+
+ # A reference to an entity used in generated `<a>` elements.
+ #
+ # It is used to refer to some specific entities when generating links.
+ # If `null` is returned, then no link are generated and `<a>` elements become `<span>`.
+ #
+ # By default, `null` is returned.
+ # Clients are therefore encouraged to redefine the method in a subclass to control where entities should link to.
+ fun hrefto(entity: MEntity): nullable String do return null
+
+ init
+ do
+ html.add_class("nitcode")
+ end
+
+ private fun full_tag(anode: ANode): nullable HTMLTag
+ do
+ var tag = anode.make_tag(self)
+ if tag == null then return null
+ var infobox = anode.infobox(self)
+ if infobox == null and anode isa Token then
+ var pa = anode.parent
+ if pa != null then
+ infobox = pa.decorate_tag(self, tag, anode)
+ end
+ end
+ if infobox != null and not show_infobox then
+ tag.attr("title", infobox.title)
+ tag.classes.add "titled"
+ infobox = null
+ end
+ var messages = anode.location.messages
+ if messages != null and show_messages then
+ tag.css("border-bottom", "solid 2px red")
+ if infobox == null then
+ infobox = new HInfoBox(self, "Messages")
+ end
+ var c = infobox.new_dropdown("{messages.length} message(s)", "")
+ for m in messages do
+ c.open("li").append(m.text)
+ end
+ end
+ if infobox != null then
+ tag.attach_infobox(infobox)
+ end
+ return tag
+ end
+
+ # Low-level highlighting between 2 tokens
+ redef fun do_highlight(first_token, last_token)
+ do
+ var stack2 = new Array[HTMLTag]
+ var stack = new Array[Prod]
+ var line = 0
+ var c: nullable Token = first_token
+ while c != null do
+ var starting
+
+ # Handle start of line
+ var cline = c.location.line_start
+ if cline != line then
+ # Handle starting block productions,
+ # Because c could be a detached token, get prods in
+ # the first AST token
+ var c0 = c.first_token_in_line
+ starting = null
+ if c0 != null then starting = c0.starting_prods
+ if starting != null then for p in starting do
+ if not p.is_block then continue
+ var tag = full_tag(p)
+ if tag == null then continue
+ tag.add_class("foldable")
+ stack2.add(html)
+ html.add tag
+ html = tag
+ stack.add(p)
+ end
+
+ # Add a div for the whole line
+ var tag = new HTMLTag("span")
+ var p = line_id_prefix
+ if p != "" then tag.attrs["id"] = "{p}{cline}"
+ tag.classes.add "line"
+ stack2.add(html)
+ html.add tag
+ html = tag
+ line = cline
+ end
+
+ # Add the blank, verbatim
+ html.add_raw_html c.blank_before
+
+ # Handle starting span production
+ starting = c.starting_prods
+ if starting != null then for p in starting do
+ if not p.is_span then continue
+ var tag = full_tag(p)
+ if tag == null then continue
+ stack2.add(html)
+ html.add tag
+ html = tag
+ stack.add(p)
+ end
+
+ # Add the token
+ if c isa TEol then
+ html.append "\n"
+ else
+ var tag = full_tag(c)
+ if tag != null then html.add tag
+ end
+
+ # Handle ending span productions
+ var ending = c.ending_prods
+ if ending != null then for p in ending do
+ if not p.is_span then continue
+ if stack.is_empty or p != stack.last then continue
+ stack.pop
+ html = stack2.pop
+ end
+
+ # Handle end of line and end of file
+ var n = c.next_token
+ if c == last_token then n = null
+ if n == null or n.location.line_start != line then
+ # closes the line div
+ html = stack2.pop
+
+ # close the block production divs
+ var c0 = c.last_token_in_line
+ ending = null
+ if c0 != null then ending = c0.ending_prods
+ if ending != null then for p in ending do
+ if not p.is_block then continue
+ if stack.is_empty or p != stack.last then continue
+ stack.pop
+ html = stack2.pop
+ end
+ end
+
+ c = n
+ end
+ if not stack2.is_empty then html = stack2.first
+ end
+
+ # Return a default CSS content related to CSS classes used in the `html` tree.
+ # Could be inlined in the `.html` file of saved as a specific `.css` file.
+ fun css_content: String
+ do
+ return """
+.nitcode a { color: inherit; cursor:pointer; }
+.nitcode .titled:hover { text-decoration: underline; } /* underline titles */
+.nitcode .popupable:hover { text-decoration: underline; cursor:help; } /* underline titles */
+.nitcode .foldable { display: block } /* for block productions*/
+.nitcode .line{ display: block } /* for lines */
+.nitcode .line:hover{ background-color: #FFFFE0; } /* current line */
+.nitcode :target { background-color: #FFF3C2 } /* target highlight*/
+/* lexical raw tokens. independent of usage or semantic: */
+.nitcode .nc_c { color: gray; font-style: italic; } /* comment */
+.nitcode .nc_d { color: #3D8127; font-style: italic; } /* documentation comments */
+.nitcode .nc_k { font-weight: bold; } /* keyword */
+.nitcode .nc_o {} /* operator */
+.nitcode .nc_i {} /* standard identifier */
+.nitcode .nc_t { color: #445588; font-weight: bold; } /* type/class identifier */
+.nitcode .nc_a { color: #445588; font-style: italic; } /* old style attribute identifier */
+.nitcode .nc_l { color: #009999; } /* char and number literal */
+.nitcode .nc_s { color: #8F1546; } /* string literal */
+/* syntactic token usage. added because of their position in the AST */
+.nitcode .nc_ast { color: blue; } /* assert label */
+.nitcode .nc_la { color: blue; } /* break/continue label */
+.nitcode .nc_m { color: #445588; } /* module name */
+/* syntactic groups */
+.nitcode .nc_def { font-weight: bold; color: blue; } /* name used in a definition */
+ .nitcode .nc_def.nc_a { color: blue; } /* name used in a attribute definition */
+ .nitcode .nc_def.nc_t { color: blue; } /* name used in a class or vt definition */
+.nitcode .nc_ss { color: #9E6BEB; } /* superstrings */
+.nitcode .nc_cdef {} /* A whole class definition */
+.nitcode .nc_pdef {} /* A whole property definition */
+/* semantic token usage */
+.nitcode .nc_v { font-style: italic; } /* local variable or parameter */
+.nitcode .nc_vt { font-style: italic; } /* virtual type or formal type */
+
+.nitcode .nc_error { border: 1px red solid;} /* not used */
+.popover { max-width: 800px !important; }
+"""
+ end
+
+ # Additional content to inject in the <head> tag
+ # Note: does not include `css_content`; handle it yourself.
+ fun head_content: String
+ do
+ return """<link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">\n"""
+ end
+
+ # Additional content to inject just before the closing </body> tag
+ fun foot_content: String
+ do
+ return """
+<script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
+<script src="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
+<script>$(".popupable").popover({html:true, placement:'top'})/*initialize bootstrap popover*/</script>"""
+ end
+
+ # Fully process `content` as a Nit source file.
+ #
+ # Set `print_errors = true` to print errors in the code to the console.
+ fun highlightcode(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
+ 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.
+ # There is no way (yet?) to have a shared module `core` with
+ # isolated and throwable user modules.
+ var model = new Model
+ var mb = new ModelBuilder(model, tc)
+
+ # Parse the code
+ var source = new SourceFile.from_string("", content + "\n")
+ var lexer = new Lexer(source)
+ var parser = new Parser(lexer)
+ var tree = parser.parse
+
+ var hlcode = new HLCode(self, content, source)
+
+ # Check syntax error
+ var eof = tree.n_eof
+ if eof isa AError then
+ mb.error(eof, eof.message)
+ highlight_source(source)
+ return hlcode
+ end
+ var amodule = tree.n_base.as(not null)
+
+ # Load the AST as a module in the model
+ # Then process it
+ mb.load_rt_module(null, amodule, "")
+ mb.run_phases
+
+ # Highlight the processed module
+ highlight_node(amodule)
+ return hlcode
+ end
+end
+
+redef class HTMLTag
+ # Attach the infobox to the node by using BootStrap popover
+ fun attach_infobox(infobox: HInfoBox)
+ do
+ classes.add("popupable")
+ attrs["title"] = infobox.title
+ var href = infobox.href
+ if href != null then
+ attrs["data-title"] = """<a href="{{{href}}}">{{{infobox.title}}}</a>"""
+ end
+ attrs["data-content"] = infobox.content.write_to_string
+ attrs["data-toggle"] = "popover"
+ end
+end
+
+
+# A generic information container that can be used to decorate AST entities
+class HInfoBox
+ # The visitor used for contextualisation, if needed
+ var visitor: HtmlightVisitor
+
+ # A short title for the AST element
+ var title: String
+
+ # The primary link where the entity points
+ # null if no link
+ var href: nullable String = null
+
+ # The content of the popuped infobox
+ var content = new HTMLTag("div")
+
+ # Append a new field in the popuped infobox
+ fun new_field(title: String): HTMLTag
+ do
+ content.open("b").text(title)
+ content.append(" ")
+ var res = content.open("span")
+ content.open("br")
+ return res
+ end
+
+ # Append a new dropdown in the popuped content
+ fun new_dropdown(title, text: String, text_is_html: nullable Bool): HTMLTag
+ do
+ content.add_raw_html """<div class="dropdown"> <a data-toggle="dropdown" href="#"><b>"""
+ content.append(title)
+ content.add_raw_html "</b> "
+ if text_is_html == true then
+ content.add_raw_html(text)
+ else content.append(text)
+ content.add_raw_html """<span class="caret"></span></a>"""
+ var res = content.open("ul").add_class("dropdown-menu").attr("role", "menu").attr("aria-labelledby", "dLabel")
+ content.add_raw_html "</div>"
+ return res
+ end
+end
+
+##
+
+# Model entity or whatever that can produce an infobox
+interface HInfoBoxable
+ # An new infobox documenting the entity
+ fun infobox(v: HtmlightVisitor): HInfoBox is abstract
+end
+
+redef class MDoc
+ # Append an entry for the doc in the given infobox
+ fun fill_infobox(res: HInfoBox)
+ do
+ if content.length < 2 then
+ res.new_field("doc").text(content.first)
+ return
+ end
+ var c = res.new_dropdown("doc", content.first)
+ for x in content.iterator.skip_head(1) do
+ c.append x
+ c.add_raw_html "<br>"
+ end
+ end
+end
+
+redef class MEntity
+ super HInfoBoxable
+
+ # A HTML version of `to_s` with hyper-links.
+ #
+ # By default, `linkto_text(v, to_s)` is used, c.f. see `linkto_text`.
+ #
+ # For some complex entities, like generic types, multiple `<a>` and `<span>` elements can be generated.
+ # E.g. `Array[Int]` might become `<a>Array</a>[<a>Int</a>]` with the correct `href` attributes
+ # provided by `v.hrefto`.
+ fun linkto(v: HtmlightVisitor): HTMLTag do return linkto_text(v, to_s)
+
+ # Link to the `self` with a specific text.
+ #
+ # The whole text is linked with a single `<a>` element.
+ #
+ # The `href` used is provided by `v.hrefto`.
+ # If `href` is null then a `<span>` element is used instead of `<a>`.
+ fun linkto_text(v: HtmlightVisitor, text: String): HTMLTag
+ do
+ var href = v.hrefto(self)
+ if href == null then
+ return (new HTMLTag("span")).text(text)
+ end
+ return (new HTMLTag("a")).attr("href", href).text(text)
+ end
+
+ # Append an entry for the doc in the given infobox
+ private fun add_doc_to_infobox(res: HInfoBox)
+ do
+ var mdoc = mdoc_or_fallback
+ if mdoc != null then mdoc.fill_infobox(res)
+ end
+end
+
+redef class MModule
+ redef fun infobox(v)
+ do
+ var res = new HInfoBox(v, "module {name}")
+ res.href = v.hrefto(self)
+ res.new_field("module").add(linkto(v))
+ add_doc_to_infobox(res)
+ if in_importation.greaters.length > 1 then
+ var c = res.new_dropdown("imports", "{in_importation.greaters.length-1} modules")
+ for x in in_importation.greaters do
+ if x == self then continue
+ c.open("li").add x.linkto(v)
+ end
+ end
+ return res
+ end
+
+ redef fun linkto(v) do return linkto_text(v, name)
+end
+
+redef class MClassDef
+ redef fun infobox(v)
+ do
+ var res = new HInfoBox(v, "class {mclass.name}")
+ res.href = v.hrefto(self)
+ if is_intro then
+ res.new_field("class").text(mclass.name)
+ else
+ res.new_field("redef class").text(mclass.name)
+ res.new_field("intro").add mclass.intro.linkto_text(v, "in {mclass.intro_mmodule.to_s}")
+ end
+ add_doc_to_infobox(res)
+
+ var in_hierarchy = self.in_hierarchy
+ if in_hierarchy == null then return res
+
+ if in_hierarchy.greaters.length > 1 then
+ var c = res.new_dropdown("hier", "super-classes")
+ for x in in_hierarchy.greaters do
+ if x == self then continue
+ if not x.is_intro then continue
+ c.open("li").add x.linkto(v)
+ end
+ end
+ if in_hierarchy.smallers.length > 1 then
+ var c = res.new_dropdown("hier", "sub-classes")
+ for x in in_hierarchy.smallers do
+ if x == self then continue
+ if not x.is_intro then continue
+ c.open("li").add x.linkto(v)
+ end
+ end
+ if mclass.mclassdefs.length > 1 then
+ var c = res.new_dropdown("redefs", "refinements")
+ for x in mclass.mclassdefs do
+ if x == self then continue
+ c.open("li").add x.linkto_text(v, "in {x.mmodule}")
+ end
+ end
+ return res
+ end
+end
+
+redef class MPropDef
+ redef fun infobox(v)
+ do
+ var res = new HInfoBox(v, to_s)
+ res.href = v.hrefto(self)
+ if self isa MMethodDef then
+ var msignature = self.msignature
+ if msignature != null then res.new_field("fun").append(mproperty.name).add msignature.linkto(v)
+ else if self isa MAttributeDef then
+ var static_mtype = self.static_mtype
+ if static_mtype != null then res.new_field("fun").append(mproperty.name).add static_mtype.linkto(v)
+ else if self isa MVirtualTypeDef then
+ var bound = self.bound
+ if bound != null then res.new_field("add").append(mproperty.name).add bound.linkto(v)
+ else
+ res.new_field("wat?").append(mproperty.name)
+ end
+
+ if is_intro then
+ else
+ res.new_field("intro").add mproperty.intro.linkto_text(v, "in {mproperty.intro.mclassdef}")
+ end
+ add_doc_to_infobox(res)
+ if mproperty.mpropdefs.length > 1 then
+ var c = res.new_dropdown("redef", "redefinitions")
+ for x in mproperty.mpropdefs do
+ c.open("li").add x.linkto_text(v, "in {x.mclassdef}")
+ end
+ end
+
+ return res
+ end
+end
+
+redef class MClassType
+ redef fun infobox(v)
+ do
+ var res = new HInfoBox(v, to_s)
+ res.href = v.hrefto(self)
+ res.new_field("class").add mclass.intro.linkto(v)
+ add_doc_to_infobox(res)
+ return res
+ end
+ redef fun linkto(v)
+ do
+ return mclass.intro.linkto(v)
+ end
+end
+redef class MVirtualType
+ redef fun infobox(v)
+ do
+ var res = new HInfoBox(v, to_s)
+ res.href = v.hrefto(mproperty)
+ var p = mproperty
+ res.new_field("virtual type").add p.intro.linkto(v)
+ add_doc_to_infobox(res)
+ return res
+ end
+ redef fun linkto(v)
+ do
+ return mproperty.intro.linkto(v)
+ end
+end
+redef class MParameterType
+ redef fun infobox(v)
+ do
+ var res = new HInfoBox(v, to_s)
+ res.new_field("parameter type").append("{name} from class ").add mclass.intro.linkto(v)
+ return res
+ end
+end
+
+redef class MNullableType
+ redef fun infobox(v)
+ do
+ return mtype.infobox(v)
+ end
+ redef fun linkto(v)
+ do
+ var res = new HTMLTag("span")
+ res.append("nullable ").add(mtype.linkto(v))
+ return res
+ end
+end
+
+redef class MNotNullType
+ redef fun infobox(v)
+ do
+ return mtype.infobox(v)
+ end
+ redef fun linkto(v)
+ do
+ var res = new HTMLTag("span")
+ res.append("not null ").add(mtype.linkto(v))
+ return res
+ end
+end
+
+redef class MNullType
+ redef fun infobox(v)
+ do
+ var res = new HInfoBox(v, to_s)
+ return res
+ end
+ redef fun linkto(v)
+ do
+ var res = new HTMLTag("span")
+ res.append("null")
+ return res
+ end
+end
+
+redef class MSignature
+ redef fun linkto(v)
+ do
+ var res = new HTMLTag("span")
+ var first = true
+ if not mparameters.is_empty then
+ res.append "("
+ for p in mparameters do
+ if first then
+ first = false
+ else
+ res.append ", "
+ end
+ res.append p.name
+ res.append ": "
+ res.add p.mtype.linkto(v)
+ end
+ res.append ")"
+ end
+ var ret = return_mtype
+ if ret != null then
+ res.append ": "
+ res.add ret.linkto(v)
+ end
+ return res
+ end
+end
+
+redef class CallSite
+ redef fun infobox(v)
+ do
+ var res = new HInfoBox(v, "call {mpropdef}")
+ res.href = v.hrefto(mpropdef)
+ res.new_field("call").add(mpropdef.linkto(v)).add(msignature.linkto(v))
+ if mpropdef.is_intro then
+ else
+ res.new_field("intro").add mproperty.intro.linkto_text(v, "in {mproperty.intro.mclassdef}")
+ end
+ add_doc_to_infobox(res)
+
+ return res
+ end
+ redef fun linkto(v)
+ do
+ return mpropdef.linkto(v)
+ end
+end
+
+redef class Variable
+ super HInfoBoxable
+ redef fun infobox(v)
+ do
+ var declared_type = self.declared_type
+ if declared_type == null then
+ var res = new HInfoBox(v, "{name}")
+ res.new_field("local var").append("{name}")
+ return res
+ end
+ var res = new HInfoBox(v, "{name}: {declared_type}")
+ res.new_field("local var").append("{name}:").add(declared_type.linkto(v))
+ return res
+ end
+end
+
+
+##
+
+redef class ANode
+ # Optionally creates a tag that encapsulate the AST element on HTML rendering
+ protected fun make_tag(v: HtmlightVisitor): nullable HTMLTag do return null
+
+ # Add aditionnal information on a child-token and return an additionnal HInfoBox on it
+ protected fun decorate_tag(v: HtmlightVisitor, res: HTMLTag, token: Token): nullable HInfoBox
+ do
+ #debug("no decoration for {token.inspect}")
+ #res.add_class("nc_error")
+ return null
+ end
+
+ # Return a optional infobox
+ fun infobox(v: HtmlightVisitor): nullable HInfoBox do return null
+end
+
+redef class AQclassid
+ redef fun decorate_tag(v, res, token)
+ do
+ if token != n_id then return null
+ var parent = self.parent
+ if parent == null then return null
+ return parent.decorate_tag(v, res, token)
+ end
+end
+
+redef class AQid
+ redef fun decorate_tag(v, res, token)
+ do
+ if token != n_id then return null
+ var parent = self.parent
+ if parent == null then return null
+ return parent.decorate_tag(v, res, token)
+ end
+end
+
+redef class AStdClassdef
+ redef fun make_tag(v)
+ do
+ var res = new HTMLTag("span")
+ res.add_class("nc_cdef")
+ var md = mclassdef
+ if md != null then res.attr("id", md.to_s)
+ return res
+ end
+ redef fun decorate_tag(v, res, token)
+ do
+ if not token isa TClassid then return null
+ res.add_class("nc_def")
+
+ var md = mclassdef
+ if md == null then return null
+ return md.infobox(v)
+ end
+end
+redef class APropdef
+ redef fun make_tag(v)
+ do
+ var res = new HTMLTag("span")
+ res.add_class("nc_pdef")
+ var mpd
+ mpd = mpropdef
+ if mpd != null then
+ #res.add(tag(mpd))
+ res.attr("id", mpd.to_s)
+ end
+ if self isa AAttrPropdef then
+ mpd = mreadpropdef
+ if mpd != null then res.add(tag(mpd))
+ mpd = mwritepropdef
+ if mpd != null then res.add(tag(mpd))
+ end
+ return res
+ end
+
+ private fun tag(mpd: MPropDef): HTMLTag
+ do
+ var a = new HTMLTag("a")
+ a.attr("id", mpd.to_s)
+ return a
+ end
+end
+
+redef class Token
+ # Produce an HTMLTag with the correct contents and CSS classes
+ # Subclasses can redefine it to decorate the tag
+ redef fun make_tag(v): HTMLTag
+ do
+ var res = new HTMLTag("span")
+ res.text(text)
+ return res
+ end
+end
+
+redef class TokenKeyword
+ redef fun make_tag(v)
+ do
+ var res = super
+ res.add_class("nc_k")
+ return res
+ end
+end
+redef class TokenOperator
+ redef fun make_tag(v)
+ do
+ var res = super
+ res.add_class("nc_o")
+ return res
+ end
+end
+
+redef class AVarFormExpr
+ redef fun decorate_tag(v, res, token)
+ do
+ if token != n_id then return null
+ var variable = self.variable
+ if variable == null then return null
+ res.add_class("nc_v")
+ return variable.infobox(v)
+ end
+end
+
+redef class AVardeclExpr
+ redef fun decorate_tag(v, res, token)
+ do
+ if token != n_id then return null
+ var variable = self.variable
+ if variable == null then return null
+ res.add_class("nc_v")
+ return variable.infobox(v)
+ end
+end
+
+redef class AForGroup
+ redef fun decorate_tag(v, res, token)
+ do
+ if not token isa TId then return null
+ var vs = variables
+ if vs == null then return null
+ res.add_class("nc_v")
+ var idx = n_ids.index_of(token)
+ var variable = vs[idx]
+ return variable.infobox(v)
+ end
+end
+
+redef class AParam
+ redef fun decorate_tag(v, res, token)
+ do
+ if token != n_id then return null
+ var mp = mparameter
+ if mp == null then return null
+ var variable = self.variable
+ if variable == null then return null
+ res.add_class("nc_v")
+ return variable.infobox(v)
+ end
+end
+
+redef class AAssertExpr
+ redef fun decorate_tag(v, res, token)
+ do
+ if not token isa TId then return null
+ res.add_class("nc_ast")
+ return null
+ end
+end
+
+redef class ALabel
+ redef fun decorate_tag(v, res, token)
+ do
+ if not token isa TId then return null
+ res.add_class("nc_la")
+ return null
+ end
+end
+
+redef class ASendExpr
+ redef fun decorate_tag(v, res, token)
+ do
+ var callsite = self.callsite
+ if callsite == null then return null
+ return callsite.infobox(v)
+ end
+end
+
+redef class ANewExpr
+ redef fun decorate_tag(v, res, token)
+ do
+ var callsite = self.callsite
+ if callsite == null then return null
+ return callsite.infobox(v)
+ end
+end
+
+redef class AAssignOp
+ redef fun decorate_tag(v, res, token)
+ do
+ var p = parent
+ assert p isa AReassignFormExpr
+
+ var callsite = p.reassign_callsite
+ if callsite == null then return null
+ return callsite.infobox(v)
+ end
+end
+
+redef class AModuleName
+ redef fun decorate_tag(v, res, token)
+ do
+ var p = parent
+ if p == null then return null
+ return p.decorate_tag(v, res, token)
+ end
+end
+
+redef class AModuledecl
+ redef fun decorate_tag(v, res, token)
+ do
+ if not token isa TId then return null
+ res.add_class("nc_def")
+ res.add_class("nc_m")
+ var p = parent
+ assert p isa AModule
+ var mm = p.mmodule
+ if mm == null then return null
+ return mm.infobox(v)
+ end
+end
+
+redef class AStdImport
+ redef fun decorate_tag(v, res, token)
+ do
+ if not token isa TId then return null
+ res.add_class("nc_m")
+ var mm = mmodule
+ if mm == null then return null
+ return mm.infobox(v)
+ end
+end
+redef class AAttrPropdef
+ redef fun decorate_tag(v, res, token)
+ do
+ if not token isa TId then return null
+ res.add_class("nc_def")
+ var mpd: nullable MPropDef
+ mpd = mreadpropdef
+ if mpd == null then mpd = mpropdef
+ if mpd == null then return null
+ return mpd.infobox(v)
+ end
+end
+
+redef class TId
+ redef fun make_tag(v)
+ do
+ var res = super
+ res.add_class("nc_i")
+ return res
+ end
+end
+redef class AMethid
+ redef fun make_tag(v)
+ do
+ var res = new HTMLTag("span")
+ res.add_class("nc_def")
+ return res
+ end
+ redef fun decorate_tag(v, res, token)
+ do
+ return null
+ # nothing to decorate
+ end
+ redef fun infobox(v)
+ do
+ var p = parent
+ if not p isa AMethPropdef then return null
+ var mpd = p.mpropdef
+ if mpd == null then return null
+ return mpd.infobox(v)
+ end
+end
+redef class TAttrid
+ redef fun make_tag(v)
+ do
+ var res = super
+ res.add_class("nc_a")
+ return res
+ end
+end
+redef class AAttrFormExpr
+ redef fun decorate_tag(v, res, token)
+ do
+ if not token isa TAttrid then return null
+ var p = mproperty
+ if p == null then return null
+ return p.intro.infobox(v)
+ end
+end
+redef class TClassid
+ redef fun make_tag(v)
+ do
+ var res = super
+ res.add_class("nc_t")
+ return res
+ end
+end
+redef class AType
+ redef fun decorate_tag(v, res, token)
+ do
+ if not token isa TClassid then return null
+ var mt = mtype
+ if mt == null then return null
+ mt = mt.undecorate
+ if mt isa MFormalType then
+ res.add_class("nc_vt")
+ end
+ return mt.infobox(v)
+ end
+end
+redef class AFormaldef
+ redef fun decorate_tag(v, res, token)
+ do
+ if not token isa TClassid then return null
+ res.add_class("nc_vt")
+ var mtype = self.mtype
+ if mtype == null then return null
+ return mtype.infobox(v)
+ end
+end
+redef class ATypePropdef
+ redef fun decorate_tag(v, res, token)
+ do
+ if not token isa TClassid then return null
+ res.add_class("nc_def")
+ var md = mpropdef
+ if md == null then return null
+ return md.infobox(v)
+ end
+end
+redef class TComment
+ redef fun make_tag(v)
+ do
+ var res = super
+ if is_loose then
+ res.add_class("nc_c")
+ end
+ return res
+ end
+end
+redef class ADoc
+ redef fun make_tag(v)
+ do
+ var res = new HTMLTag("span")
+ res.add_class("nc_d")
+ return res
+ end
+end
+redef class TokenLiteral
+ redef fun make_tag(v)
+ do
+ var res = super
+ res.add_class("nc_l")
+ return res
+ end
+end
+redef class ASuperstringExpr
+ redef fun make_tag(v)
+ do
+ var res = new HTMLTag("span")
+ res.add_class("nc_ss")
+ return res
+ end
+end
+redef class AStringFormExpr
+ redef fun decorate_tag(v, res, token)
+ do
+ # Workaround to tag strings
+ res.classes.remove("nc_l")
+ res.add_class("nc_s")
+ return super
+ end
+end
+redef class AExpr
+ redef fun decorate_tag(v, res, token)
+ do
+ var t = mtype
+ if t == null then return null
+ return t.infobox(v)
+ end
+end
# Tool that produces highlighting for Nit programs
module nitlight
-import highlight
+import htmlight
class NitlightVisitor
- super HighlightVisitor
+ super HtmlightVisitor
# The current highlight module
#
var opt_dir = new OptionString("Output html files in a specific directory (required if more than one module)", "-d", "--dir")
var opt_full = new OptionBool("Process also imported modules", "--full")
var opt_ast = new OptionBool("Generate specific HTML elements for each Node of the AST", "--ast")
-toolcontext.option_context.add_option(opt_fragment, opt_line_id_prefix, opt_first_line, opt_last_line, opt_dir, opt_full)
+var opt_txt = new OptionBool("Generate text with ANSI coloring escape sequences", "--txt")
+toolcontext.option_context.add_option(opt_fragment, opt_line_id_prefix, opt_first_line, opt_last_line, opt_dir, opt_full, opt_ast, opt_txt)
toolcontext.tooldescription = "Usage: nitlight [OPTION]... <file.nit>...\nGenerates HTML of highlited code from Nit source files."
toolcontext.process_options(args)
return
end
+if opt_txt.value then
+ for mm in mmodules do
+ var v = new AnsiHighlightVisitor
+ v.include_loose_tokens = true
+ v.include_whole_lines = true
+
+ if opt_first_line.value != 0 then v.first_line = opt_first_line.value
+ if opt_last_line.value != 0 then v.last_line = opt_last_line.value
+ var m = modelbuilder.mmodule2node(mm)
+ assert m != null
+
+ v.highlight_node(m)
+ var page = v.result
+
+ if dir != null then
+ page.write_to_file("{dir}/{mm.c_name}.txt")
+ else
+ page.write_to(stdout)
+ end
+ end
+ return
+end
+
for mm in mmodules do
if dir != null then toolcontext.info("write {dir}/{mm.c_name}.html", 1)
page.add_raw_html v.head_content
page.add_raw_html "</head><body><pre class='nit_code'>"
end
- v.enter_visit(m)
+ v.highlight_node(m)
if not opt_fragment.value then
page.add(v.html)
page.add_raw_html "</pre>"
page.add_raw_html "</li></body>"
page.write_to_file("{dir}/index.html")
- var v = new HighlightVisitor
+ var v = new HtmlightVisitor
toolcontext.info("write {dir}/style.css", 1)
var f = new FileWriter.open("{dir}/style.css")
f.write v.css_content
# ---
var app_name = project.name
- if not release then app_name += " Debug"
-
var app_package = project.namespace
- if not release then app_package += "_debug"
-
var app_version = project.version
var app_min_api = project.min_api
file(MAKE_DIRECTORY ${lib_build_DIR})
## Config
-add_definitions("-DGC_PTHREADS")
+add_definitions("-DALL_INTERIOR_POINTERS -DGC_THREADS -DUSE_MMAP -DUSE_MUNMAP -DJAVA_FINALIZATION -DNO_EXECUTE_PERMISSION -DGC_DONT_REGISTER_MAIN_STATIC_DATA")
set(enable_threads TRUE)
set(CMAKE_USE_PTHREADS_INIT TRUE)
# Generate AndroidManifest.xml
# Is there an icon?
- var resolutions = ["ldpi", "mdpi", "hdpi", "xhdpi", "xxhdpi", "xxxhdpi"]
- var icon_available = false
+ var resolutions = ["ldpi", "mdpi", "hdpi", "xhdpi", "xxhdpi", "xxxhdpi", "anydpi", "anydpi-v26"]
+ var icon_name = null
+ var has_round = false
+
for res in resolutions do
- var path = project_root / "android/res/drawable-{res}/icon.png"
- if path.file_exists then
- icon_available = true
+ # New style mipmap
+ if "{project_root}/android/res/mipmap-{res}/ic_launcher_round.png".file_exists then
+ has_round = true
+ end
+ if "{project_root}/android/res/mipmap-{res}/ic_launcher.png".file_exists then
+ icon_name = "@mipmap/ic_launcher"
+ break
+ end
+ if "{project_root}/android/res/mipmap-{res}/ic_launcher.xml".file_exists then
+ icon_name = "@mipmap/ic_launcher"
break
end
end
+ if icon_name == null then
+ # Old style drawable-hdpi/icon.png
+ for res in resolutions do
+ var path = project_root / "android/res/drawable-{res}/icon.png"
+ if path.file_exists then
+ icon_name = "@drawable/icon"
+ break
+ end
+ end
+ end
var icon_declaration
- if icon_available then
- icon_declaration = "android:icon=\"@drawable/icon\""
+ if icon_name != null then
+ icon_declaration = "android:icon=\"{icon_name}\""
+ if app_target_api >= 25 and has_round then
+ icon_declaration += "\n\t\tandroid:roundIcon=\"@mipmap/ic_launcher_round\""
+ end
else icon_declaration = ""
# TODO android:roundIcon
# Program used to test the Nit highlighter
module test_highlight
-import highlight
+import htmlight
import test_phase
class TestHighlightVisitor
- super HighlightVisitor
+ super HtmlightVisitor
redef fun hrefto(e) do
return "#" + e.c_name
end
var n = mb.mpropdef2node(pd)
if not n isa APropdef then continue
var hl = new TestHighlightVisitor
- hl.enter_visit(n)
+ hl.highlight_node(n)
print "<h1 id=\"{pd.c_name}\">{pd.full_name}</h1>"
printn "<pre><code>"
hl.html.write_to(stdout)
seen.add cn
var hl = new TestHighlightVisitor
- hl.enter_visit(n)
+ hl.highlight_node(n)
print "<h2>AST node: {cn} at {n.location}</h2>"
printn "<pre><code>"
hl.html.write_to(stdout)
private fun render_source(mentity: MEntity, modelbuilder: ModelBuilder): nullable HTMLTag do
var node = modelbuilder.mentity2node(mentity)
if node == null then return null
- var hl = new HighlightVisitor
- hl.enter_visit node
+ var hl = new HtmlightVisitor
+ hl.highlight_node node
return hl.html
end
end
module api_light
import web_base
-import highlight
+import htmlight
redef class APIRouter
redef init do
super APIHandler
redef fun post(req, res) do
- var hl = new HighlightVisitor
- var hlcode = hightlightcode(hl, req.body)
+ var hl = new HtmlightVisitor
+ var hlcode = hl.highlightcode(req.body)
res.json(hlcode)
end
end
module api_model
import web_base
-import highlight
+import htmlight
import uml
import model::model_index
private fun render_source(mentity: MEntity): nullable HTMLTag do
var node = config.modelbuilder.mentity2node(mentity)
if node == null then return null
- var hl = new HighlightVisitor
- hl.enter_visit node
+ var hl = new HtmlightVisitor
+ hl.highlight_node node
return hl.html
end
end
-f base_simple3.nit
base_simple3.nit
-f --line-id-prefix XYZ --first-line 38 --last-line 46 base_simple3.nit
+--txt base_simple3.nit
--- /dev/null
+\e[34m# This file is part of NIT ( http://www.nitlanguage.org ).
+\e[m\e[34m#
+\e[m\e[34m# Copyright 2006-2008 Jean Privat <jean@pryen.org>
+\e[m\e[34m#
+\e[m\e[34m# Licensed under the Apache License, Version 2.0 (the "License");
+\e[m\e[34m# you may not use this file except in compliance with the License.
+\e[m\e[34m# You may obtain a copy of the License at
+\e[m\e[34m#
+\e[m\e[34m# http://www.apache.org/licenses/LICENSE-2.0
+\e[m\e[34m#
+\e[m\e[34m# Unless required by applicable law or agreed to in writing, software
+\e[m\e[34m# distributed under the License is distributed on an "AS IS" BASIS,
+\e[m\e[34m# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+\e[m\e[34m# See the License for the specific language governing permissions and
+\e[m\e[34m# limitations under the License.
+\e[m
+\e[33mimport\e[m \e[33mend\e[m
+
+\e[33minterface\e[m \e[32mObject\e[m
+\e[33mend\e[m
+
+\e[33menum\e[m \e[32mBool\e[m
+\e[33mend\e[m
+
+\e[33menum\e[m \e[32mInt\e[m
+ \e[33mfun\e[m output \e[33mis\e[m intern
+\e[33mend\e[m
+
+\e[33mclass\e[m \e[32mA\e[m
+ \e[33minit\e[m \e[33mdo\e[m \e[31m5\e[m.output
+ \e[33mfun\e[m run \e[33mdo\e[m \e[31m6\e[m.output
+\e[33mend\e[m
+
+\e[33mclass\e[m \e[32mB\e[m
+ \e[33mvar\e[m val: \e[32mInt\e[m
+ \e[33minit\e[m(v: \e[32mInt\e[m)
+ \e[33mdo\e[m
+ \e[31m7\e[m.output
+ \e[33mself\e[m.val = v
+ \e[33mend\e[m
+ \e[33mfun\e[m run \e[33mdo\e[m val.output
+\e[33mend\e[m
+
+\e[33mclass\e[m \e[32mC\e[m
+ \e[33mvar\e[m val1: \e[32mInt\e[m
+ \e[33mvar\e[m val2: \e[32mInt\e[m = \e[31m10\e[m
+\e[33mend\e[m
+
+\e[33mfun\e[m foo \e[33mdo\e[m \e[31m2\e[m.output
+\e[33mfun\e[m bar(i: \e[32mInt\e[m) \e[33mdo\e[m i.output
+\e[33mfun\e[m baz: \e[32mInt\e[m \e[33mdo\e[m \e[33mreturn\e[m \e[31m4\e[m
+
+\e[31m1\e[m.output
+foo
+bar(\e[31m3\e[m)
+baz.output
+
+\e[33mvar\e[m a = \e[33mnew\e[m \e[32mA\e[m
+a.run
+
+\e[33mvar\e[m b = \e[33mnew\e[m \e[32mB\e[m(\e[31m8\e[m)
+b.run
+
+\e[33mvar\e[m c = \e[33mnew\e[m \e[32mC\e[m(\e[31m9\e[m)
+c.val1.output
+c.val2.output
Initialisation de la JVM ...
---------------------Test 1----------------------
From java, pushing premier
-From java, pushing deuxi?me
-From java, pushing troisi?me
+From java, pushing deuxième
+From java, pushing troisième
From java, popping premier
premier
-From java, popping deuxi?me
+From java, popping deuxième
deuxième
-From java, popping troisi?me
+From java, popping troisième
troisième
--------------------Test 2---------------------
true
# This shell script compile, run and verify Nit program files
# Set lang do default to avoid failed tests because of locale
-export LANG=C
-export LC_ALL=C
+export LANG=C.UTF-8
+export LC_ALL=C.UTF-8
export NIT_TESTING=true
# Use the pid as a collision prevention
export NIT_TESTING_ID=$$
if [ -f "$ff.write" ]; then
cat -- "$ff.write" >> "$ff.res"
elif [ -d "$ff.write" ]; then
- LANG=C /bin/ls -F "$ff.write" >> "$ff.res"
+ /bin/ls -F "$ff.write" >> "$ff.res"
fi
cp -- "$ff.res" "$ff.res2"
cat -- "$ff.cmp.err" "$ff.err" "$ff.res2" > "$ff.res"
if [ -f "$fff.write" ]; then
cat -- "$fff.write" >> "$fff.res"
elif [ -d "$fff.write" ]; then
- LANG=C /bin/ls -F -- "$fff.write" >> "$fff.res"
+ /bin/ls -F -- "$fff.write" >> "$fff.res"
fi
if [ -s "$fff.err" ]; then
cp -- "$fff.res" "$fff.res2"